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
27 using namespace bluetooth::vc::internal;
28
Disconnect(tGATT_IF gatt_if)29 void VolumeControlDevice::Disconnect(tGATT_IF gatt_if) {
30 LOG(INFO) << __func__ << ": " << this->ToString();
31
32 if (IsConnected()) {
33 if (volume_state_handle != 0)
34 BTA_GATTC_DeregisterForNotifications(gatt_if, address,
35 volume_state_handle);
36
37 if (volume_flags_handle != 0)
38 BTA_GATTC_DeregisterForNotifications(gatt_if, address,
39 volume_flags_handle);
40
41 BtaGattQueue::Clean(connection_id);
42 BTA_GATTC_Close(connection_id);
43 connection_id = GATT_INVALID_CONN_ID;
44 } else {
45 BTA_GATTC_CancelOpen(gatt_if, address, false);
46 }
47
48 device_ready = false;
49 handles_pending.clear();
50 }
51
52 /*
53 * Find the handle for the client characteristics configuration of a given
54 * characteristics
55 */
find_ccc_handle(uint16_t chrc_handle)56 uint16_t VolumeControlDevice::find_ccc_handle(uint16_t chrc_handle) {
57 const gatt::Characteristic* p_char =
58 BTA_GATTC_GetCharacteristic(connection_id, chrc_handle);
59 if (!p_char) {
60 LOG(WARNING) << __func__ << ": no such handle=" << loghex(chrc_handle);
61 return 0;
62 }
63
64 for (const gatt::Descriptor& desc : p_char->descriptors) {
65 if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG))
66 return desc.handle;
67 }
68
69 return 0;
70 }
71
set_volume_control_service_handles(const gatt::Service & service)72 bool VolumeControlDevice::set_volume_control_service_handles(
73 const gatt::Service& service) {
74 uint16_t state_handle = 0, state_ccc_handle = 0, control_point_handle = 0,
75 flags_handle = 0, flags_ccc_handle = 0;
76
77 for (const gatt::Characteristic& chrc : service.characteristics) {
78 if (chrc.uuid == kVolumeControlStateUuid) {
79 state_handle = chrc.value_handle;
80 state_ccc_handle = find_ccc_handle(chrc.value_handle);
81 } else if (chrc.uuid == kVolumeControlPointUuid) {
82 control_point_handle = chrc.value_handle;
83 } else if (chrc.uuid == kVolumeFlagsUuid) {
84 flags_handle = chrc.value_handle;
85 flags_ccc_handle = find_ccc_handle(chrc.value_handle);
86 } else {
87 LOG(WARNING) << __func__ << ": unknown characteristic=" << chrc.uuid;
88 }
89 }
90
91 // Validate service handles
92 if (GATT_HANDLE_IS_VALID(state_handle) &&
93 GATT_HANDLE_IS_VALID(state_ccc_handle) &&
94 GATT_HANDLE_IS_VALID(control_point_handle) &&
95 GATT_HANDLE_IS_VALID(flags_handle)
96 /* volume_flags_ccc_handle is optional */) {
97 volume_state_handle = state_handle;
98 volume_state_ccc_handle = state_ccc_handle;
99 volume_control_point_handle = control_point_handle;
100 volume_flags_handle = flags_handle;
101 volume_flags_ccc_handle = flags_ccc_handle;
102 return true;
103 }
104
105 return false;
106 }
107
UpdateHandles(void)108 bool VolumeControlDevice::UpdateHandles(void) {
109 ResetHandles();
110
111 bool vcs_found = false;
112 const std::list<gatt::Service>* services =
113 BTA_GATTC_GetServices(connection_id);
114 if (services == nullptr) {
115 LOG(ERROR) << "No services found";
116 return false;
117 }
118
119 for (auto const& service : *services) {
120 if (service.uuid == kVolumeControlUuid) {
121 LOG(INFO) << "Found VCS, handle=" << loghex(service.handle);
122 vcs_found = set_volume_control_service_handles(service);
123 if (!vcs_found) break;
124 }
125 }
126
127 return vcs_found;
128 }
129
ResetHandles(void)130 void VolumeControlDevice::ResetHandles(void) {
131 device_ready = false;
132
133 // the handles are not valid, so discard pending GATT operations
134 BtaGattQueue::Clean(connection_id);
135
136 volume_state_handle = 0;
137 volume_state_ccc_handle = 0;
138 volume_control_point_handle = 0;
139 volume_flags_handle = 0;
140 volume_flags_ccc_handle = 0;
141 }
142
ControlPointOperation(uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)143 void VolumeControlDevice::ControlPointOperation(uint8_t opcode,
144 const std::vector<uint8_t>* arg,
145 GATT_WRITE_OP_CB cb,
146 void* cb_data) {
147 std::vector<uint8_t> set_value({opcode, change_counter});
148 if (arg != nullptr)
149 set_value.insert(set_value.end(), (*arg).begin(), (*arg).end());
150
151 BtaGattQueue::WriteCharacteristic(connection_id, volume_control_point_handle,
152 set_value, GATT_WRITE, cb, cb_data);
153 }
154
subscribe_for_notifications(tGATT_IF gatt_if,uint16_t handle,uint16_t ccc_handle,GATT_WRITE_OP_CB cb)155 bool VolumeControlDevice::subscribe_for_notifications(tGATT_IF gatt_if,
156 uint16_t handle,
157 uint16_t ccc_handle,
158 GATT_WRITE_OP_CB cb) {
159 tGATT_STATUS status =
160 BTA_GATTC_RegisterForNotifications(gatt_if, address, handle);
161 if (status != GATT_SUCCESS) {
162 LOG(ERROR) << __func__ << ": failed, status=" << loghex(+status);
163 return false;
164 }
165
166 std::vector<uint8_t> value(2);
167 uint8_t* ptr = value.data();
168 UINT16_TO_STREAM(ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
169 BtaGattQueue::WriteDescriptor(connection_id, ccc_handle, std::move(value),
170 GATT_WRITE, cb, nullptr);
171
172 return true;
173 }
174
175 /**
176 * Enqueue GATT requests that are required by the Volume Control to be
177 * functional. This includes State characteristics read and subscription.
178 * Those characteristics contain the change counter needed to send any request
179 * via Control Point. Once completed successfully, the device can be stored
180 * and reported as connected. In each case we subscribe first to be sure we do
181 * not miss any value change.
182 */
EnqueueInitialRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)183 bool VolumeControlDevice::EnqueueInitialRequests(
184 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
185 GATT_WRITE_OP_CB cccd_write_cb) {
186 handles_pending.clear();
187 handles_pending.insert(volume_state_handle);
188 handles_pending.insert(volume_state_ccc_handle);
189 if (!subscribe_for_notifications(gatt_if, volume_state_handle,
190 volume_state_ccc_handle, cccd_write_cb)) {
191 return false;
192 }
193
194 BtaGattQueue::ReadCharacteristic(connection_id, volume_state_handle,
195 chrc_read_cb, nullptr);
196
197 return true;
198 }
199
200 /**
201 * Enqueue the remaining requests. Those are not so crucial and can be done
202 * once Volume Control instance indicates it's readiness to profile.
203 * This includes characteristics read and subscription.
204 * In each case we subscribe first to be sure we do not miss any value change.
205 */
EnqueueRemainingRequests(tGATT_IF gatt_if,GATT_READ_OP_CB chrc_read_cb,GATT_WRITE_OP_CB cccd_write_cb)206 void VolumeControlDevice::EnqueueRemainingRequests(
207 tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
208 GATT_WRITE_OP_CB cccd_write_cb) {
209 std::map<uint16_t, uint16_t> handle_pairs{
210 {volume_flags_handle, volume_flags_ccc_handle},
211 };
212
213 for (auto const& handles : handle_pairs) {
214 if (GATT_HANDLE_IS_VALID(handles.second)) {
215 subscribe_for_notifications(gatt_if, handles.first, handles.second,
216 cccd_write_cb);
217 }
218
219 BtaGattQueue::ReadCharacteristic(connection_id, handles.first, chrc_read_cb,
220 nullptr);
221 }
222 }
223
VerifyReady(uint16_t handle)224 bool VolumeControlDevice::VerifyReady(uint16_t handle) {
225 handles_pending.erase(handle);
226 device_ready = handles_pending.size() == 0;
227 return device_ready;
228 }
229
IsEncryptionEnabled()230 bool VolumeControlDevice::IsEncryptionEnabled() {
231 uint8_t sec_flag = 0;
232 bool device_found =
233 BTM_GetSecurityFlagsByTransport(address, &sec_flag, BT_TRANSPORT_LE);
234 LOG(INFO) << __func__ << ": found=" << static_cast<int>(device_found)
235 << " sec_flag=" << loghex(sec_flag);
236 return device_found && (sec_flag & BTM_SEC_FLAG_ENCRYPTED);
237 }
238
EnableEncryption(tBTM_SEC_CALLBACK * callback)239 bool VolumeControlDevice::EnableEncryption(tBTM_SEC_CALLBACK* callback) {
240 int result = BTM_SetEncryption(address, BT_TRANSPORT_LE, callback, nullptr,
241 BTM_BLE_SEC_ENCRYPT);
242 LOG(INFO) << __func__ << ": result=" << +result;
243 // TODO: should we care about the result??
244 return true;
245 }
246