• Home
  • History
  • Annotate
  • 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