• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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