blob: 5eb7d6c56aea93bf37603b7fd69863fd3fd45f16 [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 "bt_target.h"
#include "bta_vcp_controller_api.h"
#include "bta_gatt_api.h"
#include "btm_int.h"
#include "device/include/controller.h"
#include "gap_api.h"
#include "gatt_api.h"
#include "gattc_ops_queue.h"
#include "osi/include/properties.h"
#include <base/bind.h>
#include <base/callback.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <hardware/bt_vcp_controller.h>
#include <vector>
using base::Closure;
using bluetooth::Uuid;
using bluetooth::bap::GattOpsQueue;
using bluetooth::vcp_controller::ConnectionState;
// Assigned Numbers for VCS
Uuid VCS_UUID = Uuid::FromString("1844");
Uuid VCS_VOLUME_STATE_UUID = Uuid::FromString("2B7D");
Uuid VCS_VOLUME_CONTROL_POINT_UUID = Uuid::FromString("2B7E");
Uuid VCS_VOLUME_FLAGS_UUID = Uuid::FromString("2B7F");
#define VCS_RETRY_SET_ABS_VOL 0x01
#define VCS_RETRY_SET_MUTE_STATE 0x02
// VCS Application Error Code
#define VCS_INVALID_CHANGE_COUNTER 0x80
#define VCS_OPCODE_NOT_SUPPORTED 0x81
void vcp_controller_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
void vcp_controller_encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*, tBTM_STATUS);
const char* vcp_controller_gatt_callback_evt_str(uint8_t event);
const char* vcp_controller_handle_vcs_evt_str(uint8_t event);
class VcpControllerImpl;
static VcpControllerImpl* instance;
class RendererDevices {
private:
public:
void Add(RendererDevice 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;
}
}
RendererDevice* FindByAddress(const RawAddress& address) {
auto iter = std::find_if(devices.begin(), devices.end(),
[&address](const RendererDevice& device) {
return device.address == address;
});
return (iter == devices.end()) ? nullptr : &(*iter);
}
RendererDevice* FindByConnId(uint16_t conn_id) {
auto iter = std::find_if(devices.begin(), devices.end(),
[&conn_id](const RendererDevice& device) {
return device.conn_id == conn_id;
});
return (iter == devices.end()) ? nullptr : &(*iter);
}
size_t size() { return (devices.size()); }
std::vector<RendererDevice> devices;
};
class VcpControllerImpl : public VcpController {
private:
uint8_t gatt_if;
bluetooth::vcp_controller::VcpControllerCallbacks* callbacks;
RendererDevices rendererDevices;
public:
virtual ~VcpControllerImpl() = default;
VcpControllerImpl(bluetooth::vcp_controller::VcpControllerCallbacks* callbacks)
: gatt_if(0),
callbacks(callbacks) {
LOG(INFO) << "VcpControllerImpl gattc app register";
BTA_GATTC_AppRegister(
vcp_controller_gattc_callback,
base::Bind(
[](uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Can't start Vcp profile - no gatt "
"clients left!";
return;
}
instance->gatt_if = client_id;
}), true);
}
void Connect(const RawAddress& address, bool isDirect) override {
LOG(INFO) << __func__ << " " << address << ", isDirect = " << logbool(isDirect);
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (rendererDevice) {
LOG(INFO) << "Device already in connected/connecting state" << address;
return;
}
rendererDevices.Add(RendererDevice(address));
rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
LOG(INFO) << "Device address could not be foundL";
return;
}
rendererDevice->state = BTA_VCP_CONNECTING;
callbacks->OnConnectionState(ConnectionState::CONNECTING, rendererDevice->address);
if (!isDirect) {
rendererDevice->bg_conn = true;
}
BTA_GATTC_Open(gatt_if, address, isDirect, GATT_TRANSPORT_LE, false);
}
void Disconnect(const RawAddress& address) override {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
LOG(INFO) << __func__ << " " << address;
rendererDevice->state = BTA_VCP_DISCONNECTING;
callbacks->OnConnectionState(ConnectionState::DISCONNECTING, rendererDevice->address);
VcpGattClose(rendererDevice);
}
void SetAbsVolume(const RawAddress& address, uint8_t volume) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
if (rendererDevice->conn_id == 0) {
LOG(INFO) << __func__ << ": GATT is not connected, skip set absolute volume";
return;
}
if (rendererDevice->state != BTA_VCP_CONNECTED) {
LOG(INFO)
<< __func__ << ": VCP is not connected, skip set absolute volume, state = "
<< loghex(rendererDevice->state);
return;
}
// Send the data packet
LOG(INFO) << __func__ << ": Set abs volume. device=" << rendererDevice->address
<< ", volume=" << loghex(volume);
rendererDevice->vcs.pending_volume_setting = volume;
uint8_t p_buf[256];
SetAbsVolumeOp set_abs_vol_op;
set_abs_vol_op.op_id = VCS_CONTROL_POINT_OP_SET_ABS_VOL;
set_abs_vol_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
set_abs_vol_op.volume_setting = volume;
memcpy(p_buf, &set_abs_vol_op , sizeof(set_abs_vol_op));
std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(set_abs_vol_op));
GattOpsQueue::WriteCharacteristic(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
GATT_WRITE, VcpControllerImpl::OnSetAbsVolumeStatic, nullptr);
}
void Mute(const RawAddress& address) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
LOG(INFO) << "Device not connected to profile" << address;
return;
}
if (rendererDevice->conn_id == 0) {
LOG(INFO) << __func__ << ": GATT is not connected, skip mute";
return;
}
if (rendererDevice->state != BTA_VCP_CONNECTED) {
LOG(INFO)
<< __func__ << ": VCP is not connected, skip mute, state = "
<< loghex(rendererDevice->state);
return;
}
// Send the data packet
LOG(INFO) << __func__ << ": Mute device=" << rendererDevice->address;
rendererDevice->vcs.pending_mute_setting = VCS_MUTE_STATE;
uint8_t p_buf[256];
MuteOp mute_op;
mute_op.op_id = VCS_CONTROL_POINT_OP_MUTE;
mute_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
memcpy(p_buf, &mute_op , sizeof(mute_op));
std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(mute_op));
GattOpsQueue::WriteCharacteristic(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
GATT_WRITE, VcpControllerImpl::OnMuteStatic, nullptr);
}
void Unmute(const RawAddress& address) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
LOG(WARNING) << "Device not connected to profile" << address;
return;
}
if (rendererDevice->conn_id == 0) {
LOG(INFO) << __func__ << ": GATT is not connected, skip unmute.";
return;
}
if (rendererDevice->state != BTA_VCP_CONNECTED) {
LOG(INFO)
<< __func__ << ": VCP is not connected, skip unmute, state = "
<< loghex(rendererDevice->state);
return;
}
// Send the data packet
LOG(INFO) << __func__ << ": unmute device=" << rendererDevice->address;
rendererDevice->vcs.pending_mute_setting = VCS_UNMUTE_STATE;
uint8_t p_buf[256];
UnmuteOp unmute_op;
unmute_op.op_id = VCS_CONTROL_POINT_OP_UNMUTE;
unmute_op.change_counter = rendererDevice->vcs.volume_state.change_counter;
memcpy(p_buf, &unmute_op , sizeof(unmute_op));
std::vector<uint8_t> vect_val(p_buf, p_buf + sizeof(unmute_op));
GattOpsQueue::WriteCharacteristic(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_control_point_handle, vect_val,
GATT_WRITE, VcpControllerImpl::OnUnmuteStatic, nullptr);
}
void VcpGattClose(RendererDevice* rendererDevice) {
LOG(INFO) << __func__ << " " << rendererDevice->address;
// Removes all registrations for connection.
BTA_GATTC_CancelOpen(gatt_if, rendererDevice->address, false);
rendererDevice->bg_conn = false;
if (rendererDevice->conn_id) {
GattOpsQueue::Clean(rendererDevice->conn_id);
BTA_GATTC_Close(rendererDevice->conn_id);
} else {
// cancel pending direct connect
BTA_GATTC_CancelOpen(gatt_if, rendererDevice->address, true);
PostDisconnected(rendererDevice);
}
}
void OnGattConnected(tGATT_STATUS status, uint16_t conn_id,
tGATT_IF client_if, RawAddress address,
tBTA_TRANSPORT transport, uint16_t mtu) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
LOG(INFO) << __func__ << ": address=" << address << ", conn_id=" << conn_id;
if (!rendererDevice) {
LOG(WARNING) << "Closing connection to non volume renderer device, address="
<< address;
BTA_GATTC_Close(conn_id);
return;
}
if (status != GATT_SUCCESS) {
if (rendererDevice->bg_conn) {
// whitelist connection failed, that's ok.
LOG(INFO) << "bg conn failed, return immediately";
return;
}
LOG(INFO) << "Failed to connect to volume renderer device";
rendererDevices.Remove(address);
callbacks->OnConnectionState(ConnectionState::DISCONNECTED, address);
return;
}
if (rendererDevice->bg_conn) {
LOG(INFO) << __func__ << ": backgound connection from: address=" << address;
}
rendererDevice->bg_conn = false;
rendererDevice->conn_id = conn_id;
/* verify bond */
uint8_t sec_flag = 0;
BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
LOG(INFO) << __func__ << ": sec_flag =" << loghex(sec_flag);
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;
BTM_SetEncryption(address, BTA_TRANSPORT_LE, vcp_controller_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) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown device disconnect, conn_id="
<< loghex(conn_id);
return;
}
LOG(INFO) << __func__ << ": conn_id=" << loghex(conn_id)
<< ", reason=" << loghex(reason) << ", remote_bda=" << remote_bda;
PostDisconnected(rendererDevice);
}
void PostDisconnected(RendererDevice* rendererDevice) {
LOG(INFO) << __func__ << " " << rendererDevice->address;
rendererDevice->state = BTA_VCP_DISCONNECTED;
if(rendererDevice->vcs.volume_state_handle != 0xFFFF) {
BTIF_TRACE_WARNING("%s: Deregister notifications", __func__);
BTA_GATTC_DeregisterForNotifications(gatt_if,
rendererDevice->address,
rendererDevice->vcs.volume_state_handle);
}
if(rendererDevice->vcs.volume_flags_handle != 0xFFFF) {
BTIF_TRACE_WARNING("%s: Deregister notifications", __func__);
BTA_GATTC_DeregisterForNotifications(gatt_if,
rendererDevice->address,
rendererDevice->vcs.volume_flags_handle);
}
if (rendererDevice->conn_id) {
GattOpsQueue::Clean(rendererDevice->conn_id);
rendererDevice->conn_id = 0;
}
callbacks->OnConnectionState(ConnectionState::DISCONNECTED, rendererDevice->address);
rendererDevices.Remove(rendererDevice->address);
}
void OnEncryptionComplete(const RawAddress& address, bool success) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
LOG(INFO) << __func__ << " " << address;
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown device" << address;
return;
}
if (!success) {
LOG(ERROR) << "encryption failed";
BTA_GATTC_Close(rendererDevice->conn_id);
return;
}
BTA_GATTC_ServiceSearchRequest(rendererDevice->conn_id, &VCS_UUID);
}
void OnServiceSearchComplete(uint16_t conn_id, tGATT_STATUS status) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
LOG(INFO) << __func__;
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
if (status != GATT_SUCCESS) {
/* close connection and report service discovery complete with error */
LOG(ERROR) << "Service discovery failed";
VcpGattClose(rendererDevice);
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;
FindServerChangedCCCHandle(conn_id, service_changed_service);
*/
} else if (tmp.uuid == VCS_UUID) {
LOG(INFO) << "Found Volume Control service, handle=" << loghex(tmp.handle);
service = &tmp;
}
}
} else {
LOG(ERROR) << "no services found for conn_id: " << conn_id;
return;
}
if (!service) {
LOG(ERROR) << "No VCS found";
VcpGattClose(rendererDevice);
return;
}
for (const gatt::Characteristic& charac : service->characteristics) {
if (charac.uuid == VCS_VOLUME_STATE_UUID) {
rendererDevice->vcs.volume_state_handle = charac.value_handle;
rendererDevice->vcs.volume_state_ccc_handle =
FindCccHandle(conn_id, charac.value_handle);
if (!rendererDevice->vcs.volume_state_ccc_handle) {
LOG(ERROR) << __func__ << ": cannot find volume state CCC descriptor";
continue;
}
LOG(INFO) << __func__
<< ": vcs volume_state_handle=" << loghex(charac.value_handle)
<< ", ccc=" << loghex(rendererDevice->vcs.volume_state_ccc_handle);
} else if (charac.uuid == VCS_VOLUME_FLAGS_UUID) {
rendererDevice->vcs.volume_flags_handle = charac.value_handle;
rendererDevice->vcs.volume_flags_ccc_handle =
FindCccHandle(conn_id, charac.value_handle);
if (!rendererDevice->vcs.volume_flags_ccc_handle) {
LOG(ERROR) << __func__ << ": cannot find volume flags CCC descriptor";
continue;
}
LOG(INFO) << __func__
<< ": vcs volume_flags_handle=" << loghex(charac.value_handle)
<< ", ccc=" << loghex(rendererDevice->vcs.volume_flags_ccc_handle);
} else if (charac.uuid == VCS_VOLUME_CONTROL_POINT_UUID) {
// store volume control point!
rendererDevice->vcs.volume_control_point_handle = charac.value_handle;
} else {
LOG(WARNING) << "Unknown characteristic found:" << charac.uuid;
}
}
LOG(WARNING) << "reading vcs volume_state_handle";
GattOpsQueue::ReadCharacteristic(gatt_if,
conn_id, rendererDevice->vcs.volume_state_handle,
VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
}
void OnServiceChangeEvent(const RawAddress& address) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
VLOG(2) << "Skipping unknown device" << address;
return;
}
LOG(INFO) << __func__ << ": address=" << address;
rendererDevice->service_changed_rcvd = true;
GattOpsQueue::Clean(rendererDevice->conn_id);
}
void OnServiceDiscDoneEvent(const RawAddress& address) {
RendererDevice* rendererDevice = rendererDevices.FindByAddress(address);
if (!rendererDevice) {
VLOG(2) << "Skipping unknown device" << address;
return;
}
if (rendererDevice->service_changed_rcvd) {
BTA_GATTC_ServiceSearchRequest(rendererDevice->conn_id, &VCS_UUID);
}
}
void OnVolumeStateRead(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, uint8_t* value, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status)
<< ", renderer device state: " << loghex(rendererDevice->state);
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Error reading Volume State for device" << rendererDevice->address;
} else {
uint8_t* p = value;
uint8_t volume_setting;
STREAM_TO_UINT8(volume_setting, p);
rendererDevice->vcs.volume_state.volume_setting = volume_setting;
uint8_t mute;
STREAM_TO_UINT8(mute, p);
rendererDevice->vcs.volume_state.mute = mute;
uint8_t change_counter;
STREAM_TO_UINT8(change_counter, p);
rendererDevice->vcs.volume_state.change_counter = change_counter;
}
HandleVCSEvent(rendererDevice, VCS_VOLUME_STATE_READ_CMPL_EVT, status);
}
void OnVolumeFlagsRead(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, uint8_t* value, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Error reading Volume Flags for device" << rendererDevice->address;
} else {
uint8_t* p = value;
uint8_t volume_flags;
STREAM_TO_UINT8(volume_flags, p);
rendererDevice->vcs.volume_flags = volume_flags;
}
HandleVCSEvent(rendererDevice, VCS_VOLUME_FLAGS_READ_CMPL_EVT, status);
}
void OnVolumeStateCCCWrite(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
HandleVCSEvent(rendererDevice, VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT, status);
}
void OnVolumeFlagsCCCWrite(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
HandleVCSEvent(rendererDevice, VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT, status);
}
void OnSetAbsVolume(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
if (status != GATT_SUCCESS) {
// Check for VCS Invalid Change Counter error, it may
// conflict with GATT_NO_RESOURCES error.
if (status == VCS_INVALID_CHANGE_COUNTER ||
status == VCS_OPCODE_NOT_SUPPORTED) {
LOG(ERROR) << __func__ << ": Error code: " << status
<< " device: " << rendererDevice->address
<< " Read Volume State to update change counter";
rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_ABS_VOL;
GattOpsQueue::ReadCharacteristic(gatt_if,
conn_id, rendererDevice->vcs.volume_state_handle,
VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
} else {
LOG(ERROR) << __func__ << ": Other errors, not retry";
}
} else {
rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_ABS_VOL;
LOG(INFO) << "Set abs volume success " << rendererDevice->address;
}
}
void OnMute(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Volume State Write failed" << rendererDevice->address
<< "Read Volume State";
rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_MUTE_STATE;
GattOpsQueue::ReadCharacteristic(gatt_if,
conn_id, rendererDevice->vcs.volume_state_handle,
VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
} else {
LOG(INFO) << "Mute success" << rendererDevice->address;
}
}
void OnUnmute(uint16_t client_id, uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, void* data) {
RendererDevice* rendererDevice = rendererDevices.FindByConnId(conn_id);
if (!rendererDevice) {
LOG(WARNING) << "Skipping unknown read event, conn_id=" << loghex(conn_id);
return;
}
LOG(INFO) << __func__ << " " << rendererDevice->address << ", status: " << loghex(status);
if (status != GATT_SUCCESS) {
LOG(ERROR) << "Volume State Write failed" << rendererDevice->address
<< "Read Volume State";
rendererDevice->vcs.retry_cmd |= VCS_RETRY_SET_MUTE_STATE;
GattOpsQueue::ReadCharacteristic(gatt_if,
conn_id, rendererDevice->vcs.volume_state_handle,
VcpControllerImpl::OnVolumeStateReadStatic, nullptr);
} else {
LOG(INFO) << "Unmute success" << rendererDevice->address;
}
}
void RetryVolumeControlOp(RendererDevice* rendererDevice) {
LOG(INFO) << __func__ << " " << rendererDevice->address;
if (rendererDevice->vcs.retry_cmd & VCS_RETRY_SET_ABS_VOL) {
rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_ABS_VOL;
SetAbsVolume(rendererDevice->address, rendererDevice->vcs.pending_volume_setting);
}
if (rendererDevice->vcs.retry_cmd & VCS_RETRY_SET_MUTE_STATE) {
rendererDevice->vcs.retry_cmd &= ~VCS_RETRY_SET_MUTE_STATE;
if (rendererDevice->vcs.pending_mute_setting == VCS_MUTE_STATE) {
Mute(rendererDevice->address);
} else {
Unmute(rendererDevice->address);
}
}
}
static void OnVolumeStateReadStatic(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->OnVolumeStateRead(client_id, conn_id, status, handle, len, value,
data);
}
static void OnVolumeFlagsReadStatic(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->OnVolumeFlagsRead(client_id, conn_id, status, handle, len, value,
data);
}
static void OnVolumeStateCCCWriteStatic(uint16_t client_id, uint16_t conn_id,
tGATT_STATUS status, uint16_t handle, void* data) {
if (instance)
instance->OnVolumeStateCCCWrite(client_id, conn_id, status, handle, data);
}
static void OnVolumeFlagsCCCWriteStatic(uint16_t client_id, uint16_t conn_id,
tGATT_STATUS status, uint16_t handle, void* data) {
if (instance)
instance->OnVolumeFlagsCCCWrite(client_id, conn_id, status, handle, data);
}
static void OnSetAbsVolumeStatic(uint16_t client_id, uint16_t conn_id,
tGATT_STATUS status, uint16_t handle, void* data) {
if (instance)
instance->OnSetAbsVolume(client_id, conn_id, status, handle, data);
}
static void OnMuteStatic(uint16_t client_id, uint16_t conn_id,
tGATT_STATUS status, uint16_t handle, void* data) {
if (instance)
instance->OnMute(client_id, conn_id, status, handle, data);
}
static void OnUnmuteStatic(uint16_t client_id, uint16_t conn_id,
tGATT_STATUS status, uint16_t handle, void* data) {
if (instance)
instance->OnUnmute(client_id, conn_id, status, handle, data);
}
void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
uint8_t* value) {
RendererDevice* device = rendererDevices.FindByConnId(conn_id);
if (!device) {
LOG(INFO) << __func__
<< ": Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
if (handle == device->vcs.volume_state_handle) {
if ( len != sizeof(device->vcs.volume_state)) {
LOG(ERROR) << __func__ << ": Data Length mismatch, len=" << len
<< ", expecting " << sizeof(device->vcs.volume_state);
return;
}
LOG(INFO) << __func__ << " " << device->address << " volume state notification";
memcpy(&device->vcs.volume_state, value, len);
callbacks->OnVolumeStateChange(device->vcs.volume_state.volume_setting,
device->vcs.volume_state.mute, device->address);
} else if (handle == device->vcs.volume_flags_handle) {
if ( len != sizeof(device->vcs.volume_flags)) {
LOG(ERROR) << __func__ << ": Data Length mismatch, len=" << len
<< ", expecting " << sizeof(device->vcs.volume_flags);
return;
}
LOG(INFO) << __func__ << " " << device->address << " volume flags notification";
memcpy(&device->vcs.volume_flags, value, len);
callbacks->OnVolumeFlagsChange(device->vcs.volume_flags, device->address);
} else {
LOG(INFO) << __func__ << ": Mismatched handle, "
<< loghex(device->vcs.volume_state_handle)
<< " or " << loghex(device->vcs.volume_flags_handle)
<< "!=" << loghex(handle);
return;
}
}
void OnCongestionEvent(uint16_t conn_id, bool congested) {
RendererDevice* device = rendererDevices.FindByConnId(conn_id);
if (!device) {
LOG(INFO) << __func__
<< ": Skipping unknown device, conn_id=" << loghex(conn_id);
return;
}
LOG(WARNING) << __func__ << ": conn_id:" << loghex(conn_id)
<< ", congested: " << congested;
GattOpsQueue::CongestionCallback(conn_id, congested);
}
void HandleVCSEvent(RendererDevice* rendererDevice, uint32_t event, tGATT_STATUS status) {
LOG(INFO) << __func__ << " event = " << vcp_controller_handle_vcs_evt_str(event);
if (status != GATT_SUCCESS) {
if (rendererDevice->state == BTA_VCP_CONNECTING) {
LOG(ERROR) << __func__ << ": Error status while VCP connecting, Close GATT for device: "
<< rendererDevice->address;
VcpGattClose(rendererDevice);
return;
} else if (rendererDevice->state == BTA_VCP_CONNECTED) {
LOG(ERROR) << __func__ << ": Error status while VCP is connected for device: "
<< rendererDevice->address;
if (rendererDevice->vcs.retry_cmd != 0) {
rendererDevice->vcs.retry_cmd = 0;
}
return;
} else {
LOG(ERROR) << __func__ << ": Error status in disconnected or disconnecting "
<< "Igore handle VCS Event for device: " << rendererDevice->address;
return;
}
}
switch (event) {
case VCS_VOLUME_STATE_READ_CMPL_EVT: {
if (rendererDevice->state == BTA_VCP_CONNECTING) {
LOG(WARNING) << "Setup VCP connection, reading vcs volume_flags_handle";
GattOpsQueue::ReadCharacteristic(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_flags_handle,
VcpControllerImpl::OnVolumeFlagsReadStatic, nullptr);
break;
} else if (rendererDevice->state == BTA_VCP_CONNECTED) {
if (rendererDevice->vcs.retry_cmd != 0) {
RetryVolumeControlOp(rendererDevice);
}
}
break;
}
case VCS_VOLUME_FLAGS_READ_CMPL_EVT: {
if (rendererDevice->state == BTA_VCP_CONNECTING) {
/* Register and enable the Volume State Notification */
tGATT_STATUS register_status;
register_status = BTA_GATTC_RegisterForNotifications(
gatt_if, rendererDevice->address, rendererDevice->vcs.volume_state_handle);
if (register_status != GATT_SUCCESS) {
LOG(ERROR) << __func__
<< ": BTA_GATTC_RegisterForNotifications failed, status="
<< loghex(register_status);
VcpGattClose(rendererDevice);
return;
}
std::vector<uint8_t> value(2);
uint8_t* ptr = value.data();
UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
GattOpsQueue::WriteDescriptor(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_state_ccc_handle,
std::move(value), GATT_WRITE, VcpControllerImpl::OnVolumeStateCCCWriteStatic,
nullptr);
}
break;
}
case VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT: {
if (rendererDevice->state == BTA_VCP_CONNECTING) {
/* Register and enable the Volume State Notification */
tGATT_STATUS register_status;
register_status = BTA_GATTC_RegisterForNotifications(
gatt_if, rendererDevice->address, rendererDevice->vcs.volume_flags_handle);
if (register_status != GATT_SUCCESS) {
LOG(ERROR) << __func__
<< ": BTA_GATTC_RegisterForNotifications failed, status="
<< loghex(register_status);
VcpGattClose(rendererDevice);
return;
}
std::vector<uint8_t> value(2);
uint8_t* ptr = value.data();
UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
GattOpsQueue::WriteDescriptor(gatt_if,
rendererDevice->conn_id, rendererDevice->vcs.volume_flags_ccc_handle,
std::move(value), GATT_WRITE, VcpControllerImpl::OnVolumeFlagsCCCWriteStatic,
nullptr);
}
break;
}
case VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT: {
if (rendererDevice->state == BTA_VCP_CONNECTING) {
LOG(INFO) << __func__ << ": VCP Connection Setup complete";
rendererDevice->state = BTA_VCP_CONNECTED;
callbacks->OnConnectionState(ConnectionState::CONNECTED, rendererDevice->address);
callbacks->OnVolumeFlagsChange(rendererDevice->vcs.volume_flags,
rendererDevice->address);
callbacks->OnVolumeStateChange(rendererDevice->vcs.volume_state.volume_setting,
rendererDevice->vcs.volume_state.mute, rendererDevice->address);
break;
}
break;
}
default:
LOG(INFO) << __func__ << ": unexpected VCS event";
break;
}
}
// Find the handle for the client characteristics configuration of a given
// characteristics
uint16_t FindCccHandle(uint16_t conn_id, uint16_t char_handle) {
const gatt::Characteristic* p_char =
BTA_GATTC_GetCharacteristic(conn_id, char_handle);
LOG(INFO) << __func__ << " " << ", conn_id: " << conn_id << ", char_handle: " << 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;
}
void CleanUp() {
LOG(INFO) << __func__;
BTA_GATTC_AppDeregister(gatt_if);
for (RendererDevice& device : rendererDevices.devices) {
PostDisconnected(&device);
}
rendererDevices.devices.clear();
}
};
void vcp_controller_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
LOG(INFO) << __func__ << " event = " << vcp_controller_gatt_callback_evt_str(event);
if (p_data == nullptr) return;
switch (event) {
case BTA_GATTC_DEREG_EVT:
break;
case BTA_GATTC_OPEN_EVT: {
if (!instance) return;
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: {
if (!instance) return;
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:
if (!instance) return;
instance->OnServiceSearchComplete(p_data->search_cmpl.conn_id,
p_data->search_cmpl.status);
break;
case BTA_GATTC_NOTIF_EVT:
if (!instance) return;
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:
if (!instance) return;
instance->OnEncryptionComplete(p_data->enc_cmpl.remote_bda, true);
break;
case BTA_GATTC_SRVC_CHG_EVT:
if (!instance) return;
instance->OnServiceChangeEvent(p_data->remote_bda);
break;
case BTA_GATTC_SRVC_DISC_DONE_EVT:
if (!instance) return;
instance->OnServiceDiscDoneEvent(p_data->remote_bda);
break;
case BTA_GATTC_CONGEST_EVT:
if (!instance) return;
instance->OnCongestionEvent(p_data->congest.conn_id,
p_data->congest.congested);
break;
case BTA_GATTC_SEARCH_RES_EVT:
case BTA_GATTC_CANCEL_OPEN_EVT:
case BTA_GATTC_CONN_UPDATE_EVT:
default:
break;
}
}
void vcp_controller_encryption_callback(const RawAddress* address,
UNUSED_ATTR tGATT_TRANSPORT transport,
UNUSED_ATTR void* data, tBTM_STATUS status) {
if (instance) {
instance->OnEncryptionComplete(*address,
status == BTM_SUCCESS ? true : false);
}
}
void VcpController::Initialize(
bluetooth::vcp_controller::VcpControllerCallbacks* callbacks) {
LOG(INFO) << __func__ ;
if (instance) {
LOG(ERROR) << "Already initialized!";
}
instance = new VcpControllerImpl(callbacks);
}
bool VcpController::IsVcpControllerRunning() { return instance; }
VcpController* VcpController::Get() {
CHECK(instance);
return instance;
};
int VcpController::GetDeviceCount() {
if (!instance) {
LOG(INFO) << __func__ << ": Not initialized yet";
return 0;
}
return (instance->GetDeviceCount());
}
void VcpController::CleanUp() {
VcpControllerImpl* ptr = instance;
instance = nullptr;
ptr->CleanUp();
delete ptr;
};
/*******************************************************************************
* Debugging functions
******************************************************************************/
#define CASE_RETURN_STR(const) \
case const: \
return #const;
const char* vcp_controller_gatt_callback_evt_str(uint8_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_SEARCH_RES_EVT)
CASE_RETURN_STR(BTA_GATTC_CANCEL_OPEN_EVT)
CASE_RETURN_STR(BTA_GATTC_SRVC_CHG_EVT)
CASE_RETURN_STR(BTA_GATTC_CONN_UPDATE_EVT)
CASE_RETURN_STR(BTA_GATTC_SRVC_DISC_DONE_EVT)
CASE_RETURN_STR(BTA_GATTC_CONGEST_EVT)
default:
return (char*)"Unknown GATT Callback Event";
}
}
const char* vcp_controller_handle_vcs_evt_str(uint8_t event) {
switch (event) {
CASE_RETURN_STR(VCS_VOLUME_STATE_READ_CMPL_EVT)
CASE_RETURN_STR(VCS_VOLUME_FLAGS_READ_CMPL_EVT)
CASE_RETURN_STR(VCS_VOLUME_STATE_CCC_WRITE_CMPL_EVT)
CASE_RETURN_STR(VCS_VOLUME_FLAGS_CCC_WRITE_CMPL_EVT)
default:
return (char*)"Unknown handling VCS Event";
}
}