chromium/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl_unittest.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/fast_pair_handshake/fast_pair_gatt_service_client_impl.h"

#include <stddef.h>

#include <optional>

#include "ash/constants/ash_features.h"
#include "ash/quick_pair/common/constants.h"
#include "ash/quick_pair/common/fake_bluetooth_adapter.h"
#include "ash/quick_pair/common/logging.h"
#include "ash/quick_pair/common/pair_failure.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_data_encryptor.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 "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/include/openssl/rand.h"

namespace {

using NotifySessionCallback = base::OnceCallback<void(
    std::unique_ptr<device::BluetoothGattNotifySession>)>;
using ErrorCallback =
    base::OnceCallback<void(device::BluetoothGattService::GattErrorCode)>;

const char kTotalGattConnectionTime[] =
    "Bluetooth.ChromeOS.FastPair.TotalGattConnectionTime";
const char kGattConnectionResult[] =
    "Bluetooth.ChromeOS.FastPair.GattConnection.Result";
const char kGattConnectionErrorMetric[] =
    "Bluetooth.ChromeOS.FastPair.GattConnection.ErrorReason";
const char kGattConnectionEffectiveSuccessRate[] =
    "Bluetooth.ChromeOS.FastPair.GattConnection.EffectiveSuccessRate";
const char kGattConnectionAttemptCount[] =
    "Bluetooth.ChromeOS.FastPair.GattConnection.AttemptCount";
const char kWriteKeyBasedCharacteristicGattError[] =
    "Bluetooth.ChromeOS.FastPair.KeyBasedPairing.Write.GattErrorReason";
const char kNotifyKeyBasedCharacteristicTime[] =
    "Bluetooth.ChromeOS.FastPair.KeyBasedPairing.NotifyTime";
const char kWritePasskeyCharacteristicGattError[] =
    "Bluetooth.ChromeOS.FastPair.Passkey.Write.GattErrorReason";
const char kNotifyPasskeyCharacteristicTime[] =
    "Bluetooth.ChromeOS.FastPair.Passkey.NotifyTime";
const char kWriteAccountKeyCharacteristicGattError[] =
    "Bluetooth.ChromeOS.FastPair.AccountKey.Write.GattErrorReason";
const char kWriteAccountKeyTimeMetric[] =
    "Bluetooth.ChromeOS.FastPair.AccountKey.Write.TotalTime";
const char kFastPairGattConnectionStep[] = "FastPair.GattConnection";
const char kFastPairGattRetryFailureReason[] =
    "Bluetooth.ChromeOS.FastPair.GattConnection.RetryFailureReason";
const char kGattServiceDiscoveryTime[] =
    "FastPair.GattServiceDiscovery.Latency";
const char kPasskeyNotify[] = "FastPair.PasskeyNotify.Latency";
const char kKeyBasedNotify[] = "FastPair.KeyBasedNotify.Latency";
const char kPasskeyWriteRequest[] = "FastPair.PasskeyWriteRequest.Latency";
const char kKeyBasedWriteRequest[] = "FastPair.KeyBasedWriteRequest.Latency";
/*
 */

constexpr base::TimeDelta kConnectingTestTimeout = base::Seconds(15);
constexpr base::TimeDelta kSimulateStackFrameHangSeconds = base::Seconds(90);
constexpr base::TimeDelta kCoolOffPeriodBeforeGattConnectionAfterDisconnect =
    base::Seconds(2);
constexpr base::TimeDelta kDisconnectResponseTimeout = base::Seconds(5);

// 51 seconds is from 2 seconds for a cooloff period before GATT connection
// attempts * 3 GATT attempts + 15 seconds GATT connecting timeout * 3 GATT
// attempts.
constexpr base::TimeDelta kAllGattRetriesPeriod = base::Seconds(51);

// Below constants are used to construct MockBluetoothDevice for testing.
constexpr char kTestBleDeviceAddress[] = "11:12:13:14:15:16";
const char kTestServiceId[] = "service_id1";
const std::string kUUIDString1 = "keybased";
const std::string kUUIDString2 = "passkey";
const std::string kUUIDString3 = "accountkey";
const std::string kUUIDString4 = "additional data";
const std::string kUUIDString5 = "model id";
const device::BluetoothUUID kNonFastPairUuid("0xFE2B");

const device::BluetoothUUID kModelIDCharacteristicUuid1("1233");
const device::BluetoothUUID kModelIDCharacteristicUuid2(
    "FE2C1233-8366-4814-8EB0-01DE32100BEA");
const device::BluetoothUUID kKeyBasedCharacteristicUuid1("1234");
const device::BluetoothUUID kKeyBasedCharacteristicUuid2(
    "FE2C1234-8366-4814-8EB0-01DE32100BEA");
const device::BluetoothUUID kPasskeyCharacteristicUuid1("1235");
const device::BluetoothUUID kPasskeyCharacteristicUuid2(
    "FE2C1235-8366-4814-8EB0-01DE32100BEA");
const device::BluetoothUUID kAccountKeyCharacteristicUuid1("1236");
const device::BluetoothUUID kAccountKeyCharacteristicUuid2(
    "FE2C1236-8366-4814-8EB0-01DE32100BEA");
const device::BluetoothUUID kAdditionalDataCharacteristicUuid1("1237");
const device::BluetoothUUID kAdditionalDataCharacteristicUuid2(
    "FE2C1237-8366-4814-8EB0-01DE32100BEA");

const uint8_t kMessageType = 0x00;
const uint8_t kFlags = 0x00;
const std::string kProviderAddress = "abcde";
const std::string kSeekersAddress = "abcde";
const uint8_t kSeekerPasskey = 0x02;
const uint32_t kPasskey = 13;
const std::array<uint8_t, 16> kAccountKey = {0x04, 0x01, 0x01, 0x01, 0x01, 0x01,
                                             0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
                                             0x01, 0x01, 0x01, 0x01};

const std::array<uint8_t, 64> kPublicKey = {
    0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F,
    0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3,
    0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01,
    0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45,
    0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32,
    0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};

const std::string kPersonalizedName = "Brando's Fake Device";

const device::BluetoothRemoteGattCharacteristic::Properties kProperties =
    device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
    device::BluetoothRemoteGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE |
    device::BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE;

const device::BluetoothRemoteGattCharacteristic::Permissions kPermissions =
    device::BluetoothRemoteGattCharacteristic::PERMISSION_READ_ENCRYPTED |
    device::BluetoothRemoteGattCharacteristic::PERMISSION_WRITE_ENCRYPTED;

class FakeBluetoothDevice
    : public testing::NiceMock<device::MockBluetoothDevice> {
 public:
  FakeBluetoothDevice(ash::quick_pair::FakeBluetoothAdapter* adapter,
                      const std::string& address)
      : testing::NiceMock<device::MockBluetoothDevice>(adapter,
                                                       /*bluetooth_class=*/0u,
                                                       /*name=*/"Test Device",
                                                       address,
                                                       /*paired=*/true,
                                                       /*connected=*/true),
        fake_adapter_(adapter) {}

  void CreateGattConnection(
      device::BluetoothDevice::GattConnectionCallback callback,
      std::optional<device::BluetoothUUID> service_uuid =
          std::nullopt) override {
    if (has_gatt_connection_hang_) {
      // Fast forward time to simulate this stack frame not finishing until
      // after the timer has fired.
      task_environment_->FastForwardBy(kSimulateStackFrameHangSeconds);
      return;
    }

    gatt_connection_ = std::make_unique<
        testing::NiceMock<device::MockBluetoothGattConnection>>(
        fake_adapter_.get(), kTestBleDeviceAddress);

    if (has_gatt_connection_error_) {
      std::move(callback).Run(std::move(gatt_connection_),
                              /*error_code=*/device::BluetoothDevice::
                                  ConnectErrorCode::ERROR_FAILED);
    } else {
      ON_CALL(*gatt_connection_.get(), IsConnected)
          .WillByDefault(testing::Return(true));
      std::move(callback).Run(std::move(gatt_connection_), std::nullopt);
    }
  }

  void Disconnect(base::OnceClosure callback,
                  base::OnceClosure error_callback) override {
    was_disconnect_called_ = true;
    if (has_disconnect_error_) {
      if (!no_disconnect_response_) {
        std::move(error_callback).Run();
      }
      return;
    }
    std::move(callback).Run();
  }

  void SetDisconnectError(bool has_disconnect_error) {
    has_disconnect_error_ = has_disconnect_error;
  }

  void SetNoDisconnectResponse(bool no_disconnect_response) {
    no_disconnect_response_ = no_disconnect_response;
  }

  bool WasDisconnectCalled() { return was_disconnect_called_; }

  void SetError(bool has_gatt_connection_error) {
    has_gatt_connection_error_ = has_gatt_connection_error;
  }

  void SetHang(bool has_gatt_connection_hang,
               base::test::TaskEnvironment* task_environment) {
    has_gatt_connection_hang_ = has_gatt_connection_hang;
    task_environment_ = task_environment;
  }

  // Move-only class
  FakeBluetoothDevice(const FakeBluetoothDevice&) = delete;
  FakeBluetoothDevice& operator=(const FakeBluetoothDevice&) = delete;

 protected:
  bool was_disconnect_called_ = false;
  std::unique_ptr<testing::NiceMock<device::MockBluetoothGattConnection>>
      gatt_connection_;
  bool has_gatt_connection_error_ = false;
  bool has_gatt_connection_hang_ = false;
  bool has_disconnect_error_ = false;
  bool no_disconnect_response_ = false;
  raw_ptr<base::test::TaskEnvironment> task_environment_ = nullptr;
  raw_ptr<ash::quick_pair::FakeBluetoothAdapter> fake_adapter_ = nullptr;
};

class FakeBluetoothGattCharacteristic
    : public testing::NiceMock<device::MockBluetoothGattCharacteristic> {
 public:
  FakeBluetoothGattCharacteristic(device::MockBluetoothGattService* service,
                                  const std::string& identifier,
                                  const device::BluetoothUUID& uuid,
                                  Properties properties,
                                  Permissions permissions)
      : testing::NiceMock<device::MockBluetoothGattCharacteristic>(
            service,
            identifier,
            uuid,
            properties,
            permissions) {}

  // Move-only class
  FakeBluetoothGattCharacteristic(const FakeBluetoothGattCharacteristic&) =
      delete;
  FakeBluetoothGattCharacteristic operator=(
      const FakeBluetoothGattCharacteristic&) = delete;

  void StartNotifySession(NotifySessionCallback callback,
                          ErrorCallback error_callback) override {
    if (notify_session_error_) {
      std::move(error_callback)
          .Run(device::BluetoothGattService::GattErrorCode::kNotPermitted);
      return;
    }

    auto fake_notify_session = std::make_unique<
        testing::NiceMock<device::MockBluetoothGattNotifySession>>(
        GetWeakPtr());

    if (notify_timeout_) {
      task_environment_->FastForwardBy(kConnectingTestTimeout);
      return;
    }

    std::move(callback).Run(std::move(fake_notify_session));
  }

  void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                 WriteType write_type,
                                 base::OnceClosure callback,
                                 ErrorCallback error_callback) override {
    if (write_remote_error_) {
      std::move(error_callback)
          .Run(device::BluetoothGattService::GattErrorCode::kNotPermitted);
      return;
    }

    if (write_timeout_) {
      task_environment_->FastForwardBy(kConnectingTestTimeout);
      return;
    }

    std::move(callback).Run();
  }

