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