From 94b3263b634ef3c6f928e87ba85bb029bc46f0e9 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Mon, 5 Jun 2023 14:28:19 -0700 Subject: [PATCH] Implement PairerBroker for fast pair windows PiperOrigin-RevId: 537982117 --- .../fast_pair_gatt_service_client_impl.cc | 2 +- .../handshake/fast_pair_handshake_impl.cc | 2 +- fastpair/pairing/BUILD | 68 ++ .../pairing/fastpair/fast_pair_pairer_impl.cc | 3 +- fastpair/pairing/pairer_broker.h | 55 ++ fastpair/pairing/pairer_broker_impl.cc | 333 +++++++++ fastpair/pairing/pairer_broker_impl.h | 101 +++ fastpair/pairing/pairer_broker_impl_test.cc | 695 ++++++++++++++++++ 8 files changed, 1256 insertions(+), 3 deletions(-) create mode 100644 fastpair/pairing/BUILD create mode 100644 fastpair/pairing/pairer_broker.h create mode 100644 fastpair/pairing/pairer_broker_impl.cc create mode 100644 fastpair/pairing/pairer_broker_impl.h create mode 100644 fastpair/pairing/pairer_broker_impl_test.cc diff --git a/fastpair/handshake/fast_pair_gatt_service_client_impl.cc b/fastpair/handshake/fast_pair_gatt_service_client_impl.cc index da09bfd3..3e072ea7 100644 --- a/fastpair/handshake/fast_pair_gatt_service_client_impl.cc +++ b/fastpair/handshake/fast_pair_gatt_service_client_impl.cc @@ -473,7 +473,7 @@ void FastPairGattServiceClientImpl::WriteAccountKey( return; } NEARBY_LOGS(INFO) << __func__ - << ": Failed to write the passkey characteristic "; + << ": Failed to write the accoutkey characteristic "; NotifyWriteAccountKeyError(PairFailure::kAccountKeyCharacteristicWrite); } diff --git a/fastpair/handshake/fast_pair_handshake_impl.cc b/fastpair/handshake/fast_pair_handshake_impl.cc index 33f79739..171b282e 100644 --- a/fastpair/handshake/fast_pair_handshake_impl.cc +++ b/fastpair/handshake/fast_pair_handshake_impl.cc @@ -129,7 +129,7 @@ void FastPairHandshakeImpl::OnParseDecryptedResponse( device.SetPublicAddress( device::CanonicalizeBluetoothAddress(response->address_bytes)); completed_successfully_ = true; - std::move(on_complete_callback_)(device, absl::nullopt); + std::move(on_complete_callback_)(device, std::nullopt); } } // namespace fastpair diff --git a/fastpair/pairing/BUILD b/fastpair/pairing/BUILD new file mode 100644 index 00000000..5129a003 --- /dev/null +++ b/fastpair/pairing/BUILD @@ -0,0 +1,68 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +licenses(["notice"]) + +cc_library( + name = "pairing", + srcs = [ + "pairer_broker_impl.cc", + ], + hdrs = [ + "pairer_broker.h", + "pairer_broker_impl.h", + ], + compatible_with = ["//buildenv/target:non_prod"], + visibility = [ + "//fastpair:__subpackages__", + "//internal:__subpackages__", + ], + deps = [ + "//fastpair/common", + "//fastpair/handshake", + "//fastpair/internal/mediums", + "//fastpair/pairing/fastpair:pairing", + "//internal/base", + "//internal/platform:types", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/synchronization", + ], +) + +cc_test( + name = "pairer_broker_impl_test", + size = "small", + srcs = [ + "pairer_broker_impl_test.cc", + ], + shard_count = 16, + deps = [ + ":pairing", + "//fastpair/common", + "//fastpair/handshake", + "//fastpair/internal/mediums", + "//fastpair/pairing/fastpair:pairing", + "//fastpair/server_access:test_support", + "//internal/base:bluetooth_address", + "//internal/platform:comm", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/implementation/g3", # build_cleaner: keep + "@boringssl//:crypto", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/functional:bind_front", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc index d7068d4e..0238d45c 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc @@ -314,9 +314,10 @@ void FastPairPairerImpl::NotifyPaired() { } void FastPairPairerImpl::NotifyPairingFailed(PairFailure failure) { - NEARBY_LOGS(WARNING) << __func__ << failure; + NEARBY_LOGS(WARNING) << __func__ << ": " << failure; // Stop initiate pairing timer as piaring is terminate this time. initiate_pairing_timer_.Stop(); + if (!on_pair_failed_cb_) return; executor_->Execute("NotifyPairingFailed", [&, on_pair_failed_cb = std::move(on_pair_failed_cb_), failure = std::move(failure)]() mutable { diff --git a/fastpair/pairing/pairer_broker.h b/fastpair/pairing/pairer_broker.h new file mode 100644 index 00000000..0020a52d --- /dev/null +++ b/fastpair/pairing/pairer_broker.h @@ -0,0 +1,55 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_H_ + +#include + +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/pair_failure.h" + +namespace nearby { +namespace fastpair { + +// The PairerBroker is the entry point for the Fast Pair Pairing component. +// It is responsible for brokering the 'pair to device' calls to +// the correct concrete Pairer implementation, and exposing an observer pattern +// for other components to become aware of pairing results. + +class PairerBroker { + public: + class Observer { + public: + virtual ~Observer() = default; + virtual void OnHandshakeComplete(FastPairDevice& device) {} + virtual void OnDevicePaired(FastPairDevice& device) {} + virtual void OnAccountKeyWrite(FastPairDevice& device, + std::optional error) {} + virtual void OnPairingComplete(FastPairDevice& device) {} + virtual void OnPairFailure(FastPairDevice& device, PairFailure failure) {} + }; + + virtual ~PairerBroker() = default; + + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + virtual void PairDevice(FastPairDevice& device) = 0; + virtual bool IsPairing() = 0; + virtual void StopPairing() = 0; +}; +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_H_ diff --git a/fastpair/pairing/pairer_broker_impl.cc b/fastpair/pairing/pairer_broker_impl.cc new file mode 100644 index 00000000..b83234cb --- /dev/null +++ b/fastpair/pairing/pairer_broker_impl.cc @@ -0,0 +1,333 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/pairing/pairer_broker_impl.h" + +#include +#include +#include +#include + +#include "absl/synchronization/mutex.h" +#include "fastpair//handshake/fast_pair_handshake_lookup.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/pairing/fastpair/fast_pair_pairer_impl.h" +#include "internal/platform/single_thread_executor.h" +#include "internal/platform/timer_impl.h" + +namespace nearby { +namespace fastpair { +namespace { +constexpr int kMaxFailureRetryCount = 3; +constexpr int kMaxNumHandshakeAttempts = 3; +constexpr absl::Duration kCancelPairingRetryDelay = absl::Seconds(1); +constexpr absl::Duration kRetryHandshakeDelay = absl::Seconds(1); +} // namespace + +PairerBrokerImpl::PairerBrokerImpl(Mediums& medium, + SingleThreadExecutor* executor) + : medium_(medium), executor_(executor) {} + +void PairerBrokerImpl::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void PairerBrokerImpl::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void PairerBrokerImpl::PairDevice(FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__ << ": Start to pair with device=" << device; + { + MutexLock lock(&mutex_); + model_id_to_current_ble_address_map_.insert_or_assign( + std::string(device.GetModelId()), std::string(device.GetBleAddress())); + did_handshake_previously_complete_successfully_map_.insert_or_assign( + std::string(device.GetModelId()), false); + } + PairFastPairDevice(device); +} + +void PairerBrokerImpl::PairFastPairDevice(FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__; + { + MutexLock lock(&mutex_); + if (fast_pair_pairers_.contains(device.GetModelId())) { + NEARBY_LOGS(WARNING) << __func__ << ": Already pairing with device" + << device; + return; + } + } + + CHECK(device.GetVersion().has_value()); + if (device.GetVersion().value() == DeviceFastPairVersion::kV1) { + // For v1 headsets, the ble address is the same as the public address. + // So skip straight to 'StartBondingAttempt'. + NEARBY_LOGS(INFO) << __func__ << " : Pairing with v1 device."; + device.SetPublicAddress(device.GetBleAddress()); + StartPairingAttempt(device); + return; + } + NEARBY_LOGS(INFO) << __func__ << " : Pairing with device higher than v1."; + // For headsets higher than v1, try to create a handshake with the remote + // device to request its public address. + CreateHandshake(device); +} + +void PairerBrokerImpl::CreateHandshake(FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__; + { + MutexLock lock(&mutex_); + if (device.GetBleAddress() != + model_id_to_current_ble_address_map_[device.GetModelId()]) { + // If the current |device| has a different BLE Address than the address in + // the map, abort creating the handshake and return early; + NEARBY_LOGS(INFO) + << __func__ + << ": The device's BLE did not match the expected value, returning."; + return; + } + } + + auto* fast_pair_handshake = + FastPairHandshakeLookup::GetInstance()->Get(&device); + if (fast_pair_handshake) { + if (fast_pair_handshake->completed_successfully()) { + NEARBY_LOGS(INFO) << __func__ + << ": Reusing existing handshake for pair attempt."; + StartPairingAttempt(device); + return; + } + // If the previous handshake did not complete successfully, erase it + // before attempting to create a new handshake for the device. + FastPairHandshakeLookup::GetInstance()->Erase(&device); + } + + NEARBY_LOGS(INFO) << __func__ << ": Creating new handshake for pair attempt."; + FastPairHandshakeLookup::GetInstance()->Create( + device, medium_, + [&](FastPairDevice& cb_device, std::optional failure) { + executor_->Execute("OnHandshakeComplete", + [&, failure = std::move(failure)]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + OnHandshakeComplete(cb_device, failure); + }); + }); +} + +void PairerBrokerImpl::OnHandshakeComplete(FastPairDevice& device, + std::optional failure) { + NEARBY_LOGS(INFO) << __func__; + if (failure.has_value()) { + OnHandshakeFailure(device, failure.value()); + return; + } + if (!device.GetPublicAddress().has_value()) { + NEARBY_LOGS(WARNING) << __func__ << ": Device lost during handshake."; + OnHandshakeFailure(device, PairFailure::kDeviceLostMidPairing); + return; + } + { + MutexLock lock(&mutex_); + if (!did_handshake_previously_complete_successfully_map_ + [device.GetModelId()]) { + for (auto& observer : observers_.GetObservers()) { + observer->OnHandshakeComplete(device); + } + + did_handshake_previously_complete_successfully_map_.insert_or_assign( + device.GetModelId(), true); + } + // Reset |num_handshake_attempts_| + num_handshake_attempts_[device.GetModelId()] = 0; + } + StartPairingAttempt(device); +} + +void PairerBrokerImpl::OnHandshakeFailure(FastPairDevice& device, + PairFailure failure) { + NEARBY_LOGS(WARNING) << __func__ + << ": Handshake failed with device because: " << failure; + { + MutexLock lock(&mutex_); + if (++num_handshake_attempts_[device.GetModelId()] < + kMaxNumHandshakeAttempts) { + retry_handshake_timer_ = std::make_unique(); + retry_handshake_timer_->Start( + kRetryHandshakeDelay / absl::Milliseconds(1), 0, + [&]() { CreateHandshake(device); }); + return; + } + } + NEARBY_LOGS(WARNING) + << __func__ << ": Handshake failed to be created. Notifying observers."; + for (auto& observer : observers_.GetObservers()) { + observer->OnPairFailure(device, failure); + } + FastPairHandshakeLookup::GetInstance()->Erase(&device); +} + +void PairerBrokerImpl::StartPairingAttempt(FastPairDevice& device) { + { + MutexLock lock(&mutex_); + if (!pair_failure_counts_.contains(device.GetModelId())) { + pair_failure_counts_[device.GetModelId()] = 0; + } + } + + NEARBY_LOGS(INFO) << __func__ << ": " << device; + MutexLock lock(&mutex_); + // Create FastPairPairer instance and start pairing. + fast_pair_pairers_[device.GetModelId()] = FastPairPairerImpl::Factory::Create( + device, medium_, executor_, + [&](FastPairDevice& cb_device) { + executor_->Execute("OnFastPairDevicePaired", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + OnFastPairDevicePaired(cb_device); + }); + }, + [&](FastPairDevice& cb_device, PairFailure failure) { + executor_->Execute("OnFastPairPairingFailure", + [&, failure = std::move(failure)]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + OnFastPairPairingFailure(cb_device, failure); + }); + }, + [&](FastPairDevice& cb_device, PairFailure failure) { + executor_->Execute("OnAccountKeyFailure", + [&, failure = std::move(failure)]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + OnAccountKeyFailure(cb_device, failure); + }); + }, + [&](FastPairDevice& cb_device) { + executor_->Execute("OnFastPairProcedureComplete", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + OnFastPairProcedureComplete(cb_device); + }); + }); + fast_pair_pairers_[device.GetModelId()]->StartPairing(); +} + +void PairerBrokerImpl::OnFastPairDevicePaired(FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__ << ": Device=" << device; + for (auto& observer : observers_.GetObservers()) { + observer->OnDevicePaired(device); + } + MutexLock lock(&mutex_); + pair_failure_counts_.erase(device.GetModelId()); +} + +void PairerBrokerImpl::OnFastPairPairingFailure(FastPairDevice& device, + PairFailure failure) { + MutexLock lock(&mutex_); + ++pair_failure_counts_[device.GetModelId()]; + NEARBY_LOGS(INFO) << __func__ << ": Device=" << device + << ", Failure=" << failure << ", Failure Count = " + << pair_failure_counts_[device.GetModelId()]; + if (pair_failure_counts_[device.GetModelId()] == kMaxFailureRetryCount) { + NEARBY_LOGS(INFO) << __func__ + << ": Reached max failure count. Notifying observers."; + for (auto& observer : observers_.GetObservers()) { + observer->OnPairFailure(device, failure); + } + if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { + fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + } + executor_->Execute("EraseHandshakeAndPairers", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + EraseHandshakeAndPairers(device); + }); + return; + } + + if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { + NEARBY_LOGS(INFO) << __func__ + << ": Cancelling pairing and scheduling retry " + "for failed pair attempt."; + fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + fast_pair_pairers_.erase(device.GetModelId()); + // Create a timer to wait |kCancelPairingRetryDelay| after cancelling + // pairing to retry the pairing attempt. + cancel_pairing_timer_ = std::make_unique(); + cancel_pairing_timer_->Start( + kCancelPairingRetryDelay / absl::Milliseconds(1), 0, + [&]() { PairFastPairDevice(device); }); + return; + } + fast_pair_pairers_.erase(device.GetModelId()); + PairFastPairDevice(device); +} + +void PairerBrokerImpl::OnAccountKeyFailure(FastPairDevice& device, + PairFailure failure) { + NEARBY_LOGS(INFO) << __func__ << ": Device=" << device + << ", Failure=" << failure; + for (auto& observer : observers_.GetObservers()) { + observer->OnAccountKeyWrite(device, failure); + } + executor_->Execute("EraseHandshakeAndPairers", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + EraseHandshakeAndPairers(device); + }); +} + +void PairerBrokerImpl::OnFastPairProcedureComplete(FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__ << ": Device=" << device; + for (auto& observer : observers_.GetObservers()) { + observer->OnPairingComplete(device); + } + // If we get to this point in the flow for the initial and retroactive + // pairing scenarios, this means that the account key has successfully + // been written for devices with a version of V2 or higher. + if (device.GetVersion().has_value() && + device.GetVersion().value() == DeviceFastPairVersion::kHigherThanV1 && + (device.GetProtocol() == Protocol::kFastPairInitialPairing || + device.GetProtocol() == Protocol::kFastPairRetroactivePairing)) { + for (auto& observer : observers_.GetObservers()) { + observer->OnAccountKeyWrite(device, /*error=*/absl::nullopt); + } + } + executor_->Execute("EraseHandshakeAndPairers", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + EraseHandshakeAndPairers(device); + }); +} + +void PairerBrokerImpl::EraseHandshakeAndPairers(FastPairDevice& device) { + MutexLock lock(&mutex_); + NEARBY_LOGS(WARNING) << __func__; + // |fast_pair_pairers_| and its children objects depend on the handshake + // instance. Shut them down before destroying the handshake. + pair_failure_counts_.erase(device.GetModelId()); + fast_pair_pairers_.erase(device.GetModelId()); + FastPairHandshakeLookup::GetInstance()->Erase(&device); + did_handshake_previously_complete_successfully_map_.insert_or_assign( + std::string(device.GetModelId()), false); +} + +bool PairerBrokerImpl::IsPairing() { + MutexLock lock(&mutex_); + // We are guaranteed to not be pairing when the following two maps are + // empty. + return !fast_pair_pairers_.empty() || !pair_failure_counts_.empty(); +} + +void PairerBrokerImpl::StopPairing() { + MutexLock lock(&mutex_); + fast_pair_pairers_.clear(); + pair_failure_counts_.clear(); +} +} // namespace fastpair +} // namespace nearby diff --git a/fastpair/pairing/pairer_broker_impl.h b/fastpair/pairing/pairer_broker_impl.h new file mode 100644 index 00000000..0da1e316 --- /dev/null +++ b/fastpair/pairing/pairer_broker_impl.h @@ -0,0 +1,101 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_IMPL_H_ +#define THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_IMPL_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "fastpair/common/fast_pair_device.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/pairing/fastpair/fast_pair_pairer.h" +#include "fastpair/pairing/pairer_broker.h" +#include "internal/base/observer_list.h" +#include "internal/platform/single_thread_executor.h" +#include "internal/platform/timer_impl.h" + +namespace nearby { +namespace fastpair { + +class PairerBrokerImpl : public PairerBroker { + public: + explicit PairerBrokerImpl(Mediums& medium, SingleThreadExecutor* executor); + PairerBrokerImpl(const PairerBrokerImpl&) = delete; + PairerBrokerImpl& operator=(const PairerBrokerImpl&) = delete; + + // PairingBroker: + void AddObserver(Observer* observer) override; + // The `observer` can still be called after removing if it has already + // been scheduled to run (or is running) on the background thread. + void RemoveObserver(Observer* observer) override; + void PairDevice(FastPairDevice& device) override; + bool IsPairing() override; + void StopPairing() override; + + friend class PairerBrokerImplTest; + + private: + mutable Mutex mutex_; + void PairFastPairDevice(FastPairDevice& device); + void CreateHandshake(FastPairDevice& device); + void OnHandshakeComplete(FastPairDevice& device, + std::optional failure) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void OnHandshakeFailure(FastPairDevice& device, PairFailure failure) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + + void StartPairingAttempt(FastPairDevice& device); + + void OnFastPairDevicePaired(FastPairDevice& device) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void OnFastPairPairingFailure(FastPairDevice& device, PairFailure failure) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void OnFastPairProcedureComplete(FastPairDevice& device) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void OnAccountKeyFailure(FastPairDevice& device, PairFailure failure) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + + void EraseHandshakeAndPairers(FastPairDevice& device) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + + Mediums& medium_; + SingleThreadExecutor* executor_; + + // The key for all the following maps is a device model id. + absl::flat_hash_map> + fast_pair_pairers_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map pair_failure_counts_ + ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map + did_handshake_previously_complete_successfully_map_ + ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map num_handshake_attempts_ + ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map + model_id_to_current_ble_address_map_ ABSL_GUARDED_BY(mutex_); + + // Timer + std::unique_ptr cancel_pairing_timer_; + std::unique_ptr retry_handshake_timer_; + + ObserverList observers_; +}; + +} // namespace fastpair +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_PAIRER_BROKER_IMPL_H_ diff --git a/fastpair/pairing/pairer_broker_impl_test.cc b/fastpair/pairing/pairer_broker_impl_test.cc new file mode 100644 index 00000000..9bc99992 --- /dev/null +++ b/fastpair/pairing/pairer_broker_impl_test.cc @@ -0,0 +1,695 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fastpair/pairing/pairer_broker_impl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/functional/bind_front.h" +#include "fastpair//handshake/fast_pair_handshake_lookup.h" +#include "fastpair/common/pair_failure.h" +#include "fastpair/handshake/fast_pair_data_encryptor_impl.h" +#include "fastpair/handshake/fast_pair_handshake_impl.h" +#include "fastpair/internal/mediums/mediums.h" +#include "fastpair/pairing/fastpair/fast_pair_pairer.h" +#include "fastpair/server_access/fake_fast_pair_repository.h" +#include "internal/base/bluetooth_address.h" +#include "internal/platform/ble_v2.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/medium_environment.h" +#include + +namespace nearby { +namespace fastpair { +namespace { +using Property = nearby::api::ble_v2::GattCharacteristic::Property; +using Permission = nearby::api::ble_v2::GattCharacteristic::Permission; +using ::nearby::api::ble_v2::GattCharacteristic; +using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; + +constexpr absl::string_view kMetadataId("718c17"); +constexpr absl::string_view kPublicAntiSpoof = + "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" + "0wVcljfT3XPoiy1fntlneziyLD5knDVAJSE+RM/zlPRP/Jg=="; +constexpr std::array kRawResponseBytes = { + 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, + 0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6}; +constexpr Uuid kFastPairServiceUuid(0x0000FE2C00001000, 0x800000805F9B34FB); +constexpr Uuid kKeyBasedCharacteristicUuidV2(0xFE2C123483664814, + 0x8EB001DE32100BEA); +constexpr Uuid kPasskeyCharacteristicUuidV2(0xFE2C123583664814, + 0x8EB001DE32100BEA); +constexpr Uuid kAccountKeyCharacteristicUuidV2(0xFE2C123683664814, + 0x8EB001DE32100BEA); +constexpr absl::string_view kPasskey("123456"); +constexpr absl::string_view kWrongResponse("wrongresponse"); +constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200); +struct CharacteristicData { + // Write result returned to the gatt client. + absl::Status write_result = absl::OkStatus(); +}; +} // namespace + +class PairerBrokerObserver : public PairerBroker::Observer { + public: + explicit PairerBrokerObserver(PairerBroker* pairer_broker, + CountDownLatch* device_paired_latch, + CountDownLatch* account_key_writed_latch, + CountDownLatch* pairing_completed_latch, + CountDownLatch* pairing_failure_latch) + : pairer_broker_(pairer_broker), + device_paired_latch_(device_paired_latch), + account_key_writed_latch_(account_key_writed_latch), + pairing_completed_latch_(pairing_completed_latch), + pairing_failure_latch_(pairing_failure_latch) { + pairer_broker->AddObserver(this); + } + + ~PairerBrokerObserver() override { pairer_broker_->RemoveObserver(this); } + + void OnHandshakeComplete(FastPairDevice& device) override { + NEARBY_LOGS(INFO) << __func__; + handshake_completed_ = true; + } + + void OnDevicePaired(FastPairDevice& device) override { + NEARBY_LOGS(INFO) << __func__; + device_paired_latch_->CountDown(); + } + + void OnAccountKeyWrite(FastPairDevice& device, + std::optional error) override { + NEARBY_LOGS(INFO) << __func__; + if (error.has_value()) { + account_key_failure_ = error.value(); + } + account_key_writed_latch_->CountDown(); + } + + void OnPairingComplete(FastPairDevice& device) override { + NEARBY_LOGS(INFO) << __func__; + pairing_completed_latch_->CountDown(); + } + + void OnPairFailure(FastPairDevice& device, PairFailure failure) override { + pair_failure_ = failure; + pairing_failure_latch_->CountDown(); + } + + PairerBroker* pairer_broker_; + bool handshake_completed_ = false; + CountDownLatch* device_paired_latch_ = nullptr; + CountDownLatch* account_key_writed_latch_ = nullptr; + CountDownLatch* pairing_completed_latch_ = nullptr; + CountDownLatch* pairing_failure_latch_ = nullptr; + PairFailure account_key_failure_ = PairFailure::kUnknown; + PairFailure pair_failure_ = PairFailure::kUnknown; +}; + +class PairerBrokerImplTest : public testing::Test { + public: + void SetUp() override { + env_.Start(); + // Setups seeker device. + mediums_ = std::make_unique(); + + // Setups provider device. + adapter_provider_ = std::make_unique(); + adapter_provider_->SetStatus(BluetoothAdapter::Status::kEnabled); + adapter_provider_->SetName("Device-Provider"); + adapter_provider_->SetScanMode( + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + bt_provider_ = std::make_unique(*adapter_provider_); + + // Discovering provider device. + CountDownLatch found_latch(1); + mediums_->GetBluetoothClassic().GetMedium().StartDiscovery( + DiscoveryCallback{.device_discovered_cb = [&](BluetoothDevice& device) { + remote_device_ = &device; + found_latch.CountDown(); + }}); + found_latch.Await(); + env_.Sync(); + } + + void TearDown() override { + env_.Sync(false); + executor_.Shutdown(); + mediums_.reset(); + device_.reset(); + repository_.reset(); + handshake_ = nullptr; + remote_device_ = nullptr; + key_based_characteristic_ = std::nullopt; + passkey_characteristic_ = std::nullopt; + + adapter_provider_->SetStatus(BluetoothAdapter::Status::kDisabled); + gatt_server_->Stop(); + gatt_server_.reset(); + bt_provider_.reset(); + ble_provider_.reset(); + env_.Sync(false); + adapter_provider_.reset(); + env_.Stop(); + } + + void CreateMockDevice(DeviceFastPairVersion version, Protocol protocol) { + device_ = std::make_unique( + kMetadataId, remote_device_->GetMacAddress(), protocol); + device_->SetVersion(version); + if (version == DeviceFastPairVersion::kV1) { + device_->SetPublicAddress(remote_device_->GetMacAddress()); + } + if (protocol == Protocol::kFastPairSubsequentPairing) { + device_->SetAccountKey(AccountKey(account_key_)); + } + } + + void ConfigurePairingContext() { + api::PairingParams pairing_params; + pairing_params.pairing_type = + api::PairingParams::PairingType::kConfirmPasskey; + pairing_params.passkey = kPasskey; + env_.ConfigBluetoothPairingContext(&remote_device_->GetImpl(), + pairing_params); + } + + void CreateFastPairHandshakeInstanceForDevice() { + FastPairHandshakeLookup::SetCreateFunctionForTesting(absl::bind_front( + &PairerBrokerImplTest::CreateConnectedHandshake, this)); + + CountDownLatch latch(1); + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Create( + *device_, *mediums_, + [&](FastPairDevice& cb_device, std::optional failure) { + EXPECT_EQ(device_.get(), &cb_device); + EXPECT_EQ(failure, std::nullopt); + latch.CountDown(); + })); + latch.Await(); + EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(device_.get())); + EXPECT_TRUE(handshake_->completed_successfully()); + EXPECT_EQ( + device_->GetPublicAddress().value(), + device::CanonicalizeBluetoothAddress(remote_device_->GetMacAddress())); + } + + std::unique_ptr CreateConnectedHandshake( + FastPairDevice& device, Mediums& mediums, + FastPairHandshake::OnCompleteCallback callback) { + CountDownLatch latch(1); + auto handshake = std::make_unique( + device, mediums, + [&](FastPairDevice& callback_device, + std::optional failure) { + callback(callback_device, failure); + latch.CountDown(); + }); + handshake_ = handshake.get(); + latch.Await(); + return handshake; + } + + // Sets up provider's metadata information. + void SetUpFastPairRepository() { + repository_ = FakeFastPairRepository::Create(kMetadataId, kPublicAntiSpoof); + } + + // Sets upprovider's gatt_server. + void SetupProviderGattServer( + absl::AnyInvocable trigger_keybase_value_change, + absl::AnyInvocable trigger_passkey_value_change) { + ble_provider_ = std::make_unique(*adapter_provider_); + gatt_server_ = ble_provider_->StartGattServer( + /*ServerGattConnectionCallback=*/{ + .on_characteristic_write_cb = + [&](const api::ble_v2::BlePeripheral& remote_device, + const api::ble_v2::GattCharacteristic& characteristic, + int offset, absl::string_view data, + BleV2Medium::ServerGattConnectionCallback:: + WriteValueCallback callback) { + auto it = characteristics_.find(characteristic); + if (it == characteristics_.end()) { + callback(absl::NotFoundError("characteristic not found")); + return; + } + if (characteristic == *key_based_characteristic_) { + trigger_keybase_value_change(); + } else if (characteristic == *passkey_characteristic_) { + trigger_passkey_value_change(); + } + callback(it->second.write_result); + }}); + // Insert fast pair related gatt characteristics + key_based_characteristic_ = gatt_server_->CreateCharacteristic( + kFastPairServiceUuid, kKeyBasedCharacteristicUuidV2, permissions_, + properties_); + characteristics_[*key_based_characteristic_].write_result = + absl::OkStatus(); + + passkey_characteristic_ = gatt_server_->CreateCharacteristic( + kFastPairServiceUuid, kPasskeyCharacteristicUuidV2, permissions_, + properties_); + characteristics_[*passkey_characteristic_].write_result = absl::OkStatus(); + + accountkey_characteristic_ = gatt_server_->CreateCharacteristic( + kFastPairServiceUuid, kAccountKeyCharacteristicUuidV2, permissions_, + properties_); + characteristics_[*accountkey_characteristic_].write_result = + absl::OkStatus(); + } + + // Triggers provider's gatt_server to response with public address + absl::Status TriggerKeyBasedGattChanged() { + std::unique_ptr fast_pair_data_encryptor_unique_ptr; + FastPairDataEncryptor* fast_pair_data_encryptor_; + if (device_->GetAccountKey().Ok()) { + CountDownLatch latch(1); + FastPairDataEncryptorImpl::Factory::CreateAsync( + *device_, + [&](std::unique_ptr fast_pair_data_encryptor) { + fast_pair_data_encryptor_unique_ptr = + std::move(fast_pair_data_encryptor); + + latch.CountDown(); + }); + latch.Await(); + fast_pair_data_encryptor_ = fast_pair_data_encryptor_unique_ptr.get(); + } else { + fast_pair_data_encryptor_ = handshake_->fast_pair_data_encryptor(); + } + std::array raw_response = kRawResponseBytes; + std::array provider_address_bytes; + device::ParseBluetoothAddress( + device_->GetBleAddress(), + absl::MakeSpan(provider_address_bytes.data(), + provider_address_bytes.size())); + std::copy(provider_address_bytes.begin(), provider_address_bytes.end(), + std::begin(raw_response) + 1); + std::array encryptedResponse = + fast_pair_data_encryptor_->EncryptBytes(raw_response); + std::array response; + std::copy(encryptedResponse.begin(), encryptedResponse.end(), + response.begin()); + return gatt_server_->NotifyCharacteristicChanged( + key_based_characteristic_.value(), false, ByteArray(response)); + } + + // Triggers provider's gatt_server to response with passkey + // success == true, response with correct passkey, + // otherwise, response with wrong passkey. + absl::Status TriggerPasskeyGattChanged(absl::string_view pin_code, + uint8_t fast_pair_message_type) { + std::unique_ptr fast_pair_data_encryptor_unique_ptr; + FastPairDataEncryptor* fast_pair_data_encryptor_; + if (device_->GetAccountKey().Ok()) { + CountDownLatch latch(1); + FastPairDataEncryptorImpl::Factory::CreateAsync( + *device_, + [&](std::unique_ptr fast_pair_data_encryptor) { + fast_pair_data_encryptor_unique_ptr = + std::move(fast_pair_data_encryptor); + + latch.CountDown(); + }); + latch.Await(); + fast_pair_data_encryptor_ = fast_pair_data_encryptor_unique_ptr.get(); + } else { + fast_pair_data_encryptor_ = handshake_->fast_pair_data_encryptor(); + } + std::array raw_response; + RAND_bytes(raw_response.data(), kAesBlockByteSize); + raw_response[0] = fast_pair_message_type; + uint32_t passkey = 0; + passkey = std::stoi(std::string(pin_code)); + + // Need to convert the uint_32 to uint_8 to use in our data vector. + raw_response[1] = (passkey & 0x00ff0000) >> 16; + raw_response[2] = (passkey & 0x0000ff00) >> 8; + raw_response[3] = passkey & 0x000000ff; + + std::array encryptedResponse = + fast_pair_data_encryptor_->EncryptBytes(raw_response); + std::array response; + std::copy(encryptedResponse.begin(), encryptedResponse.end(), + response.begin()); + return gatt_server_->NotifyCharacteristicChanged( + passkey_characteristic_.value(), false, ByteArray(response)); + } + + absl::Status TriggerPasskeyGattChangedWithWrongResponse() { + return gatt_server_->NotifyCharacteristicChanged( + passkey_characteristic_.value(), false, + ByteArray(std::string(kWrongResponse))); + } + + bool SetPairingResult( + std::optional error) { + return env_.SetPairingResult(&remote_device_->GetImpl(), error); + } + + void SetPasskeyCharacteristicsWriteResultToFailure() { + auto it = characteristics_.find(*passkey_characteristic_); + it->second.write_result = absl::UnknownError("Failed to write account key"); + } + + void SetAccountkeyCharacteristicsWriteResultToFailure() { + auto it = characteristics_.find(*accountkey_characteristic_); + it->second.write_result = absl::UnknownError("Failed to write account key"); + } + + protected: + const std::vector account_key_{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, + 0xCC, 0xDD, 0xEE, 0xFF}; + std::unique_ptr mediums_; + std::unique_ptr device_; + BluetoothDevice* remote_device_ = nullptr; + std::unique_ptr fast_pair_pairer_; + + SingleThreadExecutor executor_; + + private: + MediumEnvironment& env_{MediumEnvironment::Instance()}; + std::unique_ptr repository_; + std::unique_ptr bt_provider_; + std::unique_ptr adapter_provider_; + std::unique_ptr gatt_server_; + std::unique_ptr ble_provider_; + FastPairHandshake* handshake_ = nullptr; + std::optional key_based_characteristic_; + std::optional passkey_characteristic_; + std::optional accountkey_characteristic_; + absl::flat_hash_map characteristics_; + Property properties_ = Property::kWrite | Property::kNotify; + Permission permissions_ = Permission::kWrite; +}; + +TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDeviceV1) { + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kV1, + Protocol::kFastPairInitialPairing); + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + SetUpFastPairRepository(); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + SetPairingResult(std::nullopt); + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker->PairDevice(*device_); + + device_paired_latch.Await(); + pairing_completed_latch.Await(); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(triggered_keybase_value_change); + EXPECT_FALSE(triggered_passkey_value_change); + EXPECT_FALSE(device_->GetAccountKey().Ok()); +} + +TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDevice) { + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetUpFastPairRepository(); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + CreateFastPairHandshakeInstanceForDevice(); + SetPairingResult(std::nullopt); + + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker->PairDevice(*device_); + + device_paired_latch.Await(); + pairing_completed_latch.Await(); + account_key_writed_latch.Await(); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_TRUE(triggered_passkey_value_change); + EXPECT_TRUE(device_->GetAccountKey().Ok()); + + pairer_broker->StopPairing(); + EXPECT_FALSE(pairer_broker->IsPairing()); +} + +TEST_F(PairerBrokerImplTest, SuccessSubsequentPairingWithDevice) { + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairSubsequentPairing); + SetUpFastPairRepository(); + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + SetPairingResult(std::nullopt); + + pairer_broker->PairDevice(*device_); + executor_.Execute([&]() { + // Test pairing duplicate devices. + EXPECT_TRUE(pairer_broker->IsPairing()); + pairer_broker->PairDevice(*device_); + }); + device_paired_latch.Await(); + pairing_completed_latch.Await(); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_TRUE(pairer_broker_observer.handshake_completed_); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_TRUE(triggered_passkey_value_change); +} + +TEST_F(PairerBrokerImplTest, SuccessRetroactivePairingWithDevice) { + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairRetroactivePairing); + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + SetUpFastPairRepository(); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + CreateFastPairHandshakeInstanceForDevice(); + SetPairingResult(std::nullopt); + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker->PairDevice(*device_); + EXPECT_FALSE(device_paired_latch.Await(kWaitTimeout).result()); + pairing_completed_latch.Await(); + account_key_writed_latch.Await(); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_FALSE(triggered_passkey_value_change); + EXPECT_TRUE(device_->GetAccountKey().Ok()); +} + +TEST_F(PairerBrokerImplTest, FaileToCreateHandshakeRetryThreeTimes) { + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairSubsequentPairing); + SetUpFastPairRepository(); + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + SetupProviderGattServer( + [&]() { triggered_keybase_value_change = true; }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + SetPairingResult(std::nullopt); + + pairer_broker->PairDevice(*device_); + EXPECT_FALSE(device_paired_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_completed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + pairing_failure_latch.Await(); + EXPECT_FALSE(pairer_broker_observer.handshake_completed_); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_FALSE(triggered_passkey_value_change); + EXPECT_EQ(pairer_broker_observer.pair_failure_, + PairFailure::kKeyBasedPairingResponseTimeout); +} + +TEST_F(PairerBrokerImplTest, FaileToWriteAccountkey) { + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetUpFastPairRepository(); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged(kPasskey, kProviderPasskeyType)); + }); + CreateFastPairHandshakeInstanceForDevice(); + SetPairingResult(std::nullopt); + SetAccountkeyCharacteristicsWriteResultToFailure(); + + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker->PairDevice(*device_); + + device_paired_latch.Await(); + EXPECT_FALSE(pairing_completed_latch.Await(kWaitTimeout).result()); + account_key_writed_latch.Await(); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_TRUE(triggered_passkey_value_change); + EXPECT_FALSE(device_->GetAccountKey().Ok()); + EXPECT_EQ(pairer_broker_observer.account_key_failure_, + PairFailure::kAccountKeyCharacteristicWrite); +} + +TEST_F(PairerBrokerImplTest, FailToPairRetryThreeTimes) { + bool triggered_keybase_value_change = false; + bool triggered_passkey_value_change = false; + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + ConfigurePairingContext(); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetUpFastPairRepository(); + SetupProviderGattServer( + [&]() { + triggered_keybase_value_change = true; + EXPECT_OK(TriggerKeyBasedGattChanged()); + }, + [&]() { + triggered_passkey_value_change = true; + EXPECT_OK(TriggerPasskeyGattChanged("654321", kProviderPasskeyType)); + }); + CreateFastPairHandshakeInstanceForDevice(); + SetPairingResult(std::nullopt); + + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + auto pairer_broker = + std::make_unique(*mediums_, &executor_); + PairerBrokerObserver pairer_broker_observer( + pairer_broker.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker->PairDevice(*device_); + + EXPECT_FALSE(device_paired_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_completed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + pairing_failure_latch.Await(); + EXPECT_TRUE(triggered_keybase_value_change); + EXPECT_TRUE(triggered_passkey_value_change); + EXPECT_FALSE(device_->GetAccountKey().Ok()); + EXPECT_EQ(pairer_broker_observer.pair_failure_, + PairFailure::kPairingAndConnect); +} +} // namespace fastpair +} // namespace nearby