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