blob: 1be5f7cc26387b5d03545b231f0a3ceeb00a0adf [file] [log] [blame]
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
/*
* Copyright 2018 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 "bta_gatt_api.h"
#include "bta_ascs_client_api.h"
#include "gattc_ops_queue.h"
#include <map>
#include <base/bind.h>
#include <base/callback.h>
#include <base/logging.h>
#include "stack/btm/btm_int.h"
#include "device/include/controller.h"
#include <vector>
#include "btif/include/btif_bap_config.h"
#include "osi/include/log.h"
#include "btif_util.h"
namespace bluetooth {
namespace bap {
namespace ascs {
using base::Closure;
using bluetooth::bap::GattOpsQueue;
Uuid ASCS_UUID = Uuid::FromString("184E");
Uuid ASCS_SINK_ASE_UUID = Uuid::FromString("2BC4");
Uuid ASCS_SRC_ASE_UUID = Uuid::FromString("2BC5");
Uuid ASCS_ASE_CP_UUID = Uuid::FromString("2BC6");
class AscsClientImpl;
AscsClientImpl* instance;
typedef uint8_t codec_type_t[5];
enum class ProfleOP {
CONNECT,
DISCONNECT
};
struct ProfileOperation {
uint16_t client_id;
ProfleOP type;
};
enum class DevState {
IDLE = 0,
CONNECTING,
CONNECTED,
DISCONNECTING
};
void ascs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
tBTM_STATUS);
std::map<uint8_t, std::string> resp_codes = {
{0x01, "Un Supported Opcode"},
{0x02, "Invalid Length"},
{0x03, "Invalid ASE ID"},
{0x04, "Invalid ASE SM Transition"},
{0x05, "Invalid ASE Direction"},
{0x06, "Un Supported Audio Capabilities"},
{0x07, "Un Supported Config Param"},
{0x08, "Rejected Config Param"},
{0x09, "Invalid Config Param"},
{0x0A, "Un Supported Metadata"},
{0x0B, "Rejected Metadata"},
{0x0C, "Invalid Metadata"},
{0x0D, "InSufficient Resources"},
{0x0E, "Unspecified Error"},
};
std::map<uint8_t, std::string> reason_codes = {
{0x01, "Codec ID"},
{0x02, "Codec Specific Config"},
{0x03, "SDU Interval"},
{0x04, "Framing"},
{0x05, "PHY"},
{0x06, "Maximum SDU Size"},
{0x07, "RTN"},
{0x08, "MTL"},
{0x09, "PD"},
{0x0A, "Invalid ASE CIS Mapping"},
};
std::vector<AseParams> sink_ase_value_list, src_ase_value_list;
AseParams ase;
struct AscsDevice {
RawAddress address;
/* This is true only during first connection to profile, until we store the
* device */
bool first_connection;
bool service_changed_rcvd;
uint16_t conn_id;
std::vector<Ase> sink_ase_list;
std::vector<Ase> src_ase_list;
uint16_t ase_cp_handle;
uint16_t ase_cp_ccc_handle;
uint16_t srv_changed_ccc_handle;
bool discovery_completed;
uint8_t num_ases_read;
bool notifications_enabled;
DevState state;
bool is_congested;
std::vector<ProfileOperation> profile_queue;
std::vector<uint16_t> connected_client_list; //list client requested for connection
AscsDevice(const RawAddress& address) : address(address) {}
};
class AscsDevices {
public:
void Add(AscsDevice device) {
if (FindByAddress(device.address) != nullptr) return;
devices.push_back(device);
}
void Remove(const RawAddress& address) {
for (auto it = devices.begin(); it != devices.end();) {
if (it->address != address) {
++it;
continue;
}
it = devices.erase(it);
return;
}
}
AscsDevice* FindByAddress(const RawAddress& address) {
auto iter = std::find_if(devices.begin(), devices.end(),
[&address](const AscsDevice& device) {
return device.address == address;
});
return (iter == devices.end()) ? nullptr : &(*iter);
}
AscsDevice* FindByConnId(uint16_t conn_id) {
auto iter = std::find_if(devices.begin(), devices.end(),
[&conn_id](const AscsDevice& device) {
return device.conn_id == conn_id;
});
return (iter == devices.end()) ? nullptr : &(*iter);
}
size_t size() { return (devices.size()); }
std::vector<AscsDevice> devices;
};
class AscsClientImpl : public AscsClient {
public:
~AscsClientImpl() override = default;
AscsClientImpl() : gatt_client_id(BTA_GATTS_INVALID_IF) {};
bool Register(AscsClientCallbacks *callback) {
LOG(WARNING) << __func__ << callback;
// looks for client is already registered
bool is_client_registered = false;
for (auto it : callbacks) {
AscsClientCallbacks *pac_callback = it.second;
if(callback == pac_callback) {
is_client_registered = true;
break;
}
}
LOG(WARNING) << __func__ ;
if(is_client_registered) {
LOG(WARNING) << __func__ << " already registered";
return false;
}
if(gatt_client_id == BTA_GATTS_INVALID_IF) {
BTA_GATTC_AppRegister(
ascs_gattc_callback,
base::Bind(
[](AscsClientCallbacks *callback, uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Can't start ASCS profile - no gatt "
"clients left!";
return;
}
if (instance) {
LOG(WARNING) << " ASCS gatt_client_id "
<< instance->gatt_client_id;
instance->gatt_client_id = client_id;
instance->callbacks.insert(std::make_pair(
++instance->ascs_client_id, callback));
callback->OnAscsInitialized(0, instance->ascs_client_id);
}
},
callback), true);
} else {
instance->callbacks.insert(std::make_pair(
++instance->ascs_client_id, callback));
callback->OnAscsInitialized(0, instance->ascs_client_id);
}
return true;
}
bool Deregister (uint16_t client_id) {
bool status = false;
auto it = callbacks.find(client_id);
if (it != callbacks.end()) {
callbacks.erase(it);
if(callbacks.empty()) {
// deregister with GATT
LOG(WARNING) << __func__ << " Gatt de-register from ascs";
BTA_GATTC_AppDeregister(gatt_client_id);
gatt_client_id = BTA_GATTS_INVALID_IF;
}
status = true;
}
return status;
}
uint8_t GetClientCount () {
return callbacks.size();
}
void Connect(uint16_t client_id, const RawAddress& address,
bool is_direct) override {
LOG(WARNING) << __func__ << " " << address;
AscsDevice *dev = ascsDevices.FindByAddress(address);
ProfileOperation op;
op.client_id = client_id;
op.type = ProfleOP::CONNECT;
if(dev == nullptr) {
AscsDevice pac_dev(address);
ascsDevices.Add(pac_dev);
dev = ascsDevices.FindByAddress(address);
}
if (dev == nullptr) {
LOG(ERROR) << __func__ << "dev is null";
return;
}
switch(dev->state) {
case DevState::IDLE: {
BTA_GATTC_Open(gatt_client_id, address, is_direct,
GATT_TRANSPORT_LE, false);
dev->state = DevState::CONNECTING;
dev->profile_queue.push_back(op);
} break;
case DevState::CONNECTING: {
dev->profile_queue.push_back(op);
} break;
case DevState::CONNECTED: {
auto iter = std::find_if(dev->connected_client_list.begin(),
dev->connected_client_list.end(),
[&client_id](uint16_t id) {
return id == client_id;
});
if(iter == dev->connected_client_list.end())
dev->connected_client_list.push_back(client_id);
auto it = callbacks.find(client_id);
if (it != callbacks.end()) {
AscsClientCallbacks *callback = it->second;
callback->OnConnectionState(address, GattState::CONNECTED);
}
} break;
case DevState::DISCONNECTING: {
dev->profile_queue.push_back(op);
} break;
}
}
void Disconnect(uint16_t client_id, const RawAddress& address) override {
LOG(WARNING) << __func__ << " " << address;
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
ProfileOperation op;
op.client_id = client_id;
op.type = ProfleOP::DISCONNECT;
switch(dev->state) {
case DevState::CONNECTING: {
auto iter = std::find_if(dev->profile_queue.begin(),
dev->profile_queue.end(),
[&client_id]( ProfileOperation entry) {
return ((entry.type == ProfleOP::CONNECT) &&
(entry.client_id == client_id));
});
// If it is the last client requested for disconnect
if(iter != dev->profile_queue.end() &&
dev->profile_queue.size() == 1) {
if (dev->conn_id) {
// Removes all registrations for connection.
BTA_GATTC_CancelOpen(dev->conn_id, address, false);
GattOpsQueue::Clean(dev->conn_id);
BTA_GATTC_Close(dev->conn_id);
dev->profile_queue.push_back(op);
dev->state = DevState::DISCONNECTING;
} else {
// clear the connection queue and
// move the state to DISCONNECTING to better track
dev->profile_queue.clear();
dev->state = DevState::DISCONNECTING;
dev->profile_queue.push_back(op);
}
} else {
// remove the connection entry from the list
// as the same client has requested for disconnection
dev->profile_queue.erase(iter);
}
} break;
case DevState::CONNECTED: {
auto iter = std::find_if(dev->connected_client_list.begin(),
dev->connected_client_list.end(),
[&client_id]( uint16_t stored_client_id) {
return stored_client_id == client_id;
});
// if it is the last client requested for disconnection
if(iter != dev->connected_client_list.end() &&
dev->connected_client_list.size() == 1) {
if (dev->conn_id) {
// Removes all registrations for connection.
BTA_GATTC_CancelOpen(dev->conn_id, address, false);
GattOpsQueue::Clean(dev->conn_id);
BTA_GATTC_Close(dev->conn_id);
dev->profile_queue.push_back(op);
dev->state = DevState::DISCONNECTING;
}
} else {
// remove the client from connected_client_list
dev->connected_client_list.erase(iter);
// remove the pending gatt ops( not the ongoing one )
// initiated from client which requested disconnect
// TODO and send callback as disconnected
}
} break;
case DevState::DISCONNECTING: {
dev->profile_queue.push_back(op);
} break;
default:
break;
}
}
void StartDiscovery(uint16_t client_id, const RawAddress& address) override {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(WARNING) << __func__ << " " << address;
switch(dev->state) {
case DevState::CONNECTED: {
auto iter = std::find_if(dev->connected_client_list.begin(),
dev->connected_client_list.end(),
[&client_id]( uint16_t stored_client_id) {
LOG(WARNING) << __func__ << client_id << stored_client_id;
return stored_client_id == client_id;
});
// check if the client present in the connected client list
if(iter == dev->connected_client_list.end()) {
break;
}
// check if the discovery is already finished
// send back the same results to the other client
if(dev->discovery_completed && dev->notifications_enabled) {
sink_ase_value_list.clear();
src_ase_value_list.clear();
auto iter = callbacks.find(client_id);
if (iter != callbacks.end()) {
for (auto it : dev->sink_ase_list) {
memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
sink_ase_value_list.push_back(ase);
}
for (auto it : dev->src_ase_list) {
memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
src_ase_value_list.push_back(ase);
}
AscsClientCallbacks *callback = iter->second;
// send out the callback as service discovery completed
callback->OnSearchComplete(0, dev->address,
sink_ase_value_list,
src_ase_value_list);
}
break;
}
// reset it
dev->num_ases_read = 0x00;
dev->discovery_completed = false;
dev->notifications_enabled = false;
// queue the request to GATT queue module
GattOpsQueue::ServiceSearch(client_id, dev->conn_id, &ASCS_UUID);
} break;
default:
break;
}
}
void CodecConfig(uint16_t client_id, const RawAddress& address,
std::vector<AseCodecConfigOp> codec_configs) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::CODEC_CONFIG);
uint8_t num_ases = codec_configs.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = codec_configs.begin();
while (it != codec_configs.end()) {
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
vect_val.insert(vect_val.end(), &it->tgt_latency, &it->tgt_latency + 1);
vect_val.insert(vect_val.end(), &it->tgt_phy, &it->tgt_phy + 1);
vect_val.insert(vect_val.end(), it->codec_id,
((uint8_t *)it->codec_id) + sizeof(codec_type_t));
vect_val.insert(vect_val.end(), &it->codec_params_len,
&it->codec_params_len + 1);
vect_val.insert(vect_val.end(), it->codec_params.begin(),
it->codec_params.end());
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
LOG(INFO) << ": Target Latency = " << loghex(it->tgt_latency);
LOG(INFO) << ": target Phy = " << loghex(it->tgt_phy);
LOG(INFO) << ": Codec ID = " << loghex(it->codec_id[0]);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void QosConfig(uint16_t client_id, const RawAddress& address,
std::vector<AseQosConfigOp> qos_configs) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::QOS_CONFIG);
uint8_t num_ases = qos_configs.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = qos_configs.begin();
while (it != qos_configs.end()) {
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
vect_val.insert(vect_val.end(), &it->cig_id, &it->cig_id + 1);
vect_val.insert(vect_val.end(), &it->cis_id, &it->cis_id + 1);
vect_val.insert(vect_val.end(), it->sdu_interval,
(uint8_t *)it->sdu_interval + sizeof(sdu_interval_t));
// test change it->framing = 0xFF;
vect_val.insert(vect_val.end(), &it->framing, &it->framing + 1);
vect_val.insert(vect_val.end(), &it->phy, &it->phy + 1);
vect_val.insert(vect_val.end(), (uint8_t *) &it->max_sdu_size,
(uint8_t *)&it->max_sdu_size + sizeof(uint16_t));
vect_val.insert(vect_val.end(), &it->retrans_number,
&it->retrans_number + 1);
vect_val.insert(vect_val.end(), (uint8_t *) &it->trans_latency,
(uint8_t *)&it->trans_latency + sizeof(uint16_t));
vect_val.insert(vect_val.end(), it->present_delay,
(uint8_t *)it->present_delay + sizeof(presentation_delay_t));
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
LOG(INFO) << ": Cig Id = " << loghex(it->cig_id);
LOG(INFO) << ": Cis Id = " << loghex(it->cis_id);
LOG(INFO) << ": SDU interval ="
<< " " << loghex(it->sdu_interval[0])
<< " " << loghex(it->sdu_interval[1])
<< " " << loghex(it->sdu_interval[2]);
LOG(INFO) << ": Framing = " << loghex(it->framing);
LOG(INFO) << ": Phy = " << loghex(it->phy);
LOG(INFO) << ": Max SDU size = " << loghex(it->max_sdu_size);
LOG(INFO) << ": RTN = " << loghex(it->retrans_number);
LOG(INFO) << ": MTL = " << loghex(it->trans_latency);
LOG(INFO) << ": PD ="
<< " " << loghex(it->present_delay[0])
<< " " << loghex(it->present_delay[1])
<< " " << loghex(it->present_delay[2]);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void Enable(uint16_t client_id, const RawAddress& address,
std::vector<AseEnableOp> enable_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::ENABLE);
uint8_t num_ases = enable_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = enable_ops.begin();
while (it != enable_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
// test change it->meta_data_len = 0xFF;
vect_val.insert(vect_val.end(), &it->meta_data_len,
&it->meta_data_len + 1);
vect_val.insert(vect_val.end(), it->meta_data.begin(),
it->meta_data.end());
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void StartReady(uint16_t client_id, const RawAddress& address,
std::vector<AseStartReadyOp> start_ready_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::START_READY);
uint8_t num_ases = start_ready_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = start_ready_ops.begin();
while (it != start_ready_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void Disable(uint16_t client_id, const RawAddress& address,
std::vector<AseDisableOp> disable_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::DISABLE);
uint8_t num_ases = disable_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = disable_ops.begin();
while (it != disable_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void StopReady(uint16_t client_id, const RawAddress& address,
std::vector<AseStopReadyOp> stop_ready_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::STOP_READY);
uint8_t num_ases = stop_ready_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = stop_ready_ops.begin();
while (it != stop_ready_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void Release(uint16_t client_id, const RawAddress& address,
std::vector<AseReleaseOp> release_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::RELEASE);
uint8_t num_ases = release_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = release_ops.begin();
while (it != release_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
void UpdateStream(uint16_t client_id, const RawAddress& address,
std::vector<AseUpdateMetadataOp> metadata_ops) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
std::vector<uint8_t> vect_val;
uint8_t opcode = static_cast<uint8_t> (AseOpId::UPDATE_META_DATA);
uint8_t num_ases = metadata_ops.size();
if (!dev || (dev->state != DevState::CONNECTED)) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Num ASEs :" << loghex(num_ases);
vect_val.insert(vect_val.end(), &opcode, &opcode + 1);
vect_val.insert(vect_val.end(), &num_ases, &num_ases + 1);
auto it = metadata_ops.begin();
while (it != metadata_ops.end()) {
LOG(INFO) << ": ASE Id = " << loghex(it->ase_id);
vect_val.insert(vect_val.end(), &it->ase_id, &it->ase_id + 1);
vect_val.insert(vect_val.end(), &it->meta_data_len,
&it->meta_data_len + 1);
vect_val.insert(vect_val.end(), it->meta_data.begin(),
it->meta_data.end());
it++;
}
GattOpsQueue::WriteCharacteristic(client_id, dev->conn_id,
dev->ase_cp_handle, vect_val,
GATT_WRITE, nullptr, nullptr);
}
bool GetAseParams(const RawAddress& address, uint8_t ase_id,
AseParams *ase_params) {
bool ase_found = false;
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(INFO) << "Device not connected to profile" << address;
return false;
}
// first look for sink ASEs
for (auto it = dev->sink_ase_list.begin();
it != dev->sink_ase_list.end(); it++) {
if (it->ase_params.ase_id == ase_id) {
*ase_params = it->ase_params;
ase_found = true;
break;
}
}
if(ase_found) return ase_found;
for (auto it = dev->src_ase_list.begin();
it != dev->src_ase_list.end(); it++) {
if (it->ase_params.ase_id == ase_id) {
*ase_params = it->ase_params;
ase_found = true;
break;
}
}
return ase_found;
}
bool GetAseHandle(const RawAddress& address, uint8_t ase_id,
uint16_t *ase_handle) {
bool ase_found = false;
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(INFO) << "Device not connected to profile" << address;
return false;
}
// first look for sink ASEs
for (auto it = dev->sink_ase_list.begin();
it != dev->sink_ase_list.end(); it++) {
if (it->ase_params.ase_id == ase_id) {
*ase_handle = it->ase_handle;
ase_found = true;
break;
}
}
if(ase_found) return ase_found;
for (auto it = dev->src_ase_list.begin();
it != dev->src_ase_list.end(); it++) {
if (it->ase_params.ase_id == ase_id) {
*ase_handle = it->ase_handle;
ase_found = true;
break;
}
}
return ase_found;
}
void GetAseState(uint16_t client_id, const RawAddress& address,
uint8_t ase_id) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(WARNING) << __func__ << " " << address;
switch(dev->state) {
case DevState::CONNECTED: {
uint16_t ase_handle;
auto iter = std::find_if(dev->connected_client_list.begin(),
dev->connected_client_list.end(),
[&client_id]( uint16_t stored_client_id) {
return stored_client_id == client_id;
});
// check if the client present in the connected client list
if(iter == dev->connected_client_list.end()) {
break;
}
// check if the discovery is already finished
// send back the same results to the other client
if(dev->discovery_completed && dev->notifications_enabled) {
auto iter = callbacks.find(client_id);
AseParams ase_params;
if(iter != callbacks.end() &&
GetAseParams(address, ase_id, &ase_params)) {
AscsClientCallbacks *callback = iter->second;
callback->OnAseState(dev->address, ase_params);
}
break;
}
if(GetAseHandle(address, ase_id, &ase_handle)) {
// queue the request to GATT queue module
GattOpsQueue::ReadCharacteristic(client_id, dev->conn_id,
ase_handle,
AscsClientImpl::OnReadAseStateStatic, nullptr);
}
} break;
default:
LOG(WARNING) << __func__ << "un-handled event";
break;
}
}
void OnGattConnected(tGATT_STATUS status, uint16_t conn_id,
tGATT_IF client_if, RawAddress address,
tBTA_TRANSPORT transport, uint16_t mtu) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
/* When device is quickly disabled and enabled in settings, this case
* might happen */
LOG(ERROR) << "Closing connection to non ascs device, address="
<< address;
BTA_GATTC_Close(conn_id);
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Status : " << loghex(status);
if(dev->state == DevState::CONNECTING) {
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Failed to connect to ASCS device";
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::CONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(address, GattState::DISCONNECTED);
}
dev->profile_queue.erase(it);
} else {
it++;
}
}
dev->state = DevState::IDLE;
ascsDevices.Remove(address);
return;
}
} else if(dev->state == DevState::DISCONNECTING) {
// TODO will this happens ?
// it could have called the cancel open to expect the
// open cancelled event
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Failed to connect to ASCS device";
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::DISCONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(address, GattState::DISCONNECTED);
}
dev->profile_queue.erase(it);
} else {
it++;
}
}
dev->state = DevState::IDLE;
ascsDevices.Remove(address);
return;
} else {
// gatt connected successfully
// if the disconnect entry is found we need to initiate the
// gatt disconnect. may be a race condition just after sending
// cancel open gatt connected event received
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::DISCONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
// Removes all registrations for connection.
BTA_GATTC_CancelOpen(dev->conn_id, address, false);
GattOpsQueue::Clean(dev->conn_id);
BTA_GATTC_Close(dev->conn_id);
break;
}
} else {
it++;
}
}
return;
}
} else {
// return unconditinally
return;
}
// success scenario code
dev->conn_id = conn_id;
tACL_CONN* p_acl = btm_bda_to_acl(address, BT_TRANSPORT_LE);
if (p_acl != nullptr &&
controller_get_interface()->supports_ble_2m_phy() &&
HCI_LE_2M_PHY_SUPPORTED(p_acl->peer_le_features)) {
LOG(INFO) << address << " set preferred PHY to 2M";
BTM_BleSetPhy(address, PHY_LE_2M, PHY_LE_2M, 0);
}
/* verify bond */
uint8_t sec_flag = 0;
BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) {
/* if link has been encrypted */
OnEncryptionComplete(address, true);
return;
}
if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) {
/* if bonded and link not encrypted */
sec_flag = BTM_BLE_SEC_ENCRYPT;
LOG(WARNING) << "trying to encrypt now";
BTM_SetEncryption(address, BTA_TRANSPORT_LE, encryption_callback, nullptr,
sec_flag);
return;
}
/* otherwise let it go through */
OnEncryptionComplete(address, true);
}
void OnGattDisconnected(tGATT_STATUS status, uint16_t conn_id,
tGATT_IF client_if, RawAddress remote_bda,
tBTA_GATT_REASON reason) {
AscsDevice* dev = ascsDevices.FindByAddress(remote_bda);
if (!dev) {
LOG(ERROR) << "Skipping unknown device disconnect, conn_id="
<< loghex(conn_id);
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << remote_bda
<< ", Status : " << loghex(status)
<< ", state: " << static_cast<int>(dev->state);
switch(dev->state) {
case DevState::CONNECTING: {
// sudden disconnection
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::CONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
}
it = dev->profile_queue.erase(it);
} else {
it++;
}
}
} break;
case DevState::CONNECTED: {
// sudden disconnection
for (auto it = dev->connected_client_list.begin();
it != dev->connected_client_list.end();) {
// get the callback and update the upper layers
auto iter = callbacks.find(*it);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
}
it = dev->connected_client_list.erase(it);
}
} break;
case DevState::DISCONNECTING: {
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::DISCONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(remote_bda, GattState::DISCONNECTED);
}
it = dev->profile_queue.erase(it);
} else {
it++;
}
}
for (auto it = dev->connected_client_list.begin();
it != dev->connected_client_list.end();) {
// get the callback and update the upper layers
it = dev->connected_client_list.erase(it);
}
// check if the connection queue is not empty
// if not initiate the Gatt connection
} break;
default:
break;
}
if (dev->conn_id) {
GattOpsQueue::Clean(dev->conn_id);
BTA_GATTC_Close(dev->conn_id);
dev->conn_id = 0;
}
dev->state = DevState::IDLE;
ascsDevices.Remove(remote_bda);
}
void OnConnectionUpdateComplete(uint16_t conn_id, tBTA_GATTC* p_data) {
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
}
void OnEncryptionComplete(const RawAddress& address, bool success) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(ERROR) << "Skipping unknown device" << address;
return;
}
if(dev->state != DevState::CONNECTING) {
LOG(ERROR) << "received in wrong state" << address;
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << address
<< ": Status : " << loghex(success);
// encryption failed
if (!success) {
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::CONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(address, GattState::DISCONNECTED);
}
// change the type to disconnect
it->type = ProfleOP::DISCONNECT;
} else {
it++;
}
}
dev->state = DevState::DISCONNECTING;
// Removes all registrations for connection.
BTA_GATTC_CancelOpen(dev->conn_id, address, false);
BTA_GATTC_Close(dev->conn_id);
} else {
for (auto it = dev->profile_queue.begin();
it != dev->profile_queue.end();) {
if(it->type == ProfleOP::CONNECT) {
// get the callback and update the upper layers
auto iter = callbacks.find(it->client_id);
if (iter != callbacks.end()) {
dev->connected_client_list.push_back(it->client_id);
AscsClientCallbacks *callback = iter->second;
callback->OnConnectionState(address, GattState::CONNECTED);
}
dev->profile_queue.erase(it);
} else {
it++;
}
}
dev->state = DevState::CONNECTED;
}
}
void OnServiceChangeEvent(const RawAddress& address) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(ERROR) << "Skipping unknown device" << address;
return;
}
LOG(INFO) << __func__ << ": address=" << address;
dev->first_connection = true;
dev->service_changed_rcvd = true;
GattOpsQueue::Clean(dev->conn_id);
}
void OnServiceDiscDoneEvent(const RawAddress& address) {
AscsDevice* dev = ascsDevices.FindByAddress(address);
if (!dev) {
LOG(ERROR) << "Skipping unknown device" << address;
return;
}
if (dev->service_changed_rcvd) {
// queue the request to GATT queue module with dummu client id
GattOpsQueue::ServiceSearch(0XFF, dev->conn_id, &ASCS_UUID);
}
}
void OnServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status) {
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << ": BD Addr : " << dev->address
<< ": Status : " << loghex(status);
uint16_t client_id = GattOpsQueue::ServiceSearchComplete(conn_id,
status);
auto iter = callbacks.find(client_id);
if (status != GATT_SUCCESS) {
/* close connection and report service discovery complete with error */
LOG(ERROR) << "Service discovery failed";
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
std::vector<AseParams> ase_value_list;
callback->OnSearchComplete(0xFF, dev->address, ase_value_list,
ase_value_list);
}
return;
}
const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
const gatt::Service* service = nullptr;
if (services) {
for (const gatt::Service& tmp : *services) {
if (tmp.uuid == Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER)) {
LOG(INFO) << "Found UUID_SERVCLASS_GATT_SERVER, handle="
<< loghex(tmp.handle);
const gatt::Service* service_changed_service = &tmp;
find_server_changed_ccc_handle(conn_id, service_changed_service);
} else if (tmp.uuid == ASCS_UUID) {
LOG(INFO) << "Found ASCS service, handle=" << loghex(tmp.handle);
service = &tmp;
}
}
} else {
LOG(ERROR) << "no services found for conn_id: " << conn_id;
return;
}
if (!service) {
LOG(ERROR) << "No ASCS service found";
if (iter != callbacks.end()) {
AscsClientCallbacks *callback = iter->second;
std::vector<AseParams> ase_value_list;
callback->OnSearchComplete(0xFF, dev->address, ase_value_list,
ase_value_list);
}
return;
}
for (const gatt::Characteristic& charac : service->characteristics) {
if (charac.uuid == ASCS_SINK_ASE_UUID ||
charac.uuid == ASCS_SRC_ASE_UUID) {
Ase ase_info;
ase_info.ase_handle = charac.value_handle;
GattOpsQueue::ReadCharacteristic(
client_id, conn_id, charac.value_handle,
AscsClientImpl::OnReadOnlyPropertiesReadStatic, nullptr);
ase_info.ase_ccc_handle =
find_ccc_handle(conn_id, charac.value_handle);
if(charac.uuid == ASCS_SINK_ASE_UUID) {
dev->sink_ase_list.push_back(ase_info);
} else if(charac.uuid == ASCS_SRC_ASE_UUID) {
dev->src_ase_list.push_back(ase_info);
}
if(ase_info.ase_ccc_handle) {
/* Register and enable the Audio Status Notification */
tGATT_STATUS register_status;
register_status = BTA_GATTC_RegisterForNotifications(
conn_id, dev->address, ase_info.ase_handle);
if (register_status != GATT_SUCCESS) {
LOG(ERROR) << __func__
<< ": BTA_GATTC_RegisterForNotifications failed, status="
<< loghex(register_status);
}
std::vector<uint8_t> value(2);
uint8_t* ptr = value.data();
UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
GattOpsQueue::WriteDescriptor(
client_id, conn_id, ase_info.ase_ccc_handle,
std::move(value), GATT_WRITE, nullptr, nullptr);
}
} else if (charac.uuid == ASCS_ASE_CP_UUID) {
dev->ase_cp_handle = charac.value_handle;
dev->ase_cp_ccc_handle =
find_ccc_handle(conn_id, charac.value_handle);
if(dev->ase_cp_ccc_handle) {
/* Register and enable the Audio Status Notification */
tGATT_STATUS register_status;
register_status = BTA_GATTC_RegisterForNotifications(
conn_id, dev->address, dev->ase_cp_handle);
if (register_status != GATT_SUCCESS) {
LOG(ERROR) << __func__
<< ": BTA_GATTC_RegisterForNotifications failed, status="
<< loghex(register_status);
}
std::vector<uint8_t> value(2);
uint8_t* ptr = value.data();
UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
GattOpsQueue::WriteDescriptor(
client_id, conn_id, dev->ase_cp_ccc_handle,
std::move(value), GATT_WRITE, nullptr, nullptr);
}
} else {
LOG(WARNING) << "Unknown characteristic found:" << charac.uuid;
}
}
dev->notifications_enabled = true;
if (dev->service_changed_rcvd) {
dev->service_changed_rcvd = false;
}
}
const char* GetAseState(uint8_t event) {
switch (event) {
CASE_RETURN_STR(ASE_STATE_IDLE)
CASE_RETURN_STR(ASE_STATE_CODEC_CONFIGURED)
CASE_RETURN_STR(ASE_STATE_QOS_CONFIGURED)
CASE_RETURN_STR(ASE_STATE_ENABLING)
CASE_RETURN_STR(ASE_STATE_STREAMING)
CASE_RETURN_STR(ASE_STATE_DISABLING)
CASE_RETURN_STR(ASE_STATE_RELEASING)
default:
return "Unknown State";
}
}
const char* GetAseDirection(uint8_t event) {
switch (event) {
CASE_RETURN_STR(ASE_DIRECTION_SINK)
CASE_RETURN_STR(ASE_DIRECTION_SOURCE)
default:
return "Unknown Direction";
}
}
void ParseAseParams(uint8_t *p, AseParams *ase_params, uint8_t ase_dir) {
STREAM_TO_UINT8(ase_params->ase_id, p);
STREAM_TO_UINT8(ase_params->ase_state, p);
LOG(INFO) << __func__
<< ": ASE Id = " << loghex(ase_params->ase_id)
<< ": ASE State = " << GetAseState(ase_params->ase_state)
<< ": ASE Direction = " << GetAseDirection(ase_dir);
switch(ase_params->ase_state) {
case ASE_STATE_CODEC_CONFIGURED: {
AseCodecConfigParams *codec_config =
&ase_params->codec_config_params;
STREAM_TO_UINT8(codec_config->framing, p);
STREAM_TO_UINT8(codec_config->pref_phy, p);
STREAM_TO_UINT8(codec_config->pref_rtn, p);
STREAM_TO_UINT16(codec_config->mtl, p);
STREAM_TO_ARRAY(&(codec_config->pd_min), p,
static_cast<int> (sizeof(presentation_delay_t)));
STREAM_TO_ARRAY(&(codec_config->pd_max), p,
static_cast<int> (sizeof(presentation_delay_t)));
STREAM_TO_ARRAY(&(codec_config->pref_pd_min), p,
static_cast<int> (sizeof(presentation_delay_t)));
STREAM_TO_ARRAY(&(codec_config->pref_pd_max), p,
static_cast<int> (sizeof(presentation_delay_t)));
STREAM_TO_ARRAY(&(codec_config->codec_id),
p, static_cast<int> (sizeof(codec_type_t)));
STREAM_TO_UINT8(codec_config->codec_params_len, p);
if(codec_config->codec_params_len) {
codec_config->codec_params.resize(codec_config->codec_params_len);
STREAM_TO_ARRAY(codec_config->codec_params.data(),
p, codec_config->codec_params_len);
}
LOG(INFO) << ": Framing = " << loghex(codec_config->framing);
LOG(INFO) << ": Pref Phy = " << loghex(codec_config->pref_phy);
LOG(INFO) << ": Pref RTN = " << loghex(codec_config->pref_rtn);
LOG(INFO) << ": MTL = " << loghex(codec_config->mtl);
LOG(INFO) << ": PD Min ="
<< " " << loghex(codec_config->pd_min[0])
<< " " << loghex(codec_config->pd_min[1])
<< " " << loghex(codec_config->pd_min[2]);
LOG(INFO) << ": PD Max ="
<< " " << loghex(codec_config->pd_max[0])
<< " " << loghex(codec_config->pd_max[1])
<< " " << loghex(codec_config->pd_max[2]);
LOG(INFO) << ": Pref PD Min ="
<< " " << loghex(codec_config->pref_pd_min[0])
<< " " << loghex(codec_config->pref_pd_min[1])
<< " " << loghex(codec_config->pref_pd_min[2]);
LOG(INFO) << ": Pref PD Max ="
<< " " << loghex(codec_config->pref_pd_max[0])
<< " " << loghex(codec_config->pref_pd_max[1])
<< " " << loghex(codec_config->pref_pd_max[2]);
LOG(INFO) << ": Codec ID = " << loghex(codec_config->codec_id[0]);
} break;
case ASE_STATE_QOS_CONFIGURED: {
AseQosConfigParams *qos_config = &ase_params->qos_config_params;
STREAM_TO_UINT8(qos_config->cig_id, p);
STREAM_TO_UINT8(qos_config->cis_id, p);
STREAM_TO_ARRAY(&(qos_config->sdu_interval), p,
static_cast<int> (sizeof(sdu_interval_t)));
STREAM_TO_UINT8(qos_config->framing, p);
STREAM_TO_UINT8(qos_config->phy, p);
STREAM_TO_UINT16(qos_config->max_sdu_size, p);
STREAM_TO_UINT8(qos_config->rtn, p);
STREAM_TO_UINT16(qos_config->mtl, p);
STREAM_TO_ARRAY(&(qos_config->pd), p,
static_cast<int> (sizeof(presentation_delay_t)));
LOG(INFO) << ": Cig Id = " << loghex(qos_config->cig_id);
LOG(INFO) << ": Cis Id = " << loghex(qos_config->cis_id);
LOG(INFO) << ": SDU interval ="
<< " " << loghex(qos_config->sdu_interval[0])
<< " " << loghex(qos_config->sdu_interval[1])
<< " " << loghex(qos_config->sdu_interval[2]);
LOG(INFO) << ": Framing = " << loghex(qos_config->framing);
LOG(INFO) << ": Phy = " << loghex(qos_config->phy);
LOG(INFO) << ": Max SDU size = " << loghex(qos_config->max_sdu_size);
LOG(INFO) << ": RTN = " << loghex(qos_config->rtn);
LOG(INFO) << ": MTL = " << loghex(qos_config->mtl);
LOG(INFO) << ": PD ="
<< " " << loghex(qos_config->pd[0])
<< " " << loghex(qos_config->pd[1])
<< " " << loghex(qos_config->pd[2]);
} break;
case ASE_STATE_ENABLING:
case ASE_STATE_STREAMING:
case ASE_STATE_DISABLING: {
AseGenericParams *gen_params = &ase_params->generic_params;
STREAM_TO_UINT8(gen_params->cig_id, p);
STREAM_TO_UINT8(gen_params->cis_id, p);
STREAM_TO_UINT8(gen_params->meta_data_len, p);
if(gen_params->meta_data_len) {
gen_params->meta_data.resize(gen_params->meta_data_len);
STREAM_TO_ARRAY(gen_params->meta_data.data(),
p, gen_params->meta_data_len);
}
LOG(INFO) << ": Cig Id = " << loghex(gen_params->cig_id);
LOG(INFO) << ": Cis Id = " << loghex(gen_params->cis_id);
} break;
}
}
void ParseAseNotification(uint16_t conn_id,
uint16_t handle, uint16_t len, uint8_t* value ) {
uint8_t *p = value;
bool ase_found = false;
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(INFO) << __func__
<< ": Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
for (auto it = dev->sink_ase_list.begin();
it != dev->sink_ase_list.end(); it++) {
if (it->ase_handle == handle) {
LOG(INFO) << __func__ << ": BD Addr : " << dev->address;
ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SINK);
for (auto iter : callbacks) {
AscsClientCallbacks *ascs_callback = iter.second;
ascs_callback->OnAseState(dev->address, it->ase_params);
}
ase_found = true;
break;
}
}
if(!ase_found) {
for (auto it = dev->src_ase_list.begin();
it != dev->src_ase_list.end(); it++) {
if (it->ase_handle == handle) {
LOG(INFO) << __func__ << ": BD Addr : " << dev->address;
ParseAseParams(p, &it->ase_params,ASE_DIRECTION_SOURCE);
for (auto iter : callbacks) {
AscsClientCallbacks *ascs_callback = iter.second;
ascs_callback->OnAseState(dev->address, it->ase_params);
}
ase_found = true;
break;
}
}
}
}
void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
uint8_t* value) {
uint8_t* p = value;
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(INFO) << __func__
<< ": Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
// check if the notification is for ASEs
if( dev->ase_cp_handle == handle) { // control point notification
AseCpNotification cp_notification;
STREAM_TO_UINT8(cp_notification.ase_opcode, p);
STREAM_TO_UINT8(cp_notification.num_ases, p);
uint8_t num_ases = cp_notification.num_ases;
std::vector<AseOpStatus> ase_cp_notify_list;
AseOpStatus status;
bool notify = false;
while(num_ases--) {
STREAM_TO_UINT8(status.ase_id, p);
STREAM_TO_UINT8(status.resp_code, p);
STREAM_TO_UINT8(status.reason, p);
if(status.resp_code) {
LOG(ERROR) << __func__
<< ": ASE Id = " << loghex(status.ase_id)
<< ": Resp code = " << resp_codes[status.resp_code];
if(status.reason) {
LOG(ERROR) << ": Reason = " << reason_codes[status.reason];
}
notify = true;
}
ase_cp_notify_list.push_back(status);
}
if(notify) {
for (auto iter : callbacks) {
AscsClientCallbacks *ascs_callback = iter.second;
LOG(ERROR) << __func__ << " ASE Operation failed";
ascs_callback->OnAseOpFailed(dev->address,
(AseOpId) cp_notification.ase_opcode,
ase_cp_notify_list);
}
}
} else {
ParseAseNotification(conn_id, handle, len, value);
}
}
void OnCongestionEvent(uint16_t conn_id, bool congested) {
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(INFO) << __func__
<< ": Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
LOG(WARNING) << __func__ << ": conn_id:" << loghex(conn_id)
<< ", congested: " << congested;
dev->is_congested = congested;
GattOpsQueue::CongestionCallback(conn_id, congested);
}
void OnReadAseState(uint16_t client_id,
uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, uint8_t* value,
void* data) {
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
if (!dev) {
LOG(ERROR) << __func__ << "unknown conn_id=" << loghex(conn_id);
return;
}
LOG(WARNING) << __func__;
// check if the notification is for ASEs
ParseAseNotification(conn_id, handle, len, value);
}
void OnReadOnlyPropertiesRead(uint16_t client_id,
uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len,
uint8_t *value, void* data) {
AscsDevice* dev = ascsDevices.FindByConnId(conn_id);
uint8_t *p = value;
if (!dev) {
LOG(ERROR) << __func__ << "unknown conn_id=" << loghex(conn_id);
return;
}
for (auto it = dev->sink_ase_list.begin();
it != dev->sink_ase_list.end(); it++) {
if (it->ase_handle == handle) {
dev->num_ases_read++;
ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SINK);
break;
}
}
for (auto it = dev->src_ase_list.begin();
it != dev->src_ase_list.end(); it++) {
if (it->ase_handle == handle) {
dev->num_ases_read++;
ParseAseParams(p, &it->ase_params, ASE_DIRECTION_SOURCE);
break;
}
}
LOG(INFO) << __func__ << ": num_ases_read : "
<< loghex(dev->num_ases_read);
if(dev->num_ases_read == (dev->sink_ase_list.size() +
dev->src_ase_list.size())) {
sink_ase_value_list.clear();
src_ase_value_list.clear();
dev->discovery_completed = true;
// Now update using service discovery callback
auto iter = callbacks.find(client_id);
if (iter != callbacks.end()) {
for (auto it : dev->sink_ase_list) {
memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
sink_ase_value_list.push_back(ase);
}
for (auto it : dev->src_ase_list) {
memcpy(&ase, (void *) &it.ase_params, sizeof(ase));
src_ase_value_list.push_back(ase);
}
AscsClientCallbacks *callback = iter->second;
// check if all ascs characteristics are read
// send out the callback as service discovery completed
callback->OnSearchComplete(0, dev->address,
sink_ase_value_list,
src_ase_value_list);
}
}
}
static void OnReadOnlyPropertiesReadStatic(uint16_t client_id,
uint16_t conn_id,
tGATT_STATUS status,
uint16_t handle, uint16_t len,
uint8_t* value, void* data) {
if (instance)
instance->OnReadOnlyPropertiesRead(client_id, conn_id, status, handle,
len, value, data);
}
static void OnReadAseStateStatic(uint16_t client_id,
uint16_t conn_id,
tGATT_STATUS status,
uint16_t handle, uint16_t len,
uint8_t* value, void* data) {
if (instance)
instance->OnReadAseState(client_id, conn_id, status, handle,
len, value, data);
}
private:
uint8_t gatt_client_id = BTA_GATTS_INVALID_IF;
uint16_t ascs_client_id = 0;
AscsDevices ascsDevices;
// client id to callbacks mapping
std::map<uint16_t, AscsClientCallbacks *> callbacks;
void find_server_changed_ccc_handle(uint16_t conn_id,
const gatt::Service* service) {
AscsDevice* ascsDevice = ascsDevices.FindByConnId(conn_id);
if (!ascsDevice) {
LOG(ERROR) << "Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
for (const gatt::Characteristic& charac : service->characteristics) {
if (charac.uuid == Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD)) {
ascsDevice->srv_changed_ccc_handle =
find_ccc_handle(conn_id, charac.value_handle);
if (!ascsDevice->srv_changed_ccc_handle) {
LOG(ERROR) << __func__
<< ": cannot find service changed CCC descriptor";
continue;
}
LOG(INFO) << __func__ << " service_changed_ccc="
<< loghex(ascsDevice->srv_changed_ccc_handle);
break;
}
}
}
// Find the handle for the client characteristics configuration of a given
// characteristics
uint16_t find_ccc_handle(uint16_t conn_id, uint16_t char_handle) {
const gatt::Characteristic* p_char =
BTA_GATTC_GetCharacteristic(conn_id, char_handle);
if (!p_char) {
LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle;
return 0;
}
for (const gatt::Descriptor& desc : p_char->descriptors) {
if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
return desc.handle;
}
return 0;
}
};
const char* get_gatt_event_name(uint32_t event) {
switch (event) {
CASE_RETURN_STR(BTA_GATTC_DEREG_EVT)
CASE_RETURN_STR(BTA_GATTC_OPEN_EVT)
CASE_RETURN_STR(BTA_GATTC_CLOSE_EVT)
CASE_RETURN_STR(BTA_GATTC_SEARCH_CMPL_EVT)
CASE_RETURN_STR(BTA_GATTC_NOTIF_EVT)
CASE_RETURN_STR(BTA_GATTC_ENC_CMPL_CB_EVT)
CASE_RETURN_STR(BTA_GATTC_CONN_UPDATE_EVT)
CASE_RETURN_STR(BTA_GATTC_SRVC_CHG_EVT)
CASE_RETURN_STR(BTA_GATTC_SRVC_DISC_DONE_EVT)
CASE_RETURN_STR(BTA_GATTC_CONGEST_EVT)
default:
return "Unknown Event";
}
}
void ascs_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
if (p_data == nullptr || !instance) return;
LOG(INFO) << __func__ << ": Event : " << get_gatt_event_name(event);
switch (event) {
case BTA_GATTC_DEREG_EVT:
break;
case BTA_GATTC_OPEN_EVT: {
tBTA_GATTC_OPEN& o = p_data->open;
instance->OnGattConnected(o.status, o.conn_id, o.client_if, o.remote_bda,
o.transport, o.mtu);
break;
}
case BTA_GATTC_CLOSE_EVT: {
tBTA_GATTC_CLOSE& c = p_data->close;
instance->OnGattDisconnected(c.status, c.conn_id, c.client_if,
c.remote_bda, c.reason);
} break;
case BTA_GATTC_SEARCH_CMPL_EVT:
instance->OnServiceSearchComplete(p_data->search_cmpl.conn_id,
p_data->search_cmpl.status);
break;
case BTA_GATTC_NOTIF_EVT:
if (!p_data->notify.is_notify ||
p_data->notify.len > GATT_MAX_ATTR_LEN) {
LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify="
<< p_data->notify.is_notify
<< ", len=" << p_data->notify.len;
break;
}
instance->OnNotificationEvent(p_data->notify.conn_id,
p_data->notify.handle, p_data->notify.len,
p_data->notify.value);
break;
case BTA_GATTC_ENC_CMPL_CB_EVT:
instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
break;
case BTA_GATTC_CONN_UPDATE_EVT:
instance->OnConnectionUpdateComplete(p_data->conn_update.conn_id, p_data);
break;
case BTA_GATTC_SRVC_CHG_EVT:
instance->OnServiceChangeEvent(p_data->remote_bda);
break;
case BTA_GATTC_SRVC_DISC_DONE_EVT:
instance->OnServiceDiscDoneEvent(p_data->remote_bda);
break;
case BTA_GATTC_CONGEST_EVT:
instance->OnCongestionEvent(p_data->congest.conn_id,
p_data->congest.congested);
break;
default:
break;
}
}
void encryption_callback(const RawAddress* address, tGATT_TRANSPORT, void*,
tBTM_STATUS status) {
if (instance) {
instance->OnEncryptionComplete(*address,
status == BTM_SUCCESS ? true : false);
}
}
void AscsClient::Init(AscsClientCallbacks* callbacks) {
if (instance) {
instance->Register(callbacks);
} else {
instance = new AscsClientImpl();
instance->Register(callbacks);
}
}
void AscsClient::CleanUp(uint16_t client_id) {
if(instance->GetClientCount()) {
instance->Deregister(client_id);
if(!instance->GetClientCount()) {
delete instance;
instance = nullptr;
}
}
}
AscsClient* AscsClient::Get() {
CHECK(instance);
return instance;
}
} // namespace ascs
} // namespace bap
} // namespace bluetooth