chromium/ash/quick_pair/pairing/pairer_broker_impl_unittest.cc

// Copyright 2022 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/pairing/pairer_broker_impl.h"
#include "ash/constants/ash_features.h"
#include "ash/quick_pair/common/account_key_failure.h"
#include "ash/quick_pair/common/device.h"
#include "ash/quick_pair/common/fake_bluetooth_adapter.h"
#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
#include "ash/quick_pair/common/pair_failure.h"
#include "ash/quick_pair/common/protocol.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_data_encryptor.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake_lookup.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor_impl.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_handshake_lookup.h"
#include "ash/quick_pair/pairing/fast_pair/fast_pair_pairer.h"
#include "ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

constexpr char kValidModelId[] = "718c17";
constexpr char kTestDeviceAddress[] = "test_address";
constexpr char kTestDeviceAddress2[] = "test_address_2";
constexpr char kDeviceName[] = "test_device_name";
constexpr char kBluetoothCanonicalizedAddress[] = "0C:0E:4C:C8:05:08";
constexpr base::TimeDelta kCancelPairingRetryDelay = base::Seconds(1);

const char kFastPairRetryCountMetricName[] =
    "Bluetooth.ChromeOS.FastPair.PairRetry.Count";
constexpr char kInitializePairingProcessInitial[] =
    "FastPair.InitialPairing.Initialization";
constexpr char kInitializePairingProcessSubsequent[] =
    "FastPair.SubsequentPairing.Initialization";
constexpr char kInitializePairingProcessRetroactive[] =
    "FastPair.RetroactivePairing.Initialization";
constexpr char kInitializePairingProcessFailureReasonInitial[] =
    "FastPair.InitialPairing.Initialization.FailureReason";
constexpr char kInitializePairingProcessFailureReasonSubsequent[] =
    "FastPair.SubsequentPairing.Initialization.FailureReason";
constexpr char kInitializePairingProcessFailureReasonRetroactive[] =
    "FastPair.RetroactivePairing.Initialization.FailureReason";

constexpr char kProtocolPairingStepInitial[] =
    "FastPair.InitialPairing.Pairing";
constexpr char kProtocolPairingStepSubsequent[] =
    "FastPair.SubsequentPairing.Pairing";
const char kHandshakeEffectiveSuccessRate[] =
    "FastPair.Handshake.EffectiveSuccessRate";
const char kHandshakeAttemptCount[] = "FastPair.Handshake.AttemptCount";

class FakeFastPairPairer : public ash::quick_pair::FastPairPairer {
 public:
  FakeFastPairPairer(
      scoped_refptr<device::BluetoothAdapter> adapter,
      scoped_refptr<ash::quick_pair::Device> device,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
          paired_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                              ash::quick_pair::PairFailure)>
          pair_failed_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                              ash::quick_pair::AccountKeyFailure)>
          account_key_failure_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
          pairing_procedure_complete)
      : adapter_(adapter),
        device_(device),
        paired_callback_(std::move(paired_callback)),
        pair_failed_callback_(std::move(pair_failed_callback)),
        account_key_failure_callback_(std::move(account_key_failure_callback)),
        pairing_procedure_complete_(std::move(pairing_procedure_complete)) {}

  ~FakeFastPairPairer() override = default;

  void TriggerPairedCallback() {
    EXPECT_TRUE(paired_callback_);
    std::move(paired_callback_).Run(device_);
  }

  void TriggerPairingProcedureCompleteCallback() {
    EXPECT_TRUE(pairing_procedure_complete_);
    std::move(pairing_procedure_complete_).Run(device_);
  }

  void TriggerAccountKeyFailureCallback(
      ash::quick_pair::AccountKeyFailure failure) {
    EXPECT_TRUE(account_key_failure_callback_);
    std::move(account_key_failure_callback_).Run(device_, failure);
  }

  void TriggerPairFailureCallback(ash::quick_pair::PairFailure failure) {
    EXPECT_TRUE(pair_failed_callback_);
    std::move(pair_failed_callback_).Run(device_, failure);
  }

 private:
  scoped_refptr<device::BluetoothAdapter> adapter_;
  scoped_refptr<ash::quick_pair::Device> device_;
  base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
      paired_callback_;
  base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                          ash::quick_pair::PairFailure)>
      pair_failed_callback_;
  base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                          ash::quick_pair::AccountKeyFailure)>
      account_key_failure_callback_;
  base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
      pairing_procedure_complete_;
};

