mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Implement FastPairGattServiceClient for fast pair windows
PiperOrigin-RevId: 521005152
This commit is contained in:
committed by
Copybara-Service
parent
be99b60feb
commit
c1835fcfa1
@@ -86,6 +86,10 @@ std::ostream& operator<<(std::ostream& stream, PairFailure failure) {
|
||||
case PairFailure::kPasskeyMismatch:
|
||||
stream << "[Passkeys did not match]";
|
||||
break;
|
||||
case PairFailure::kPairingDeviceLostBetweenGattConnectionAttempts:
|
||||
stream
|
||||
<< "[Potential pairing device lost between GATT connection attempts]";
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
@@ -66,7 +66,9 @@ enum class PairFailure {
|
||||
kIncorrectPasskeyResponseType = 18,
|
||||
// Passkeys did not match.
|
||||
kPasskeyMismatch = 19,
|
||||
kMaxValue = kPasskeyMismatch,
|
||||
// Potential pairing device lost between GATT connection attempts.
|
||||
kPairingDeviceLostBetweenGattConnectionAttempts = 20,
|
||||
kMaxValue = kPairingDeviceLostBetweenGattConnectionAttempts,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, PairFailure failure);
|
||||
|
||||
@@ -71,6 +71,7 @@ cc_test(
|
||||
deps = [
|
||||
":decoder",
|
||||
"//fastpair/testing",
|
||||
"//internal/platform/implementation/g3", # build_cleaner: keep
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
|
||||
@@ -18,10 +18,13 @@ cc_library(
|
||||
name = "handshake",
|
||||
srcs = [
|
||||
"fast_pair_data_encryptor_impl.cc",
|
||||
"fast_pair_gatt_service_client_impl.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"fast_pair_data_encryptor.h",
|
||||
"fast_pair_data_encryptor_impl.h",
|
||||
"fast_pair_gatt_service_client.h",
|
||||
"fast_pair_gatt_service_client_impl.h",
|
||||
],
|
||||
visibility = [
|
||||
"//:__subpackages__",
|
||||
@@ -31,11 +34,15 @@ cc_library(
|
||||
"//fastpair/common",
|
||||
"//fastpair/crypto",
|
||||
"//fastpair/dataparser",
|
||||
"//fastpair/internal/ble",
|
||||
"//fastpair/repository",
|
||||
"//fastpair/server_access",
|
||||
"//internal/base:bluetooth_address",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:comm",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform:uuid",
|
||||
"@boringssl//:crypto",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/functional:bind_front",
|
||||
@@ -71,6 +78,7 @@ cc_test(
|
||||
"//fastpair/common",
|
||||
"//fastpair/crypto",
|
||||
"//fastpair/dataparser",
|
||||
"//fastpair/internal/ble",
|
||||
"//fastpair/server_access:test_support",
|
||||
"//fastpair/testing",
|
||||
"//internal/platform:logging",
|
||||
@@ -83,3 +91,29 @@ cc_test(
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fast_pair_gatt_service_client_impl_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"fast_pair_gatt_service_client_impl_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":handshake",
|
||||
":test_support",
|
||||
"//fastpair/common",
|
||||
"//fastpair/testing",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:comm",
|
||||
"//internal/platform:test_util",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation/g3", # build_cleaner: keep
|
||||
"//internal/test",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_
|
||||
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "fastpair/common/constant.h"
|
||||
#include "fastpair/common/pair_failure.h"
|
||||
#include "fastpair/handshake/fast_pair_data_encryptor.h"
|
||||
#include "internal/platform/ble_v2.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
using WriteResponseCallback = absl::AnyInvocable<void(
|
||||
absl::string_view value, std::optional<PairFailure> failure)>;
|
||||
|
||||
// This class is responsible for connecting to the Fast Pair GATT service for a
|
||||
// device and invoking a callback when ready, or when an error is discovered
|
||||
// during initialization.
|
||||
class FastPairGattServiceClient {
|
||||
public:
|
||||
virtual ~FastPairGattServiceClient() = default;
|
||||
|
||||
virtual void InitializeGattConnection(
|
||||
absl::AnyInvocable<void(std::optional<PairFailure>)>
|
||||
on_gatt_initialized_callback) = 0;
|
||||
|
||||
// Constructs a data vector based on the message type, flags, provider
|
||||
// address, and seekers address.
|
||||
// Subscribe a notification for key based Pairing.
|
||||
// Once the notification subscribed successfully, the message data will be
|
||||
// written to the key based characteristic.
|
||||
virtual void WriteRequestAsync(
|
||||
uint8_t message_type, uint8_t flags, absl::string_view provider_address,
|
||||
absl::string_view seekers_address,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback write_response_callback) = 0;
|
||||
|
||||
// Constructs a data vector based on the message type and passkey.
|
||||
// Subscribe a notification for the passkey.
|
||||
// Once the notification subscribed successfully, the passkey data will be
|
||||
// written to the passkey characteristic.
|
||||
virtual void WritePasskeyAsync(
|
||||
uint8_t message_type, uint32_t passkey,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback write_response_callback) = 0;
|
||||
};
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_H_
|
||||
@@ -0,0 +1,456 @@
|
||||
// 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/handshake/fast_pair_gatt_service_client_impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/functional/bind_front.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "fastpair/common/constant.h"
|
||||
#include "fastpair/handshake/fast_pair_data_encryptor.h"
|
||||
#include "fastpair/handshake/fast_pair_gatt_service_client.h"
|
||||
#include "internal/base/bluetooth_address.h"
|
||||
#include "internal/platform/ble_v2.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/uuid.h"
|
||||
#include <openssl/rand.h>
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
namespace {
|
||||
// We have two UUID possibilities for each characteristic because they changed
|
||||
// across different Fast Pair versions.
|
||||
constexpr Uuid kFastPairServiceUuid(0x0000FE2C00001000, 0x800000805F9B34FB);
|
||||
constexpr Uuid kKeyBasedCharacteristicUuidV1(0x0000123400001000,
|
||||
0x800000805F9B34FB);
|
||||
constexpr Uuid kKeyBasedCharacteristicUuidV2(0xFE2C123483664814,
|
||||
0x8EB001DE32100BEA);
|
||||
constexpr Uuid kPasskeyCharacteristicUuidV1(0x0000123500001000,
|
||||
0x800000805F9B34FB);
|
||||
constexpr Uuid kPasskeyCharacteristicUuidV2(0xFE2C123583664814,
|
||||
0x8EB001DE32100BEA);
|
||||
|
||||
constexpr absl::Duration kGattOperationTimeout = absl::Seconds(15);
|
||||
constexpr int kMaxNumGattConnectionAttempts = 3;
|
||||
} // namespace
|
||||
|
||||
// FastPairGattServiceClientImpl::Factory
|
||||
// static
|
||||
FastPairGattServiceClientImpl::Factory*
|
||||
FastPairGattServiceClientImpl::Factory::g_test_factory_ = nullptr;
|
||||
|
||||
// static
|
||||
std::unique_ptr<FastPairGattServiceClient>
|
||||
FastPairGattServiceClientImpl::Factory::Create(const FastPairDevice& device) {
|
||||
if (g_test_factory_) {
|
||||
return g_test_factory_->CreateInstance();
|
||||
}
|
||||
|
||||
return std::make_unique<FastPairGattServiceClientImpl>(device);
|
||||
}
|
||||
|
||||
// static
|
||||
void FastPairGattServiceClientImpl::Factory::SetFactoryForTesting(
|
||||
Factory* g_test_factory) {
|
||||
g_test_factory_ = g_test_factory;
|
||||
}
|
||||
|
||||
FastPairGattServiceClientImpl::Factory::~Factory() = default;
|
||||
|
||||
FastPairGattServiceClientImpl::FastPairGattServiceClientImpl(
|
||||
const FastPairDevice& device)
|
||||
: device_address_(device.GetBleAddress()) {}
|
||||
|
||||
void FastPairGattServiceClientImpl::InitializeGattConnection(
|
||||
absl::AnyInvocable<void(std::optional<PairFailure>)>
|
||||
on_gatt_initialized_callback) {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Starting the GATT connection to the device.";
|
||||
on_gatt_initialized_callback_ = std::move(on_gatt_initialized_callback);
|
||||
AttemptGattConnection();
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::AttemptGattConnection() {
|
||||
NEARBY_LOGS(INFO) << __func__ << ": Attempt to connect to the device.";
|
||||
if (num_gatt_connection_attempts_ == kMaxNumGattConnectionAttempts) {
|
||||
NotifyInitializedError(PairFailure::kCreateGattConnection);
|
||||
return;
|
||||
}
|
||||
num_gatt_connection_attempts_++;
|
||||
NEARBY_LOGS(INFO) << __func__ << ": Starting GATT connection attempt #"
|
||||
<< num_gatt_connection_attempts_ << " to device";
|
||||
|
||||
if (gatt_client_) {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Disconnecting previous connections before attempt";
|
||||
gatt_client_->Disconnect();
|
||||
gatt_client_ = nullptr;
|
||||
}
|
||||
CreateGattConnection();
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::CreateGattConnection() {
|
||||
NEARBY_LOGS(INFO) << __func__ << " : Create Gatt Connection to the device.";
|
||||
gatt_client_ = ble_.ConnectToGattServer(device_address_);
|
||||
if (!gatt_client_) {
|
||||
// The device must have been lost between connection attempts.
|
||||
NotifyInitializedError(
|
||||
PairFailure::kPairingDeviceLostBetweenGattConnectionAttempts);
|
||||
return;
|
||||
}
|
||||
DiscoverServiceAndCharacteristics();
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::DiscoverServiceAndCharacteristics() {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< " : Start to discovery servie and characteristic.";
|
||||
gatt_service_discovery_timer_.Start(
|
||||
kGattOperationTimeout / absl::Milliseconds(1), 0,
|
||||
[&]() { OnGattServiceDiscoveryTimeout(); });
|
||||
|
||||
if (gatt_client_->DiscoverServiceAndCharacteristics(
|
||||
kFastPairServiceUuid,
|
||||
{kKeyBasedCharacteristicUuidV2, kPasskeyCharacteristicUuidV2}) ||
|
||||
gatt_client_->DiscoverServiceAndCharacteristics(
|
||||
kFastPairServiceUuid,
|
||||
{kKeyBasedCharacteristicUuidV1, kPasskeyCharacteristicUuidV1})) {
|
||||
gatt_service_discovery_timer_.Stop();
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Completed discovery for Fast Pair GATT service and "
|
||||
"characterisitc.";
|
||||
GetFastPairGattCharacteristics();
|
||||
return;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Failed to discovery for Fast Pair GATT service and "
|
||||
"characterisitc."
|
||||
<< PairFailure::kGattServiceDiscovery;
|
||||
AttemptGattConnection();
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::GetFastPairGattCharacteristics() {
|
||||
NEARBY_LOGS(INFO) << __func__ << " :Start to get Fast Pair characteristic.";
|
||||
key_based_characteristic_ = GetCharacteristicsByUUIDs(
|
||||
kKeyBasedCharacteristicUuidV1, kKeyBasedCharacteristicUuidV2);
|
||||
if (!key_based_characteristic_.has_value()) {
|
||||
NotifyInitializedError(
|
||||
PairFailure::kKeyBasedPairingCharacteristicDiscovery);
|
||||
return;
|
||||
}
|
||||
|
||||
passkey_characteristic_ = GetCharacteristicsByUUIDs(
|
||||
kPasskeyCharacteristicUuidV1, kPasskeyCharacteristicUuidV2);
|
||||
if (!passkey_characteristic_.has_value()) {
|
||||
NotifyInitializedError(PairFailure::kPasskeyCharacteristicDiscovery);
|
||||
return;
|
||||
}
|
||||
|
||||
is_initialized_ = true;
|
||||
std::move(on_gatt_initialized_callback_)(absl::nullopt);
|
||||
}
|
||||
|
||||
std::optional<GattCharacteristic>
|
||||
FastPairGattServiceClientImpl::GetCharacteristicsByUUIDs(const Uuid& uuidV1,
|
||||
const Uuid& uuidV2) {
|
||||
// Default to V2 device to match Android implementation.
|
||||
std::optional<GattCharacteristic> characteristics =
|
||||
gatt_client_->GetCharacteristic(kFastPairServiceUuid, uuidV2);
|
||||
if (characteristics.has_value()) {
|
||||
return characteristics;
|
||||
}
|
||||
return gatt_client_->GetCharacteristic(kFastPairServiceUuid, uuidV1);
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::OnGattServiceDiscoveryTimeout() {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": reattempting from previous GATT connection failure: "
|
||||
<< PairFailure::kGattServiceDiscoveryTimeout;
|
||||
AttemptGattConnection();
|
||||
}
|
||||
|
||||
std::array<uint8_t, kAesBlockByteSize>
|
||||
FastPairGattServiceClientImpl::CreateRequest(
|
||||
uint8_t message_type, uint8_t flags, absl::string_view provider_address,
|
||||
absl::string_view seekers_address) {
|
||||
std::array<uint8_t, kAesBlockByteSize> data_to_write;
|
||||
RAND_bytes(data_to_write.data(), kAesBlockByteSize);
|
||||
|
||||
data_to_write[0] = message_type;
|
||||
data_to_write[1] = flags;
|
||||
|
||||
std::array<uint8_t, 6> provider_address_bytes;
|
||||
|
||||
device::ParseBluetoothAddress(provider_address,
|
||||
absl::MakeSpan(provider_address_bytes.data(),
|
||||
provider_address_bytes.size()));
|
||||
std::copy(provider_address_bytes.begin(), provider_address_bytes.end(),
|
||||
std::begin(data_to_write) + kProviderAddressStartIndex);
|
||||
|
||||
// Seekers address can be empty, in which we would just have the bytes be
|
||||
// the salt.
|
||||
if (!seekers_address.empty()) {
|
||||
std::array<uint8_t, 6> seeker_address_bytes;
|
||||
device::ParseBluetoothAddress(seekers_address,
|
||||
absl::MakeSpan(seeker_address_bytes.data(),
|
||||
seeker_address_bytes.size()));
|
||||
std::copy(seeker_address_bytes.begin(), seeker_address_bytes.end(),
|
||||
std::begin(data_to_write) + kSeekerAddressStartIndex);
|
||||
}
|
||||
|
||||
return data_to_write;
|
||||
}
|
||||
|
||||
std::array<uint8_t, kAesBlockByteSize>
|
||||
FastPairGattServiceClientImpl::CreatePasskeyBlock(uint8_t message_type,
|
||||
uint32_t passkey) {
|
||||
std::array<uint8_t, kAesBlockByteSize> data_to_write;
|
||||
RAND_bytes(data_to_write.data(), kAesBlockByteSize);
|
||||
|
||||
data_to_write[0] = message_type;
|
||||
|
||||
// Need to convert the uint_32 to uint_8 to use in our data vector.
|
||||
data_to_write[1] = (passkey & 0x00ff0000) >> 16;
|
||||
data_to_write[2] = (passkey & 0x0000ff00) >> 8;
|
||||
data_to_write[3] = passkey & 0x000000ff;
|
||||
return data_to_write;
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::WriteRequestAsync(
|
||||
uint8_t message_type, uint8_t flags, absl::string_view provider_address,
|
||||
absl::string_view seekers_address,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback callback) {
|
||||
DCHECK(is_initialized_);
|
||||
DCHECK(!key_based_write_response_callback_);
|
||||
// The key based request should only ever be written once
|
||||
DCHECK(!is_key_based_notification_subscribed_);
|
||||
|
||||
key_based_write_response_callback_ = std::move(callback);
|
||||
|
||||
const std::array<uint8_t, kAesBlockByteSize> data_to_write =
|
||||
fast_pair_data_encryptor.EncryptBytes(CreateRequest(
|
||||
message_type, flags, provider_address, seekers_address));
|
||||
std::vector<uint8_t> data_to_write_vec(data_to_write.begin(),
|
||||
data_to_write.end());
|
||||
|
||||
// Append the public version of the private key to the message so thedevice
|
||||
// can generate the shared secret to decrypt the message.
|
||||
const std::optional<std::array<uint8_t, 64>> public_key =
|
||||
fast_pair_data_encryptor.GetPublicKey();
|
||||
|
||||
if (public_key) {
|
||||
const std::vector<uint8_t> public_key_vec = std::vector<uint8_t>(
|
||||
public_key.value().begin(), public_key.value().end());
|
||||
data_to_write_vec.insert(data_to_write_vec.end(), public_key_vec.begin(),
|
||||
public_key_vec.end());
|
||||
}
|
||||
// Subscribe the notification once the keybased characteristic's value changed
|
||||
if (SubscribeKeyBasedCharacteristic()) {
|
||||
is_key_based_notification_subscribed_ = true;
|
||||
// Write public address request to the keybased characteristic
|
||||
WriteKeyBasedCharacteristic(
|
||||
std::string(data_to_write_vec.begin(), data_to_write_vec.end()));
|
||||
}
|
||||
}
|
||||
|
||||
bool FastPairGattServiceClientImpl::SubscribeKeyBasedCharacteristic() {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< " :Start to subscribe notification "
|
||||
"once keybased characteristic changed.";
|
||||
key_based_subscription_timer_.Start(
|
||||
kGattOperationTimeout / absl::Milliseconds(1), 0,
|
||||
absl::bind_front(
|
||||
&FastPairGattServiceClientImpl::NotifyWriteRequestError, this,
|
||||
PairFailure::kKeyBasedPairingCharacteristicSubscriptionTimeout));
|
||||
|
||||
if (gatt_client_->SetCharacteristicSubscription(
|
||||
key_based_characteristic_.value(), true,
|
||||
[this](absl::string_view value) {
|
||||
FastPairGattServiceClientImpl::OnCharacteristicValueChanged(
|
||||
key_based_characteristic_.value(), value);
|
||||
})) {
|
||||
key_based_subscription_timer_.Stop();
|
||||
NEARBY_LOGS(INFO)
|
||||
<< __func__ << ": Successfully subscribe the key based characteristic.";
|
||||
return true;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Failed to subscribe the key based characteristic.";
|
||||
NotifyWriteRequestError(
|
||||
PairFailure::kKeyBasedPairingCharacteristicSubscription);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::WriteKeyBasedCharacteristic(
|
||||
absl::string_view request) {
|
||||
NEARBY_LOGS(INFO) << __func__ << " :Start to write keybased characteristic.";
|
||||
key_based_write_request_timer_.Start(
|
||||
kGattOperationTimeout / absl::Milliseconds(1), 0,
|
||||
absl::bind_front(&FastPairGattServiceClientImpl::NotifyWriteRequestError,
|
||||
this, PairFailure::kKeyBasedPairingResponseTimeout));
|
||||
|
||||
if (gatt_client_->WriteCharacteristic(
|
||||
key_based_characteristic_.value(), request,
|
||||
api::ble_v2::GattClient::WriteType::kWithResponse)) {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Successfully write the key basedcharacteristic.";
|
||||
return;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Failed to write the key based characteristic ";
|
||||
NotifyWriteRequestError(PairFailure::kKeyBasedPairingCharacteristicWrite);
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::WritePasskeyAsync(
|
||||
uint8_t message_type, uint32_t passkey,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback callback) {
|
||||
DCHECK(is_initialized_);
|
||||
DCHECK(message_type == kSeekerPasskey);
|
||||
|
||||
passkey_write_response_callback_ = std::move(callback);
|
||||
const std::array<uint8_t, kAesBlockByteSize> data_to_write =
|
||||
fast_pair_data_encryptor.EncryptBytes(
|
||||
CreatePasskeyBlock(message_type, passkey));
|
||||
std::vector<uint8_t> data_to_write_vec(data_to_write.begin(),
|
||||
data_to_write.end());
|
||||
|
||||
// Subscribe the notification once the passkey characteristic's value changed
|
||||
if (SubscribePasskeyCharacteristic()) {
|
||||
is_passkey_notification_subscribed_ = true;
|
||||
// Write passkey confonirmation request to the passkey characteristic
|
||||
WritePasskeyCharacteristic(
|
||||
std::string(data_to_write_vec.begin(), data_to_write_vec.end()));
|
||||
}
|
||||
}
|
||||
|
||||
bool FastPairGattServiceClientImpl::SubscribePasskeyCharacteristic() {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< " :Start to subscribe notification "
|
||||
"once passkey characteristic changed.";
|
||||
passkey_subscription_timer_.Start(
|
||||
kGattOperationTimeout / absl::Milliseconds(1), 0,
|
||||
absl::bind_front(&FastPairGattServiceClientImpl::NotifyWritePasskeyError,
|
||||
this,
|
||||
PairFailure::kPasskeyCharacteristicSubscriptionTimeout));
|
||||
|
||||
if (gatt_client_->SetCharacteristicSubscription(
|
||||
passkey_characteristic_.value(), true,
|
||||
[this](absl::string_view value) {
|
||||
FastPairGattServiceClientImpl::OnCharacteristicValueChanged(
|
||||
passkey_characteristic_.value(), value);
|
||||
})) {
|
||||
passkey_subscription_timer_.Stop();
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Successfully subscribe the passkey characteristic.";
|
||||
return true;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Failed to subscribe the passkey characteristic.";
|
||||
NotifyWritePasskeyError(PairFailure::kPasskeyCharacteristicSubscription);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::WritePasskeyCharacteristic(
|
||||
absl::string_view request) {
|
||||
passkey_write_request_timer_.Start(
|
||||
kGattOperationTimeout / absl::Milliseconds(1), 0,
|
||||
absl::bind_front(&FastPairGattServiceClientImpl::NotifyWritePasskeyError,
|
||||
this, PairFailure::kPasskeyResponseTimeout));
|
||||
if (gatt_client_->WriteCharacteristic(
|
||||
passkey_characteristic_.value(), request,
|
||||
api::ble_v2::GattClient::WriteType::kWithResponse)) {
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Successfully write the passkey characteristic.";
|
||||
return;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Failed to write the passkey characteristic ";
|
||||
NotifyWritePasskeyError(PairFailure::kPasskeyPairingCharacteristicWrite);
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::OnCharacteristicValueChanged(
|
||||
const GattCharacteristic& characteristic, absl::string_view value) {
|
||||
// We check that the callbacks still exists still before we run the
|
||||
// it with the response bytes to handle the case where the callback
|
||||
// has already been used to notify error. This can happen if the timer for
|
||||
// fires with an error, and then the write completes successfully after and
|
||||
// we get response bytes here.
|
||||
if (characteristic == key_based_characteristic_.value() &&
|
||||
key_based_write_response_callback_) {
|
||||
key_based_write_request_timer_.Stop();
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": key based characteristic value changed.";
|
||||
std::move(key_based_write_response_callback_)(value,
|
||||
/*failure=*/std::nullopt);
|
||||
} else if (characteristic == passkey_characteristic_.value() &&
|
||||
passkey_write_response_callback_) {
|
||||
passkey_write_request_timer_.Stop();
|
||||
NEARBY_LOGS(INFO) << __func__ << ": Passkey characteristic value changed.";
|
||||
std::move(passkey_write_response_callback_)(value,
|
||||
/*failure=*/std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::NotifyInitializedError(
|
||||
PairFailure failure) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << failure;
|
||||
ClearCurrentState();
|
||||
|
||||
if (on_gatt_initialized_callback_) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << "Executing initialized callback";
|
||||
std::move(on_gatt_initialized_callback_)(failure);
|
||||
}
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::NotifyWriteRequestError(
|
||||
PairFailure failure) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << "NotifyWriteRequestError";
|
||||
key_based_write_request_timer_.Stop();
|
||||
DCHECK(key_based_write_response_callback_);
|
||||
std::move(key_based_write_response_callback_)("", failure);
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::NotifyWritePasskeyError(
|
||||
PairFailure failure) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << "NotifyWritePasskeyError";
|
||||
passkey_write_request_timer_.Stop();
|
||||
DCHECK(passkey_write_response_callback_);
|
||||
std::move(passkey_write_response_callback_)("", failure);
|
||||
}
|
||||
|
||||
void FastPairGattServiceClientImpl::ClearCurrentState() {
|
||||
gatt_client_.reset();
|
||||
key_based_characteristic_ = std::nullopt;
|
||||
passkey_characteristic_ = std::nullopt;
|
||||
gatt_service_discovery_timer_.Stop();
|
||||
passkey_subscription_timer_.Stop();
|
||||
key_based_subscription_timer_.Stop();
|
||||
passkey_write_request_timer_.Stop();
|
||||
key_based_write_request_timer_.Stop();
|
||||
}
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_IMPL_H_
|
||||
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_IMPL_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "fastpair/common/fast_pair_device.h"
|
||||
#include "fastpair/common/pair_failure.h"
|
||||
#include "fastpair/handshake/fast_pair_gatt_service_client.h"
|
||||
#include "fastpair/internal/ble/ble.h"
|
||||
#include "internal/platform/ble_v2.h"
|
||||
#include "internal/platform/timer_impl.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
using GattCharacteristic = api::ble_v2::GattCharacteristic;
|
||||
|
||||
// This class is responsible for connecting to the Fast Pair GATT service for a
|
||||
// device and invoking a callback when ready, or when an error is discovered
|
||||
// during initialization.
|
||||
class FastPairGattServiceClientImpl : public FastPairGattServiceClient {
|
||||
public:
|
||||
class Factory {
|
||||
public:
|
||||
static std::unique_ptr<FastPairGattServiceClient> Create(
|
||||
const FastPairDevice& device);
|
||||
static void SetFactoryForTesting(Factory* test_factory);
|
||||
|
||||
protected:
|
||||
virtual ~Factory();
|
||||
virtual std::unique_ptr<FastPairGattServiceClient> CreateInstance() = 0;
|
||||
|
||||
private:
|
||||
static Factory* g_test_factory_;
|
||||
};
|
||||
|
||||
explicit FastPairGattServiceClientImpl(const FastPairDevice& device);
|
||||
FastPairGattServiceClientImpl(const FastPairGattServiceClientImpl&) = delete;
|
||||
FastPairGattServiceClientImpl& operator=(
|
||||
const FastPairGattServiceClientImpl&) = delete;
|
||||
~FastPairGattServiceClientImpl() override = default;
|
||||
|
||||
void InitializeGattConnection(
|
||||
absl::AnyInvocable<void(std::optional<PairFailure>)>
|
||||
on_gatt_initialized_callback) override;
|
||||
|
||||
void WriteRequestAsync(
|
||||
uint8_t message_type, uint8_t flags, absl::string_view provider_address,
|
||||
absl::string_view seekers_address,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback write_response_callback) override;
|
||||
|
||||
void WritePasskeyAsync(
|
||||
uint8_t message_type, uint32_t passkey,
|
||||
const FastPairDataEncryptor& fast_pair_data_encryptor,
|
||||
WriteResponseCallback write_response_callback) override;
|
||||
|
||||
private:
|
||||
// Attempt to create a GATT connection with the device. This method may be
|
||||
// called multiple times.
|
||||
void AttemptGattConnection();
|
||||
void CreateGattConnection();
|
||||
void DiscoverServiceAndCharacteristics();
|
||||
void GetFastPairGattCharacteristics();
|
||||
std::optional<GattCharacteristic> GetCharacteristicsByUUIDs(
|
||||
const Uuid& uuidV1, const Uuid& uuidV2);
|
||||
|
||||
// Operations on KeyBased Characteristic
|
||||
// Creates a data vector based on parameter information.
|
||||
std::array<uint8_t, kAesBlockByteSize> CreateRequest(
|
||||
uint8_t message_type, uint8_t flags, absl::string_view provider_address,
|
||||
absl::string_view seekers_address);
|
||||
// Subscribe notification when KeyBased Characteristic value changes
|
||||
bool SubscribeKeyBasedCharacteristic();
|
||||
// Write request to KeyBased Characteristic
|
||||
void WriteKeyBasedCharacteristic(absl::string_view request);
|
||||
|
||||
// Operations on Passkey Characteristic
|
||||
// Creates a data vector based on parameter information.
|
||||
std::array<uint8_t, kAesBlockByteSize> CreatePasskeyBlock(
|
||||
uint8_t message_type, uint32_t passkey);
|
||||
// Subscribe notification when Passkey Characteristic value changes
|
||||
bool SubscribePasskeyCharacteristic();
|
||||
// Write request to Passkey Characteristic
|
||||
void WritePasskeyCharacteristic(absl::string_view request);
|
||||
|
||||
// Callback is triggered when characteristic value changes
|
||||
void OnCharacteristicValueChanged(const GattCharacteristic& characteristic,
|
||||
absl::string_view value);
|
||||
|
||||
// Invokes the initialized callback with the proper PairFailure and clears
|
||||
// local state.
|
||||
void NotifyInitializedError(PairFailure failure);
|
||||
// Invokes the write response callback with the proper PairFailure on a
|
||||
// write error.
|
||||
void NotifyWriteRequestError(PairFailure failure);
|
||||
void NotifyWritePasskeyError(PairFailure failure);
|
||||
|
||||
void ClearCurrentState();
|
||||
|
||||
// Timers
|
||||
TimerImpl gatt_service_discovery_timer_;
|
||||
TimerImpl key_based_subscription_timer_;
|
||||
TimerImpl passkey_subscription_timer_;
|
||||
TimerImpl key_based_write_request_timer_;
|
||||
TimerImpl passkey_write_request_timer_;
|
||||
|
||||
void OnGattServiceDiscoveryTimeout();
|
||||
|
||||
// Callback
|
||||
absl::AnyInvocable<void(std::optional<PairFailure>)>
|
||||
on_gatt_initialized_callback_;
|
||||
WriteResponseCallback key_based_write_response_callback_;
|
||||
WriteResponseCallback passkey_write_response_callback_;
|
||||
|
||||
// Fast Pair Characteristic
|
||||
std::optional<GattCharacteristic> key_based_characteristic_;
|
||||
std::optional<GattCharacteristic> passkey_characteristic_;
|
||||
|
||||
bool is_key_based_notification_subscribed_ = false;
|
||||
bool is_passkey_notification_subscribed_ = false;
|
||||
|
||||
// Initialize with zero failures.
|
||||
int num_gatt_connection_attempts_ = 0;
|
||||
|
||||
bool is_initialized_ = false;
|
||||
std::string device_address_;
|
||||
std::unique_ptr<GattClient> gatt_client_;
|
||||
Ble ble_;
|
||||
};
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_GATT_SERVICE_CLIENT_IMPL_H_
|
||||
@@ -0,0 +1,347 @@
|
||||
// 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/handshake/fast_pair_gatt_service_client_impl.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "fastpair/common/constant.h"
|
||||
#include "fastpair/common/fast_pair_device.h"
|
||||
#include "fastpair/common/pair_failure.h"
|
||||
#include "fastpair/handshake/fake_fast_pair_data_encryptor.h"
|
||||
#include "internal/platform/ble_v2.h"
|
||||
#include "internal/platform/bluetooth_adapter.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/count_down_latch.h"
|
||||
#include "internal/platform/implementation/system_clock.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
namespace {
|
||||
using Property = nearby::api::ble_v2::GattCharacteristic::Property;
|
||||
using Permission = nearby::api::ble_v2::GattCharacteristic::Permission;
|
||||
using GattCharacteristic = nearby::api::ble_v2::GattCharacteristic;
|
||||
using WriteType = nearby::api::ble_v2::GattClient::WriteType;
|
||||
|
||||
constexpr absl::Duration kGattOperationTimeout = absl::Seconds(15);
|
||||
constexpr absl::string_view kMetadataId("test_id");
|
||||
constexpr absl::string_view kProviderAddress("11:22:33:44:55:66");
|
||||
constexpr absl::string_view kSeekerAddress("AA:BB:CC:DD:EE:00");
|
||||
constexpr Uuid kFastPairServiceUuid(0x0000FE2C00001000, 0x800000805F9B34FB);
|
||||
constexpr Uuid kKeyBasedCharacteristicUuidV2(0xFE2C123483664814,
|
||||
0x8EB001DE32100BEA);
|
||||
constexpr Uuid kPasskeyCharacteristicUuidV2(0xFE2C123583664814,
|
||||
0x8EB001DE32100BEA);
|
||||
// Length of advertisement byte should be 16
|
||||
constexpr absl::string_view kKeyBasedCharacteristicAdvertisementByte =
|
||||
"keyBasedCharacte";
|
||||
constexpr absl::string_view kPasskeyharacteristicAdvertisementByte =
|
||||
"passkeyCharacter";
|
||||
constexpr Uuid kWrongServiceId(0x0000FE2B00001000, 0x800000805F9B34FB);
|
||||
constexpr uint8_t kMessageType = 0x00;
|
||||
constexpr uint8_t kFlags = 0x00;
|
||||
|
||||
constexpr uint32_t kPasskey = 123456;
|
||||
|
||||
constexpr std::array<uint8_t, 64> kPublicKey = {
|
||||
0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F,
|
||||
0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3,
|
||||
0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01,
|
||||
0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45,
|
||||
0x61, 0xC3, 0x32, 0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32,
|
||||
0x1D, 0x01, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};
|
||||
} // namespace
|
||||
|
||||
class FastPairGattServiceClientTest : public testing::Test {
|
||||
public:
|
||||
FastPairGattServiceClientTest() {
|
||||
fast_pair_data_encryptor_ = std::make_unique<FakeFastPairDataEncryptor>();
|
||||
fast_pair_data_encryptor_->SetPublickey(kPublicKey);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
env_.Start({.use_simulated_clock = true});
|
||||
BluetoothAdapter adapter;
|
||||
BleV2Medium ble(adapter);
|
||||
gatt_server_ = ble.StartGattServer(/*ServerGattConnectionCallback=*/{});
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
key_based_characteristic_ = std::nullopt;
|
||||
passkey_characteristic_ = std::nullopt;
|
||||
initalized_failure_ = std::nullopt;
|
||||
write_failure_ = std::nullopt;
|
||||
gatt_client_.reset();
|
||||
gatt_server_->Stop();
|
||||
gatt_server_.reset();
|
||||
env_.Stop();
|
||||
}
|
||||
|
||||
void InsertCorrectGattCharacteristics() {
|
||||
key_based_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kKeyBasedCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
key_based_characteristic_.value(),
|
||||
ByteArray(std::string(kKeyBasedCharacteristicAdvertisementByte)));
|
||||
|
||||
passkey_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kPasskeyCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
passkey_characteristic_.value(),
|
||||
ByteArray(std::string(kPasskeyharacteristicAdvertisementByte)));
|
||||
}
|
||||
|
||||
void InsertCharacteristicsWithWrongServiceId() {
|
||||
key_based_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kWrongServiceId, kKeyBasedCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
key_based_characteristic_.value(),
|
||||
ByteArray(std::string(kKeyBasedCharacteristicAdvertisementByte)));
|
||||
|
||||
passkey_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kWrongServiceId, kPasskeyCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
passkey_characteristic_.value(),
|
||||
ByteArray(std::string(kPasskeyharacteristicAdvertisementByte)));
|
||||
}
|
||||
|
||||
void InsertKeyBasedGattCharacteristicsWithEmptyValue() {
|
||||
key_based_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kKeyBasedCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(key_based_characteristic_.value(),
|
||||
ByteArray(""));
|
||||
|
||||
passkey_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kPasskeyCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
passkey_characteristic_.value(),
|
||||
ByteArray(std::string(kPasskeyharacteristicAdvertisementByte)));
|
||||
}
|
||||
|
||||
void InsertPasskeyGattCharacteristicsWithEmptyValue() {
|
||||
key_based_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kKeyBasedCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(
|
||||
key_based_characteristic_.value(),
|
||||
ByteArray(std::string(kKeyBasedCharacteristicAdvertisementByte)));
|
||||
|
||||
passkey_characteristic_ = gatt_server_->CreateCharacteristic(
|
||||
kFastPairServiceUuid, kPasskeyCharacteristicUuidV2, permissions_,
|
||||
properties_);
|
||||
gatt_server_->UpdateCharacteristic(passkey_characteristic_.value(),
|
||||
ByteArray(""));
|
||||
}
|
||||
|
||||
void InitializeFastPairGattServiceClient() {
|
||||
FastPairDevice device(kMetadataId, kProviderAddress,
|
||||
Protocol::kFastPairInitialPairing);
|
||||
gatt_client_ = FastPairGattServiceClientImpl::Factory::Create(device);
|
||||
gatt_client_->InitializeGattConnection(
|
||||
[this](std::optional<PairFailure> failure) {
|
||||
initalized_failure_ = failure;
|
||||
});
|
||||
}
|
||||
|
||||
void RemoveDiscoveredKeyBasedCharacteristic() {
|
||||
env_.EraseBleV2MediumGattCharacteristicsForDiscovery(
|
||||
key_based_characteristic_.value());
|
||||
}
|
||||
|
||||
void RemoveDiscoveredPasskeyCharacteristic() {
|
||||
env_.EraseBleV2MediumGattCharacteristicsForDiscovery(
|
||||
passkey_characteristic_.value());
|
||||
}
|
||||
|
||||
bool UnsubceibeKeyBasedCharacteristic() {
|
||||
return env_.SetBleV2MediumGattCharacteristicSubscription(
|
||||
key_based_characteristic_.value(), false, {});
|
||||
}
|
||||
|
||||
bool UnsubceibePasskeyCharacteristic() {
|
||||
return env_.SetBleV2MediumGattCharacteristicSubscription(
|
||||
passkey_characteristic_.value(), false, {});
|
||||
}
|
||||
|
||||
absl::optional<PairFailure> GetInitializedCallbackResult() {
|
||||
return initalized_failure_;
|
||||
}
|
||||
|
||||
void WriteTestCallback(absl::string_view response,
|
||||
absl::optional<PairFailure> failure) {
|
||||
write_failure_ = failure;
|
||||
}
|
||||
|
||||
absl::optional<PairFailure> GetWriteCallbackResult() {
|
||||
return write_failure_;
|
||||
}
|
||||
|
||||
void WriteRequestToKeyBased() {
|
||||
gatt_client_->WriteRequestAsync(
|
||||
kMessageType, kFlags, kProviderAddress, /* Seeker Address*/ "",
|
||||
*fast_pair_data_encryptor_,
|
||||
[&](absl::string_view response, std::optional<PairFailure> failure) {
|
||||
WriteTestCallback(response, failure);
|
||||
});
|
||||
}
|
||||
|
||||
void WriteRequestToPasskey() {
|
||||
gatt_client_->WritePasskeyAsync(
|
||||
kSeekerPasskey, kPasskey, *fast_pair_data_encryptor_,
|
||||
[this](absl::string_view response, std::optional<PairFailure> failure) {
|
||||
WriteTestCallback(response, failure);
|
||||
});
|
||||
}
|
||||
|
||||
absl::Status TriggerKeyBasedGattChanged() {
|
||||
return gatt_server_->NotifyCharacteristicChanged(
|
||||
key_based_characteristic_.value(), false,
|
||||
ByteArray(std::string(kKeyBasedCharacteristicAdvertisementByte)));
|
||||
}
|
||||
|
||||
absl::Status TriggerPasskeyGattChanged() {
|
||||
return gatt_server_->NotifyCharacteristicChanged(
|
||||
passkey_characteristic_.value(), false,
|
||||
ByteArray(std::string(kPasskeyharacteristicAdvertisementByte)));
|
||||
}
|
||||
|
||||
protected:
|
||||
MediumEnvironment& env_{MediumEnvironment::Instance()};
|
||||
std::unique_ptr<GattClient> internal_gatt_client_;
|
||||
std::unique_ptr<FastPairGattServiceClient> gatt_client_;
|
||||
std::unique_ptr<GattServer> gatt_server_;
|
||||
std::unique_ptr<FakeFastPairDataEncryptor> fast_pair_data_encryptor_;
|
||||
|
||||
private:
|
||||
std::optional<GattCharacteristic> key_based_characteristic_;
|
||||
std::optional<GattCharacteristic> passkey_characteristic_;
|
||||
absl::optional<PairFailure> initalized_failure_;
|
||||
absl::optional<PairFailure> write_failure_;
|
||||
Property properties_ = Property::kWrite | Property::kNotify;
|
||||
Permission permissions_ = Permission::kWrite;
|
||||
};
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, SuccessInitializeGattConnection) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
EXPECT_EQ(GetInitializedCallbackResult(), std::nullopt);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, FailedDiscoverServiceAndCharacteristics) {
|
||||
InsertCharacteristicsWithWrongServiceId();
|
||||
InitializeFastPairGattServiceClient();
|
||||
EXPECT_EQ(GetInitializedCallbackResult(), PairFailure::kCreateGattConnection);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, FailedGetKeyBasedCharacteristics) {
|
||||
InsertKeyBasedGattCharacteristicsWithEmptyValue();
|
||||
InitializeFastPairGattServiceClient();
|
||||
EXPECT_EQ(GetInitializedCallbackResult(),
|
||||
PairFailure::kKeyBasedPairingCharacteristicDiscovery);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, FailedToGetPasskeyCharacteristics) {
|
||||
InsertPasskeyGattCharacteristicsWithEmptyValue();
|
||||
InitializeFastPairGattServiceClient();
|
||||
EXPECT_EQ(GetInitializedCallbackResult(),
|
||||
PairFailure::kPasskeyCharacteristicDiscovery);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, SuccessfulWriteKeyBaseCharacteristics) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
WriteRequestToKeyBased();
|
||||
EXPECT_EQ(TriggerKeyBasedGattChanged(), absl::OkStatus());
|
||||
EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, SuccessfulWritePasskeyCharacteristics) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
WriteRequestToPasskey();
|
||||
EXPECT_EQ(TriggerPasskeyGattChanged(), absl::OkStatus());
|
||||
EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, FailedSubscribeKeybaseCharacteristic) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
RemoveDiscoveredKeyBasedCharacteristic();
|
||||
WriteRequestToKeyBased();
|
||||
EXPECT_EQ(GetWriteCallbackResult(),
|
||||
PairFailure::kKeyBasedPairingCharacteristicSubscription);
|
||||
WriteRequestToPasskey();
|
||||
EXPECT_EQ(TriggerPasskeyGattChanged(), absl::OkStatus());
|
||||
EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, FailedSubscribePasskeyCharacteristic) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
RemoveDiscoveredPasskeyCharacteristic();
|
||||
WriteRequestToKeyBased();
|
||||
EXPECT_EQ(TriggerKeyBasedGattChanged(), absl::OkStatus());
|
||||
EXPECT_EQ(GetWriteCallbackResult(), absl::nullopt);
|
||||
WriteRequestToPasskey();
|
||||
EXPECT_EQ(GetWriteCallbackResult(),
|
||||
PairFailure::kPasskeyCharacteristicSubscription);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, KeyBasedPairingResponseTimeout) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
CountDownLatch latch(1);
|
||||
gatt_client_->WriteRequestAsync(
|
||||
kMessageType, kFlags, kProviderAddress, kSeekerAddress,
|
||||
*fast_pair_data_encryptor_,
|
||||
[&](absl::string_view response, std::optional<PairFailure> failure) {
|
||||
WriteTestCallback(response, failure);
|
||||
latch.CountDown();
|
||||
});
|
||||
SystemClock::Sleep(kGattOperationTimeout);
|
||||
latch.Await();
|
||||
EXPECT_EQ(GetWriteCallbackResult(),
|
||||
PairFailure::kKeyBasedPairingResponseTimeout);
|
||||
}
|
||||
|
||||
TEST_F(FastPairGattServiceClientTest, PasskeyResponseTimeout) {
|
||||
InsertCorrectGattCharacteristics();
|
||||
InitializeFastPairGattServiceClient();
|
||||
CountDownLatch latch(1);
|
||||
gatt_client_->WritePasskeyAsync(
|
||||
kSeekerPasskey, kPasskey, *fast_pair_data_encryptor_,
|
||||
[&](absl::string_view response, std::optional<PairFailure> failure) {
|
||||
WriteTestCallback(response, failure);
|
||||
latch.CountDown();
|
||||
});
|
||||
SystemClock::Sleep(kGattOperationTimeout);
|
||||
latch.Await();
|
||||
EXPECT_EQ(GetWriteCallbackResult(), PairFailure::kPasskeyResponseTimeout);
|
||||
}
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
@@ -188,6 +188,7 @@ cc_library(
|
||||
],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user