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