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