class FakeFastPairPairerFactory
    : public ash::quick_pair::FastPairPairerImpl::Factory {
 public:
  std::unique_ptr<ash::quick_pair::FastPairPairer> CreateInstance(
      scoped_refptr<device::BluetoothAdapter> adapter,
      scoped_refptr<ash::quick_pair::Device> device,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
          paired_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                              ash::quick_pair::PairFailure)>
          pair_failed_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>,
                              ash::quick_pair::AccountKeyFailure)>
          account_key_failure_callback,
      base::OnceCallback<void(scoped_refptr<ash::quick_pair::Device>)>
          pairing_procedure_complete) override {
    auto fake_fast_pair_pairer = std::make_unique<FakeFastPairPairer>(
        std::move(adapter), std::move(device), std::move(paired_callback),
        std::move(pair_failed_callback),
        std::move(account_key_failure_callback),
        std::move(pairing_procedure_complete));
    fake_fast_pair_pairer_ = fake_fast_pair_pairer.get();
    return fake_fast_pair_pairer;
  }

  ~FakeFastPairPairerFactory() override = default;

  FakeFastPairPairer* fake_fast_pair_pairer() { return fake_fast_pair_pairer_; }

 protected:
  raw_ptr<FakeFastPairPairer, DanglingUntriaged> fake_fast_pair_pairer_ =
      nullptr;
};

class FakeFastPairGattServiceClientImplFactory
    : public ash::quick_pair::FastPairGattServiceClientImpl::Factory {
 public:
  ~FakeFastPairGattServiceClientImplFactory() override = default;

  ash::quick_pair::FakeFastPairGattServiceClient*
  fake_fast_pair_gatt_service_client() {
    return fake_fast_pair_gatt_service_client_;
  }

 private:
  // FastPairGattServiceClientImpl::Factory:
  std::unique_ptr<ash::quick_pair::FastPairGattServiceClient> CreateInstance(
      device::BluetoothDevice* device,
      scoped_refptr<device::BluetoothAdapter> adapter,
      base::OnceCallback<void(std::optional<ash::quick_pair::PairFailure>)>
          on_initialized_callback) override {
    auto fake_fast_pair_gatt_service_client =
        std::make_unique<ash::quick_pair::FakeFastPairGattServiceClient>(
            device, adapter, std::move(on_initialized_callback));
    fake_fast_pair_gatt_service_client_ =
        fake_fast_pair_gatt_service_client.get();
    return fake_fast_pair_gatt_service_client;
  }

  raw_ptr<ash::quick_pair::FakeFastPairGattServiceClient, DanglingUntriaged>
      fake_fast_pair_gatt_service_client_ = nullptr;
};

}  // namespace

namespace ash {
namespace quick_pair {

class PairerBrokerImplTest : public AshTestBase, public PairerBroker::Observer {
 public:
  PairerBrokerImplTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

  void SetUp() override {
    AshTestBase::SetUp();

    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();

    device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);

    fast_pair_pairer_factory_ = std::make_unique<FakeFastPairPairerFactory>();
    FastPairPairerImpl::Factory::SetFactoryForTesting(
        fast_pair_pairer_factory_.get());

    FastPairGattServiceClientImpl::Factory::SetFactoryForTesting(
        &fast_pair_gatt_service_factory_);
    FastPairHandshakeLookup::UseFakeInstance();
    pairer_broker_ = std::make_unique<PairerBrokerImpl>();
    pairer_broker_->AddObserver(this);

    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        mock_bluetooth_device_ptr_, adapter_.get(), base::DoNothing());

    // We have to pass in a unique_ptr when we create a Handshake, however
    // we also want to be able to set fake responses on the encryptor. Thus
    // we maintain 2 pointers. We won't touch fake_fast_pair_data_encryptor_
    // aside from CreateHandshake.
    fake_fast_pair_data_encryptor_ =
        std::make_unique<FakeFastPairDataEncryptor>();
  }

