1 /* 2 * Copyright 2021 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #pragma once 19 20 #include <algorithm> 21 #include <cstdint> 22 #include <unordered_set> 23 #include <vector> 24 25 #include "bta/include/bta_gatt_api.h" 26 #include "bta/vc/types.h" 27 #include "include/hardware/bt_vc.h" 28 #include "types/raw_address.h" 29 30 namespace bluetooth { 31 namespace vc { 32 namespace internal { 33 34 class VolumeControlDevice { 35 public: 36 RawAddress address; 37 /* This is true only during first connection to profile, until we store the 38 * device 39 */ 40 bool first_connection; 41 42 /* we are making active attempt to connect to this device, 'direct connect'. 43 * This is true only during initial phase of first connection. */ 44 bool connecting_actively; 45 46 bool service_changed_rcvd; 47 48 uint8_t volume; 49 uint8_t change_counter; 50 bool mute; 51 uint8_t flags; 52 53 uint16_t connection_id; 54 55 /* Volume Control Service */ 56 uint16_t volume_state_handle; 57 uint16_t volume_state_ccc_handle; 58 uint16_t volume_control_point_handle; 59 uint16_t volume_flags_handle; 60 uint16_t volume_flags_ccc_handle; 61 62 VolumeOffsets audio_offsets; 63 64 bool device_ready; /* Set when device read server status and registgered for 65 notifications */ 66 VolumeControlDevice(const RawAddress & address,bool first_connection)67 VolumeControlDevice(const RawAddress& address, bool first_connection) 68 : address(address), 69 first_connection(first_connection), 70 connecting_actively(first_connection), 71 service_changed_rcvd(false), 72 volume(0), 73 change_counter(0), 74 mute(false), 75 flags(0), 76 connection_id(GATT_INVALID_CONN_ID), 77 volume_state_handle(0), 78 volume_state_ccc_handle(0), 79 volume_control_point_handle(0), 80 volume_flags_handle(0), 81 volume_flags_ccc_handle(0), 82 device_ready(false) {} 83 84 ~VolumeControlDevice() = default; 85 ToString()86 inline std::string ToString() { return address.ToString(); } 87 DebugDump(int fd)88 void DebugDump(int fd) { 89 std::stringstream stream; 90 stream << " == device address: " << address << " == \n"; 91 92 if (connection_id == GATT_INVALID_CONN_ID) 93 stream << " Not connected\n"; 94 else 95 stream << " Connected. Conn_id = " << connection_id << "\n"; 96 97 stream << " volume: " << +volume << "\n" 98 << " mute: " << +mute << "\n" 99 << " flags: " << +flags << "\n" 100 << " device read: " << device_ready << "\n" 101 << " first_connection_: " << first_connection << "\n" 102 << " connecting_actively_: " << connecting_actively << "\n"; 103 104 dprintf(fd, "%s", stream.str().c_str()); 105 audio_offsets.Dump(fd); 106 } 107 IsConnected()108 bool IsConnected() { return connection_id != GATT_INVALID_CONN_ID; } 109 110 void Disconnect(tGATT_IF gatt_if); 111 112 void DeregisterNotifications(tGATT_IF gatt_if); 113 114 bool UpdateHandles(void); 115 116 void ResetHandles(void); 117 HasHandles(void)118 bool HasHandles(void) { return GATT_HANDLE_IS_VALID(volume_state_handle); } 119 120 void ControlPointOperation(uint8_t opcode, const std::vector<uint8_t>* arg, 121 GATT_WRITE_OP_CB cb, void* cb_data); 122 void GetExtAudioOutVolumeOffset(uint8_t ext_output_id, GATT_READ_OP_CB cb, 123 void* cb_data); 124 void SetExtAudioOutLocation(uint8_t ext_output_id, uint32_t location); 125 void GetExtAudioOutLocation(uint8_t ext_output_id, GATT_READ_OP_CB cb, 126 void* cb_data); 127 void GetExtAudioOutDescription(uint8_t ext_output_id, GATT_READ_OP_CB cb, 128 void* cb_data); 129 void SetExtAudioOutDescription(uint8_t ext_output_id, std::string& descr); 130 void ExtAudioOutControlPointOperation(uint8_t ext_output_id, uint8_t opcode, 131 const std::vector<uint8_t>* arg, 132 GATT_WRITE_OP_CB cb, void* cb_data); 133 bool IsEncryptionEnabled(); 134 135 void EnableEncryption(); 136 137 bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 138 GATT_WRITE_OP_CB cccd_write_cb); 139 void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 140 GATT_WRITE_OP_CB cccd_write_cb); 141 bool VerifyReady(uint16_t handle); IsReady()142 bool IsReady() { return device_ready; } 143 144 private: 145 /* 146 * This is used to track the pending GATT operation handles. Once the list is 147 * empty the device is assumed ready and connected. We are doing it because we 148 * want to make sure all the required characteristics and descritors are 149 * available on server side. 150 */ 151 std::unordered_set<uint16_t> handles_pending; 152 153 uint16_t find_ccc_handle(uint16_t chrc_handle); 154 bool set_volume_control_service_handles(const gatt::Service& service); 155 void set_volume_offset_control_service_handles(const gatt::Service& service); 156 bool subscribe_for_notifications(tGATT_IF gatt_if, uint16_t handle, 157 uint16_t ccc_handle, GATT_WRITE_OP_CB cb); 158 }; 159 160 class VolumeControlDevices { 161 public: Add(const RawAddress & address,bool first_connection)162 void Add(const RawAddress& address, bool first_connection) { 163 if (FindByAddress(address) != nullptr) return; 164 165 devices_.emplace_back(address, first_connection); 166 } 167 Remove(const RawAddress & address)168 void Remove(const RawAddress& address) { 169 for (auto it = devices_.begin(); it != devices_.end(); it++) { 170 if (it->address == address) { 171 it = devices_.erase(it); 172 break; 173 } 174 } 175 } 176 FindByAddress(const RawAddress & address)177 VolumeControlDevice* FindByAddress(const RawAddress& address) { 178 auto iter = std::find_if(devices_.begin(), devices_.end(), 179 [&address](const VolumeControlDevice& device) { 180 return device.address == address; 181 }); 182 183 return (iter == devices_.end()) ? nullptr : &(*iter); 184 } 185 FindByConnId(uint16_t connection_id)186 VolumeControlDevice* FindByConnId(uint16_t connection_id) { 187 auto iter = 188 std::find_if(devices_.begin(), devices_.end(), 189 [&connection_id](const VolumeControlDevice& device) { 190 return device.connection_id == connection_id; 191 }); 192 193 return (iter == devices_.end()) ? nullptr : &(*iter); 194 } 195 Size()196 size_t Size() { return (devices_.size()); } 197 Clear()198 void Clear() { devices_.clear(); } 199 DebugDump(int fd)200 void DebugDump(int fd) { 201 if (devices_.empty()) { 202 dprintf(fd, " No VC devices:\n"); 203 } else { 204 dprintf(fd, " Devices:\n"); 205 for (auto& device : devices_) { 206 device.DebugDump(fd); 207 } 208 } 209 } 210 Disconnect(tGATT_IF gatt_if)211 void Disconnect(tGATT_IF gatt_if) { 212 for (auto& device : devices_) { 213 device.Disconnect(gatt_if); 214 } 215 } 216 ControlPointOperation(std::vector<RawAddress> & devices,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)217 void ControlPointOperation(std::vector<RawAddress>& devices, uint8_t opcode, 218 const std::vector<uint8_t>* arg, 219 GATT_WRITE_OP_CB cb, void* cb_data) { 220 for (auto& addr : devices) { 221 VolumeControlDevice* device = FindByAddress(addr); 222 if (device && device->IsConnected()) 223 device->ControlPointOperation(opcode, arg, cb, cb_data); 224 } 225 } 226 227 private: 228 std::vector<VolumeControlDevice> devices_; 229 }; 230 231 } // namespace internal 232 } // namespace vc 233 } // namespace bluetooth 234