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