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