• 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 /* Volume Control Interface */
19 
20 #include <base/functional/bind.h>
21 #include <base/location.h>
22 #include <base/logging.h>
23 #include <hardware/bluetooth.h>
24 #include <hardware/bt_vc.h>
25 
26 #include "bta_vc_api.h"
27 #include "btif_common.h"
28 #include "stack/include/btu.h"
29 #include "types/raw_address.h"
30 
31 using base::Bind;
32 using base::Unretained;
33 using bluetooth::vc::ConnectionState;
34 using bluetooth::vc::VolumeControlCallbacks;
35 using bluetooth::vc::VolumeControlInterface;
36 
37 namespace {
38 std::unique_ptr<VolumeControlInterface> vc_instance;
39 std::atomic_bool initialized = false;
40 
41 class VolumeControlInterfaceImpl : public VolumeControlInterface,
42                                    public VolumeControlCallbacks {
43   ~VolumeControlInterfaceImpl() override = default;
44 
Init(VolumeControlCallbacks * callbacks)45   void Init(VolumeControlCallbacks* callbacks) override {
46     DVLOG(2) << __func__;
47     this->callbacks_ = callbacks;
48     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::Initialize, this));
49 
50     /* It might be not yet initialized, but setting this flag here is safe,
51      * because other calls will check this and the native instance
52      */
53     initialized = true;
54   }
55 
OnConnectionState(ConnectionState state,const RawAddress & address)56   void OnConnectionState(ConnectionState state,
57                          const RawAddress& address) override {
58     DVLOG(2) << __func__ << " address: " << address;
59     do_in_jni_thread(FROM_HERE, Bind(&VolumeControlCallbacks::OnConnectionState,
60                                      Unretained(callbacks_), state, address));
61   }
62 
OnVolumeStateChanged(const RawAddress & address,uint8_t volume,bool mute,bool isAutonomous)63   void OnVolumeStateChanged(const RawAddress& address, uint8_t volume,
64                             bool mute, bool isAutonomous) override {
65     DVLOG(2) << __func__ << " address: " << address << " volume: " << volume
66              << " mute: " << mute << " isAutonomous: " << isAutonomous;
67     do_in_jni_thread(
68         FROM_HERE,
69         Bind(&VolumeControlCallbacks::OnVolumeStateChanged,
70              Unretained(callbacks_), address, volume, mute, isAutonomous));
71   }
72 
OnGroupVolumeStateChanged(int group_id,uint8_t volume,bool mute,bool isAutonomous)73   void OnGroupVolumeStateChanged(int group_id, uint8_t volume, bool mute,
74                                  bool isAutonomous) override {
75     DVLOG(2) << __func__ << " group_id: " << group_id << " volume: " << volume
76              << " mute: " << mute << " isAutonomous: " << isAutonomous;
77     do_in_jni_thread(
78         FROM_HERE,
79         Bind(&VolumeControlCallbacks::OnGroupVolumeStateChanged,
80              Unretained(callbacks_), group_id, volume, mute, isAutonomous));
81   }
82 
OnDeviceAvailable(const RawAddress & address,uint8_t num_offset)83   void OnDeviceAvailable(const RawAddress& address,
84                          uint8_t num_offset) override {
85     DVLOG(2) << __func__ << " address: " << address;
86     do_in_jni_thread(FROM_HERE,
87                      Bind(&VolumeControlCallbacks::OnDeviceAvailable,
88                           Unretained(callbacks_), address, num_offset));
89   }
90 
91   /* Callbacks for Volume Offset Control Service (VOCS) - Extended Audio Outputs
92    */
93 
OnExtAudioOutVolumeOffsetChanged(const RawAddress & address,uint8_t ext_output_id,int16_t offset)94   void OnExtAudioOutVolumeOffsetChanged(const RawAddress& address,
95                                         uint8_t ext_output_id,
96                                         int16_t offset) override {
97     DVLOG(2) << __func__ << " address: " << address
98              << "ext_output_id: " << ext_output_id << "offset:" << offset;
99 
100     do_in_jni_thread(
101         FROM_HERE,
102         Bind(&VolumeControlCallbacks::OnExtAudioOutVolumeOffsetChanged,
103              Unretained(callbacks_), address, ext_output_id, offset));
104   }
105 
OnExtAudioOutLocationChanged(const RawAddress & address,uint8_t ext_output_id,uint32_t location)106   void OnExtAudioOutLocationChanged(const RawAddress& address,
107                                     uint8_t ext_output_id,
108                                     uint32_t location) override {
109     DVLOG(2) << __func__ << " address: " << address
110              << "ext_output_id: " << ext_output_id
111              << "location:" << loghex(location);
112 
113     do_in_jni_thread(
114         FROM_HERE,
115         Bind(&VolumeControlCallbacks::OnExtAudioOutLocationChanged,
116              Unretained(callbacks_), address, ext_output_id, location));
117   }
118 
OnExtAudioOutDescriptionChanged(const RawAddress & address,uint8_t ext_output_id,std::string descr)119   void OnExtAudioOutDescriptionChanged(const RawAddress& address,
120                                        uint8_t ext_output_id,
121                                        std::string descr) override {
122     DVLOG(2) << __func__ << " address: " << address
123              << "ext_output_id: " << ext_output_id << "descr:" << descr;
124     do_in_jni_thread(
125         FROM_HERE,
126         Bind(&VolumeControlCallbacks::OnExtAudioOutDescriptionChanged,
127              Unretained(callbacks_), address, ext_output_id, descr));
128   }
129 
Connect(const RawAddress & address)130   void Connect(const RawAddress& address) override {
131     DVLOG(2) << __func__ << " address: " << address;
132 
133     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
134       DVLOG(2) << __func__
135                << " call ignored, due to already started cleanup procedure or "
136                   "service being not read";
137       return;
138     }
139 
140     do_in_main_thread(FROM_HERE,
141                       Bind(&VolumeControl::Connect,
142                            Unretained(VolumeControl::Get()), address));
143   }
144 
Disconnect(const RawAddress & address)145   void Disconnect(const RawAddress& address) override {
146     DVLOG(2) << __func__ << " address: " << address;
147 
148     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
149       DVLOG(2) << __func__
150                << " call ignored, due to already started cleanup procedure or "
151                   "service being not read";
152       return;
153     }
154     do_in_main_thread(FROM_HERE,
155                       Bind(&VolumeControl::Disconnect,
156                            Unretained(VolumeControl::Get()), address));
157   }
158 
SetVolume(std::variant<RawAddress,int> addr_or_group_id,uint8_t volume)159   void SetVolume(std::variant<RawAddress, int> addr_or_group_id,
160                  uint8_t volume) override {
161     DVLOG(2) << __func__ << " volume: " << volume;
162 
163     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
164       DVLOG(2) << __func__
165                << " call ignored, due to already started cleanup procedure or "
166                   "service being not read";
167       return;
168     }
169 
170     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::SetVolume,
171                                       Unretained(VolumeControl::Get()),
172                                       std::move(addr_or_group_id), volume));
173   }
174 
Mute(std::variant<RawAddress,int> addr_or_group_id)175   void Mute(std::variant<RawAddress, int> addr_or_group_id) override {
176     DVLOG(2) << __func__;
177 
178     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
179       DVLOG(2) << __func__
180                << " call ignored, due to already started cleanup procedure or "
181                   "service being not read";
182       return;
183     }
184 
185     do_in_main_thread(
186         FROM_HERE, Bind(&VolumeControl::Mute, Unretained(VolumeControl::Get()),
187                         std::move(addr_or_group_id)));
188   }
189 
Unmute(std::variant<RawAddress,int> addr_or_group_id)190   void Unmute(std::variant<RawAddress, int> addr_or_group_id) override {
191     DVLOG(2) << __func__;
192 
193     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
194       DVLOG(2) << __func__
195                << " call ignored, due to already started cleanup procedure or "
196                   "service being not read";
197       return;
198     }
199 
200     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::UnMute,
201                                       Unretained(VolumeControl::Get()),
202                                       std::move(addr_or_group_id)));
203   }
204 
RemoveDevice(const RawAddress & address)205   void RemoveDevice(const RawAddress& address) override {
206     DVLOG(2) << __func__ << " address: " << address;
207 
208     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
209       DVLOG(2) << __func__
210                << " call ignored, due to already started cleanup procedure or "
211                   "service being not read";
212       return;
213     }
214 
215     /* RemoveDevice can be called on devices that don't have HA enabled */
216     if (VolumeControl::IsVolumeControlRunning()) {
217       do_in_main_thread(FROM_HERE,
218                         Bind(&VolumeControl::Disconnect,
219                              Unretained(VolumeControl::Get()), address));
220     }
221 
222     /* Placeholder: Remove things from storage here */
223   }
224 
GetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id)225   void GetExtAudioOutVolumeOffset(const RawAddress& address,
226                                   uint8_t ext_output_id) override {
227     DVLOG(2) << __func__ << " address: " << address
228              << "ext_output_id:" << ext_output_id;
229 
230     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
231       DVLOG(2) << __func__
232                << " call ignored, due to already started cleanup procedure or "
233                   "service being not read";
234       return;
235     }
236 
237     do_in_main_thread(
238         FROM_HERE,
239         Bind(&VolumeControl::GetExtAudioOutVolumeOffset,
240              Unretained(VolumeControl::Get()), address, ext_output_id));
241   }
242 
SetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id,int16_t offset_val)243   void SetExtAudioOutVolumeOffset(const RawAddress& address,
244                                   uint8_t ext_output_id,
245                                   int16_t offset_val) override {
246     DVLOG(2) << __func__ << " address: " << address
247              << "ext_output_id:" << ext_output_id
248              << "ext_output_id:" << offset_val;
249 
250     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
251       DVLOG(2) << __func__
252                << " call ignored, due to already started cleanup procedure or "
253                   "service being not read";
254       return;
255     }
256 
257     do_in_main_thread(FROM_HERE,
258                       Bind(&VolumeControl::SetExtAudioOutVolumeOffset,
259                            Unretained(VolumeControl::Get()), address,
260                            ext_output_id, offset_val));
261   }
262 
GetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id)263   void GetExtAudioOutLocation(const RawAddress& address,
264                               uint8_t ext_output_id) override {
265     DVLOG(2) << __func__ << " address: " << address
266              << "ext_output_id:" << ext_output_id;
267 
268     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
269       DVLOG(2) << __func__
270                << " call ignored, due to already started cleanup procedure or "
271                   "service being not read";
272       return;
273     }
274 
275     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::GetExtAudioOutLocation,
276                                       Unretained(VolumeControl::Get()), address,
277                                       ext_output_id));
278   }
279 
SetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id,uint32_t location)280   void SetExtAudioOutLocation(const RawAddress& address, uint8_t ext_output_id,
281                               uint32_t location) override {
282     DVLOG(2) << __func__ << " address: " << address
283              << "ext_output_id:" << ext_output_id
284              << "location:" << loghex(location);
285 
286     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
287       DVLOG(2) << __func__
288                << " call ignored, due to already started cleanup procedure or "
289                   "service being not read";
290       return;
291     }
292 
293     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::SetExtAudioOutLocation,
294                                       Unretained(VolumeControl::Get()), address,
295                                       ext_output_id, location));
296   }
297 
GetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id)298   void GetExtAudioOutDescription(const RawAddress& address,
299                                  uint8_t ext_output_id) override {
300     DVLOG(2) << __func__ << " address: " << address
301              << "ext_output_id:" << ext_output_id;
302 
303     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
304       DVLOG(2) << __func__
305                << " call ignored, due to already started cleanup procedure or "
306                   "service being not read";
307       return;
308     }
309 
310     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::GetExtAudioOutDescription,
311                                       Unretained(VolumeControl::Get()), address,
312                                       ext_output_id));
313   }
314 
SetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id,std::string descr)315   void SetExtAudioOutDescription(const RawAddress& address,
316                                  uint8_t ext_output_id,
317                                  std::string descr) override {
318     DVLOG(2) << __func__ << " address: " << address
319              << "ext_output_id:" << ext_output_id << "description:" << descr;
320 
321     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
322       DVLOG(2) << __func__
323                << " call ignored, due to already started cleanup procedure or "
324                   "service being not read";
325       return;
326     }
327 
328     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::SetExtAudioOutDescription,
329                                       Unretained(VolumeControl::Get()), address,
330                                       ext_output_id, descr));
331   }
332 
Cleanup(void)333   void Cleanup(void) override {
334     DVLOG(2) << __func__;
335     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
336       DVLOG(2) << __func__
337                << " call ignored, due to already started cleanup procedure or "
338                   "service being not read";
339       return;
340     }
341 
342     initialized = false;
343     do_in_main_thread(FROM_HERE, Bind(&VolumeControl::CleanUp));
344   }
345 
346  private:
347   VolumeControlCallbacks* callbacks_;
348 };
349 
350 } /* namespace */
351 
btif_volume_control_get_interface(void)352 VolumeControlInterface* btif_volume_control_get_interface(void) {
353   if (!vc_instance) vc_instance.reset(new VolumeControlInterfaceImpl());
354 
355   return vc_instance.get();
356 }
357