Implement FastPairPairer to handle the pairing process

PiperOrigin-RevId: 537124512
This commit is contained in:
Qin Wang
2023-06-01 14:07:22 -07:00
committed by Copybara-Service
parent 7c931aa9c7
commit 3dd893dd4c
12 changed files with 1657 additions and 15 deletions
+16
View File
@@ -3,6 +3,7 @@ cc_library(
srcs = [
"fast_pair_decryption.cc",
"fast_pair_encryption.cc",
"fast_pair_message_type.cc",
],
hdrs = [
"decrypted_passkey.h",
@@ -102,3 +103,18 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "fast_pair_message_type_test",
size = "small",
srcs = [
"fast_pair_message_type_test.cc",
],
shard_count = 1,
deps = [
":crypto",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2022 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/crypto/fast_pair_message_type.h"
#include <ostream>
namespace nearby {
namespace fastpair {
std::ostream& operator<<(std::ostream& stream,
FastPairMessageType message_type) {
switch (message_type) {
case FastPairMessageType::kKeyBasedPairingRequest:
stream << "[Key-Based Pairing Request]";
break;
case FastPairMessageType::kKeyBasedPairingResponse:
stream << "[Key-Based Pairing Response]";
break;
case FastPairMessageType::kSeekersPasskey:
stream << "[Seeker's Passkey]";
break;
case FastPairMessageType::kProvidersPasskey:
stream << "[Providers' Passkey]";
break;
default:
stream << "[Unknown]";
}
return stream;
}
} // namespace fastpair
} // namespace nearby
+14 -9
View File
@@ -15,23 +15,28 @@
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_CRYPTO_FAST_PAIR_MESSAGE_TYPE_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_CRYPTO_FAST_PAIR_MESSAGE_TYPE_H_
#include <ostream>
namespace nearby {
namespace fastpair {
// Type values for Fast Pair messages.
enum class FastPairMessageType {
// Key-based Pairing Request.
kKeyBasedPairingRequest,
// Key-based Pairing Response.
kKeyBasedPairingResponse,
// Seeker's passkey.
kSeekersPasskey,
// Provider's passkey.
kProvidersPasskey,
// Unknown message type.
kUnknown,
kUnknown = 0,
// Key-based Pairing Request.
kKeyBasedPairingRequest = 1,
// Key-based Pairing Response.
kKeyBasedPairingResponse = 2,
// Seeker's passkey.
kSeekersPasskey = 3,
// Provider's passkey.
kProvidersPasskey = 4,
};
std::ostream& operator<<(std::ostream& stream,
FastPairMessageType message_type);
} // namespace fastpair
} // namespace nearby
@@ -0,0 +1,32 @@
// 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/crypto/fast_pair_message_type.h"
#include "gtest/gtest.h"
namespace nearby {
namespace fastpair {
namespace {
TEST(FastPairMessageTypeTest, FastPairMessageTypeValue) {
EXPECT_EQ(static_cast<int>(FastPairMessageType::kUnknown), 0);
EXPECT_EQ(static_cast<int>(FastPairMessageType::kKeyBasedPairingRequest), 1);
EXPECT_EQ(static_cast<int>(FastPairMessageType::kKeyBasedPairingResponse), 2);
EXPECT_EQ(static_cast<int>(FastPairMessageType::kSeekersPasskey), 3);
EXPECT_EQ(static_cast<int>(FastPairMessageType::kProvidersPasskey), 4);
}
} // namespace
} // namespace fastpair
} // namespace nearby
@@ -15,18 +15,22 @@
#include "fastpair/handshake/fast_pair_handshake_lookup.h"
#include <memory>
#include <optional>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "fastpair/handshake/fast_pair_handshake_impl.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace fastpair {
FastPairHandshakeLookup* FastPairHandshakeLookup::instance_ = nullptr;
absl::Mutex FastPairHandshakeLookup::mutex_(absl::kConstInit);
namespace {
absl::optional<FastPairHandshakeLookup::CreateFunction> g_test_create_function =
std::nullopt;
}
// static
FastPairHandshakeLookup* FastPairHandshakeLookup::GetInstance() {
@@ -37,6 +41,12 @@ FastPairHandshakeLookup* FastPairHandshakeLookup::GetInstance() {
return instance_;
}
// static Create function override which can be set by tests.
void FastPairHandshakeLookup::SetCreateFunctionForTesting(
CreateFunction create_function) {
g_test_create_function = std::move(create_function);
}
FastPairHandshake* FastPairHandshakeLookup::Get(FastPairDevice* device) {
absl::MutexLock lock(&mutex_);
auto it = fast_pair_handshakes_.find(device);
@@ -80,8 +90,11 @@ FastPairHandshake* FastPairHandshakeLookup::Create(
FastPairDevice& device, Mediums& mediums, OnCompleteCallback on_complete) {
absl::MutexLock lock(&mutex_);
auto it = fast_pair_handshakes_.emplace(
&device, std::make_unique<FastPairHandshakeImpl>(device, mediums,
std::move(on_complete)));
&device, g_test_create_function.has_value()
? g_test_create_function.value()(device, mediums,
std::move(on_complete))
: std::make_unique<FastPairHandshakeImpl>(
device, mediums, std::move(on_complete)));
DCHECK(it.second);
return it.first->second.get();
}
@@ -15,6 +15,7 @@
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_LOOKUP_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_HANDSHAKE_LOOKUP_H_
#include <functional>
#include <memory>
#include <optional>
@@ -35,12 +36,17 @@ class FastPairHandshakeLookup {
using OnCompleteCallback = absl::AnyInvocable<void(
FastPairDevice& device, std::optional<PairFailure> failure)>;
using CreateFunction = absl::AnyInvocable<std::unique_ptr<FastPairHandshake>(
FastPairDevice& device, Mediums& mediums, OnCompleteCallback callback)>;
// This is the static method that controls the access to the singleton
// instance. On the first run, it creates a singleton object and places it
// into the static field. On subsequent runs, it returns the existing object
// stored in the static field.
static FastPairHandshakeLookup* GetInstance();
static void SetCreateFunctionForTesting(CreateFunction create_function);
// Singletons should not be cloneable.
FastPairHandshakeLookup(const FastPairHandshakeLookup&) = delete;
// Singletons should not be assignable.
+68
View File
@@ -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 = [
"fast_pair_pairer_impl.cc",
],
hdrs = [
"fast_pair_pairer.h",
"fast_pair_pairer_impl.h",
],
compatible_with = ["//buildenv/target:non_prod"],
visibility = [
"//fastpair:__subpackages__",
"//internal:__subpackages__",
],
deps = [
"//fastpair/common",
"//fastpair/crypto",
"//fastpair/handshake",
"//fastpair/internal/mediums",
"//internal/platform:comm",
"//internal/platform:types",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/time",
],
)
cc_test(
name = "fast_pair_pairer_impl_test",
size = "small",
srcs = [
"fast_pair_pairer_impl_test.cc",
],
shard_count = 16,
deps = [
":pairing",
"//fastpair/common",
"//fastpair/handshake",
"//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:any_invocable",
"@com_google_absl//absl/functional:bind_front",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
)
@@ -0,0 +1,51 @@
// 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_FASTPAIR_FAST_PAIR_PAIRER_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_FASTPAIR_FAST_PAIR_PAIRER_H_
#include "absl/functional/any_invocable.h"
#include "fastpair/common/fast_pair_device.h"
#include "fastpair/common/pair_failure.h"
namespace nearby {
namespace fastpair {
// A FastPairPairer instance is responsible for the pairing procedure to a
// single device. Pairing begins on instantiation.
class FastPairPairer {
public:
// Triggered when paired with the remote device.
using OnPairedCallback = absl::AnyInvocable<void(FastPairDevice& device)>;
// Triggered when failed to pair with the remote device.
using OnPairingFailedCallback =
absl::AnyInvocable<void(FastPairDevice& device, PairFailure failure)>;
// Triggered when completed the whole pairing process,
// including pairing with the remote device and writing the accountkey to it.
using OnPairingCompletedCallback =
absl::AnyInvocable<void(FastPairDevice& device)>;
// Triggered when failed to write accountkey to the remote device.
using OnAccountKeyFailureCallback =
absl::AnyInvocable<void(FastPairDevice& device, PairFailure failure)>;
virtual ~FastPairPairer() = default;
virtual void StartPairing() = 0;
virtual bool IsPaired() = 0;
virtual bool CancelPairing() = 0;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_FASTPAIR_FAST_PAIR_PAIRER_H_
@@ -0,0 +1,350 @@
// 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/fastpair/fast_pair_pairer_impl.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/time/time.h"
#include "fastpair/common/fast_pair_device.h"
#include "fastpair/common/pair_failure.h"
#include "fastpair/crypto/fast_pair_message_type.h"
#include "fastpair/handshake/fast_pair_handshake_lookup.h"
#include "fastpair/internal/mediums/mediums.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/single_thread_executor.h"
namespace nearby {
namespace fastpair {
namespace {
constexpr absl::Duration kInitiatePairingTimeout = absl::Seconds(20);
} // namespace
// static
FastPairPairerImpl::Factory* FastPairPairerImpl::Factory::g_test_factory_ =
nullptr;
// static
std::unique_ptr<FastPairPairer> FastPairPairerImpl::Factory::Create(
FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor,
OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb,
OnAccountKeyFailureCallback on_account_failure_cb,
OnPairingCompletedCallback on_pairing_completed_cb) {
if (g_test_factory_) {
return g_test_factory_->CreateInstance(
device, medium, executor, std::move(on_paired_cb),
std::move(on_pair_failed_cb), std::move(on_account_failure_cb),
std::move(on_pairing_completed_cb));
}
return std::make_unique<FastPairPairerImpl>(
device, medium, executor, std::move(on_paired_cb),
std::move(on_pair_failed_cb), std::move(on_account_failure_cb),
std::move(on_pairing_completed_cb));
}
// static
void FastPairPairerImpl::Factory::SetFactoryForTesting(
Factory* g_test_factory) {
g_test_factory_ = g_test_factory;
}
FastPairPairerImpl::FastPairPairerImpl(
FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor,
OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb,
OnAccountKeyFailureCallback on_account_failure_cb,
OnPairingCompletedCallback on_pairing_completed_cb)
: device_(device),
mediums_(medium),
executor_(executor),
on_paired_cb_(std::move(on_paired_cb)),
on_pair_failed_cb_(std::move(on_pair_failed_cb)),
on_account_key_failure_cb_(std::move(on_account_failure_cb)),
on_pairing_completed_cb_(std::move(on_pairing_completed_cb)) {
if (device_.GetVersion().value() == DeviceFastPairVersion::kHigherThanV1) {
// Obtains the established GATT connection for use in the pairing process:
// confirm the passkey, write the account key, etc.
fast_pair_handshake_ =
FastPairHandshakeLookup::GetInstance()->Get(&device_);
CHECK(fast_pair_handshake_);
CHECK(fast_pair_handshake_->completed_successfully());
fast_pair_gatt_service_client_ =
fast_pair_handshake_->fast_pair_gatt_service_client();
}
}
void FastPairPairerImpl::StartPairing() {
executor_->Execute(
"Start Pairing", [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
NEARBY_LOGS(INFO) << __func__ << device_;
switch (device_.GetProtocol()) {
case Protocol::kFastPairInitialPairing:
case Protocol::kFastPairSubsequentPairing:
// This timer captures a pairing timeout.
initiate_pairing_timer_.Start(
kInitiatePairingTimeout / absl::Milliseconds(1), 0, [&]() {
NEARBY_LOGS(WARNING)
<< __func__
<< ": Timeout while attempting to initiate "
"pairing with device.";
NotifyPairingFailed(PairFailure::kPairingTimeout);
});
InitiatePairing();
break;
case Protocol::kFastPairRetroactivePairing:
// Because the devices are already paired, we will directly write an
// account key to the Provider after a shared secret is established.
AttemptSendAccountKey();
break;
}
});
}
// Blocking functions
void FastPairPairerImpl::InitiatePairing() {
NEARBY_LOGS(INFO) << __func__;
// TODO(b/278810942) : Check if device lost first
if (mediums_.GetBluetoothRadio().Enable() &&
mediums_.GetBluetoothClassic().IsAvailable()) {
bluetooth_pairing_ = mediums_.GetBluetoothClassic().CreatePairing(
device_.GetPublicAddress().value());
}
if (!bluetooth_pairing_) {
NotifyPairingFailed(PairFailure::kPairingAndConnect);
return;
}
// Unpair with the remote device before initializing a new pairing request
if (!bluetooth_pairing_->Unpair()) {
NotifyPairingFailed(PairFailure::kPairingAndConnect);
return;
}
if (!bluetooth_pairing_->InitiatePairing({
.on_paired_cb =
[&]() {
if (!initiate_pairing_timer_.IsRunning()) {
NEARBY_LOGS(INFO)
<< __func__ << " Initiating pairing has timed out.";
return;
}
initiate_pairing_timer_.Stop();
NEARBY_LOGS(INFO) << __func__ << " Paired with " << device_;
// On Windows, Pair is exactly the same as Connect.
NotifyPaired();
executor_->Execute(
"Write Accountkey",
[&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) {
AttemptSendAccountKey();
});
},
.on_pairing_error_cb =
[&](api::BluetoothPairingCallback::PairingError error) {
NEARBY_LOGS(INFO)
<< __func__ << "Failed to pair with device due to error "
<< static_cast<int>(error);
NotifyPairingFailed(PairFailure::kPairingAndConnect);
},
.on_pairing_initiated_cb =
[&](api::PairingParams pairingParams) {
if (!initiate_pairing_timer_.IsRunning()) {
NEARBY_LOGS(INFO)
<< __func__ << " Initiating pairing has timed out.";
return;
}
NEARBY_LOGS(INFO) << __func__ << "Initiated pairing request.";
if (device_.GetVersion().value() ==
DeviceFastPairVersion::kV1) {
NEARBY_LOGS(INFO)
<< __func__
<< ": For v1 headset, skip passkey confirmation.";
bluetooth_pairing_->FinishPairing(std::nullopt);
} else {
NEARBY_LOGS(INFO)
<< __func__ << ": For headsets higher than v1, "
<< "confirm passkey before accepting pairing.";
ConfirmPasskey(pairingParams);
}
},
})) {
NotifyPairingFailed(PairFailure::kPairingAndConnect);
}
}
void FastPairPairerImpl::ConfirmPasskey(api::PairingParams pairingParams) {
if (!FastPairHandshakeLookup::GetInstance()->Get(&device_)) {
NEARBY_LOGS(ERROR) << __func__
<< ": BLE device instance lost during passkey exchange";
bluetooth_pairing_->CancelPairing();
NotifyPairingFailed(PairFailure::kDeviceLostMidPairing);
return;
}
expected_passkey_ = pairingParams.passkey;
NEARBY_LOGS(INFO) << __func__
<< " Star to confirm passkey: " << expected_passkey_;
fast_pair_gatt_service_client_->WritePasskeyAsync(
/*message_type=*/0x02, std::stoi(expected_passkey_),
*fast_pair_handshake_->fast_pair_data_encryptor(),
[&](absl::string_view response,
std::optional<fastpair::PairFailure> failure) {
OnPasskeyResponse(response, failure);
});
}
void FastPairPairerImpl::OnPasskeyResponse(absl::string_view response,
std::optional<PairFailure> failure) {
NEARBY_LOGS(INFO) << __func__;
if (failure.has_value()) {
NotifyPairingFailed(failure.value());
return;
}
std::vector<uint8_t> response_bytes(response.begin(), response.end());
fast_pair_handshake_->fast_pair_data_encryptor()->ParseDecryptPasskey(
response_bytes, [&](const std::optional<DecryptedPasskey> passkey) {
OnParseDecryptedPasskey(passkey);
});
}
void FastPairPairerImpl::OnParseDecryptedPasskey(
std::optional<DecryptedPasskey> passkey) {
if (!passkey.has_value()) {
NotifyPairingFailed(PairFailure::kPasskeyDecryptFailure);
return;
}
if (passkey->message_type != FastPairMessageType::kProvidersPasskey) {
NEARBY_LOGS(WARNING)
<< "Incorrect message type from decrypted passkey. Expected: "
<< FastPairMessageType::kProvidersPasskey
<< ". Actual: " << passkey->message_type;
NotifyPairingFailed(PairFailure::kIncorrectPasskeyResponseType);
return;
}
if (passkey->passkey != std::stoi(expected_passkey_)) {
NEARBY_LOGS(ERROR) << "Passkeys do not match. "
<< "Expected: " << expected_passkey_
<< ". Actual: " << std::to_string(passkey->passkey);
NotifyPairingFailed(PairFailure::kPasskeyMismatch);
return;
}
// TODO(b/278810942) : Check if device lost
NEARBY_LOGS(INFO) << __func__ << ": Passkeys match, confirming pairing";
bluetooth_pairing_->FinishPairing(std::nullopt);
}
bool FastPairPairerImpl::CancelPairing() {
if (!bluetooth_pairing_) {
NEARBY_LOGS(WARNING) << __func__ << ": No on-going pairing process.";
return false;
}
return bluetooth_pairing_->CancelPairing();
}
bool FastPairPairerImpl::IsPaired() {
if (!bluetooth_pairing_) {
return false;
}
return bluetooth_pairing_->IsPaired();
}
void FastPairPairerImpl::AttemptSendAccountKey() {
if (device_.GetVersion().value() == DeviceFastPairVersion::kV1) {
NotifyPairingCompleted();
return;
}
// We only send the account key if we're doing an initial or retroactive
// pairing. For subsequent pairing, we have to save the account key
// locally so that we can refer to it in API calls to the server.
if (device_.GetProtocol() == Protocol::kFastPairSubsequentPairing) {
NEARBY_LOGS(VERBOSE) << __func__
<< ": Saving Account Key locally for subsequent pair";
// TODO(b/278807993): Saving Account Key locally for subsequent pair
NotifyPairingCompleted();
return;
}
// TODO(b/281781730) : Check if we need to send account key
// TODO(b/281782018) : Handle BLE address rotation
fast_pair_gatt_service_client_->WriteAccountKey(
*fast_pair_handshake_->fast_pair_data_encryptor(),
[&](const std::optional<AccountKey> account_key,
const std::optional<PairFailure> failure) {
OnWriteAccountKey(account_key, failure);
});
}
void FastPairPairerImpl::OnWriteAccountKey(
std::optional<AccountKey> account_key, std::optional<PairFailure> failure) {
if (failure.has_value()) {
NEARBY_LOGS(WARNING)
<< __func__ << "Failed to write account key to device due to error: "
<< failure.value();
NotifyAccountKeyFailure(failure.value());
return;
}
if (!account_key.has_value()) {
NotifyAccountKeyFailure(PairFailure::kAccountKeyCharacteristicWrite);
return;
}
device_.SetAccountKey(account_key.value());
// // TODO(b/281785681): Write account association to footprints
NotifyPairingCompleted();
}
void FastPairPairerImpl::NotifyPaired() {
NEARBY_LOGS(INFO) << __func__ << device_;
executor_->Execute("NotifyPaired",
[&, on_paired_cb = std::move(on_paired_cb_)]() mutable {
on_paired_cb(device_);
});
}
void FastPairPairerImpl::NotifyPairingFailed(PairFailure failure) {
NEARBY_LOGS(WARNING) << __func__ << failure;
// Stop initiate pairing timer as piaring is terminate this time.
initiate_pairing_timer_.Stop();
executor_->Execute("NotifyPairingFailed",
[&, on_pair_failed_cb = std::move(on_pair_failed_cb_),
failure = std::move(failure)]() mutable {
on_pair_failed_cb(device_, failure);
});
}
void FastPairPairerImpl::NotifyPairingCompleted() {
NEARBY_LOGS(INFO)
<< __func__
<< "Account key written to device. Pairing procedure complete.";
executor_->Execute("Notify Pairing Completed",
[&, on_pairing_completed_cb =
std::move(on_pairing_completed_cb_)]() mutable {
on_pairing_completed_cb(device_);
});
}
void FastPairPairerImpl::NotifyAccountKeyFailure(PairFailure failure) {
NEARBY_LOGS(WARNING) << __func__
<< "Failed to write account key to device due to error: "
<< failure;
executor_->Execute(
"Notify AccountKey Failure",
[&, on_account_key_failure_cb = std::move(on_account_key_failure_cb_),
failure = std::move(failure)]() mutable {
on_account_key_failure_cb(device_, failure);
});
}
} // namespace fastpair
} // namespace nearby
@@ -0,0 +1,119 @@
// 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_FASTPAIR_FAST_PAIR_PAIRER_IMPL_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_FASTPAIR_FAST_PAIR_PAIRER_IMPL_H_
#include <memory>
#include <optional>
#include <string>
#include "fastpair/common/fast_pair_device.h"
#include "fastpair/common/pair_failure.h"
#include "fastpair/crypto/decrypted_passkey.h"
#include "fastpair/handshake/fast_pair_gatt_service_client.h"
#include "fastpair/handshake/fast_pair_handshake.h"
#include "fastpair/internal/mediums/mediums.h"
#include "fastpair/pairing/fastpair/fast_pair_pairer.h"
#include "internal/platform/bluetooth_classic.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/platform/timer_impl.h"
namespace nearby {
namespace fastpair {
class FastPairPairerImpl : public FastPairPairer {
public:
class Factory {
public:
static std::unique_ptr<FastPairPairer> Create(
FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor,
OnPairedCallback on_paired_cb,
OnPairingFailedCallback on_pair_failed_cb,
OnAccountKeyFailureCallback on_account_failure_cb,
OnPairingCompletedCallback on_pairing_completed_cb);
static void SetFactoryForTesting(Factory* test_factory);
protected:
virtual ~Factory() = default;
virtual std::unique_ptr<FastPairPairer> CreateInstance(
FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor,
OnPairedCallback on_paired_cb,
OnPairingFailedCallback on_pair_failed_cb,
OnAccountKeyFailureCallback on_account_failure_cb,
OnPairingCompletedCallback on_pairing_completed_cb) = 0;
private:
static Factory* g_test_factory_;
};
FastPairPairerImpl(FastPairDevice& device, Mediums& medium,
SingleThreadExecutor* executor,
OnPairedCallback on_paired_cb,
OnPairingFailedCallback on_pair_failed_cb,
OnAccountKeyFailureCallback on_account_failure_cb,
OnPairingCompletedCallback on_pairing_completed_cb);
FastPairPairerImpl(const FastPairPairerImpl&) = delete;
FastPairPairerImpl& operator=(const FastPairPairerImpl&) = delete;
FastPairPairerImpl(FastPairPairerImpl&&) = delete;
FastPairPairerImpl& operator=(FastPairPairerImpl&&) = delete;
bool CancelPairing() override;
bool IsPaired() override;
void StartPairing() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) override;
private:
void InitiatePairing();
void ConfirmPasskey(api::PairingParams pairingParams);
// FastPairGattServiceClient::WritePasskey callback
void OnPasskeyResponse(absl::string_view response,
std::optional<PairFailure> failure);
// FastPairDataEncryptor::ParseDecryptedPasskey callback
void OnParseDecryptedPasskey(std::optional<DecryptedPasskey> passkey);
// Attempts to write account key to remote device
void AttemptSendAccountKey() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
// FastPairDataEncryptor::WriteAccountKey callback
void OnWriteAccountKey(std::optional<AccountKey> account_key,
std::optional<PairFailure> failure);
// Notify the result of pairing and writing accoutkey.
void NotifyPaired();
void NotifyPairingFailed(PairFailure failure);
void NotifyPairingCompleted();
void NotifyAccountKeyFailure(PairFailure failure);
std::string expected_passkey_;
FastPairHandshake* fast_pair_handshake_;
FastPairGattServiceClient* fast_pair_gatt_service_client_;
std::unique_ptr<BluetoothPairing> bluetooth_pairing_;
FastPairDevice& device_;
Mediums& mediums_;
SingleThreadExecutor* executor_;
OnPairedCallback on_paired_cb_ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
OnPairingFailedCallback on_pair_failed_cb_
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
OnAccountKeyFailureCallback on_account_key_failure_cb_
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
OnPairingCompletedCallback on_pairing_completed_cb_
ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_);
TimerImpl initiate_pairing_timer_;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_PAIRING_FASTPAIR_FAST_PAIR_PAIRER_IMPL_H_
@@ -0,0 +1,937 @@
// 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/fastpair/fast_pair_pairer_impl.h"
#include <unistd.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/functional/any_invocable.h"
#include "absl/functional/bind_front.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "fastpair//handshake/fast_pair_handshake_lookup.h"
#include "fastpair/common/account_key.h"
#include "fastpair/common/constant.h"
#include "fastpair/common/fast_pair_device.h"
#include "fastpair/common/protocol.h"
#include "fastpair/handshake/fast_pair_data_encryptor_impl.h"
#include "fastpair/handshake/fast_pair_handshake_impl.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/bluetooth_adapter.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/medium_environment.h"
#include "internal/platform/single_thread_executor.h"
#include <openssl/rand.h>
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<uint8_t, kAesBlockByteSize> 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 FastPairPairerImplTest : public testing::Test {
public:
void SetUp() override {
env_.Start();
// Setups seeker device.
mediums_ = std::make_unique<Mediums>();
// Setups provider device.
adapter_provider_ = std::make_unique<BluetoothAdapter>();
adapter_provider_->SetStatus(BluetoothAdapter::Status::kEnabled);
adapter_provider_->SetName("Device-Provider");
adapter_provider_->SetScanMode(
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
bt_provider_ = std::make_unique<BluetoothClassicMedium>(*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<FastPairDevice>(
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(
&FastPairPairerImplTest::CreateConnectedHandshake, this));
CountDownLatch latch(1);
EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Create(
*device_, *mediums_,
[&](FastPairDevice& cb_device, std::optional<PairFailure> 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<FastPairHandshake> CreateConnectedHandshake(
FastPairDevice& device, Mediums& mediums,
FastPairHandshake::OnCompleteCallback callback) {
CountDownLatch latch(1);
auto handshake = std::make_unique<FastPairHandshakeImpl>(
device, mediums,
[&](FastPairDevice& callback_device,
std::optional<PairFailure> 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<void()> trigger_keybase_value_change,
absl::AnyInvocable<void()> trigger_passkey_value_change) {
ble_provider_ = std::make_unique<BleV2Medium>(*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<FastPairDataEncryptor> 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<FastPairDataEncryptor> 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<uint8_t, kAesBlockByteSize> raw_response = kRawResponseBytes;
std::array<uint8_t, 6> 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<uint8_t, kAesBlockByteSize> encryptedResponse =
fast_pair_data_encryptor_->EncryptBytes(raw_response);
std::array<char, kAesBlockByteSize> 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) {
FastPairDataEncryptor* fast_pair_data_encryptor_ =
handshake_->fast_pair_data_encryptor();
std::array<uint8_t, kAesBlockByteSize> 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<uint8_t, kAesBlockByteSize> encryptedResponse =
fast_pair_data_encryptor_->EncryptBytes(raw_response);
std::array<char, kAesBlockByteSize> 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<api::BluetoothPairingCallback::PairingError> 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<uint8_t> account_key_{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
0xCC, 0xDD, 0xEE, 0xFF};
std::unique_ptr<Mediums> mediums_;
std::unique_ptr<FastPairDevice> device_;
BluetoothDevice* remote_device_ = nullptr;
std::unique_ptr<FastPairPairer> fast_pair_pairer_;
SingleThreadExecutor executor_;
private:
MediumEnvironment& env_{MediumEnvironment::Instance()};
std::unique_ptr<FakeFastPairRepository> repository_;
std::unique_ptr<BluetoothClassicMedium> bt_provider_;
std::unique_ptr<BluetoothAdapter> adapter_provider_;
std::unique_ptr<GattServer> gatt_server_;
std::unique_ptr<BleV2Medium> ble_provider_;
FastPairHandshake* handshake_ = nullptr;
std::optional<GattCharacteristic> key_based_characteristic_;
std::optional<GattCharacteristic> passkey_characteristic_;
std::optional<GattCharacteristic> accountkey_characteristic_;
absl::flat_hash_map<GattCharacteristic, CharacteristicData> characteristics_;
Property properties_ = Property::kWrite | Property::kNotify;
Permission permissions_ = Permission::kWrite;
};
TEST_F(FastPairPairerImplTest,
SuccessInitialPairingWithDeviceVersionHigherThanV1) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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));
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
paired_latch.Await();
EXPECT_FALSE(failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
complete_latch.Await();
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_TRUE(fast_pair_pairer_->IsPaired());
EXPECT_TRUE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, 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 paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_FALSE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
paired_latch.Await();
EXPECT_FALSE(failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
complete_latch.Await();
EXPECT_FALSE(triggered_keybase_value_change);
EXPECT_FALSE(triggered_passkey_value_change);
EXPECT_TRUE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, SuccessSubsequentPairingWithDevice) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
Protocol::kFastPairSubsequentPairing);
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 paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
paired_latch.Await();
EXPECT_FALSE(failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
complete_latch.Await();
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_TRUE(fast_pair_pairer_->IsPaired());
}
TEST_F(FastPairPairerImplTest, 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 paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
complete_latch.Await();
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_FALSE(triggered_passkey_value_change);
EXPECT_TRUE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, FailedToUnPair) {
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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));
});
CreateFastPairHandshakeInstanceForDevice();
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPairingAndConnect);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_FALSE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, FailedToPairingWithAuthTimeout) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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));
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(api::BluetoothPairingCallback::PairingError::kAuthTimeout);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPairingAndConnect);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, NoPasskeyResponse) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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; });
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPasskeyResponseTimeout);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, PasskeyMismatch) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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("654321", kProviderPasskeyType));
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPasskeyMismatch);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, ReceiveWithWrongPasskeyResponse) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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(TriggerPasskeyGattChangedWithWrongResponse());
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPasskeyDecryptFailure);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, ReceiveWithWrongPasskeyMessageType) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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, kSeekerPasskeyType));
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kIncorrectPasskeyResponseType);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest,
SuccessPairingWithDeviceButFailedToWriteAccountkey) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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));
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
SetAccountkeyCharacteristicsWriteResultToFailure();
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) {
EXPECT_TRUE(device.GetAccountKey().Ok());
complete_latch.CountDown();
});
fast_pair_pairer_->StartPairing();
paired_latch.Await();
EXPECT_FALSE(failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
account_failure_latch.Await();
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_TRUE(fast_pair_pairer_->IsPaired());
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
TEST_F(FastPairPairerImplTest, TestCancelPairing) {
ConfigurePairingContext();
CreateMockDevice(DeviceFastPairVersion::kHigherThanV1,
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;
fast_pair_pairer_->CancelPairing();
});
CreateFastPairHandshakeInstanceForDevice();
SetPairingResult(std::nullopt);
CountDownLatch paired_latch(1);
CountDownLatch complete_latch(1);
CountDownLatch failure_latch(1);
CountDownLatch account_failure_latch(1);
EXPECT_FALSE(device_->GetAccountKey().Ok());
fast_pair_pairer_ = FastPairPairerImpl::Factory::Create(
*device_, *mediums_, &executor_,
[&](FastPairDevice& cb_device) { paired_latch.CountDown(); },
[&](FastPairDevice& device, PairFailure failure) {
EXPECT_EQ(failure, PairFailure::kPairingAndConnect);
failure_latch.CountDown();
},
[&](FastPairDevice& device, PairFailure failure) {
account_failure_latch.CountDown();
},
[&](FastPairDevice& device) { complete_latch.CountDown(); });
fast_pair_pairer_->StartPairing();
EXPECT_FALSE(paired_latch.Await(kWaitTimeout).result());
failure_latch.Await();
EXPECT_FALSE(account_failure_latch.Await(kWaitTimeout).result());
EXPECT_FALSE(complete_latch.Await(kWaitTimeout).result());
EXPECT_TRUE(triggered_keybase_value_change);
EXPECT_TRUE(triggered_passkey_value_change);
EXPECT_FALSE(device_->GetAccountKey().Ok());
}
} // namespace fastpair
} // namespace nearby
@@ -143,7 +143,7 @@ bool BluetoothPairing::FinishPairing(
bool BluetoothPairing::CancelPairing() {
NEARBY_LOGS(VERBOSE) << __func__
<< "Start to cancel ongoing pairing process.";
<< " Start to cancel ongoing pairing process.";
try {
if (!pairing_deferral_) {
NEARBY_LOGS(VERBOSE) << __func__ << "No ongoing pairing process.";
@@ -156,7 +156,7 @@ bool BluetoothPairing::CancelPairing() {
// |was_cancelled_| is set so that OnPair(), which is called when the
// deferral is completed, will know that cancellation was the actual result.
was_cancelled_ = true;
pairing_deferral_.Complete();
pairing_deferral_.Close();
NEARBY_LOGS(VERBOSE) << __func__ << "Canceled ongoing pairing process.";
return true;
} catch (std::exception exception) {
@@ -202,7 +202,7 @@ bool BluetoothPairing::Unpair() {
bool BluetoothPairing::IsPaired() {
try {
bool is_paired = bluetooth_device_.DeviceInformation().Pairing().IsPaired();
NEARBY_LOGS(INFO) << __func__ << (is_paired ? "True" : "False");
NEARBY_LOGS(INFO) << __func__ << (is_paired ? " True" : " False");
return is_paired;
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get IsPaired. exception: "