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 <bluetooth/log.h>
19
20 #include <map>
21 #include <vector>
22
23 #include "bta_gatt_api.h"
24 #include "bta_gatt_queue.h"
25 #include "devices.h"
26 #include "gatt_api.h"
27 #include "internal_include/bt_trace.h"
28 #include "os/log.h"
29 #include "os/logging/log_adapter.h"
30 #include "stack/btm/btm_sec.h"
31 #include "stack/include/bt_types.h"
32 #include "types/bluetooth/uuid.h"
33
34 using namespace bluetooth::vc::internal;
35
DeregisterNotifications(tGATT_IF gatt_if)36 void VolumeControlDevice::DeregisterNotifications(tGATT_IF gatt_if) {
37 if (volume_state_handle != 0)
38 BTA_GATTC_DeregisterForNotifications(gatt_if, address, volume_state_handle);
39
40 if (volume_flags_handle != 0)
41 BTA_GATTC_DeregisterForNotifications(gatt_if, address, volume_flags_handle);
42
43 for (const VolumeOffset& of : audio_offsets.volume_offsets) {
44 BTA_GATTC_DeregisterForNotifications(gatt_if, address,
45 of.audio_descr_handle);
46 BTA_GATTC_DeregisterForNotifications(gatt_if, address,
47 of.audio_location_handle);
48 BTA_GATTC_DeregisterForNotifications(gatt_if, address, of.state_handle);
49 }
50 }
51
Disconnect(tGATT_IF gatt_if)52 void VolumeControlDevice::Disconnect(tGATT_IF gatt_if) {
53 log::info("{}", address);
54
55 if (IsConnected()) {
56 DeregisterNotifications(gatt_if);
57 BtaGattQueue::Clean(connection_id);
58 BTA_GATTC_Close(connection_id);
59 connection_id = GATT_INVALID_CONN_ID;
60 }
61
62 device_ready = false;
63 handles_pending.clear();
64 }
65
66 /*
67 * Find the handle for the client characteristics configuration of a given
68 * characteristics
69 */
find_ccc_handle(uint16_t chrc_handle)70 uint16_t VolumeControlDevice::find_ccc_handle(uint16_t chrc_handle) {
71 const gatt::Characteristic* p_char =
72 BTA_GATTC_GetCharacteristic(connection_id, chrc_handle);
73 if (!p_char) {
74 log::warn("no such handle=0x{:x}", chrc_handle);
75 return 0;
76 }
77
78 for (const gatt::Descriptor& desc : p_char->descriptors) {
79 if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
80 return desc.handle;
81 }
82
83 return 0;
84 }
85
set_volume_control_service_handles(const gatt::Service & service)86 bool VolumeControlDevice::set_volume_control_service_handles(
87 const gatt::Service& service) {
88 uint16_t state_handle = 0, state_ccc_handle = 0, control_point_handle = 0,
89 flags_handle = 0, flags_ccc_handle = 0;
90
91 for (const gatt::Characteristic& chrc : service.characteristics) {
92 if (chrc.uuid == kVolumeControlStateUuid) {
93 state_handle = chrc.value_handle;
94 state_ccc_handle = find_ccc_handle(chrc.value_handle);
95 } else if (chrc.uuid == kVolumeControlPointUuid) {
96 control_point_handle = chrc.value_handle;
97 } else if (chrc.uuid == kVolumeFlagsUuid) {
98 flags_handle = chrc.value_handle;
99 flags_ccc_handle = find_ccc_handle(chrc.value_handle);
100 } else {
101 log::warn("unknown characteristic={}", chrc.uuid);
102 }
103 }
104
105 // Validate service handles
106 if (GATT_HANDLE_IS_VALID(state_handle) &&
107 GATT_HANDLE_IS_VALID(state_ccc_handle) &&
108 GATT_HANDLE_IS_VALID(control_point_handle) &&
109 GATT_HANDLE_IS_VALID(flags_handle)
110 /* volume_flags_ccc_handle is optional */) {
111 volume_state_handle = state_handle;
112 volume_state_ccc_handle = state_ccc_handle;
113 volume_control_point_handle = control_point_handle;
114 volume_flags_handle = flags_handle;
115 volume_flags_ccc_handle = flags_ccc_handle;
116 return true;
117 }
118
119 return false;
120 }
121
set_volume_offset_control_service_handles(const gatt::Service & service)122 void VolumeControlDevice::set_volume_offset_control_service_handles(
123 const gatt::Service& service) {
124 VolumeOffset offset = VolumeOffset(service.handle);
125
126 for (const gatt::Characteristic& chrc : service.characteristics) {
127 if (chrc.uuid == kVolumeOffsetStateUuid) {
128 offset.state_handle = chrc.value_handle;
129 offset.state_ccc_handle = find_ccc_handle(chrc.value_handle);
130
131 } else if (chrc.uuid == kVolumeOffsetLocationUuid) {
132 offset.audio_location_handle = chrc.value_handle;
133 offset.audio_location_ccc_handle = find_ccc_handle(chrc.value_handle);
134 offset.audio_location_writable =
135 chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
136
137 } else if (chrc.uuid == kVolumeOffsetControlPointUuid) {
138 offset.control_point_handle = chrc.value_handle;
139
140 } else if (chrc.uuid == kVolumeOffsetOutputDescriptionUuid) {
141 offset.audio_descr_handle = chrc.value_handle;
142 offset.audio_descr_ccc_handle = find_ccc_handle(chrc.value_handle);
143 offset.audio_descr_writable =
144 chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
145
146 } else {
147 log::warn("unknown characteristic={}", chrc.uuid);
148 }
149 }
150
151 // Check if all mandatory attributes are present
152 if (GATT_HANDLE_IS_VALID(offset.state_handle) &&
153 GATT_HANDLE_IS_VALID(offset.state_ccc_handle) &&
154 GATT_HANDLE_IS_VALID(offset.audio_location_handle) &&
155 /* audio_location_ccc_handle is optional */
156 GATT_HANDLE_IS_VALID(offset.control_point_handle) &&
157 GATT_HANDLE_IS_VALID(offset.audio_descr_handle)
158 /* audio_descr_ccc_handle is optional */) {
159 audio_offsets.Add(offset);
160 log::info("Offset added id=0x{:x}", offset.id);
161 } else {
162 log::warn("Ignoring offset handle=0x{:x}", service.handle);
163 }
164 }
165
UpdateHandles(void)166 bool VolumeControlDevice::UpdateHandles(void) {
167 ResetHandles();
168
169 bool vcs_found = false;
170 const std::list<gatt::Service>* services =
171 BTA_GATTC_GetServices(connection_id);
172 if (services == nullptr) {
173 log::error("No services found");
174 return false;
175 }
176
177 for (auto const& service : *services) {
178 if (service.uuid == kVolumeControlUuid) {
179 log::info("Found VCS, handle=0x{:x}", service.handle);
180 vcs_found = set_volume_control_service_handles(service);
181 if (!vcs_found) break;
182
183 known_service_handles_ = true;
184 for (auto const& included : service.included_services) {
185 const gatt::Service* service =
186 BTA_GATTC_GetOwningService(connection_id, included.start_handle);
187 if (service == nullptr) continue;
188
189 if (included.uuid == kVolumeOffsetUuid) {
190 log::info("Found VOCS, handle=0x{:x}", service->handle);
191 set_volume_offset_control_service_handles(*service);
192
193 } else {
194 log::warn("unknown service={}", service->uuid);
195 }
196 }
197 }
198 }
199
200 return vcs_found;
201 }
202
ResetHandles(void)203 void VolumeControlDevice::ResetHandles(void) {
204 known_service_handles_ = false;
205 device_ready = false;
206
207 // the handles are not valid, so discard pending GATT operations
208 BtaGattQueue::Clean(connection_id);
209
210 volume_state_handle = 0;
211 volume_state_ccc_handle = 0;
212 volume_control_point_handle = 0;
213 volume_flags_handle = 0;
214 volume_flags_ccc_handle = 0;
215
216 if (audio_offsets.Size() != 0) audio_offsets.Clear();
217 }
218
ControlPointOperation(uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)219 void VolumeControlDevice::ControlPointOperation(uint8_t opcode,
220 const std::vector<uint8_t>* arg,
221 GATT_WRITE_OP_CB cb,
222 void* cb_data) {
223 std::vector<uint8_t> set_value({opcode, change_counter});
224 if (arg != nullptr)
225 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
226
227 BtaGattQueue::WriteCharacteristic(connection_id, volume_control_point_handle,
228 set_value, GATT_WRITE, cb, cb_data);
229 }
230
subscribe_for_notifications(tGATT_IF gatt_if,uint16_t handle,uint16_t ccc_handle,GATT_WRITE_OP_CB cb)231 bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if,
232 uint16_t handle,
233 uint16_t ccc_handle,
234 GATT_WRITE_OP_CB cb) {
235 tGATT_STATUS status =
236 BTA_GATTC_RegisterForNotifications(gatt_if, address, handle);
237 if (status != GATT_SUCCESS) {
238 log::error("failed, status=0x{:x}", status);
239 return false;
240 }
241
242 std::vector<uint8_t> value(2);
243 uint8_t* ptr = value.data();
244 UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
245 BtaGattQueue::WriteDescriptor(connection_id, ccc_handle, std::move(value),
246 GATT_WRITE, cb, nullptr);
247
248 return true;
249 }
250
251 /**
252 * Enqueue GATT requests that are required by the Volume Control to be
253 * functional. This includes State characteristics read and subscription.
254 * Those characteristics contain the change counter needed to send any request
255 * via Control Point. Once completed successfully, the device can be stored
256 * and reported as connected. In each case we subscribe first to be sure we do
257 * not miss any value change.
258 */
EnqueueInitialRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)259 bool VolumeControlDevice::EnqueueInitialRequests(
260 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
261 GATT_WRITE_OP_CB cccd_write_cb) {
262 handles_pending.clear();
263 handles_pending.insert(volume_state_handle);
264 handles_pending.insert(volume_state_ccc_handle);
265 if (!subscribe_for_notifications(gatt_if, volume_state_handle,
266 volume_state_ccc_handle, cccd_write_cb)) {
267 return false;
268 }
269
270 for (auto const& offset : audio_offsets.volume_offsets) {
271 handles_pending.insert(offset.state_handle);
272 handles_pending.insert(offset.state_ccc_handle);
273 if (!subscribe_for_notifications(gatt_if, offset.state_handle,
274 offset.state_ccc_handle, cccd_write_cb)) {
275 return false;
276 }
277
278 BtaGattQueue::ReadCharacteristic(connection_id, offset.state_handle,
279 chrc_read_cb, nullptr);
280 }
281
282 BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle,
283 chrc_read_cb, nullptr);
284
285 return true;
286 }
287
288 /**
289 * Enqueue the remaining requests. Those are not so crucial and can be done
290 * once Volume Control instance indicates it's readiness to profile.
291 * This includes characteristics read and subscription.
292 * In each case we subscribe first to be sure we do not miss any value change.
293 */
EnqueueRemainingRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)294 void VolumeControlDevice::EnqueueRemainingRequests(
295 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
296 GATT_WRITE_OP_CB cccd_write_cb) {
297 std::map<uint16_t, uint16_t> handle_pairs{
298 {volume_flags_handle, volume_flags_ccc_handle},
299 };
300
301 for (auto const& offset : audio_offsets.volume_offsets) {
302 handle_pairs[offset.audio_location_handle] =
303 offset.audio_location_ccc_handle;
304 handle_pairs[offset.audio_descr_handle] = offset.audio_descr_ccc_handle;
305 }
306
307 for (auto const& handles : handle_pairs) {
308 if (GATT_HANDLE_IS_VALID(handles.second)) {
309 subscribe_for_notifications(gatt_if, handles.first, handles.second,
310 cccd_write_cb);
311 }
312
313 BtaGattQueue::ReadCharacteristic(connection_id, handles.first, chrc_read_cb,
314 nullptr);
315 }
316 }
317
VerifyReady(uint16_t handle)318 bool VolumeControlDevice::VerifyReady(uint16_t handle) {
319 handles_pending.erase(handle);
320 device_ready = handles_pending.size() == 0;
321 return device_ready;
322 }
323
GetExtAudioOutVolumeOffset(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)324 void VolumeControlDevice::GetExtAudioOutVolumeOffset(uint8_t ext_output_id,
325 GATT_READ_OP_CB cb,
326 void* cb_data) {
327 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
328 if (!offset) {
329 log::error("no such offset!");
330 return;
331 }
332
333 BtaGattQueue::ReadCharacteristic(connection_id, offset->state_handle, cb,
334 cb_data);
335 }
336
GetExtAudioOutLocation(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)337 void VolumeControlDevice::GetExtAudioOutLocation(uint8_t ext_output_id,
338 GATT_READ_OP_CB cb,
339 void* cb_data) {
340 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
341 if (!offset) {
342 log::error("no such offset!");
343 return;
344 }
345
346 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_location_handle,
347 cb, cb_data);
348 }
349
SetExtAudioOutLocation(uint8_t ext_output_id,uint32_t location)350 void VolumeControlDevice::SetExtAudioOutLocation(uint8_t ext_output_id,
351 uint32_t location) {
352 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
353 if (!offset) {
354 log::error("no such offset!");
355 return;
356 }
357
358 if (!offset->audio_location_writable) {
359 log::warn("not writable");
360 return;
361 }
362
363 std::vector<uint8_t> value(4);
364 uint8_t* ptr = value.data();
365 UINT32_TO_STREAM(ptr, location);
366 BtaGattQueue::WriteCharacteristic(connection_id,
367 offset->audio_location_handle, value,
368 GATT_WRITE_NO_RSP, nullptr, nullptr);
369 }
370
GetExtAudioOutDescription(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)371 void VolumeControlDevice::GetExtAudioOutDescription(uint8_t ext_output_id,
372 GATT_READ_OP_CB cb,
373 void* cb_data) {
374 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
375 if (!offset) {
376 log::error("no such offset!");
377 return;
378 }
379
380 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_descr_handle,
381 cb, cb_data);
382 }
383
SetExtAudioOutDescription(uint8_t ext_output_id,std::string & descr)384 void VolumeControlDevice::SetExtAudioOutDescription(uint8_t ext_output_id,
385 std::string& descr) {
386 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
387 if (!offset) {
388 log::error("no such offset!");
389 return;
390 }
391
392 if (!offset->audio_descr_writable) {
393 log::warn("not writable");
394 return;
395 }
396
397 std::vector<uint8_t> value(descr.begin(), descr.end());
398 BtaGattQueue::WriteCharacteristic(connection_id, offset->audio_descr_handle,
399 value, GATT_WRITE_NO_RSP, nullptr, nullptr);
400 }
401
ExtAudioOutControlPointOperation(uint8_t ext_output_id,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)402 void VolumeControlDevice::ExtAudioOutControlPointOperation(
403 uint8_t ext_output_id, uint8_t opcode, const std::vector<uint8_t>* arg,
404 GATT_WRITE_OP_CB cb, void* cb_data) {
405 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
406 if (!offset) {
407 log::error("no such offset!");
408 return;
409 }
410
411 std::vector<uint8_t> set_value({opcode, offset->change_counter});
412 if (arg != nullptr)
413 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
414
415 BtaGattQueue::WriteCharacteristic(connection_id, offset->control_point_handle,
416 set_value, GATT_WRITE, cb, cb_data);
417 }
418
IsEncryptionEnabled()419 bool VolumeControlDevice::IsEncryptionEnabled() {
420 return BTM_IsEncrypted(address, BT_TRANSPORT_LE);
421 }
422
EnableEncryption()423 bool VolumeControlDevice::EnableEncryption() {
424 int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, nullptr, nullptr,
425 BTM_BLE_SEC_ENCRYPT);
426 log::info("{}: result=0x{:02x}", address, result);
427
428 return result != BTM_ERR_KEY_MISSING;
429 }
430