• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 #pragma once
19 
20 #include <algorithm>
21 #include <cstdint>
22 #include <unordered_set>
23 #include <vector>
24 
25 #include "bta/include/bta_gatt_api.h"
26 #include "bta/vc/types.h"
27 #include "common/interfaces/ILoggable.h"
28 #include "include/hardware/bt_vc.h"
29 #include "types/raw_address.h"
30 
31 namespace bluetooth {
32 namespace vc {
33 namespace internal {
34 
35 class VolumeControlDevice : public bluetooth::common::IRedactableLoggable {
36  public:
37   RawAddress address;
38 
39   /* We are making active attempt to connect to this device */
40   bool connecting_actively;
41 
42   bool known_service_handles_;
43 
44   uint8_t volume;
45   uint8_t change_counter;
46   bool mute;
47   uint8_t flags;
48 
49   uint16_t connection_id;
50 
51   /* Volume Control Service */
52   uint16_t volume_state_handle;
53   uint16_t volume_state_ccc_handle;
54   uint16_t volume_control_point_handle;
55   uint16_t volume_flags_handle;
56   uint16_t volume_flags_ccc_handle;
57 
58   VolumeOffsets audio_offsets;
59 
60   /* Set when device successfully reads server status and registers for
61    * notifications */
62   bool device_ready;
63 
VolumeControlDevice(const RawAddress & address,bool connecting_actively)64   VolumeControlDevice(const RawAddress& address, bool connecting_actively)
65       : address(address),
66         connecting_actively(connecting_actively),
67         known_service_handles_(false),
68         volume(0),
69         change_counter(0),
70         mute(false),
71         flags(0),
72         connection_id(GATT_INVALID_CONN_ID),
73         volume_state_handle(0),
74         volume_state_ccc_handle(0),
75         volume_control_point_handle(0),
76         volume_flags_handle(0),
77         volume_flags_ccc_handle(0),
78         device_ready(false) {}
79 
80   ~VolumeControlDevice() = default;
81 
82   // TODO: remove
ToString()83   inline std::string ToString() { return address.ToString(); }
84 
ToStringForLogging()85   std::string ToStringForLogging() const override {
86     return address.ToStringForLogging();
87   }
88 
ToRedactedStringForLogging()89   std::string ToRedactedStringForLogging() const override {
90     return address.ToRedactedStringForLogging();
91   }
92 
DebugDump(int fd)93   void DebugDump(int fd) {
94     std::stringstream stream;
95     stream << "   == device address: " << ADDRESS_TO_LOGGABLE_STR(address)
96            << " == \n";
97 
98     if (connection_id == GATT_INVALID_CONN_ID)
99       stream << "    Not connected\n";
100     else
101       stream << "    Connected. Conn_id = " << connection_id << "\n";
102 
103     stream << "    volume: " << +volume << "\n"
104            << "    mute: " << +mute << "\n"
105            << "    flags: " << +flags << "\n"
106            << "    device read: " << device_ready << "\n"
107            << "    connecting_actively_: " << connecting_actively << "\n";
108 
109     dprintf(fd, "%s", stream.str().c_str());
110     audio_offsets.Dump(fd);
111   }
112 
IsConnected()113   bool IsConnected() { return connection_id != GATT_INVALID_CONN_ID; }
114 
115   void Disconnect(tGATT_IF gatt_if);
116 
117   void DeregisterNotifications(tGATT_IF gatt_if);
118 
119   bool UpdateHandles(void);
120 
121   void ResetHandles(void);
122 
HasHandles(void)123   bool HasHandles(void) { return GATT_HANDLE_IS_VALID(volume_state_handle); }
124 
125   void ControlPointOperation(uint8_t opcode, const std::vector<uint8_t>* arg,
126                              GATT_WRITE_OP_CB cb, void* cb_data);
127   void GetExtAudioOutVolumeOffset(uint8_t ext_output_id, GATT_READ_OP_CB cb,
128                                   void* cb_data);
129   void SetExtAudioOutLocation(uint8_t ext_output_id, uint32_t location);
130   void GetExtAudioOutLocation(uint8_t ext_output_id, GATT_READ_OP_CB cb,
131                               void* cb_data);
132   void GetExtAudioOutDescription(uint8_t ext_output_id, GATT_READ_OP_CB cb,
133                                  void* cb_data);
134   void SetExtAudioOutDescription(uint8_t ext_output_id, std::string& descr);
135   void ExtAudioOutControlPointOperation(uint8_t ext_output_id, uint8_t opcode,
136                                         const std::vector<uint8_t>* arg,
137                                         GATT_WRITE_OP_CB cb, void* cb_data);
138   bool IsEncryptionEnabled();
139 
140   void EnableEncryption();
141 
142   bool EnqueueInitialRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
143                               GATT_WRITE_OP_CB cccd_write_cb);
144   void EnqueueRemainingRequests(tGATT_IF gatt_if, GATT_READ_OP_CB chrc_read_cb,
145                                 GATT_WRITE_OP_CB cccd_write_cb);
146   bool VerifyReady(uint16_t handle);
IsReady()147   bool IsReady() { return device_ready; }
148 
149  private:
150   /*
151    * This is used to track the pending GATT operation handles. Once the list is
152    * empty the device is assumed ready and connected. We are doing it because we
153    * want to make sure all the required characteristics and descritors are
154    * available on server side.
155    */
156   std::unordered_set<uint16_t> handles_pending;
157 
158   uint16_t find_ccc_handle(uint16_t chrc_handle);
159   bool set_volume_control_service_handles(const gatt::Service& service);
160   void set_volume_offset_control_service_handles(const gatt::Service& service);
161   bool subscribe_for_notifications(tGATT_IF gatt_if, uint16_t handle,
162                                    uint16_t ccc_handle, GATT_WRITE_OP_CB cb);
163 };
164 
165 class VolumeControlDevices {
166  public:
Add(const RawAddress & address,bool connecting_actively)167   void Add(const RawAddress& address, bool connecting_actively) {
168     if (FindByAddress(address) != nullptr) return;
169 
170     devices_.emplace_back(address, connecting_actively);
171   }
172 
Remove(const RawAddress & address)173   void Remove(const RawAddress& address) {
174     for (auto it = devices_.begin(); it != devices_.end(); it++) {
175       if (it->address == address) {
176         it = devices_.erase(it);
177         break;
178       }
179     }
180   }
181 
FindByAddress(const RawAddress & address)182   VolumeControlDevice* FindByAddress(const RawAddress& address) {
183     auto iter = std::find_if(devices_.begin(), devices_.end(),
184                              [&address](const VolumeControlDevice& device) {
185                                return device.address == address;
186                              });
187 
188     return (iter == devices_.end()) ? nullptr : &(*iter);
189   }
190 
FindByConnId(uint16_t connection_id)191   VolumeControlDevice* FindByConnId(uint16_t connection_id) {
192     auto iter =
193         std::find_if(devices_.begin(), devices_.end(),
194                      [&connection_id](const VolumeControlDevice& device) {
195                        return device.connection_id == connection_id;
196                      });
197 
198     return (iter == devices_.end()) ? nullptr : &(*iter);
199   }
200 
Size()201   size_t Size() { return (devices_.size()); }
202 
Clear()203   void Clear() { devices_.clear(); }
204 
DebugDump(int fd)205   void DebugDump(int fd) {
206     if (devices_.empty()) {
207       dprintf(fd, "  No VC devices:\n");
208     } else {
209       dprintf(fd, "  Devices:\n");
210       for (auto& device : devices_) {
211         device.DebugDump(fd);
212       }
213     }
214   }
215 
Disconnect(tGATT_IF gatt_if)216   void Disconnect(tGATT_IF gatt_if) {
217     for (auto& device : devices_) {
218       device.Disconnect(gatt_if);
219     }
220   }
221 
ControlPointOperation(std::vector<RawAddress> & devices,uint8_t opcode,const std::vector<uint8_t> * arg,GATT_WRITE_OP_CB cb,void * cb_data)222   void ControlPointOperation(std::vector<RawAddress>& devices, uint8_t opcode,
223                              const std::vector<uint8_t>* arg,
224                              GATT_WRITE_OP_CB cb, void* cb_data) {
225     for (auto& addr : devices) {
226       VolumeControlDevice* device = FindByAddress(addr);
227       if (device && device->IsConnected())
228         device->ControlPointOperation(opcode, arg, cb, cb_data);
229     }
230   }
231 
232  private:
233   std::vector<VolumeControlDevice> devices_;
234 };
235 
236 }  // namespace internal
237 }  // namespace vc
238 }  // namespace bluetooth
239