  void SetWriteError(bool write_remote_error) {
    write_remote_error_ = write_remote_error;
  }

  void SetWriteTimeout(bool write_timeout,
                       base::test::TaskEnvironment* task_environment) {
    write_timeout_ = write_timeout;
    task_environment_ = task_environment;
  }

  void SetNotifySessionError(bool notify_session_error) {
    notify_session_error_ = notify_session_error;
  }

  void SetNotifySessionTimeout(bool timeout,
                               base::test::TaskEnvironment* task_environment) {
    notify_timeout_ = timeout;
    task_environment_ = task_environment;
  }

 private:
  bool notify_session_error_ = false;
  bool write_remote_error_ = false;
  bool notify_timeout_ = false;
  bool write_timeout_ = false;
  raw_ptr<base::test::TaskEnvironment> task_environment_ = nullptr;
};

std::unique_ptr<FakeBluetoothDevice> CreateTestBluetoothDevice(
    ash::quick_pair::FakeBluetoothAdapter* adapter,
    device::BluetoothUUID uuid) {
  auto mock_device = std::make_unique<FakeBluetoothDevice>(
      /*adapter=*/adapter, kTestBleDeviceAddress);
  mock_device->SetPaired(false);
  mock_device->AddUUID(uuid);
  mock_device->SetServiceDataForUUID(uuid, {1, 2, 3});
  return mock_device;
}

}  // namespace

