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 <string> 23 #include <unordered_set> 24 #include <vector> 25 26 #include "bta/include/bta_gatt_api.h" 27 #include "bta/vc/types.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 38 /* We are making active attempt to connect to this device */ 39 bool connecting_actively; 40 41 bool known_service_handles_; 42 43 uint8_t volume; 44 uint8_t change_counter; 45 bool mute; 46 uint8_t flags; 47 48 tCONN_ID connection_id; 49 uint16_t mtu_ = GATT_DEF_BLE_MTU_SIZE; 50 51 /* Volume Control Service */ 52 uint16_t volume_state_handle; 53 uint16_t volume_state_ccc_handle; 54 uint16_t volume_control_point_handle; 55 uint16_t volume_flags_handle; 56 uint16_t volume_flags_ccc_handle; 57 58 VolumeAudioInputs audio_inputs; 59 VolumeOffsets audio_offsets; 60 61 /* Set when device successfully reads server status and registers for 62 * notifications */ 63 bool device_ready; 64 VolumeControlDevice(const RawAddress & address,bool connecting_actively)65 VolumeControlDevice(const RawAddress& address, bool connecting_actively) 66 : address(address), 67 connecting_actively(connecting_actively), 68 known_service_handles_(false), 69 volume(0), 70 change_counter(0), 71 mute(false), 72 flags(0), 73 connection_id(GATT_INVALID_CONN_ID), 74 volume_state_handle(0), 75 volume_state_ccc_handle(0), 76 volume_control_point_handle(0), 77 volume_flags_handle(0), 78 volume_flags_ccc_handle(0), 79 device_ready(false) {} 80 81 ~VolumeControlDevice() = default; 82 ToStringForLogging()83 std::string ToStringForLogging() const { return address.ToStringForLogging(); } 84 ToRedactedStringForLogging()85 std::string ToRedactedStringForLogging() const { return address.ToRedactedStringForLogging(); } 86 DebugDump(int fd)87 void DebugDump(int fd) { 88 std::stringstream stream; 89 stream << " == device address: " << address.ToRedactedStringForLogging() << " == \n"; 90 91 if (connection_id == GATT_INVALID_CONN_ID) { 92 stream << " Not connected\n"; 93 } else { 94 stream << " Connected. Conn_id = " << static_cast<int>(connection_id) << "\n"; 95 } 96 97 stream << " volume: " << +volume << "\n" 98 << " mute: " << +mute << "\n" 99 << " change_counter: " << +change_counter << "\n" 100 << " flags: " << +flags << "\n" 101 << " device ready: " << device_ready << "\n" 102 << " connecting_actively: " << connecting_actively << "\n" 103 << " is encrypted: " << IsEncryptionEnabled() << "\n" 104 << " GATT operations pending: " << handles_pending.size() << "\n"; 105 106 dprintf(fd, "%s", stream.str().c_str()); 107 audio_offsets.Dump(fd); 108 audio_inputs.Dump(fd); 109 } 110 IsConnected()111 bool IsConnected() { return connection_id != GATT_INVALID_CONN_ID; } 112 113 void Disconnect(tGATT_IF gatt_if); 114 115 void DeregisterNotifications(tGATT_IF gatt_if); 116 117 bool UpdateHandles(void); 118 119 void ResetHandles(void); 120 HasHandles(void)121 bool HasHandles(void) { return GATT_HANDLE_IS_VALID(volume_state_handle); } 122 123 void ControlPointOperation(uint8_t opcode, const std::vector<uint8_t>* arg, GATT_WRITE_OP_CB cb, 124 void* cb_data); 125 void GetExtAudioOutVolumeOffset(uint8_t ext_output_id, GATT_READ_OP_CB cb, void* cb_data); 126 void SetExtAudioOutLocation(uint8_t ext_output_id, uint32_t location); 127 void GetExtAudioOutLocation(uint8_t ext_output_id, GATT_READ_OP_CB cb, void* cb_data); 128 void GetExtAudioOutDescription(uint8_t ext_output_id, GATT_READ_OP_CB cb, void* cb_data); 129 void SetExtAudioOutDescription(uint8_t ext_output_id, const std::string& descr); 130 void ExtAudioOutControlPointOperation(uint8_t ext_output_id, uint8_t opcode, 131 const std::vector<uint8_t>* arg, GATT_WRITE_OP_CB cb, 132 void* cb_data); 133 void GetExtAudioInState(uint8_t ext_input_id, GATT_READ_OP_CB cb, void* cb_data); 134 void GetExtAudioInStatus(uint8_t ext_input_id, GATT_READ_OP_CB cb, void* cb_data); 135 void GetExtAudioInType(uint8_t ext_input_id, GATT_READ_OP_CB cb, void* cb_data); 136 void GetExtAudioInGainProps(uint8_t ext_input_id, GATT_READ_OP_CB cb, void* cb_data); 137 void GetExtAudioInDescription(uint8_t ext_input_id, GATT_READ_OP_CB cb, void* cb_data); 138 void SetExtAudioInDescription(uint8_t ext_input_id, const std::string& descr); 139 bool ExtAudioInControlPointOperation(uint8_t ext_input_id, uint8_t opcode, 140 const std::vector<uint8_t>* arg, GATT_WRITE_OP_CB cb, 141 void* cb_data); 142 bool IsEncryptionEnabled(); 143 144 bool EnableEncryption(); 145 146 bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 147 GATT_WRITE_OP_CB cccd_write_cb); 148 void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 149 GATT_READ_MULTI_OP_CB chrc_multi_read, 150 GATT_WRITE_OP_CB cccd_write_cb); 151 bool VerifyReady(uint16_t handle); IsReady()152 bool IsReady() { return device_ready; } 153 154 private: 155 /* 156 * This is used to track the pending GATT operation handles. Once the list is 157 * empty the device is assumed ready and connected. We are doing it because we 158 * want to make sure all the required characteristics and descriptors are 159 * available on server side. 160 */ 161 std::unordered_set<uint16_t> handles_pending; 162 163 uint16_t find_ccc_handle(uint16_t chrc_handle); 164 bool set_volume_control_service_handles(const gatt::Service& service); 165 void set_volume_offset_control_service_handles(const gatt::Service& service); 166 void set_audio_input_control_service_handles(const gatt::Service& service); 167 bool subscribe_for_notifications(tGATT_IF gatt_if, uint16_t handle, uint16_t ccc_handle, 168 GATT_WRITE_OP_CB cb); 169 }; 170 171 class VolumeControlDevices { 172 public: Add(const RawAddress & address,bool connecting_actively)173 void Add(const RawAddress& address, bool connecting_actively) { 174 if (FindByAddress(address) != nullptr) { 175 return; 176 } 177 178 devices_.emplace_back(address, connecting_actively); 179 } 180 Remove(const RawAddress & address)181 void Remove(const RawAddress& address) { 182 for (auto it = devices_.begin(); it != devices_.end(); it++) { 183 if (it->address == address) { 184 it = devices_.erase(it); 185 break; 186 } 187 } 188 } 189 FindByAddress(const RawAddress & address)190 VolumeControlDevice* FindByAddress(const RawAddress& address) { 191 auto iter = std::find_if( 192 devices_.begin(), devices_.end(), 193 [&address](const VolumeControlDevice& device) { return device.address == address; }); 194 195 return (iter == devices_.end()) ? nullptr : &(*iter); 196 } 197 FindByConnId(tCONN_ID connection_id)198 VolumeControlDevice* FindByConnId(tCONN_ID connection_id) { 199 auto iter = std::find_if(devices_.begin(), devices_.end(), 200 [&connection_id](const VolumeControlDevice& device) { 201 return device.connection_id == connection_id; 202 }); 203 204 return (iter == devices_.end()) ? nullptr : &(*iter); 205 } 206 Size()207 size_t Size() { return devices_.size(); } 208 Clear()209 void Clear() { devices_.clear(); } 210 DebugDump(int fd)211 void DebugDump(int fd) { 212 if (devices_.empty()) { 213 dprintf(fd, " No VC devices:\n"); 214 } else { 215 dprintf(fd, " Devices:\n"); 216 for (auto& device : devices_) { 217 device.DebugDump(fd); 218 } 219 } 220 } 221 Disconnect(tGATT_IF gatt_if)222 void Disconnect(tGATT_IF gatt_if) { 223 for (auto& device : devices_) { 224 device.Disconnect(gatt_if); 225 } 226 } 227 ControlPointOperation(const std::vector<RawAddress> & devices,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)228 void ControlPointOperation(const std::vector<RawAddress>& devices, uint8_t opcode, 229 const std::vector<uint8_t>* arg, GATT_WRITE_OP_CB cb, void* cb_data) { 230 for (auto& addr : devices) { 231 VolumeControlDevice* device = FindByAddress(addr); 232 if (device && device->IsConnected()) { 233 device->ControlPointOperation(opcode, arg, cb, cb_data); 234 } 235 } 236 } 237 238 private: 239 std::vector<VolumeControlDevice> devices_; 240 }; 241 242 } // namespace internal 243 } // namespace vc 244 } // namespace bluetooth 245