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 #include <com_android_bluetooth_flags.h>
20
21 #include <algorithm>
22 #include <cstddef>
23 #include <cstdint>
24 #include <list>
25 #include <map>
26 #include <string>
27 #include <utility>
28 #include <vector>
29
30 #include "bta/include/bta_gatt_api.h"
31 #include "bta/include/bta_gatt_queue.h"
32 #include "bta/vc/devices.h"
33 #include "btm_ble_api_types.h"
34 #include "btm_sec_api_types.h"
35 #include "btm_status.h"
36 #include "gatt/database.h"
37 #include "gattdefs.h"
38 #include "stack/btm/btm_sec.h"
39 #include "stack/include/bt_types.h"
40 #include "stack/include/gatt_api.h"
41 #include "types/bluetooth/uuid.h"
42 #include "types/bt_transport.h"
43 #include "vc/types.h"
44
45 using bluetooth::vc::internal::VolumeControlDevice;
46
DeregisterNotifications(tGATT_IF gatt_if)47 void VolumeControlDevice::DeregisterNotifications(tGATT_IF gatt_if) {
48 if (volume_state_handle != 0) {
49 BTA_GATTC_DeregisterForNotifications(gatt_if, address, volume_state_handle);
50 }
51
52 if (volume_flags_handle != 0) {
53 BTA_GATTC_DeregisterForNotifications(gatt_if, address, volume_flags_handle);
54 }
55
56 for (const VolumeOffset& of : audio_offsets.volume_offsets) {
57 BTA_GATTC_DeregisterForNotifications(gatt_if, address, of.audio_descr_handle);
58 BTA_GATTC_DeregisterForNotifications(gatt_if, address, of.audio_location_handle);
59 BTA_GATTC_DeregisterForNotifications(gatt_if, address, of.state_handle);
60 }
61
62 for (const VolumeAudioInput& in : audio_inputs.volume_audio_inputs) {
63 BTA_GATTC_DeregisterForNotifications(gatt_if, address, in.description_handle);
64 BTA_GATTC_DeregisterForNotifications(gatt_if, address, in.state_handle);
65 BTA_GATTC_DeregisterForNotifications(gatt_if, address, in.status_handle);
66 }
67 }
68
Disconnect(tGATT_IF gatt_if)69 void VolumeControlDevice::Disconnect(tGATT_IF gatt_if) {
70 log::info("{}", address);
71
72 if (IsConnected()) {
73 DeregisterNotifications(gatt_if);
74 BtaGattQueue::Clean(connection_id);
75 BTA_GATTC_Close(connection_id);
76 connection_id = GATT_INVALID_CONN_ID;
77 }
78
79 device_ready = false;
80 handles_pending.clear();
81 }
82
83 /*
84 * Find the handle for the client characteristics configuration of a given
85 * characteristics
86 */
find_ccc_handle(uint16_t chrc_handle)87 uint16_t VolumeControlDevice::find_ccc_handle(uint16_t chrc_handle) {
88 const gatt::Characteristic* p_char = BTA_GATTC_GetCharacteristic(connection_id, chrc_handle);
89 if (!p_char) {
90 log::warn("{}, no such handle={:#x}", address, chrc_handle);
91 return 0;
92 }
93
94 for (const gatt::Descriptor& desc : p_char->descriptors) {
95 if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)) {
96 return desc.handle;
97 }
98 }
99
100 return 0;
101 }
102
set_volume_control_service_handles(const gatt::Service & service)103 bool VolumeControlDevice::set_volume_control_service_handles(const gatt::Service& service) {
104 uint16_t state_handle = 0, state_ccc_handle = 0, control_point_handle = 0, flags_handle = 0,
105 flags_ccc_handle = 0;
106
107 for (const gatt::Characteristic& chrc : service.characteristics) {
108 if (chrc.uuid == kVolumeControlStateUuid) {
109 state_handle = chrc.value_handle;
110 state_ccc_handle = find_ccc_handle(chrc.value_handle);
111 } else if (chrc.uuid == kVolumeControlPointUuid) {
112 control_point_handle = chrc.value_handle;
113 } else if (chrc.uuid == kVolumeFlagsUuid) {
114 flags_handle = chrc.value_handle;
115 flags_ccc_handle = find_ccc_handle(chrc.value_handle);
116 } else {
117 log::warn("unknown characteristic={}", chrc.uuid);
118 }
119 }
120
121 // Validate service handles
122 if (GATT_HANDLE_IS_VALID(state_handle) && GATT_HANDLE_IS_VALID(state_ccc_handle) &&
123 GATT_HANDLE_IS_VALID(control_point_handle) && GATT_HANDLE_IS_VALID(flags_handle)
124 /* volume_flags_ccc_handle is optional */) {
125 volume_state_handle = state_handle;
126 volume_state_ccc_handle = state_ccc_handle;
127 volume_control_point_handle = control_point_handle;
128 volume_flags_handle = flags_handle;
129 volume_flags_ccc_handle = flags_ccc_handle;
130 return true;
131 }
132
133 return false;
134 }
135
set_audio_input_control_service_handles(const gatt::Service & service)136 void VolumeControlDevice::set_audio_input_control_service_handles(const gatt::Service& service) {
137 uint16_t state_handle{0};
138 uint16_t state_ccc_handle{0};
139 uint16_t gain_setting_handle{0};
140 uint16_t type_handle{0};
141 uint16_t status_handle{0};
142 uint16_t status_ccc_handle{0};
143 uint16_t control_point_handle{0};
144 uint16_t description_handle{0};
145 uint16_t description_ccc_handle{0};
146 uint16_t description_writable{0};
147
148 for (const gatt::Characteristic& chrc : service.characteristics) {
149 if (chrc.uuid == kVolumeAudioInputStateUuid) {
150 state_handle = chrc.value_handle;
151 state_ccc_handle = find_ccc_handle(chrc.value_handle);
152 log::debug("{} state_handle={:#x} ccc={:#x}", address, state_handle, state_ccc_handle);
153 } else if (chrc.uuid == kVolumeAudioInputGainSettingPropertiesUuid) {
154 gain_setting_handle = chrc.value_handle;
155 } else if (chrc.uuid == kVolumeAudioInputTypeUuid) {
156 type_handle = chrc.value_handle;
157 } else if (chrc.uuid == kVolumeAudioInputStatusUuid) {
158 status_handle = chrc.value_handle;
159 status_ccc_handle = find_ccc_handle(chrc.value_handle);
160 log::debug("{} status_handle={:#x} ccc={:#x}", address, status_handle, status_ccc_handle);
161 } else if (chrc.uuid == kVolumeAudioInputControlPointUuid) {
162 control_point_handle = chrc.value_handle;
163 } else if (chrc.uuid == kVolumeAudioInputDescriptionUuid) {
164 description_handle = chrc.value_handle;
165 description_ccc_handle = find_ccc_handle(chrc.value_handle);
166 description_writable = chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
167 log::debug("{} description_handle={:#x} ccc={:#x}", address, description_handle,
168 description_ccc_handle);
169 } else {
170 log::info("found unexpected characteristic={}", chrc.uuid);
171 }
172 }
173
174 // Check if all mandatory attributes are present
175 if (!GATT_HANDLE_IS_VALID(state_handle) || !GATT_HANDLE_IS_VALID(state_ccc_handle) ||
176 !GATT_HANDLE_IS_VALID(gain_setting_handle) || !GATT_HANDLE_IS_VALID(type_handle) ||
177 !GATT_HANDLE_IS_VALID(status_handle) || !GATT_HANDLE_IS_VALID(status_ccc_handle) ||
178 !GATT_HANDLE_IS_VALID(control_point_handle) || !GATT_HANDLE_IS_VALID(description_handle)
179 /* description_ccc_handle is optional */) {
180 log::error(
181 "The remote device {} does not comply with AICS 1-0, some handles are invalid. "
182 "The aics service with handle {:#x} will be ignored",
183 address, service.handle);
184 return;
185 }
186 VolumeAudioInput input = VolumeAudioInput(
187 audio_inputs.Size(), service.handle, state_handle, state_ccc_handle, gain_setting_handle,
188 type_handle, status_handle, status_ccc_handle, control_point_handle, description_handle,
189 description_ccc_handle, description_writable);
190 audio_inputs.Add(input);
191 log::info("{}, input added id={:#x}", address, input.id);
192 }
193
set_volume_offset_control_service_handles(const gatt::Service & service)194 void VolumeControlDevice::set_volume_offset_control_service_handles(const gatt::Service& service) {
195 VolumeOffset offset = VolumeOffset(service.handle);
196
197 for (const gatt::Characteristic& chrc : service.characteristics) {
198 if (chrc.uuid == kVolumeOffsetStateUuid) {
199 offset.state_handle = chrc.value_handle;
200 offset.state_ccc_handle = find_ccc_handle(chrc.value_handle);
201 log::debug("{}, offset_state handle={:#x}, ccc {:#x}", address, offset.state_handle,
202 offset.state_ccc_handle);
203
204 } else if (chrc.uuid == kVolumeOffsetLocationUuid) {
205 offset.audio_location_handle = chrc.value_handle;
206 offset.audio_location_ccc_handle = find_ccc_handle(chrc.value_handle);
207 offset.audio_location_writable = chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
208 log::debug("{}, offset_audio_location handle={:#x}, ccc {:#x}", address,
209 offset.audio_location_handle, offset.audio_location_ccc_handle);
210
211 } else if (chrc.uuid == kVolumeOffsetControlPointUuid) {
212 offset.control_point_handle = chrc.value_handle;
213
214 } else if (chrc.uuid == kVolumeOffsetOutputDescriptionUuid) {
215 offset.audio_descr_handle = chrc.value_handle;
216 offset.audio_descr_ccc_handle = find_ccc_handle(chrc.value_handle);
217 offset.audio_descr_writable = chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
218 log::debug("{}, offset_audio_des handle={:#x}, ccc {:#x}", address, offset.audio_descr_handle,
219 offset.audio_descr_ccc_handle);
220
221 } else {
222 log::warn("unknown characteristic={}", chrc.uuid);
223 }
224 }
225
226 // Check if all mandatory attributes are present
227 if (GATT_HANDLE_IS_VALID(offset.state_handle) && GATT_HANDLE_IS_VALID(offset.state_ccc_handle) &&
228 GATT_HANDLE_IS_VALID(offset.audio_location_handle) &&
229 /* audio_location_ccc_handle is optional */
230 GATT_HANDLE_IS_VALID(offset.control_point_handle) &&
231 GATT_HANDLE_IS_VALID(offset.audio_descr_handle)
232 /* audio_descr_ccc_handle is optional */) {
233 audio_offsets.Add(offset);
234 log::info("{}, offset added id={:#x}", address, offset.id);
235 } else {
236 log::warn("{}, ignoring offset handle={:#x}", address, service.handle);
237 }
238 }
239
UpdateHandles(void)240 bool VolumeControlDevice::UpdateHandles(void) {
241 ResetHandles();
242
243 bool vcs_found = false;
244 const std::list<gatt::Service>* services = BTA_GATTC_GetServices(connection_id);
245 if (services == nullptr) {
246 log::error("{}, no services found", address);
247 return false;
248 }
249
250 for (auto const& service : *services) {
251 if (service.uuid == kVolumeControlUuid) {
252 log::info("{}, found VCS, handle={:#x}", address, service.handle);
253 vcs_found = set_volume_control_service_handles(service);
254 if (!vcs_found) {
255 break;
256 }
257
258 known_service_handles_ = true;
259 for (auto const& included : service.included_services) {
260 const gatt::Service* service =
261 BTA_GATTC_GetOwningService(connection_id, included.start_handle);
262 if (service == nullptr) {
263 continue;
264 }
265
266 if (included.uuid == kVolumeOffsetUuid) {
267 log::info("{}, found VOCS, handle={:#x}", address, service->handle);
268 set_volume_offset_control_service_handles(*service);
269
270 } else if (included.uuid == kVolumeAudioInputUuid) {
271 log::info("{}, found AICS, handle={:#x}", address, service->handle);
272 if (com::android::bluetooth::flags::leaudio_add_aics_support()) {
273 set_audio_input_control_service_handles(*service);
274 } else {
275 log::info("Flag leaudio_add_aics_support is not enabled");
276 }
277 } else {
278 log::warn("{}, unknown service={}", address, service->uuid);
279 }
280 }
281 }
282 }
283
284 return vcs_found;
285 }
286
ResetHandles(void)287 void VolumeControlDevice::ResetHandles(void) {
288 known_service_handles_ = false;
289 device_ready = false;
290
291 // the handles are not valid, so discard pending GATT operations
292 BtaGattQueue::Clean(connection_id);
293
294 volume_state_handle = 0;
295 volume_state_ccc_handle = 0;
296 volume_control_point_handle = 0;
297 volume_flags_handle = 0;
298 volume_flags_ccc_handle = 0;
299
300 if (audio_offsets.Size() != 0) {
301 audio_offsets.Clear();
302 }
303
304 if (audio_inputs.Size() != 0) {
305 audio_inputs.Clear();
306 }
307 }
308
ControlPointOperation(uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)309 void VolumeControlDevice::ControlPointOperation(uint8_t opcode, const std::vector<uint8_t>* arg,
310 GATT_WRITE_OP_CB cb, void* cb_data) {
311 std::vector<uint8_t> set_value({opcode, change_counter});
312 if (arg != nullptr) {
313 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
314 }
315
316 BtaGattQueue::WriteCharacteristic(connection_id, volume_control_point_handle, set_value,
317 GATT_WRITE, cb, cb_data);
318 }
319
subscribe_for_notifications(tGATT_IF gatt_if,uint16_t handle,uint16_t ccc_handle,GATT_WRITE_OP_CB cb)320 bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if, uint16_t handle,
321 uint16_t ccc_handle, GATT_WRITE_OP_CB cb) {
322 tGATT_STATUS status = BTA_GATTC_RegisterForNotifications(gatt_if, address, handle);
323 log::debug("gatt_if:{}, {} , {:#x} : {:#x}", gatt_if, address, handle, ccc_handle);
324
325 if (status != GATT_SUCCESS) {
326 log::error("failed for {}, status={:#x}", address, status);
327 return false;
328 }
329
330 log::debug("{} ok to proceed with writing descriptor {:#x}", address, ccc_handle);
331
332 std::vector<uint8_t> value(2);
333 uint8_t* ptr = value.data();
334 UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
335 BtaGattQueue::WriteDescriptor(connection_id, ccc_handle, std::move(value), GATT_WRITE, cb,
336 nullptr);
337
338 return true;
339 }
340
341 /**
342 * Enqueue GATT requests that are required by the Volume Control to be
343 * functional. This includes State characteristics read and subscription.
344 * Those characteristics contain the change counter needed to send any request
345 * via Control Point. Once completed successfully, the device can be stored
346 * and reported as connected. In each case we subscribe first to be sure we do
347 * not miss any value change.
348 */
EnqueueInitialRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)349 bool VolumeControlDevice::EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
350 GATT_WRITE_OP_CB cccd_write_cb) {
351 log::debug("{}", address);
352
353 std::map<uint16_t, uint16_t> hdls_to_subscribe{
354 {volume_state_handle, volume_state_ccc_handle},
355 };
356
357 handles_pending.clear();
358 // Status and Flags are mandatory
359 handles_pending.insert(volume_state_handle);
360 handles_pending.insert(volume_state_ccc_handle);
361
362 handles_pending.insert(volume_flags_handle);
363
364 if (GATT_HANDLE_IS_VALID(volume_flags_ccc_handle)) {
365 hdls_to_subscribe[volume_flags_handle] = volume_flags_ccc_handle;
366 handles_pending.insert(volume_flags_ccc_handle);
367 }
368
369 // Register for notifications
370 for (auto const& input : audio_inputs.volume_audio_inputs) {
371 // State is mandatory
372 hdls_to_subscribe[input.state_handle] = input.state_ccc_handle;
373 handles_pending.insert(input.state_ccc_handle);
374 // State is mandatory
375 hdls_to_subscribe[input.status_handle] = input.status_ccc_handle;
376 handles_pending.insert(input.status_ccc_handle);
377
378 if (GATT_HANDLE_IS_VALID(input.description_ccc_handle)) {
379 hdls_to_subscribe[input.description_handle] = input.description_ccc_handle;
380 handles_pending.insert(input.description_ccc_handle);
381 }
382 }
383
384 for (auto const& offset : audio_offsets.volume_offsets) {
385 hdls_to_subscribe[offset.state_handle] = offset.state_ccc_handle;
386 handles_pending.insert(offset.state_ccc_handle);
387
388 if (GATT_HANDLE_IS_VALID(offset.audio_descr_ccc_handle)) {
389 hdls_to_subscribe[offset.audio_descr_handle] = offset.audio_descr_ccc_handle;
390 handles_pending.insert(offset.audio_descr_ccc_handle);
391 }
392
393 if (GATT_HANDLE_IS_VALID(offset.audio_location_ccc_handle)) {
394 hdls_to_subscribe[offset.audio_location_handle] = offset.audio_location_ccc_handle;
395 handles_pending.insert(offset.audio_location_ccc_handle);
396 }
397 }
398
399 for (auto const& handles : hdls_to_subscribe) {
400 log::debug("{}, handle={:#x}, ccc_handle={:#x}", address, handles.first, handles.second);
401 if (!subscribe_for_notifications(gatt_if, handles.first, handles.second, cccd_write_cb)) {
402 log::error("{}, failed to subscribe for handle={:#x}, ccc_handle={:#x}", address,
403 handles.first, handles.second);
404 return false;
405 }
406 }
407
408 BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle, chrc_read_cb, nullptr);
409 BtaGattQueue::ReadCharacteristic(connection_id, volume_flags_handle, chrc_read_cb, nullptr);
410
411 return true;
412 }
413
414 /**
415 * Enqueue the remaining requests. Those are not so crucial and can be done
416 * once Volume Control instance indicates it's readiness to profile.
417 * This includes characteristics read and subscription.
418 * In each case we subscribe first to be sure we do not miss any value change.
419 */
EnqueueRemainingRequests(tGATT_IF,GATT_READ_OP_CB chrc_read_cb,GATT_READ_MULTI_OP_CB chrc_multi_read_cb,GATT_WRITE_OP_CB)420 void VolumeControlDevice::EnqueueRemainingRequests(tGATT_IF /*gatt_if*/,
421 GATT_READ_OP_CB chrc_read_cb,
422 GATT_READ_MULTI_OP_CB chrc_multi_read_cb,
423 GATT_WRITE_OP_CB /*cccd_write_cb*/) {
424 std::vector<uint16_t> handles_to_read;
425
426 for (auto const& input : audio_inputs.volume_audio_inputs) {
427 handles_to_read.push_back(input.state_handle);
428 handles_to_read.push_back(input.gain_setting_handle);
429 handles_to_read.push_back(input.type_handle);
430 handles_to_read.push_back(input.status_handle);
431 handles_to_read.push_back(input.description_handle);
432 }
433
434 for (auto const& offset : audio_offsets.volume_offsets) {
435 handles_to_read.push_back(offset.state_handle);
436 handles_to_read.push_back(offset.audio_location_handle);
437 handles_to_read.push_back(offset.audio_descr_handle);
438 }
439
440 log::debug("{}, number of handles={}", address, handles_to_read.size());
441
442 if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) {
443 for (auto const& handle : handles_to_read) {
444 BtaGattQueue::ReadCharacteristic(connection_id, handle, chrc_read_cb, nullptr);
445 }
446 return;
447 }
448
449 size_t sent_cnt = 0;
450
451 while (sent_cnt < handles_to_read.size()) {
452 tBTA_GATTC_MULTI multi_read{};
453 size_t remain_cnt = (handles_to_read.size() - sent_cnt);
454
455 multi_read.num_attr =
456 remain_cnt > GATT_MAX_READ_MULTI_HANDLES ? GATT_MAX_READ_MULTI_HANDLES : remain_cnt;
457
458 auto handles_begin = handles_to_read.begin() + sent_cnt;
459 std::copy(handles_begin, handles_begin + multi_read.num_attr, multi_read.handles);
460
461 sent_cnt += multi_read.num_attr;
462 log::debug{"{}, calling multi with {} attributes, sent_cnt {} ", address, multi_read.num_attr,
463 sent_cnt};
464
465 BtaGattQueue::ReadMultiCharacteristic(connection_id, multi_read, chrc_multi_read_cb, nullptr);
466 }
467 }
468
VerifyReady(uint16_t handle)469 bool VolumeControlDevice::VerifyReady(uint16_t handle) {
470 handles_pending.erase(handle);
471 device_ready = handles_pending.size() == 0;
472
473 log::debug("{}, handles_pending size={}", address, handles_pending.size());
474
475 return device_ready;
476 }
477
GetExtAudioOutVolumeOffset(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)478 void VolumeControlDevice::GetExtAudioOutVolumeOffset(uint8_t ext_output_id, GATT_READ_OP_CB cb,
479 void* cb_data) {
480 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
481 if (!offset) {
482 log::error("{}, no such offset={:#x}!", address, ext_output_id);
483 return;
484 }
485
486 BtaGattQueue::ReadCharacteristic(connection_id, offset->state_handle, cb, cb_data);
487 }
488
GetExtAudioOutLocation(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)489 void VolumeControlDevice::GetExtAudioOutLocation(uint8_t ext_output_id, GATT_READ_OP_CB cb,
490 void* cb_data) {
491 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
492 if (!offset) {
493 log::error("{}, no such offset={:#x}!", address, ext_output_id);
494 return;
495 }
496
497 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_location_handle, cb, cb_data);
498 }
499
SetExtAudioOutLocation(uint8_t ext_output_id,uint32_t location)500 void VolumeControlDevice::SetExtAudioOutLocation(uint8_t ext_output_id, uint32_t location) {
501 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
502 if (!offset) {
503 log::error("{}, no such offset={:#x}!", address, ext_output_id);
504 return;
505 }
506
507 if (!offset->audio_location_writable) {
508 log::warn("not writable");
509 return;
510 }
511
512 std::vector<uint8_t> value(4);
513 uint8_t* ptr = value.data();
514 UINT32_TO_STREAM(ptr, location);
515 BtaGattQueue::WriteCharacteristic(connection_id, offset->audio_location_handle, value,
516 GATT_WRITE_NO_RSP, nullptr, nullptr);
517 }
518
GetExtAudioOutDescription(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)519 void VolumeControlDevice::GetExtAudioOutDescription(uint8_t ext_output_id, GATT_READ_OP_CB cb,
520 void* cb_data) {
521 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
522 if (!offset) {
523 log::error("{}, no such offset={:#x}!", address, ext_output_id);
524 return;
525 }
526
527 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_descr_handle, cb, cb_data);
528 }
529
SetExtAudioOutDescription(uint8_t ext_output_id,const std::string & descr)530 void VolumeControlDevice::SetExtAudioOutDescription(uint8_t ext_output_id,
531 const std::string& descr) {
532 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
533 if (!offset) {
534 log::error("{}, no such offset={:#x}!", address, ext_output_id);
535 return;
536 }
537
538 if (!offset->audio_descr_writable) {
539 log::warn("not writable");
540 return;
541 }
542
543 std::vector<uint8_t> value(descr.begin(), descr.end());
544 BtaGattQueue::WriteCharacteristic(connection_id, offset->audio_descr_handle, value,
545 GATT_WRITE_NO_RSP, nullptr, nullptr);
546 }
547
ExtAudioOutControlPointOperation(uint8_t ext_output_id,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)548 void VolumeControlDevice::ExtAudioOutControlPointOperation(uint8_t ext_output_id, uint8_t opcode,
549 const std::vector<uint8_t>* arg,
550 GATT_WRITE_OP_CB cb, void* cb_data) {
551 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
552 if (!offset) {
553 log::error("{}, no such offset={:#x}!", address, ext_output_id);
554 return;
555 }
556
557 std::vector<uint8_t> set_value({opcode, offset->change_counter});
558 if (arg != nullptr) {
559 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
560 }
561
562 BtaGattQueue::WriteCharacteristic(connection_id, offset->control_point_handle, set_value,
563 GATT_WRITE, cb, cb_data);
564 }
565
GetExtAudioInState(uint8_t ext_input_id,GATT_READ_OP_CB cb,void * cb_data)566 void VolumeControlDevice::GetExtAudioInState(uint8_t ext_input_id, GATT_READ_OP_CB cb,
567 void* cb_data) {
568 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
569 if (!input) {
570 log::error("{}, no such input={:#x}", address, ext_input_id);
571 return;
572 }
573
574 BtaGattQueue::ReadCharacteristic(connection_id, input->state_handle, cb, cb_data);
575 }
576
GetExtAudioInStatus(uint8_t ext_input_id,GATT_READ_OP_CB cb,void * cb_data)577 void VolumeControlDevice::GetExtAudioInStatus(uint8_t ext_input_id, GATT_READ_OP_CB cb,
578 void* cb_data) {
579 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
580 if (!input) {
581 log::error("{}, no such input={:#x}", address, ext_input_id);
582 return;
583 }
584
585 BtaGattQueue::ReadCharacteristic(connection_id, input->status_handle, cb, cb_data);
586 }
587
GetExtAudioInType(uint8_t ext_input_id,GATT_READ_OP_CB cb,void * cb_data)588 void VolumeControlDevice::GetExtAudioInType(uint8_t ext_input_id, GATT_READ_OP_CB cb,
589 void* cb_data) {
590 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
591 if (!input) {
592 log::error("{}, no such input={:#x}", address, ext_input_id);
593 return;
594 }
595
596 BtaGattQueue::ReadCharacteristic(connection_id, input->type_handle, cb, cb_data);
597 }
598
GetExtAudioInGainProps(uint8_t ext_input_id,GATT_READ_OP_CB cb,void * cb_data)599 void VolumeControlDevice::GetExtAudioInGainProps(uint8_t ext_input_id, GATT_READ_OP_CB cb,
600 void* cb_data) {
601 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
602 if (!input) {
603 log::error("{}, no such input={:#x}", address, ext_input_id);
604 return;
605 }
606
607 BtaGattQueue::ReadCharacteristic(connection_id, input->gain_setting_handle, cb, cb_data);
608 }
609
GetExtAudioInDescription(uint8_t ext_input_id,GATT_READ_OP_CB cb,void * cb_data)610 void VolumeControlDevice::GetExtAudioInDescription(uint8_t ext_input_id, GATT_READ_OP_CB cb,
611 void* cb_data) {
612 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
613 if (!input) {
614 log::error("{}, no such input={:#x}", address, ext_input_id);
615 return;
616 }
617
618 BtaGattQueue::ReadCharacteristic(connection_id, input->description_handle, cb, cb_data);
619 }
620
SetExtAudioInDescription(uint8_t ext_input_id,const std::string & descr)621 void VolumeControlDevice::SetExtAudioInDescription(uint8_t ext_input_id, const std::string& descr) {
622 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
623 if (!input) {
624 log::error("{} no such input={:#x}", address, ext_input_id);
625 return;
626 }
627
628 if (!input->description_writable) {
629 log::warn("{} input={:#x} input description is not writable", address, ext_input_id);
630 return;
631 }
632
633 std::vector<uint8_t> value(descr.begin(), descr.end());
634 BtaGattQueue::WriteCharacteristic(connection_id, input->description_handle, value,
635 GATT_WRITE_NO_RSP, nullptr, nullptr);
636 }
637
ExtAudioInControlPointOperation(uint8_t ext_input_id,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)638 bool VolumeControlDevice::ExtAudioInControlPointOperation(uint8_t ext_input_id, uint8_t opcode,
639 const std::vector<uint8_t>* arg,
640 GATT_WRITE_OP_CB cb, void* cb_data) {
641 VolumeAudioInput* input = audio_inputs.FindById(ext_input_id);
642 if (!input) {
643 log::error("{}, no such input={:#x}", address, ext_input_id);
644 return false;
645 }
646
647 std::vector<uint8_t> set_value({opcode, input->change_counter});
648 if (arg != nullptr) {
649 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
650 }
651
652 BtaGattQueue::WriteCharacteristic(connection_id, input->control_point_handle, set_value,
653 GATT_WRITE, cb, cb_data);
654 return true;
655 }
656
IsEncryptionEnabled()657 bool VolumeControlDevice::IsEncryptionEnabled() {
658 return BTM_IsEncrypted(address, BT_TRANSPORT_LE);
659 }
660
EnableEncryption()661 bool VolumeControlDevice::EnableEncryption() {
662 tBTM_STATUS result =
663 BTM_SetEncryption(address, BT_TRANSPORT_LE, nullptr, nullptr, BTM_BLE_SEC_ENCRYPT);
664 log::info("{}: result=0x{:02x}", address, result);
665
666 return result != tBTM_STATUS::BTM_ERR_KEY_MISSING;
667 }
668