namespace ash {
namespace quick_pair {

class FastPairGattServiceClientTest : public testing::Test {
 public:
  void SetUp() override { fast_pair_data_encryptor_->public_key(kPublicKey); }

  void SuccessfulGattConnectionSetUp() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ = CreateTestBluetoothDevice(
        adapter_.get(), ash::quick_pair::kFastPairBluetoothUuid);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void FailedGattConnectionSetUp() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ = CreateTestBluetoothDevice(
        adapter_.get(), ash::quick_pair::kFastPairBluetoothUuid);
    unique_fake_bt_device_->SetError(true);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void HungGattConnectionSetUp() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ = CreateTestBluetoothDevice(
        adapter_.get(), ash::quick_pair::kFastPairBluetoothUuid);
    unique_fake_bt_device_->SetHang(true, &task_environment_);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void DisconectFailGattSetup() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ = CreateTestBluetoothDevice(
        adapter_.get(), ash::quick_pair::kFastPairBluetoothUuid);
    unique_fake_bt_device_->SetDisconnectError(true);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void NoDisconectResponseGattSetup() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ = CreateTestBluetoothDevice(
        adapter_.get(), ash::quick_pair::kFastPairBluetoothUuid);
    unique_fake_bt_device_->SetDisconnectError(true);
    unique_fake_bt_device_->SetNoDisconnectResponse(true);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void NonFastPairServiceDataSetUp() {
    adapter_ = base::MakeRefCounted<FakeBluetoothAdapter>();
    unique_fake_bt_device_ =
        CreateTestBluetoothDevice(adapter_.get(), kNonFastPairUuid);
    raw_fake_bt_device_ = unique_fake_bt_device_.get();
    adapter_->AddMockDevice(std::move(unique_fake_bt_device_));
    gatt_service_client_ = FastPairGattServiceClientImpl::Factory::Create(
        adapter_->GetDevice(kTestBleDeviceAddress), adapter_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                InitializedTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void SetGattServiceCharacteristics() {
    if (!keybased_char_error_) {
      fake_key_based_characteristic_ =
          std::make_unique<FakeBluetoothGattCharacteristic>(
              gatt_service_.get(), kUUIDString1, kKeyBasedCharacteristicUuid1,
              kProperties, kPermissions);

      if (keybased_notify_session_error_)
        fake_key_based_characteristic_->SetNotifySessionError(true);

      if (keybased_notify_session_timeout_) {
        fake_key_based_characteristic_->SetNotifySessionTimeout(
            true, &task_environment_);
      }

      if (key_based_write_error_) {
        fake_key_based_characteristic_->SetWriteError(true);
      }

      if (key_based_write_timeout_) {
        fake_key_based_characteristic_->SetWriteTimeout(true,
                                                        &task_environment_);
      }

      temp_fake_key_based_characteristic_ =
          fake_key_based_characteristic_.get();
      gatt_service_->AddMockCharacteristic(
          std::move(fake_key_based_characteristic_));
    }

    if (!passkey_char_error_) {
      fake_passkey_characteristic_ =
          std::make_unique<FakeBluetoothGattCharacteristic>(
              gatt_service_.get(), kUUIDString2, kPasskeyCharacteristicUuid1,
              kProperties, kPermissions);

      if (passkey_notify_session_error_)
        fake_passkey_characteristic_->SetNotifySessionError(true);

      if (passkey_notify_session_timeout_) {
        fake_passkey_characteristic_->SetNotifySessionTimeout(
            true, &task_environment_);
      }

      if (passkey_write_error_) {
        fake_passkey_characteristic_->SetWriteError(true);
      }

      if (passkey_write_timeout_) {
        fake_passkey_characteristic_->SetWriteTimeout(true, &task_environment_);
      }

      temp_passkey_based_characteristic_ = fake_passkey_characteristic_.get();

      gatt_service_->AddMockCharacteristic(
          std::move(fake_passkey_characteristic_));
    }

    auto fake_account_key_characteristic =
        std::make_unique<FakeBluetoothGattCharacteristic>(
            gatt_service_.get(), kUUIDString3, kAccountKeyCharacteristicUuid1,
            kProperties, kPermissions);
    if (account_key_write_error_) {
      fake_account_key_characteristic->SetWriteError(true);
    }

    if (write_account_key_timeout_) {
      fake_account_key_characteristic->SetWriteTimeout(true,
                                                       &task_environment_);
    }

    gatt_service_->AddMockCharacteristic(
        std::move(fake_account_key_characteristic));

    if (!model_id_char_error_) {
      auto fake_model_id_characteristic =
          std::make_unique<FakeBluetoothGattCharacteristic>(
              gatt_service_.get(), kUUIDString5, kModelIDCharacteristicUuid2,
              kProperties, kPermissions);
      gatt_service_->AddMockCharacteristic(
          std::move(fake_model_id_characteristic));
    }

    auto fake_additional_data_characteristic =
        std::make_unique<FakeBluetoothGattCharacteristic>(
            gatt_service_.get(), kUUIDString4,
            kAdditionalDataCharacteristicUuid2, kProperties, kPermissions);
    additional_data_characteristic_ = fake_additional_data_characteristic.get();
    gatt_service_->AddMockCharacteristic(
        std::move(fake_additional_data_characteristic));
  }

  void NotifyGattDiscoveryCompleteForService(const device::BluetoothUUID uuid) {
    auto gatt_service =
        std::make_unique<testing::NiceMock<device::MockBluetoothGattService>>(
            CreateTestBluetoothDevice(adapter_.get(), uuid).get(),
            kTestServiceId, uuid,
            /*is_primary=*/true);
    gatt_service_ = std::move(gatt_service);
    ON_CALL(*(gatt_service_.get()), GetDevice)
        .WillByDefault(
            testing::Return(adapter_->GetDevice(kTestBleDeviceAddress)));
    SetGattServiceCharacteristics();
    adapter_->NotifyGattDiscoveryCompleteForService(gatt_service_.get());
  }

  bool ServiceIsSet() {
    if (!gatt_service_client_->gatt_service())
      return false;
    return gatt_service_client_->gatt_service() == gatt_service_.get();
  }

  void SetPasskeyCharacteristicError(bool passkey_char_error) {
    passkey_char_error_ = passkey_char_error;
  }

  void SetKeybasedCharacteristicError(bool keybased_char_error) {
    keybased_char_error_ = keybased_char_error;
  }

  void SetAccountKeyCharacteristicWriteError(bool account_key_write_error) {
    account_key_write_error_ = account_key_write_error;
  }

  void SetPasskeyNotifySessionError(bool passkey_notify_session_error) {
    passkey_notify_session_error_ = passkey_notify_session_error;
  }

  void SetKeybasedNotifySessionError(bool keybased_notify_session_error) {
    keybased_notify_session_error_ = keybased_notify_session_error;
  }

  void SetModelIdCharacteristicError(bool model_id_char_error) {
    model_id_char_error_ = model_id_char_error;
  }

  void InitializedTestCallback(std::optional<PairFailure> failure) {
    initalized_failure_ = failure;
  }

  std::optional<PairFailure> GetInitializedCallbackResult() {
    return initalized_failure_;
  }

  void ReadModelIdCallback(
      std::optional<device::BluetoothGattService::GattErrorCode> error_code,
      const std::vector<uint8_t>& value) {
    read_failure_ = error_code;
  }

  void WriteTestCallback(std::vector<uint8_t> response,
                         std::optional<PairFailure> failure) {
    write_failure_ = failure;
  }

  void AccountKeyCallback(
      std::optional<ash::quick_pair::AccountKeyFailure> failure) {
    account_key_error_ = failure;
    if (failure.has_value()) {
      gatt_service_client_.reset();
    }
  }

  std::optional<ash::quick_pair::AccountKeyFailure> GetAccountKeyCallback() {
    return account_key_error_;
  }

  std::optional<PairFailure> GetWriteCallbackResult() { return write_failure_; }

  std::optional<device::BluetoothGattService::GattErrorCode>
  GetReadCallbackResult() {
    return read_failure_;
  }

  void SetPasskeyNotifySessionTimeout(bool timeout) {
    passkey_notify_session_timeout_ = timeout;
  }

  void SetKeybasedNotifySessionTimeout(bool timeout) {
    keybased_notify_session_timeout_ = timeout;
  }

  void SetWriteAccountKeyTimeout(bool timeout) {
    write_account_key_timeout_ = timeout;
  }

  void FastForwardTimeByConnectingTimeout() {
    task_environment_.FastForwardBy(kConnectingTestTimeout);
  }

  void FastForwardTimeByGattDisconnectCoolOff() {
    task_environment_.FastForwardBy(
        kCoolOffPeriodBeforeGattConnectionAfterDisconnect);
  }

  void FastForwardTimeByGattDisconnectResponseTimeout() {
    task_environment_.FastForwardBy(kDisconnectResponseTimeout);
  }

  void FastForwardTimeByAllGattRetries() {
    task_environment_.FastForwardBy(kAllGattRetriesPeriod);
  }

  void ReadModelId() {
    gatt_service_client_->ReadModelIdAsync(base::BindRepeating(
        &::ash::quick_pair::FastPairGattServiceClientTest::ReadModelIdCallback,
        weak_ptr_factory_.GetWeakPtr()));
  }

  void WriteRequestToKeyBased() {
    gatt_service_client_->WriteRequestAsync(
        kMessageType, kFlags, kProviderAddress, kSeekersAddress,
        fast_pair_data_encryptor_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                WriteTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void WriteRequestToPasskey() {
    gatt_service_client_->WritePasskeyAsync(
        kSeekerPasskey, kPasskey, fast_pair_data_encryptor_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                WriteTestCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void WriteAccountKey() {
    gatt_service_client_->WriteAccountKey(
        kAccountKey, fast_pair_data_encryptor_.get(),
        base::BindRepeating(&::ash::quick_pair::FastPairGattServiceClientTest::
                                AccountKeyCallback,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  void TriggerKeyBasedGattChanged() {
    adapter_->NotifyGattCharacteristicValueChanged(
        temp_fake_key_based_characteristic_);
  }

  void TriggerPasskeyGattChanged() {
    adapter_->NotifyGattCharacteristicValueChanged(
        temp_passkey_based_characteristic_);
  }

  void WritePersonalizedName(const std::string& name) {
    gatt_service_client_->WritePersonalizedName(
        name, kProviderAddress, fast_pair_data_encryptor_.get(),
        write_additional_data_callback_.Get());
  }

  std::optional<PairFailure> GetAdditionalDataWriteResult() {
    return additional_data_failure_;
  }

  void SetKeyBasedWriteError() { key_based_write_error_ = true; }

  void SetPasskeyWriteError() { passkey_write_error_ = true; }

  void SetWriteRequestTimeout() { key_based_write_timeout_ = true; }

  void SetWritePasskeyTimeout() { passkey_write_timeout_ = true; }

  void SetAdditionalDataWriteError(bool error) {
    additional_data_characteristic_->SetWriteError(error);
  }

  void SetAdditionalDataWriteTimeout(bool is_timeout) {
    additional_data_characteristic_->SetWriteTimeout(is_timeout,
                                                     &task_environment_);
  }

  base::HistogramTester& histogram_tester() { return histogram_tester_; }

  base::MockCallback<base::OnceCallback<void(std::optional<PairFailure>)>>
      write_additional_data_callback_;

 protected:
  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  base::HistogramTester histogram_tester_;
  std::unique_ptr<FastPairGattServiceClient> gatt_service_client_;
  raw_ptr<FakeBluetoothDevice, DanglingUntriaged> raw_fake_bt_device_;
  scoped_refptr<FakeBluetoothAdapter> adapter_;

 private:
  // We need temporary pointers to use for write/ready requests because we
  // move the unique pointers when we notify the session.
  raw_ptr<FakeBluetoothGattCharacteristic, DanglingUntriaged>
      temp_fake_key_based_characteristic_;
  raw_ptr<FakeBluetoothGattCharacteristic, DanglingUntriaged>
      temp_passkey_based_characteristic_;
  std::optional<ash::quick_pair::AccountKeyFailure> account_key_error_ =
      std::nullopt;
  bool passkey_char_error_ = false;
  bool keybased_char_error_ = false;
  bool account_key_write_error_ = false;
  bool passkey_notify_session_error_ = false;
  bool keybased_notify_session_error_ = false;
  bool passkey_notify_session_timeout_ = false;
  bool keybased_notify_session_timeout_ = false;
  bool key_based_write_error_ = false;
  bool key_based_write_timeout_ = false;
  bool passkey_write_error_ = false;
  bool passkey_write_timeout_ = false;
  bool write_account_key_timeout_ = false;
  bool model_id_char_error_ = false;

  std::optional<PairFailure> initalized_failure_;
  std::optional<PairFailure> write_failure_;
  std::optional<PairFailure> additional_data_failure_;

  std::optional<device::BluetoothGattService::GattErrorCode> read_failure_;

  std::unique_ptr<FakeBluetoothDevice> unique_fake_bt_device_;
  std::unique_ptr<FakeBluetoothGattCharacteristic>
      fake_key_based_characteristic_;
  raw_ptr<FakeBluetoothGattCharacteristic, DanglingUntriaged>
      additional_data_characteristic_;
  std::unique_ptr<FakeFastPairDataEncryptor> fast_pair_data_encryptor_ =
      std::make_unique<FakeFastPairDataEncryptor>();
  std::unique_ptr<FakeBluetoothGattCharacteristic> fake_passkey_characteristic_;
  std::unique_ptr<testing::NiceMock<device::MockBluetoothGattService>>
      gatt_service_;
  base::WeakPtrFactory<FastPairGattServiceClientTest> weak_ptr_factory_{this};
};

TEST_F(FastPairGattServiceClientTest, FailedGattConnection) {
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  FailedGattConnectionSetUp();
  FastForwardTimeByAllGattRetries();
  histogram_tester_.ExpectBucketCount(
      kFastPairGattRetryFailureReason,
      PairFailure::kBluetoothDeviceFailureCreatingGattConnection, 3);
  EXPECT_EQ(GetInitializedCallbackResult(), PairFailure::kCreateGattConnection);
  EXPECT_FALSE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 1);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 3);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 3);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest,
       GattConnectionSuccess_HandshakeRefactorDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_TRUE(gatt_service_client_->IsConnected());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 1);
  histogram_tester().ExpectTotalCount(kGattServiceDiscoveryTime, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest,
       GattConnectionSuccess_HandshakeRefactorEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 0);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_TRUE(gatt_service_client_->IsConnected());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kTotalGattConnectionTime, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionResult, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionEffectiveSuccessRate, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionAttemptCount, 1);
  histogram_tester().ExpectTotalCount(kGattServiceDiscoveryTime, 1);
  histogram_tester().ExpectTotalCount(kGattConnectionErrorMetric, 0);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, IgnoreNonFastPairServices) {
  NonFastPairServiceDataSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_FALSE(ServiceIsSet());
}

TEST_F(FastPairGattServiceClientTest, FailedKeyBasedCharacteristics) {
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SetKeybasedCharacteristicError(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(),
            PairFailure::kKeyBasedPairingCharacteristicDiscovery);
  EXPECT_FALSE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 2);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, FailedPasskeyCharacteristics) {
  SetPasskeyCharacteristicError(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(),
            PairFailure::kPasskeyCharacteristicDiscovery);
  EXPECT_FALSE(ServiceIsSet());
}

TEST_F(FastPairGattServiceClientTest,
       SuccessfulCharacteristicsStartNotify_HandshakeRefactorDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SetKeybasedCharacteristicError(false);
  SetPasskeyCharacteristicError(false);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToPasskey();
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 5);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest,
       SuccessfulCharacteristicsStartNotify_HandshakeRefactorEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SetKeybasedCharacteristicError(false);
  SetPasskeyCharacteristicError(false);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToPasskey();
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 5);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, StartNotifyPasskeyFailure) {
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  SetPasskeyNotifySessionError(true);
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToPasskey();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kPasskeyCharacteristicNotifySession);
  EXPECT_TRUE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, StartNotifyKeybasedFailure) {
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  SetKeybasedNotifySessionError(true);
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToKeyBased();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kKeyBasedPairingCharacteristicNotifySession);
  EXPECT_TRUE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, PasskeyStartNotifyTimeout) {
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  SetPasskeyNotifySessionTimeout(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToPasskey();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kPasskeyCharacteristicNotifySessionTimeout);
  EXPECT_TRUE(ServiceIsSet());
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest, KeyBasedStartNotifyTimeout) {
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SetKeybasedNotifySessionTimeout(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  WriteRequestToKeyBased();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kKeyBasedPairingCharacteristicNotifySessionTimeout);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
}

TEST_F(FastPairGattServiceClientTest,
       WriteKeyBasedRequest_HandshakeRefactorDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 1);
  histogram_tester().ExpectTotalCount(kKeyBasedNotify, 1);
  histogram_tester().ExpectTotalCount(kKeyBasedWriteRequest, 1);
}