  void CreateMockDevice(DeviceFastPairVersion version, Protocol protocol) {
    device_ = base::MakeRefCounted<Device>(kValidModelId, kTestDeviceAddress,
                                           protocol);
    device_->set_classic_address(kBluetoothCanonicalizedAddress);

    device_->set_version(version);

    // Add a matching mock device to the bluetooth adapter with the
    // same address to mock the relationship between Device and
    // device::BluetoothDevice.
    mock_bluetooth_device_ =
        std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
            adapter_.get(), /*bluetooth_class=*/0, kDeviceName,
            kBluetoothCanonicalizedAddress,
            /*initially_paired=*/true, /*connected=*/false);
    mock_bluetooth_device_ptr_ = mock_bluetooth_device_.get();
    adapter_->AddMockDevice(std::move(mock_bluetooth_device_));
  }

  void EraseHandshake() {
    FastPairHandshakeLookup::GetInstance()->Erase(device_);
  }

  void InvokeHandshakeLookupCallbackSuccess() {
    FakeFastPairHandshakeLookup::GetFakeInstance()->InvokeCallbackForTesting(
        device_, std::nullopt);
  }

  void InvokeHandshakeLookupCallbackFailure(PairFailure failure) {
    FakeFastPairHandshakeLookup::GetFakeInstance()->InvokeCallbackForTesting(
        device_, failure);
  }

  void ExpectHandshakeExistsForDevice(scoped_refptr<Device> device) {
    auto* handshake = FastPairHandshakeLookup::GetInstance()->Get(device);
    EXPECT_TRUE(handshake);
  }

  void ExpectBleRotatedForDevice(scoped_refptr<Device> device) {
    auto* handshake = FastPairHandshakeLookup::GetInstance()->Get(device);
    EXPECT_TRUE(handshake->DidBleAddressRotate());
  }

  void TearDown() override {
    pairer_broker_->RemoveObserver(this);
    pairer_broker_.reset();
    AshTestBase::TearDown();
  }

  void OnDevicePaired(scoped_refptr<Device> device) override {
    ++device_paired_count_;
  }

  void OnPairFailure(scoped_refptr<Device> device,
                     PairFailure failure) override {
    ++pair_failure_count_;
  }

  void OnAccountKeyWrite(scoped_refptr<Device> device,
                         std::optional<AccountKeyFailure> error) override {
    ++account_key_write_count_;
  }

  void OnPairingStart(scoped_refptr<Device> device) override {
    pairing_started_ = true;
  }

  void OnHandshakeComplete(scoped_refptr<Device> device) override {
    handshake_complete_ = true;
  }

  void OnPairingComplete(scoped_refptr<Device> device) override {
    device_pair_complete_ = true;
  }

  void PairerSetCurrentBleAddress(std::string ble_address) {
    pairer_broker_
        ->model_id_to_current_ble_address_map_[device_->metadata_id()] =
        ble_address;
  }

  void PairerCreateHandshake(scoped_refptr<Device> device) {
    pairer_broker_->CreateHandshake(device);
  }

  void PairerOnBleAddressRotation(scoped_refptr<Device> device) {
    pairer_broker_->OnBleAddressRotation(device);
  }

 protected:
  int device_paired_count_ = 0;
  int pair_failure_count_ = 0;
  int account_key_write_count_ = 0;
  bool pairing_started_ = false;
  bool handshake_complete_ = false;
  bool device_pair_complete_ = false;

  base::HistogramTester histogram_tester_;
  scoped_refptr<FakeBluetoothAdapter> adapter_;
  raw_ptr<device::MockBluetoothDevice> mock_bluetooth_device_ptr_ = nullptr;
  std::unique_ptr<FakeFastPairPairerFactory> fast_pair_pairer_factory_;

  std::unique_ptr<PairerBrokerImpl> pairer_broker_;
  raw_ptr<FakeFastPairHandshake, DanglingUntriaged> fake_fast_pair_handshake_ =
      nullptr;
  std::unique_ptr<FastPairGattServiceClient> gatt_service_client_;
  FakeFastPairGattServiceClientImplFactory fast_pair_gatt_service_factory_;
  std::unique_ptr<FakeFastPairDataEncryptor> fake_fast_pair_data_encryptor_;
  std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>>
      mock_bluetooth_device_ = nullptr;
  scoped_refptr<Device> device_;
};

TEST_F(PairerBrokerImplTest, PairV1Device_Initial) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);

  CreateMockDevice(DeviceFastPairVersion::kV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();

  EXPECT_EQ(account_key_write_count_, 0);
}

TEST_F(PairerBrokerImplTest, PairV2Device_Initial) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(account_key_write_count_, 1);
}

TEST_F(PairerBrokerImplTest, PairDevice_Subsequent) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairing_started_);
  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_TRUE(device_pair_complete_);
}

