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__ << ": " << ADDRESS_TO_LOGGABLE_STR(address);
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 known_service_handles_ = true;
182 for (auto const& included : service.included_services) {
183 const gatt::Service* service =
184 BTA_GATTC_GetOwningService(connection_id, included.start_handle);
185 if (service == nullptr) continue;
186
187 if (included.uuid == kVolumeOffsetUuid) {
188 LOG(INFO) << "Found VOCS, handle=" << loghex(service->handle);
189 set_volume_offset_control_service_handles(*service);
190
191 } else {
192 LOG(WARNING) << __func__ << ": unknown service=" << service->uuid;
193 }
194 }
195 }
196 }
197
198 return vcs_found;
199 }
200
ResetHandles(void)201 void VolumeControlDevice::ResetHandles(void) {
202 known_service_handles_ = false;
203 device_ready = false;
204
205 // the handles are not valid, so discard pending GATT operations
206 BtaGattQueue::Clean(connection_id);
207
208 volume_state_handle = 0;
209 volume_state_ccc_handle = 0;
210 volume_control_point_handle = 0;
211 volume_flags_handle = 0;
212 volume_flags_ccc_handle = 0;
213
214 if (audio_offsets.Size() != 0) audio_offsets.Clear();
215 }
216
ControlPointOperation(uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)217 void VolumeControlDevice::ControlPointOperation(uint8_t opcode,
218 const std::vector<uint8_t>* arg,
219 GATT_WRITE_OP_CB cb,
220 void* cb_data) {
221 std::vector<uint8_t> set_value({opcode, change_counter});
222 if (arg != nullptr)
223 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
224
225 BtaGattQueue::WriteCharacteristic(connection_id, volume_control_point_handle,
226 set_value, GATT_WRITE, cb, cb_data);
227 }
228
subscribe_for_notifications(tGATT_IF gatt_if,uint16_t handle,uint16_t ccc_handle,GATT_WRITE_OP_CB cb)229 bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if,
230 uint16_t handle,
231 uint16_t ccc_handle,
232 GATT_WRITE_OP_CB cb) {
233 tGATT_STATUS status =
234 BTA_GATTC_RegisterForNotifications(gatt_if, address, handle);
235 if (status != GATT_SUCCESS) {
236 LOG(ERROR) << __func__ << ": failed, status=" << loghex(+status);
237 return false;
238 }
239
240 std::vector<uint8_t> value(2);
241 uint8_t* ptr = value.data();
242 UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
243 BtaGattQueue::WriteDescriptor(connection_id, ccc_handle, std::move(value),
244 GATT_WRITE, cb, nullptr);
245
246 return true;
247 }
248
249 /**
250 * Enqueue GATT requests that are required by the Volume Control to be
251 * functional. This includes State characteristics read and subscription.
252 * Those characteristics contain the change counter needed to send any request
253 * via Control Point. Once completed successfully, the device can be stored
254 * and reported as connected. In each case we subscribe first to be sure we do
255 * not miss any value change.
256 */
EnqueueInitialRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)257 bool VolumeControlDevice::EnqueueInitialRequests(
258 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
259 GATT_WRITE_OP_CB cccd_write_cb) {
260 handles_pending.clear();
261 handles_pending.insert(volume_state_handle);
262 handles_pending.insert(volume_state_ccc_handle);
263 if (!subscribe_for_notifications(gatt_if, volume_state_handle,
264 volume_state_ccc_handle, cccd_write_cb)) {
265 return false;
266 }
267
268 for (auto const& offset : audio_offsets.volume_offsets) {
269 handles_pending.insert(offset.state_handle);
270 handles_pending.insert(offset.state_ccc_handle);
271 if (!subscribe_for_notifications(gatt_if, offset.state_handle,
272 offset.state_ccc_handle, cccd_write_cb)) {
273 return false;
274 }
275
276 BtaGattQueue::ReadCharacteristic(connection_id, offset.state_handle,
277 chrc_read_cb, nullptr);
278 }
279
280 BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle,
281 chrc_read_cb, nullptr);
282
283 return true;
284 }
285
286 /**
287 * Enqueue the remaining requests. Those are not so crucial and can be done
288 * once Volume Control instance indicates it's readiness to profile.
289 * This includes characteristics read and subscription.
290 * In each case we subscribe first to be sure we do not miss any value change.
291 */
EnqueueRemainingRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)292 void VolumeControlDevice::EnqueueRemainingRequests(
293 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
294 GATT_WRITE_OP_CB cccd_write_cb) {
295 std::map<uint16_t, uint16_t> handle_pairs{
296 {volume_flags_handle, volume_flags_ccc_handle},
297 };
298
299 for (auto const& offset : audio_offsets.volume_offsets) {
300 handle_pairs[offset.audio_location_handle] =
301 offset.audio_location_ccc_handle;
302 handle_pairs[offset.audio_descr_handle] = offset.audio_descr_ccc_handle;
303 }
304
305 for (auto const& handles : handle_pairs) {
306 if (GATT_HANDLE_IS_VALID(handles.second)) {
307 subscribe_for_notifications(gatt_if, handles.first, handles.second,
308 cccd_write_cb);
309 }
310
311 BtaGattQueue::ReadCharacteristic(connection_id, handles.first, chrc_read_cb,
312 nullptr);
313 }
314 }
315
VerifyReady(uint16_t handle)316 bool VolumeControlDevice::VerifyReady(uint16_t handle) {
317 handles_pending.erase(handle);
318 device_ready = handles_pending.size() == 0;
319 return device_ready;
320 }
321
GetExtAudioOutVolumeOffset(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)322 void VolumeControlDevice::GetExtAudioOutVolumeOffset(uint8_t ext_output_id,
323 GATT_READ_OP_CB cb,
324 void* cb_data) {
325 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
326 if (!offset) {
327 LOG(ERROR) << __func__ << ": no such offset!";
328 return;
329 }
330
331 BtaGattQueue::ReadCharacteristic(connection_id, offset->state_handle, cb,
332 cb_data);
333 }
334
GetExtAudioOutLocation(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)335 void VolumeControlDevice::GetExtAudioOutLocation(uint8_t ext_output_id,
336 GATT_READ_OP_CB cb,
337 void* cb_data) {
338 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
339 if (!offset) {
340 LOG(ERROR) << __func__ << ": no such offset!";
341 return;
342 }
343
344 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_location_handle,
345 cb, cb_data);
346 }
347
SetExtAudioOutLocation(uint8_t ext_output_id,uint32_t location)348 void VolumeControlDevice::SetExtAudioOutLocation(uint8_t ext_output_id,
349 uint32_t location) {
350 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
351 if (!offset) {
352 LOG(ERROR) << __func__ << ": no such offset!";
353 return;
354 }
355
356 if (!offset->audio_location_writable) {
357 LOG(WARNING) << __func__ << ": not writable";
358 return;
359 }
360
361 std::vector<uint8_t> value(4);
362 uint8_t* ptr = value.data();
363 UINT32_TO_STREAM(ptr, location);
364 BtaGattQueue::WriteCharacteristic(connection_id,
365 offset->audio_location_handle, value,
366 GATT_WRITE_NO_RSP, nullptr, nullptr);
367 }
368
GetExtAudioOutDescription(uint8_t ext_output_id,GATT_READ_OP_CB cb,void * cb_data)369 void VolumeControlDevice::GetExtAudioOutDescription(uint8_t ext_output_id,
370 GATT_READ_OP_CB cb,
371 void* cb_data) {
372 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
373 if (!offset) {
374 LOG(ERROR) << __func__ << ": no such offset!";
375 return;
376 }
377
378 BtaGattQueue::ReadCharacteristic(connection_id, offset->audio_descr_handle,
379 cb, cb_data);
380 }
381
SetExtAudioOutDescription(uint8_t ext_output_id,std::string & descr)382 void VolumeControlDevice::SetExtAudioOutDescription(uint8_t ext_output_id,
383 std::string& descr) {
384 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
385 if (!offset) {
386 LOG(ERROR) << __func__ << ": no such offset!";
387 return;
388 }
389
390 if (!offset->audio_descr_writable) {
391 LOG(WARNING) << __func__ << ": not writable";
392 return;
393 }
394
395 std::vector<uint8_t> value(descr.begin(), descr.end());
396 BtaGattQueue::WriteCharacteristic(connection_id, offset->audio_descr_handle,
397 value, GATT_WRITE_NO_RSP, nullptr, nullptr);
398 }
399
ExtAudioOutControlPointOperation(uint8_t ext_output_id,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)400 void VolumeControlDevice::ExtAudioOutControlPointOperation(
401 uint8_t ext_output_id, uint8_t opcode, const std::vector<uint8_t>* arg,
402 GATT_WRITE_OP_CB cb, void* cb_data) {
403 VolumeOffset* offset = audio_offsets.FindById(ext_output_id);
404 if (!offset) {
405 LOG(ERROR) << __func__ << ": no such offset!";
406 return;
407 }
408
409 std::vector<uint8_t> set_value({opcode, offset->change_counter});
410 if (arg != nullptr)
411 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
412
413 BtaGattQueue::WriteCharacteristic(connection_id, offset->control_point_handle,
414 set_value, GATT_WRITE, cb, cb_data);
415 }
416
IsEncryptionEnabled()417 bool VolumeControlDevice::IsEncryptionEnabled() {
418 return BTM_IsEncrypted(address, BT_TRANSPORT_LE);
419 }
420
EnableEncryption()421 void VolumeControlDevice::EnableEncryption() {
422 int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, nullptr, nullptr,
423 BTM_BLE_SEC_ENCRYPT);
424 LOG(INFO) << __func__ << ": result=" << +result;
425 }
426