/*
* Copyright 2024 The Android Open Source Project
*
* 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
*
* http://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
#include "bta/include/bta_gatt_api.h"
#include "bta/include/bta_ras_api.h"
#include "bta/ras/ras_types.h"
#include "os/logging/log_adapter.h"
#include "stack/include/bt_types.h"
#include "stack/include/btm_ble_addr.h"
#include "stack/include/gap_api.h"
using namespace bluetooth;
using namespace ::ras;
using namespace ::ras::feature;
using namespace ::ras::uuid;
using bluetooth::ras::VendorSpecificCharacteristic;
namespace {
class RasClientImpl;
RasClientImpl* instance;
enum CallbackDataType { VENDOR_SPECIFIC_REPLY };
class RasClientImpl : public bluetooth::ras::RasClient {
public:
struct GattWriteCallbackData {
const CallbackDataType type_;
};
struct RasTracker {
RasTracker(const RawAddress& address, const RawAddress& address_for_cs)
: address_(address), address_for_cs_(address_for_cs) {}
uint16_t conn_id_;
RawAddress address_;
RawAddress address_for_cs_;
const gatt::Service* service_ = nullptr;
uint32_t remote_supported_features_;
uint16_t latest_ranging_counter_ = 0;
bool handling_on_demand_data_ = false;
std::vector vendor_specific_characteristics_;
uint8_t writeReplyCounter_ = 0;
uint8_t writeReplySuccessCounter_ = 0;
const gatt::Characteristic* FindCharacteristicByUuid(Uuid uuid) {
for (auto& characteristic : service_->characteristics) {
if (characteristic.uuid == uuid) {
return &characteristic;
}
}
return nullptr;
}
const gatt::Characteristic* FindCharacteristicByHandle(uint16_t handle) {
for (auto& characteristic : service_->characteristics) {
if (characteristic.value_handle == handle) {
return &characteristic;
}
}
return nullptr;
}
VendorSpecificCharacteristic* GetVendorSpecificCharacteristic(
const bluetooth::Uuid& uuid) {
for (auto& characteristic : vendor_specific_characteristics_) {
if (characteristic.characteristicUuid_ == uuid) {
return &characteristic;
}
}
return nullptr;
}
};
void Initialize() override {
BTA_GATTC_AppRegister(
[](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
if (instance && p_data) instance->GattcCallback(event, p_data);
},
base::Bind([](uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
log::error("Can't start Gatt client for Ranging Service");
return;
}
log::info("Initialize, client_id {}", client_id);
instance->gatt_if_ = client_id;
}),
true);
}
void RegisterCallbacks(bluetooth::ras::RasClientCallbacks* callbacks) {
callbacks_ = callbacks;
}
void Connect(const RawAddress& address) override {
tBLE_BD_ADDR ble_bd_addr;
ResolveAddress(ble_bd_addr, address);
log::info("address {}, resolve {}", address, ble_bd_addr.bda);
auto tracker = FindTrackerByAddress(ble_bd_addr.bda);
if (tracker == nullptr) {
trackers_.emplace_back(
std::make_shared(ble_bd_addr.bda, address));
}
BTA_GATTC_Open(gatt_if_, ble_bd_addr.bda, BTM_BLE_DIRECT_CONNECTION, false);
}
void SendVendorSpecificReply(
const RawAddress& address,
const std::vector& vendor_specific_data) {
tBLE_BD_ADDR ble_bd_addr;
ResolveAddress(ble_bd_addr, address);
log::info("address {}, resolve {}", address, ble_bd_addr.bda);
auto tracker = FindTrackerByAddress(ble_bd_addr.bda);
for (auto& vendor_specific_characteristic : vendor_specific_data) {
auto characteristic = tracker->FindCharacteristicByUuid(
vendor_specific_characteristic.characteristicUuid_);
if (characteristic == nullptr) {
log::warn("Can't find characteristic uuid {}",
vendor_specific_characteristic.characteristicUuid_);
return;
}
log::debug("write to remote, uuid {}, len {}",
vendor_specific_characteristic.characteristicUuid_,
vendor_specific_characteristic.value_.size());
BTA_GATTC_WriteCharValue(
tracker->conn_id_, characteristic->value_handle, GATT_WRITE,
vendor_specific_characteristic.value_, GATT_AUTH_REQ_MITM,
GattWriteCallback, &gatt_write_callback_data_);
}
}
void GattcCallback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
log::debug("event: {}", gatt_client_event_text(event));
switch (event) {
case BTA_GATTC_OPEN_EVT: {
OnGattConnected(p_data->open);
} break;
case BTA_GATTC_SEARCH_CMPL_EVT: {
OnGattServiceSearchComplete(p_data->search_cmpl);
} break;
case BTA_GATTC_NOTIF_EVT: {
OnGattNotification(p_data->notify);
} break;
default:
log::warn("Unhandled event: {}", gatt_client_event_text(event));
}
}
void OnGattConnected(const tBTA_GATTC_OPEN& evt) {
log::info("{}, conn_id=0x{:04x}, transport:{}, status:{}", evt.remote_bda,
evt.conn_id, bt_transport_text(evt.transport),
gatt_status_text(evt.status));
if (evt.transport != BT_TRANSPORT_LE) {
log::warn("Only LE connection is allowed (transport {})",
bt_transport_text(evt.transport));
BTA_GATTC_Close(evt.conn_id);
return;
}
auto tracker = FindTrackerByAddress(evt.remote_bda);
if (tracker == nullptr) {
log::warn("Skipping unknown device, address: {}", evt.remote_bda);
BTA_GATTC_Close(evt.conn_id);
return;
}
if (evt.status != GATT_SUCCESS) {
log::error("Failed to connect to server device {}", evt.remote_bda);
return;
}
tracker->conn_id_ = evt.conn_id;
log::info("Search service");
BTA_GATTC_ServiceSearchRequest(tracker->conn_id_, kRangingService);
}
void OnGattServiceSearchComplete(const tBTA_GATTC_SEARCH_CMPL& evt) {
auto tracker = FindTrackerByHandle(evt.conn_id);
if (tracker == nullptr) {
log::warn("Can't find tracker for conn_id:{}", evt.conn_id);
return;
}
// Get Ranging Service
bool service_found = false;
const std::list* all_services =
BTA_GATTC_GetServices(evt.conn_id);
for (const auto& service : *all_services) {
if (service.uuid == kRangingService) {
tracker->service_ = &service;
service_found = true;
break;
}
}
if (!service_found) {
log::error("Can't find Ranging Service in the services list");
return;
} else {
log::info("Found Ranging Service");
ListCharacteristic(tracker);
}
// Read Vendor Specific Uuid
if (!tracker->vendor_specific_characteristics_.empty()) {
for (auto& vendor_specific_characteristic :
tracker->vendor_specific_characteristics_) {
log::debug("Read vendor specific characteristic uuid {}",
vendor_specific_characteristic.characteristicUuid_);
auto characteristic = tracker->FindCharacteristicByUuid(
vendor_specific_characteristic.characteristicUuid_);
BTA_GATTC_ReadCharacteristic(
tracker->conn_id_, characteristic->value_handle, GATT_AUTH_REQ_MITM,
[](uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, uint8_t* value, void* data) {
instance->OnReadCharacteristicCallback(conn_id, status, handle,
len, value, data);
},
nullptr);
}
}
// Read Ras Features
log::info("Read Ras Features");
auto characteristic =
tracker->FindCharacteristicByUuid(kRasFeaturesCharacteristic);
if (characteristic == nullptr) {
log::error("Can not find Characteristic for Ras Features");
return;
}
BTA_GATTC_ReadCharacteristic(
tracker->conn_id_, characteristic->value_handle, GATT_AUTH_REQ_MITM,
[](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
uint8_t* value, void* data) {
instance->OnReadCharacteristicCallback(conn_id, status, handle, len,
value, data);
},
nullptr);
SubscribeCharacteristic(tracker, kRasControlPointCharacteristic);
}
void OnGattNotification(const tBTA_GATTC_NOTIFY& evt) {
auto tracker = FindTrackerByHandle(evt.conn_id);
if (tracker == nullptr) {
log::warn("Can't find tracker for conn_id:{}", evt.conn_id);
return;
}
auto characteristic = tracker->FindCharacteristicByHandle(evt.handle);
if (characteristic == nullptr) {
log::warn("Can't find characteristic for handle:{}", evt.handle);
return;
}
uint16_t uuid_16bit = characteristic->uuid.As16Bit();
log::debug("Handle uuid 0x{:04x}, {}, size {}", uuid_16bit,
getUuidName(characteristic->uuid), evt.len);
switch (uuid_16bit) {
case kRasRealTimeRangingDataCharacteristic16bit:
case kRasOnDemandDataCharacteristic16bit: {
OnRemoteData(evt, tracker);
break;
}
case kRasControlPointCharacteristic16bit: {
OnControlPointEvent(evt, tracker);
} break;
case kRasRangingDataReadyCharacteristic16bit: {
OnRangingDataReady(evt, tracker);
} break;
default:
log::warn("Unexpected UUID");
}
}
void OnRemoteData(const tBTA_GATTC_NOTIFY& evt,
std::shared_ptr tracker) {
std::vector data;
data.resize(evt.len);
std::copy(evt.value, evt.value + evt.len, data.begin());
callbacks_->OnRemoteData(tracker->address_for_cs_, data);
}
void OnControlPointEvent(const tBTA_GATTC_NOTIFY& evt,
std::shared_ptr tracker) {
switch (evt.value[0]) {
case (uint8_t)EventCode::COMPLETE_RANGING_DATA_RESPONSE: {
uint16_t ranging_counter = evt.value[1];
ranging_counter |= (evt.value[2] << 8);
log::debug(
"Received complete ranging data response, ranging_counter: {}",
ranging_counter);
AckRangingData(ranging_counter, tracker);
} break;
case (uint8_t)EventCode::RESPONSE_CODE: {
tracker->handling_on_demand_data_ = false;
log::debug("Received response code 0x{:02x}", evt.value[1]);
} break;
default:
log::warn("Unexpected event code 0x{:02x}", evt.value[0]);
}
}
void OnRangingDataReady(const tBTA_GATTC_NOTIFY& evt,
std::shared_ptr tracker) {
if (evt.len != kRingingCounterSize) {
log::error("Invalid len for ranging data ready");
return;
}
uint16_t ranging_counter = evt.value[0];
ranging_counter |= (evt.value[1] << 8);
log::debug("ranging_counter: {}", ranging_counter);
// Send get ranging data command
tracker->latest_ranging_counter_ = ranging_counter;
GetRangingData(ranging_counter, tracker);
}
void GetRangingData(uint16_t ranging_counter,
std::shared_ptr tracker) {
log::debug("ranging_counter:{}", ranging_counter);
if (tracker->handling_on_demand_data_) {
log::warn("Handling other procedure, skip");
return;
}
auto characteristic =
tracker->FindCharacteristicByUuid(kRasControlPointCharacteristic);
if (characteristic == nullptr) {
log::warn("Can't find characteristic for RAS-CP");
return;
}
tracker->handling_on_demand_data_ = true;
std::vector value(3);
value[0] = (uint8_t)Opcode::GET_RANGING_DATA;
value[1] = (uint8_t)(ranging_counter & 0xFF);
value[2] = (uint8_t)((ranging_counter >> 8) & 0xFF);
BTA_GATTC_WriteCharValue(tracker->conn_id_, characteristic->value_handle,
GATT_WRITE, value, GATT_AUTH_REQ_MITM,
GattWriteCallback, nullptr);
}
void AckRangingData(uint16_t ranging_counter,
std::shared_ptr tracker) {
log::debug("ranging_counter:{}", ranging_counter);
auto characteristic =
tracker->FindCharacteristicByUuid(kRasControlPointCharacteristic);
if (characteristic == nullptr) {
log::warn("Can't find characteristic for RAS-CP");
return;
}
tracker->handling_on_demand_data_ = false;
std::vector value(3);
value[0] = (uint8_t)Opcode::ACK_RANGING_DATA;
value[1] = (uint8_t)(ranging_counter & 0xFF);
value[2] = (uint8_t)((ranging_counter >> 8) & 0xFF);
BTA_GATTC_WriteCharValue(tracker->conn_id_, characteristic->value_handle,
GATT_WRITE, value, GATT_AUTH_REQ_MITM,
GattWriteCallback, nullptr);
if (ranging_counter != tracker->latest_ranging_counter_) {
GetRangingData(tracker->latest_ranging_counter_, tracker);
}
}
void GattWriteCallbackForVendorSpecificData(uint16_t conn_id,
tGATT_STATUS status,
uint16_t handle,
const uint8_t* value,
GattWriteCallbackData* data) {
if (data != nullptr) {
GattWriteCallbackData* structPtr =
static_cast(data);
if (structPtr->type_ == CallbackDataType::VENDOR_SPECIFIC_REPLY) {
log::info("Write vendor specific reply complete");
auto tracker = FindTrackerByHandle(conn_id);
tracker->writeReplyCounter_++;
if (status == GATT_SUCCESS) {
tracker->writeReplySuccessCounter_++;
} else {
log::error(
"Fail to write vendor specific reply conn_id {}, status {}, "
"handle {}",
conn_id, gatt_status_text(status), handle);
}
// All reply complete
if (tracker->writeReplyCounter_ ==
tracker->vendor_specific_characteristics_.size()) {
log::info(
"All vendor specific reply write complete, size {} "
"successCounter {}",
tracker->vendor_specific_characteristics_.size(),
tracker->writeReplySuccessCounter_);
bool success = tracker->writeReplySuccessCounter_ ==
tracker->vendor_specific_characteristics_.size();
tracker->writeReplyCounter_ = 0;
tracker->writeReplySuccessCounter_ = 0;
callbacks_->OnWriteVendorSpecificReplyComplete(
tracker->address_for_cs_, success);
}
return;
}
}
}
void GattWriteCallback(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
const uint8_t* value) {
if (status != GATT_SUCCESS) {
log::error("Fail to write conn_id {}, status {}, handle {}", conn_id,
gatt_status_text(status), handle);
auto tracker = FindTrackerByHandle(conn_id);
if (tracker == nullptr) {
log::warn("Can't find tracker for conn_id:{}", conn_id);
return;
}
auto characteristic = tracker->FindCharacteristicByHandle(handle);
if (characteristic == nullptr) {
log::warn("Can't find characteristic for handle:{}", handle);
return;
}
if (characteristic->uuid == kRasControlPointCharacteristic) {
log::error("Write RAS-CP command fail");
tracker->handling_on_demand_data_ = false;
}
return;
}
}
static void GattWriteCallback(uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len,
const uint8_t* value, void* data) {
if (instance != nullptr) {
if (data != nullptr) {
GattWriteCallbackData* structPtr =
static_cast(data);
if (structPtr->type_ == CallbackDataType::VENDOR_SPECIFIC_REPLY) {
instance->GattWriteCallbackForVendorSpecificData(
conn_id, status, handle, value, structPtr);
return;
}
}
instance->GattWriteCallback(conn_id, status, handle, value);
}
}
void SubscribeCharacteristic(std::shared_ptr tracker,
const Uuid uuid) {
auto characteristic = tracker->FindCharacteristicByUuid(uuid);
if (characteristic == nullptr) {
log::warn("Can't find characteristic 0x{:04x}", uuid.As16Bit());
return;
}
uint16_t ccc_handle = FindCccHandle(characteristic);
if (ccc_handle == GAP_INVALID_HANDLE) {
log::warn("Can't find Client Characteristic Configuration descriptor");
return;
}
tGATT_STATUS register_status = BTA_GATTC_RegisterForNotifications(
gatt_if_, tracker->address_, characteristic->value_handle);
if (register_status != GATT_SUCCESS) {
log::error("Fail to register, {}", gatt_status_text(register_status));
return;
}
std::vector value(2);
uint8_t* value_ptr = value.data();
// Register notify is supported
if (characteristic->properties & GATT_CHAR_PROP_BIT_NOTIFY) {
UINT16_TO_STREAM(value_ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
} else {
UINT16_TO_STREAM(value_ptr, GATT_CHAR_CLIENT_CONFIG_INDICTION);
}
BTA_GATTC_WriteCharDescr(
tracker->conn_id_, ccc_handle, value, GATT_AUTH_REQ_NONE,
[](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
const uint8_t* value, void* data) {
if (instance)
instance->OnDescriptorWrite(conn_id, status, handle, len, value,
data);
},
nullptr);
}
void OnDescriptorWrite(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, const uint8_t* value, void* data) {
log::info("conn_id:{}, handle:{}, status:{}", conn_id, handle,
gatt_status_text(status));
}
void ListCharacteristic(std::shared_ptr tracker) {
for (auto& characteristic : tracker->service_->characteristics) {
bool vendor_specific =
!IsRangingServiceCharacteristic(characteristic.uuid);
log::info(
"{}Characteristic uuid:0x{:04x}, handle:0x{:04x}, "
"properties:0x{:02x}, "
"{}",
vendor_specific ? "Vendor Specific " : "",
characteristic.uuid.As16Bit(), characteristic.value_handle,
characteristic.properties, getUuidName(characteristic.uuid));
if (vendor_specific) {
VendorSpecificCharacteristic vendor_specific_characteristic;
vendor_specific_characteristic.characteristicUuid_ =
characteristic.uuid;
tracker->vendor_specific_characteristics_.emplace_back(
vendor_specific_characteristic);
}
for (auto& descriptor : characteristic.descriptors) {
log::info("\tDescriptor uuid:0x{:04x}, handle:0x{:04x}, {}",
descriptor.uuid.As16Bit(), descriptor.handle,
getUuidName(descriptor.uuid));
}
}
}
void ResolveAddress(tBLE_BD_ADDR& ble_bd_addr, const RawAddress& address) {
ble_bd_addr.bda = address;
ble_bd_addr.type = BLE_ADDR_RANDOM;
maybe_resolve_address(&ble_bd_addr.bda, &ble_bd_addr.type);
}
void OnReadCharacteristicCallback(uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len,
uint8_t* value, void* data) {
log::info("conn_id: {}, handle: {}, len: {}", conn_id, handle, len);
if (status != GATT_SUCCESS) {
log::error("Fail with status {}", gatt_status_text(status));
return;
}
auto tracker = FindTrackerByHandle(conn_id);
if (tracker == nullptr) {
log::warn("Can't find tracker for conn_id:{}", conn_id);
return;
}
auto characteristic = tracker->FindCharacteristicByHandle(handle);
if (characteristic == nullptr) {
log::warn("Can't find characteristic for handle:{}", handle);
return;
}
auto vendor_specific_characteristic =
tracker->GetVendorSpecificCharacteristic(characteristic->uuid);
if (vendor_specific_characteristic != nullptr) {
log::info("Update vendor specific data, uuid: {}",
vendor_specific_characteristic->characteristicUuid_);
vendor_specific_characteristic->value_.clear();
vendor_specific_characteristic->value_.reserve(len);
vendor_specific_characteristic->value_.assign(value, value + len);
return;
}
uint16_t uuid_16bit = characteristic->uuid.As16Bit();
log::info("Handle uuid 0x{:04x}, {}", uuid_16bit,
getUuidName(characteristic->uuid));
switch (uuid_16bit) {
case kRasFeaturesCharacteristic16bit: {
if (len != kFeatureSize) {
log::error("Invalid len for Ras features");
return;
}
STREAM_TO_UINT32(tracker->remote_supported_features_, value);
log::info("Remote supported features : {}",
getFeaturesString(tracker->remote_supported_features_));
if (tracker->remote_supported_features_ &
feature::kRealTimeRangingData) {
log::info("Subscribe Real-time Ranging Data");
SubscribeCharacteristic(tracker,
kRasRealTimeRangingDataCharacteristic);
} else {
log::info("Subscribe On-demand Ranging Data");
SubscribeCharacteristic(tracker, kRasOnDemandDataCharacteristic);
SubscribeCharacteristic(tracker, kRasRangingDataReadyCharacteristic);
SubscribeCharacteristic(tracker,
kRasRangingDataOverWrittenCharacteristic);
}
uint16_t att_handle = tracker
->FindCharacteristicByUuid(
kRasRealTimeRangingDataCharacteristic)
->value_handle;
callbacks_->OnConnected(tracker->address_for_cs_, att_handle,
tracker->vendor_specific_characteristics_);
} break;
default:
log::warn("Unexpected UUID");
}
}
std::string getFeaturesString(uint32_t value) {
std::stringstream ss;
ss << value;
if (value == 0) {
ss << "|No feature supported";
} else {
if ((value & kRealTimeRangingData) != 0) {
ss << "|Real-time Ranging Data";
}
if ((value & kRetrieveLostRangingDataSegments) != 0) {
ss << "|Retrieve Lost Ranging Data Segments";
}
if ((value & kAbortOperation) != 0) {
ss << "|Abort Operation";
}
if ((value & kFilterRangingData) != 0) {
ss << "|Filter Ranging Data";
}
if ((value & kPctPhaseFormat) != 0) {
ss << "|PCT Phase Format";
}
}
return ss.str();
}
uint16_t FindCccHandle(const gatt::Characteristic* characteristic) {
for (auto descriptor : characteristic->descriptors) {
if (descriptor.uuid == kClientCharacteristicConfiguration) {
return descriptor.handle;
}
}
return GAP_INVALID_HANDLE;
}
std::shared_ptr FindTrackerByHandle(uint16_t conn_id) const {
for (auto tracker : trackers_) {
if (tracker->conn_id_ == conn_id) {
return tracker;
}
}
return nullptr;
}
std::shared_ptr FindTrackerByAddress(
const RawAddress& address) const {
for (auto tracker : trackers_) {
if (tracker->address_ == address) {
return tracker;
}
}
return nullptr;
}
private:
uint16_t gatt_if_;
std::list> trackers_;
bluetooth::ras::RasClientCallbacks* callbacks_;
GattWriteCallbackData gatt_write_callback_data_{
CallbackDataType::VENDOR_SPECIFIC_REPLY};
};
} // namespace
bluetooth::ras::RasClient* bluetooth::ras::GetRasClient() {
if (instance == nullptr) {
instance = new RasClientImpl();
}
return instance;
};