TEST_F(PairerBrokerImplTest, Ble_Address_Matches_Create_Handshake) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures({ash::features::kFastPairBleRotation}, {});

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);

  // Populate the ble_address map with the correct address.
  PairerSetCurrentBleAddress(device_->ble_address());
  PairerCreateHandshake(device_);
  ExpectHandshakeExistsForDevice(device_);
}

TEST_F(PairerBrokerImplTest, Ble_Address_Mismatch_No_Handshake) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairBleRotation};
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);

  // Populate the ble_address map with a different address than the current BLE
  // address.
  PairerSetCurrentBleAddress(kTestDeviceAddress2);
  PairerCreateHandshake(device_);
  EXPECT_EQ(fake_fast_pair_handshake_, nullptr);
}

TEST_F(PairerBrokerImplTest, Ble_Address_Mismatch_Set_Callback) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairBleRotation};
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  // Populate the ble_address map with a different address than the current BLE
  // address.
  PairerSetCurrentBleAddress(device_->ble_address());
  PairerCreateHandshake(device_);

  scoped_refptr<Device> device_after_ble_rotation_ =
      base::MakeRefCounted<Device>(kValidModelId, kTestDeviceAddress2,
                                   Protocol::kFastPairRetroactive);
  device_after_ble_rotation_->set_classic_address(
      kBluetoothCanonicalizedAddress);
  device_after_ble_rotation_->set_version(DeviceFastPairVersion::kHigherThanV1);

  pairer_broker_->PairDevice(device_after_ble_rotation_);

  ExpectBleRotatedForDevice(device_);
}

TEST_F(PairerBrokerImplTest, OnBleAddressRotation_Pairs_Successfully) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures({ash::features::kFastPairBleRotation}, {});

  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);

  // Populate the ble_address map with the correct address.
  PairerSetCurrentBleAddress(device_->ble_address());

  // Call PairerOnBleAddressRotation, this is analogous to the function being
  // called as a callback from the pairer.
  PairerOnBleAddressRotation(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());
}

TEST_F(PairerBrokerImplTest, PairDevice_Retroactive) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());
}

TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Initial) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();
  pairer_broker_->PairDevice(device_);
  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessInitial,
                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
            1);
}

TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Subsequent) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  pairer_broker_->PairDevice(device_);

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessSubsequent,
                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
            1);
}

TEST_F(PairerBrokerImplTest, AlreadyPairingDevice_Retroactive) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  pairer_broker_->PairDevice(device_);

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessRetroactive,
                FastPairInitializePairingProcessEvent::kAlreadyPairingFailure),
            1);
}

TEST_F(PairerBrokerImplTest, PairAfterCancelPairing) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);

  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();
  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_CALL(*mock_bluetooth_device_ptr_, IsPaired())
      .WillOnce(testing::Return(false));

  // Attempt to pair with a failure.
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);

  // Fast forward |kCancelPairingDelay| seconds to allow the retry callback to
  // be called.
  task_environment()->FastForwardBy(kCancelPairingRetryDelay);

  // Now allow the pairing to succeed.
  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);
}

TEST_F(PairerBrokerImplTest, PairDeviceFailureMax_Initial) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(pair_failure_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  histogram_tester_.ExpectTotalCount(kProtocolPairingStepInitial, 1);
}

TEST_F(PairerBrokerImplTest, PairDeviceFailureMax_Subsequent) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(pair_failure_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  histogram_tester_.ExpectTotalCount(kProtocolPairingStepSubsequent, 1);
}

TEST_F(PairerBrokerImplTest, PairDeviceFailureMax_Retroactive) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);
  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairFailureCallback(
          PairFailure::kPasskeyCharacteristicNotifySession);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(pair_failure_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
}

TEST_F(PairerBrokerImplTest, AccountKeyFailure_Initial) {
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerAccountKeyFailureCallback(
          AccountKeyFailure::kAccountKeyCharacteristicDiscovery);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(account_key_write_count_, 1);
}

TEST_F(PairerBrokerImplTest, AccountKeyFailure_Subsequent) {
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();
  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerAccountKeyFailureCallback(
          AccountKeyFailure::kAccountKeyCharacteristicDiscovery);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(account_key_write_count_, 1);
}

TEST_F(PairerBrokerImplTest, AccountKeyFailure_Retroactive) {
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerAccountKeyFailureCallback(
          AccountKeyFailure::kAccountKeyCharacteristicDiscovery);

  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(account_key_write_count_, 1);
}

