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