• 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 
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