TEST_F(PairerBrokerImplTest, StopPairing) {
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  // Stop Pairing mid pair.
  pairer_broker_->StopPairing();
  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(pair_failure_count_, 0);

  // Stop Pairing when we are not pairing should cause no issues.
  pairer_broker_->StopPairing();
  EXPECT_FALSE(pairer_broker_->IsPairing());
}

TEST_F(PairerBrokerImplTest, ReuseHandshake_Initial) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);

  FakeFastPairHandshakeLookup::GetFakeInstance()->CreateForTesting(
      adapter_, device_, base::DoNothing(), nullptr, nullptr);

  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessInitial,
                FastPairInitializePairingProcessEvent::kHandshakeReused),
            1);
}

TEST_F(PairerBrokerImplTest, ReuseHandshake_Subsequent) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);

  FakeFastPairHandshakeLookup::GetFakeInstance()->CreateForTesting(
      adapter_, device_, base::DoNothing(), nullptr, nullptr);

  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairing_started_);
  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());
  EXPECT_TRUE(device_pair_complete_);

  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessSubsequent,
                FastPairInitializePairingProcessEvent::kHandshakeReused),
            1);
}

TEST_F(PairerBrokerImplTest, ReuseHandshake_Retroactive) {
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 0);
  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);

  FakeFastPairHandshakeLookup::GetFakeInstance()->CreateForTesting(
      adapter_, device_, base::DoNothing(), nullptr, nullptr);

  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackSuccess();

  EXPECT_TRUE(pairer_broker_->IsPairing());

  fast_pair_pairer_factory_->fake_fast_pair_pairer()->TriggerPairedCallback();

  EXPECT_TRUE(pairer_broker_->IsPairing());
  EXPECT_EQ(device_paired_count_, 1);
  histogram_tester_.ExpectTotalCount(kFastPairRetryCountMetricName, 1);

  fast_pair_pairer_factory_->fake_fast_pair_pairer()
      ->TriggerPairingProcedureCompleteCallback();
  EXPECT_FALSE(pairer_broker_->IsPairing());

  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessRetroactive,
                FastPairInitializePairingProcessEvent::kHandshakeReused),
            1);
}

TEST_F(PairerBrokerImplTest, NoPairingIfHandshakeFailed_Initial) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairHandshakeLongTermRefactor};
  histogram_tester_.ExpectTotalCount(kHandshakeEffectiveSuccessRate, 0);
  histogram_tester_.ExpectTotalCount(kHandshakeAttemptCount, 0);

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairInitial);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackFailure(PairFailure::kCreateGattConnection);

  EXPECT_EQ(device_paired_count_, 0);
  EXPECT_EQ(pair_failure_count_, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessFailureReasonInitial,
                PairFailure::kCreateGattConnection),
            1);
}

TEST_F(PairerBrokerImplTest, NoPairingIfHandshakeFailed_Subsequent) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairHandshakeLongTermRefactor};
  histogram_tester_.ExpectTotalCount(kHandshakeEffectiveSuccessRate, 0);
  histogram_tester_.ExpectTotalCount(kHandshakeAttemptCount, 0);

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairSubsequent);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackFailure(PairFailure::kCreateGattConnection);

  EXPECT_EQ(device_paired_count_, 0);
  EXPECT_EQ(pair_failure_count_, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessFailureReasonSubsequent,
                PairFailure::kCreateGattConnection),
            1);
}

TEST_F(PairerBrokerImplTest, NoPairingIfHandshakeFailed_Retroactive) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairHandshakeLongTermRefactor};
  histogram_tester_.ExpectTotalCount(kHandshakeEffectiveSuccessRate, 0);
  histogram_tester_.ExpectTotalCount(kHandshakeAttemptCount, 0);

  CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
                   /*protocol=*/Protocol::kFastPairRetroactive);
  pairer_broker_->PairDevice(device_);
  InvokeHandshakeLookupCallbackFailure(PairFailure::kCreateGattConnection);

  EXPECT_EQ(device_paired_count_, 0);
  EXPECT_EQ(pair_failure_count_, 1);
  EXPECT_EQ(histogram_tester_.GetBucketCount(
                kInitializePairingProcessFailureReasonRetroactive,
                PairFailure::kCreateGattConnection),
            1);
}

}  // namespace quick_pair
}  // namespace ash