mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
457 lines
18 KiB
C++
457 lines
18 KiB
C++
// 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
|