TEST_F(FastPairGattServiceClientTest,
       WriteKeyBasedRequest_HandshakeRefactorEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 0);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  histogram_tester().ExpectTotalCount(kFastPairGattConnectionStep, 4);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyKeyBasedCharacteristicTime, 1);
  histogram_tester().ExpectTotalCount(kKeyBasedNotify, 1);
  histogram_tester().ExpectTotalCount(kKeyBasedWriteRequest, 1);
}

TEST_F(FastPairGattServiceClientTest, WriteKeyBasedRequestError) {
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 0);
  SetKeyBasedWriteError();
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kKeyBasedPairingCharacteristicWrite);
  histogram_tester().ExpectTotalCount(kWriteKeyBasedCharacteristicGattError, 1);
}

TEST_F(FastPairGattServiceClientTest, WriteKeyBasedRequestTimeout) {
  SetWriteRequestTimeout();
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_TRUE(ServiceIsSet());
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kKeyBasedPairingResponseTimeout);
}
TEST_F(FastPairGattServiceClientTest,
       WritePasskeyRequest_HandshakeRefactorDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToPasskey();
  TriggerPasskeyGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 1);
  histogram_tester().ExpectTotalCount(kPasskeyNotify, 1);
  histogram_tester().ExpectTotalCount(kPasskeyWriteRequest, 1);
}

