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