1 /* 2 * Copyright 2020 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 #include <map> 19 #include <vector> 20 21 #include "bta_gatt_api.h" 22 #include "bta_gatt_queue.h" 23 #include "devices.h" 24 #include "gatt_api.h" 25 #include "stack/btm/btm_sec.h" 26 27 using namespace bluetooth::vc::internal; 28 Disconnect(tGATT_IF gatt_if)29 void VolumeControlDevice::Disconnect(tGATT_IF gatt_if) { 30 LOG(INFO) << __func__ << ": " << this->ToString(); 31 32 if (IsConnected()) { 33 if (volume_state_handle != 0) 34 BTA_GATTC_DeregisterForNotifications(gatt_if, address, 35 volume_state_handle); 36 37 if (volume_flags_handle != 0) 38 BTA_GATTC_DeregisterForNotifications(gatt_if, address, 39 volume_flags_handle); 40 41 BtaGattQueue::Clean(connection_id); 42 BTA_GATTC_Close(connection_id); 43 connection_id = GATT_INVALID_CONN_ID; 44 } else { 45 BTA_GATTC_CancelOpen(gatt_if, address, false); 46 } 47 48 device_ready = false; 49 handles_pending.clear(); 50 } 51 52 /* 53 * Find the handle for the client characteristics configuration of a given 54 * characteristics 55 */ find_ccc_handle(uint16_t chrc_handle)56 uint16_t VolumeControlDevice::find_ccc_handle(uint16_t chrc_handle) { 57 const gatt::Characteristic* p_char = 58 BTA_GATTC_GetCharacteristic(connection_id, chrc_handle); 59 if (!p_char) { 60 LOG(WARNING) << __func__ << ": no such handle=" << loghex(chrc_handle); 61 return 0; 62 } 63 64 for (const gatt::Descriptor& desc : p_char->descriptors) { 65 if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)) 66 return desc.handle; 67 } 68 69 return 0; 70 } 71 set_volume_control_service_handles(const gatt::Service & service)72 bool VolumeControlDevice::set_volume_control_service_handles( 73 const gatt::Service& service) { 74 uint16_t state_handle = 0, state_ccc_handle = 0, control_point_handle = 0, 75 flags_handle = 0, flags_ccc_handle = 0; 76 77 for (const gatt::Characteristic& chrc : service.characteristics) { 78 if (chrc.uuid == kVolumeControlStateUuid) { 79 state_handle = chrc.value_handle; 80 state_ccc_handle = find_ccc_handle(chrc.value_handle); 81 } else if (chrc.uuid == kVolumeControlPointUuid) { 82 control_point_handle = chrc.value_handle; 83 } else if (chrc.uuid == kVolumeFlagsUuid) { 84 flags_handle = chrc.value_handle; 85 flags_ccc_handle = find_ccc_handle(chrc.value_handle); 86 } else { 87 LOG(WARNING) << __func__ << ": unknown characteristic=" << chrc.uuid; 88 } 89 } 90 91 // Validate service handles 92 if (GATT_HANDLE_IS_VALID(state_handle) && 93 GATT_HANDLE_IS_VALID(state_ccc_handle) && 94 GATT_HANDLE_IS_VALID(control_point_handle) && 95 GATT_HANDLE_IS_VALID(flags_handle) 96 /* volume_flags_ccc_handle is optional */) { 97 volume_state_handle = state_handle; 98 volume_state_ccc_handle = state_ccc_handle; 99 volume_control_point_handle = control_point_handle; 100 volume_flags_handle = flags_handle; 101 volume_flags_ccc_handle = flags_ccc_handle; 102 return true; 103 } 104 105 return false; 106 } 107 UpdateHandles(void)108 bool VolumeControlDevice::UpdateHandles(void) { 109 ResetHandles(); 110 111 bool vcs_found = false; 112 const std::list<gatt::Service>* services = 113 BTA_GATTC_GetServices(connection_id); 114 if (services == nullptr) { 115 LOG(ERROR) << "No services found"; 116 return false; 117 } 118 119 for (auto const& service : *services) { 120 if (service.uuid == kVolumeControlUuid) { 121 LOG(INFO) << "Found VCS, handle=" << loghex(service.handle); 122 vcs_found = set_volume_control_service_handles(service); 123 if (!vcs_found) break; 124 } 125 } 126 127 return vcs_found; 128 } 129 ResetHandles(void)130 void VolumeControlDevice::ResetHandles(void) { 131 device_ready = false; 132 133 // the handles are not valid, so discard pending GATT operations 134 BtaGattQueue::Clean(connection_id); 135 136 volume_state_handle = 0; 137 volume_state_ccc_handle = 0; 138 volume_control_point_handle = 0; 139 volume_flags_handle = 0; 140 volume_flags_ccc_handle = 0; 141 } 142 ControlPointOperation(uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)143 void VolumeControlDevice::ControlPointOperation(uint8_t opcode, 144 const std::vector<uint8_t>* arg, 145 GATT_WRITE_OP_CB cb, 146 void* cb_data) { 147 std::vector<uint8_t> set_value({opcode, change_counter}); 148 if (arg != nullptr) 149 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end()); 150 151 BtaGattQueue::WriteCharacteristic(connection_id, volume_control_point_handle, 152 set_value, GATT_WRITE, cb, cb_data); 153 } 154 subscribe_for_notifications(tGATT_IF gatt_if,uint16_t handle,uint16_t ccc_handle,GATT_WRITE_OP_CB cb)155 bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if, 156 uint16_t handle, 157 uint16_t ccc_handle, 158 GATT_WRITE_OP_CB cb) { 159 tGATT_STATUS status = 160 BTA_GATTC_RegisterForNotifications(gatt_if, address, handle); 161 if (status != GATT_SUCCESS) { 162 LOG(ERROR) << __func__ << ": failed, status=" << loghex(+status); 163 return false; 164 } 165 166 std::vector<uint8_t> value(2); 167 uint8_t* ptr = value.data(); 168 UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION); 169 BtaGattQueue::WriteDescriptor(connection_id, ccc_handle, std::move(value), 170 GATT_WRITE, cb, nullptr); 171 172 return true; 173 } 174 175 /** 176 * Enqueue GATT requests that are required by the Volume Control to be 177 * functional. This includes State characteristics read and subscription. 178 * Those characteristics contain the change counter needed to send any request 179 * via Control Point. Once completed successfully, the device can be stored 180 * and reported as connected. In each case we subscribe first to be sure we do 181 * not miss any value change. 182 */ EnqueueInitialRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)183 bool VolumeControlDevice::EnqueueInitialRequests( 184 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 185 GATT_WRITE_OP_CB cccd_write_cb) { 186 handles_pending.clear(); 187 handles_pending.insert(volume_state_handle); 188 handles_pending.insert(volume_state_ccc_handle); 189 if (!subscribe_for_notifications(gatt_if, volume_state_handle, 190 volume_state_ccc_handle, cccd_write_cb)) { 191 return false; 192 } 193 194 BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle, 195 chrc_read_cb, nullptr); 196 197 return true; 198 } 199 200 /** 201 * Enqueue the remaining requests. Those are not so crucial and can be done 202 * once Volume Control instance indicates it's readiness to profile. 203 * This includes characteristics read and subscription. 204 * In each case we subscribe first to be sure we do not miss any value change. 205 */ EnqueueRemainingRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)206 void VolumeControlDevice::EnqueueRemainingRequests( 207 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb, 208 GATT_WRITE_OP_CB cccd_write_cb) { 209 std::map<uint16_t, uint16_t> handle_pairs{ 210 {volume_flags_handle, volume_flags_ccc_handle}, 211 }; 212 213 for (auto const& handles : handle_pairs) { 214 if (GATT_HANDLE_IS_VALID(handles.second)) { 215 subscribe_for_notifications(gatt_if, handles.first, handles.second, 216 cccd_write_cb); 217 } 218 219 BtaGattQueue::ReadCharacteristic(connection_id, handles.first, chrc_read_cb, 220 nullptr); 221 } 222 } 223 VerifyReady(uint16_t handle)224 bool VolumeControlDevice::VerifyReady(uint16_t handle) { 225 handles_pending.erase(handle); 226 device_ready = handles_pending.size() == 0; 227 return device_ready; 228 } 229 IsEncryptionEnabled()230 bool VolumeControlDevice::IsEncryptionEnabled() { 231 uint8_t sec_flag = 0; 232 bool device_found = 233 BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE); 234 LOG(INFO) << __func__ << ": found=" << static_cast<int>(device_found) 235 << " sec_flag=" << loghex(sec_flag); 236 return device_found && (sec_flag & BTM_SEC_FLAG_ENCRYPTED); 237 } 238 EnableEncryption(tBTM_SEC_CALLBACK * callback)239 bool VolumeControlDevice::EnableEncryption(tBTM_SEC_CALLBACK* callback) { 240 int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, callback, nullptr, 241 BTM_BLE_SEC_ENCRYPT); 242 LOG(INFO) << __func__ << ": result=" << +result; 243 // TODO: should we care about the result?? 244 return true; 245 } 246