TEST_F(FastPairGattServiceClientTest,
       WritePasskeyRequest_HandshakeRefactorEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToPasskey();
  TriggerPasskeyGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  histogram_tester().ExpectTotalCount(kNotifyPasskeyCharacteristicTime, 1);
  histogram_tester().ExpectTotalCount(kPasskeyNotify, 1);
  histogram_tester().ExpectTotalCount(kPasskeyWriteRequest, 1);
}

TEST_F(FastPairGattServiceClientTest, WritePasskeyRequestError) {
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  SetPasskeyWriteError();
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToPasskey();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(),
            PairFailure::kPasskeyPairingCharacteristicWrite);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 1);
}

TEST_F(FastPairGattServiceClientTest, WritePasskeyRequestTimeout) {
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
  SetWritePasskeyTimeout();
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToPasskey();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), PairFailure::kPasskeyResponseTimeout);
  histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicGattError, 0);
}

TEST_F(FastPairGattServiceClientTest,
       WriteAccountKey_HandshakeRefactorDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  WriteAccountKey();
  EXPECT_EQ(GetAccountKeyCallback(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 1);
}

TEST_F(FastPairGattServiceClientTest,
       WriteAccountKey_HandshakeRefactorEnabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      ash::features::kFastPairHandshakeLongTermRefactor);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  WriteAccountKey();
  EXPECT_EQ(GetAccountKeyCallback(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 1);
}

TEST_F(FastPairGattServiceClientTest, WriteAccountKeyFailure) {
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
  SetAccountKeyCharacteristicWriteError(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  WriteAccountKey();
  EXPECT_NE(GetAccountKeyCallback(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      1);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
}

TEST_F(FastPairGattServiceClientTest, WriteAccountKeyTimeout) {
  histogram_tester().ExpectTotalCount(kWriteAccountKeyCharacteristicGattError,
                                      0);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
  SetWriteAccountKeyTimeout(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  WriteRequestToKeyBased();
  TriggerKeyBasedGattChanged();
  EXPECT_EQ(GetWriteCallbackResult(), std::nullopt);
  WriteAccountKey();
  EXPECT_NE(GetAccountKeyCallback(), std::nullopt);
  histogram_tester().ExpectTotalCount(kWriteAccountKeyTimeMetric, 0);
}

TEST_F(FastPairGattServiceClientTest, TimeoutOnNonFastPairServiceDiscovery) {
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();

  NotifyGattDiscoveryCompleteForService(kNonFastPairUuid);

  // Simulate all the GATT retries timing out following
  // `NotifyGattCompleteForService` on an invalid UUID because the timeout have
  // fired (not stopped because invalid UUID), which cause us to retry.
  FastForwardTimeByAllGattRetries();

  histogram_tester_.ExpectBucketCount(kFastPairGattRetryFailureReason,
                                      PairFailure::kGattServiceDiscoveryTimeout,
                                      3);
  EXPECT_EQ(GetInitializedCallbackResult(), PairFailure::kCreateGattConnection);
}

TEST_F(FastPairGattServiceClientTest, HungGattConnectionTimesOut) {
  HungGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  histogram_tester_.ExpectBucketCount(kFastPairGattRetryFailureReason,
                                      PairFailure::kGattServiceDiscoveryTimeout,
                                      3);
  EXPECT_EQ(GetInitializedCallbackResult(), PairFailure::kCreateGattConnection);
  EXPECT_FALSE(ServiceIsSet());
}

TEST_F(FastPairGattServiceClientTest, FailedToDisconnectGattResultsInError) {
  DisconectFailGattSetup();
  FastForwardTimeByGattDisconnectCoolOff();
  EXPECT_EQ(GetInitializedCallbackResult(),
            PairFailure::kFailureToDisconnectGattBetweenRetries);
  EXPECT_FALSE(ServiceIsSet());
  EXPECT_TRUE(raw_fake_bt_device_->WasDisconnectCalled());
}

TEST_F(FastPairGattServiceClientTest,
       FailedToRecieveDisconnectResponseAfterGattFailure) {
  DisconectFailGattSetup();
  FastForwardTimeByGattDisconnectResponseTimeout();
  EXPECT_EQ(GetInitializedCallbackResult(),
            PairFailure::kFailureToDisconnectGattBetweenRetries);
  EXPECT_FALSE(ServiceIsSet());
  EXPECT_TRUE(raw_fake_bt_device_->WasDisconnectCalled());
}

TEST_F(FastPairGattServiceClientTest, SuccessfulGattConnectionDisconnects) {
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_TRUE(gatt_service_client_->IsConnected());
  EXPECT_TRUE(raw_fake_bt_device_->WasDisconnectCalled());
}

TEST_F(FastPairGattServiceClientTest, PairingDeviceLostBetweenRetries) {
  FailedGattConnectionSetUp();
  adapter_->RemoveMockDevice(kTestBleDeviceAddress);
  FastForwardTimeByGattDisconnectCoolOff();
  EXPECT_EQ(GetInitializedCallbackResult(),
            PairFailure::kPairingDeviceLostBetweenGattConnectionAttempts);
  EXPECT_FALSE(ServiceIsSet());
}

TEST_F(FastPairGattServiceClientTest, PersonalizedNameWriteSuccess) {
  SuccessfulGattConnectionSetUp();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_CALL(write_additional_data_callback_, Run(testing::Eq(std::nullopt)))
      .Times(1);
  WritePersonalizedName(kPersonalizedName);
}

TEST_F(FastPairGattServiceClientTest,
       PersonalizedNameWrite_AdditionalDataCharacteristicWriteError) {
  SuccessfulGattConnectionSetUp();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  SetAdditionalDataWriteError(true);
  EXPECT_CALL(write_additional_data_callback_,
              Run(testing::Eq(PairFailure::kAdditionalDataCharacteristicWrite)))
      .Times(1);
  WritePersonalizedName(kPersonalizedName);
}

TEST_F(FastPairGattServiceClientTest,
       kPersonalizedNameWrite_AdditionalDataCharacteristicWriteTimeout) {
  SuccessfulGattConnectionSetUp();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  SetAdditionalDataWriteTimeout(true);
  EXPECT_CALL(
      write_additional_data_callback_,
      Run(testing::Eq(PairFailure::kAdditionalDataCharacteristicWriteTimeout)))
      .Times(1);
  WritePersonalizedName(kPersonalizedName);
}

TEST_F(FastPairGattServiceClientTest, WriteEmptyPersonalizedName) {
  SuccessfulGattConnectionSetUp();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  const std::string empty = "";
  EXPECT_CALL(write_additional_data_callback_, Run(testing::Eq(std::nullopt)))
      .Times(1);
  WritePersonalizedName(empty);
}

TEST_F(FastPairGattServiceClientTest, SuccessfulReadModelId) {
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  ReadModelId();
  EXPECT_EQ(GetReadCallbackResult(), std::nullopt);
}

TEST_F(FastPairGattServiceClientTest, FailedReadModelId) {
  SetModelIdCharacteristicError(true);
  SuccessfulGattConnectionSetUp();
  FastForwardTimeByGattDisconnectCoolOff();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
  EXPECT_TRUE(ServiceIsSet());
  ReadModelId();
  EXPECT_EQ(GetReadCallbackResult(),
            device::BluetoothGattService::GattErrorCode::kNotSupported);
}

// Regression test for b/300596153
TEST_F(FastPairGattServiceClientTest,
       NoCrashWhenGattDiscoveryCompleteForServiceCalledTwice) {
  SuccessfulGattConnectionSetUp();
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
  NotifyGattDiscoveryCompleteForService(
      ash::quick_pair::kFastPairBluetoothUuid);
}

}  // namespace quick_pair
}  // namespace ash