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 <aics/api.h>
21 #include <base/functional/bind.h>
22 #include <base/location.h>
23 #include <bluetooth/log.h>
24 #include <hardware/bt_vc.h>
25 
26 #include <atomic>
27 #include <cstdint>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <variant>
32 
33 #include "bta/include/bta_vc_api.h"
34 #include "btif/include/btif_common.h"
35 #include "btif/include/btif_profile_storage.h"
36 #include "stack/include/main_thread.h"
37 #include "types/raw_address.h"
38 
39 // TODO(b/369381361) Enfore -Wmissing-prototypes
40 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
41 
42 using base::Bind;
43 using base::Unretained;
44 using bluetooth::aics::GainMode;
45 using bluetooth::aics::Mute;
46 using bluetooth::vc::ConnectionState;
47 using bluetooth::vc::VolumeControlCallbacks;
48 using bluetooth::vc::VolumeControlInterface;
49 
50 namespace {
51 std::unique_ptr<VolumeControlInterface> vc_instance;
52 std::atomic_bool initialized = false;
53 
54 class VolumeControlInterfaceImpl : public VolumeControlInterface, public VolumeControlCallbacks {
55   ~VolumeControlInterfaceImpl() override = default;
56 
Init(VolumeControlCallbacks * callbacks)57   void Init(VolumeControlCallbacks* callbacks) override {
58     this->callbacks_ = callbacks;
59     do_in_main_thread(
60             Bind(&VolumeControl::Initialize, this,
61                  jni_thread_wrapper(Bind(&btif_storage_load_bonded_volume_control_devices))));
62 
63     /* It might be not yet initialized, but setting this flag here is safe,
64      * because other calls will check this and the native instance
65      */
66     initialized = true;
67   }
68 
OnConnectionState(ConnectionState state,const RawAddress & address)69   void OnConnectionState(ConnectionState state, const RawAddress& address) override {
70     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnConnectionState, Unretained(callbacks_), state,
71                           address));
72   }
73 
OnVolumeStateChanged(const RawAddress & address,uint8_t volume,bool mute,uint8_t flags,bool isAutonomous)74   void OnVolumeStateChanged(const RawAddress& address, uint8_t volume, bool mute, uint8_t flags,
75                             bool isAutonomous) override {
76     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnVolumeStateChanged, Unretained(callbacks_),
77                           address, volume, mute, flags, isAutonomous));
78   }
79 
OnGroupVolumeStateChanged(int group_id,uint8_t volume,bool mute,bool isAutonomous)80   void OnGroupVolumeStateChanged(int group_id, uint8_t volume, bool mute,
81                                  bool isAutonomous) override {
82     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnGroupVolumeStateChanged,
83                           Unretained(callbacks_), group_id, volume, mute, isAutonomous));
84   }
85 
OnDeviceAvailable(const RawAddress & address,uint8_t num_offset,uint8_t num_inputs)86   void OnDeviceAvailable(const RawAddress& address, uint8_t num_offset,
87                          uint8_t num_inputs) override {
88     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnDeviceAvailable, Unretained(callbacks_),
89                           address, num_offset, num_inputs));
90   }
91 
92   /* Callbacks for Volume Offset Control Service (VOCS) - Extended Audio Outputs
93    */
94 
OnExtAudioOutVolumeOffsetChanged(const RawAddress & address,uint8_t ext_output_id,int16_t offset)95   void OnExtAudioOutVolumeOffsetChanged(const RawAddress& address, uint8_t ext_output_id,
96                                         int16_t offset) override {
97     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioOutVolumeOffsetChanged,
98                           Unretained(callbacks_), address, ext_output_id, offset));
99   }
100 
OnExtAudioOutLocationChanged(const RawAddress & address,uint8_t ext_output_id,uint32_t location)101   void OnExtAudioOutLocationChanged(const RawAddress& address, uint8_t ext_output_id,
102                                     uint32_t location) override {
103     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioOutLocationChanged,
104                           Unretained(callbacks_), address, ext_output_id, location));
105   }
106 
OnExtAudioOutDescriptionChanged(const RawAddress & address,uint8_t ext_output_id,std::string descr)107   void OnExtAudioOutDescriptionChanged(const RawAddress& address, uint8_t ext_output_id,
108                                        std::string descr) override {
109     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioOutDescriptionChanged,
110                           Unretained(callbacks_), address, ext_output_id, descr));
111   }
112 
113   /* Callbacks for Audio Input Stream (AIS) - Extended Audio Inputs */
OnExtAudioInStateChanged(const RawAddress & address,uint8_t ext_input_id,int8_t gain_setting,::Mute mute,::GainMode gain_mode)114   void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id,
115                                 int8_t gain_setting, ::Mute mute, ::GainMode gain_mode) override {
116     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInStateChanged, Unretained(callbacks_),
117                           address, ext_input_id, gain_setting, mute, gain_mode));
118   }
119 
OnExtAudioInSetGainSettingFailed(const RawAddress & address,uint8_t ext_input_id)120   void OnExtAudioInSetGainSettingFailed(const RawAddress& address, uint8_t ext_input_id) override {
121     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInSetGainSettingFailed,
122                           Unretained(callbacks_), address, ext_input_id));
123   }
124 
OnExtAudioInSetMuteFailed(const RawAddress & address,uint8_t ext_input_id)125   void OnExtAudioInSetMuteFailed(const RawAddress& address, uint8_t ext_input_id) override {
126     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInSetMuteFailed,
127                           Unretained(callbacks_), address, ext_input_id));
128   }
OnExtAudioInSetGainModeFailed(const RawAddress & address,uint8_t ext_input_id)129   void OnExtAudioInSetGainModeFailed(const RawAddress& address, uint8_t ext_input_id) override {
130     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInSetGainModeFailed,
131                           Unretained(callbacks_), address, ext_input_id));
132   }
133 
OnExtAudioInStatusChanged(const RawAddress & address,uint8_t ext_input_id,bluetooth::vc::VolumeInputStatus status)134   void OnExtAudioInStatusChanged(const RawAddress& address, uint8_t ext_input_id,
135                                  bluetooth::vc::VolumeInputStatus status) override {
136     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInStatusChanged,
137                           Unretained(callbacks_), address, ext_input_id, status));
138   }
139 
OnExtAudioInTypeChanged(const RawAddress & address,uint8_t ext_input_id,bluetooth::vc::VolumeInputType type)140   void OnExtAudioInTypeChanged(const RawAddress& address, uint8_t ext_input_id,
141                                bluetooth::vc::VolumeInputType type) override {
142     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInTypeChanged, Unretained(callbacks_),
143                           address, ext_input_id, type));
144   }
145 
OnExtAudioInGainSettingPropertiesChanged(const RawAddress & address,uint8_t ext_input_id,uint8_t unit,int8_t min,int8_t max)146   void OnExtAudioInGainSettingPropertiesChanged(const RawAddress& address, uint8_t ext_input_id,
147                                                 uint8_t unit, int8_t min, int8_t max) override {
148     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInGainSettingPropertiesChanged,
149                           Unretained(callbacks_), address, ext_input_id, unit, min, max));
150   }
151 
OnExtAudioInDescriptionChanged(const RawAddress & address,uint8_t ext_input_id,std::string description,bool is_writable)152   void OnExtAudioInDescriptionChanged(const RawAddress& address, uint8_t ext_input_id,
153                                       std::string description, bool is_writable) override {
154     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInDescriptionChanged,
155                           Unretained(callbacks_), address, ext_input_id, description, is_writable));
156   }
157 
Connect(const RawAddress & address)158   void Connect(const RawAddress& address) override {
159     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
160       bluetooth::log::verbose(
161               "call ignored, due to already started cleanup procedure or service "
162               "being not read");
163       return;
164     }
165 
166     do_in_main_thread(Bind(&VolumeControl::Connect, Unretained(VolumeControl::Get()), address));
167   }
168 
Disconnect(const RawAddress & address)169   void Disconnect(const RawAddress& address) override {
170     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
171       bluetooth::log::verbose(
172               "call ignored, due to already started cleanup procedure or service "
173               "being not read");
174       return;
175     }
176     do_in_main_thread(Bind(&VolumeControl::Disconnect, Unretained(VolumeControl::Get()), address));
177   }
178 
SetVolume(std::variant<RawAddress,int> addr_or_group_id,uint8_t volume)179   void SetVolume(std::variant<RawAddress, int> addr_or_group_id, uint8_t volume) override {
180     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
181       bluetooth::log::verbose(
182               "call ignored, due to already started cleanup procedure or service "
183               "being not read");
184       return;
185     }
186 
187     do_in_main_thread(Bind(&VolumeControl::SetVolume, Unretained(VolumeControl::Get()),
188                            std::move(addr_or_group_id), volume));
189   }
190 
Mute(std::variant<RawAddress,int> addr_or_group_id)191   void Mute(std::variant<RawAddress, int> addr_or_group_id) override {
192     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
193       bluetooth::log::verbose(
194               "call ignored, due to already started cleanup procedure or service "
195               "being not read");
196       return;
197     }
198 
199     do_in_main_thread(Bind(&VolumeControl::Mute, Unretained(VolumeControl::Get()),
200                            std::move(addr_or_group_id)));
201   }
202 
Unmute(std::variant<RawAddress,int> addr_or_group_id)203   void Unmute(std::variant<RawAddress, int> addr_or_group_id) override {
204     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
205       bluetooth::log::verbose(
206               "call ignored, due to already started cleanup procedure or service "
207               "being not read");
208       return;
209     }
210 
211     do_in_main_thread(Bind(&VolumeControl::UnMute, Unretained(VolumeControl::Get()),
212                            std::move(addr_or_group_id)));
213   }
214 
RemoveDevice(const RawAddress & address)215   void RemoveDevice(const RawAddress& address) override {
216     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
217       bluetooth::log::verbose(
218               "call ignored, due to already started cleanup procedure or service "
219               "being not read");
220       return;
221     }
222 
223     /* RemoveDevice can be called on devices that don't have HA enabled */
224     if (VolumeControl::IsVolumeControlRunning()) {
225       do_in_main_thread(Bind(&VolumeControl::Remove, Unretained(VolumeControl::Get()), address));
226     }
227   }
228 
GetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id)229   void GetExtAudioOutVolumeOffset(const RawAddress& address, uint8_t ext_output_id) override {
230     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
231       bluetooth::log::verbose(
232               "call ignored, due to already started cleanup procedure or service "
233               "being not read");
234       return;
235     }
236 
237     do_in_main_thread(Bind(&VolumeControl::GetExtAudioOutVolumeOffset,
238                            Unretained(VolumeControl::Get()), address, ext_output_id));
239   }
240 
SetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id,int16_t offset_val)241   void SetExtAudioOutVolumeOffset(const RawAddress& address, uint8_t ext_output_id,
242                                   int16_t offset_val) override {
243     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
244       bluetooth::log::verbose(
245               "call ignored, due to already started cleanup procedure or service "
246               "being not read");
247       return;
248     }
249 
250     do_in_main_thread(Bind(&VolumeControl::SetExtAudioOutVolumeOffset,
251                            Unretained(VolumeControl::Get()), address, ext_output_id, offset_val));
252   }
253 
GetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id)254   void GetExtAudioOutLocation(const RawAddress& address, uint8_t ext_output_id) override {
255     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
256       bluetooth::log::verbose(
257               "call ignored, due to already started cleanup procedure or service "
258               "being not read");
259       return;
260     }
261 
262     do_in_main_thread(Bind(&VolumeControl::GetExtAudioOutLocation, Unretained(VolumeControl::Get()),
263                            address, ext_output_id));
264   }
265 
SetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id,uint32_t location)266   void SetExtAudioOutLocation(const RawAddress& address, uint8_t ext_output_id,
267                               uint32_t location) override {
268     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
269       bluetooth::log::verbose(
270               "call ignored, due to already started cleanup procedure or service "
271               "being not read");
272       return;
273     }
274 
275     do_in_main_thread(Bind(&VolumeControl::SetExtAudioOutLocation, Unretained(VolumeControl::Get()),
276                            address, ext_output_id, location));
277   }
278 
GetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id)279   void GetExtAudioOutDescription(const RawAddress& address, uint8_t ext_output_id) override {
280     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
281       bluetooth::log::verbose(
282               "call ignored, due to already started cleanup procedure or service "
283               "being not read");
284       return;
285     }
286 
287     do_in_main_thread(Bind(&VolumeControl::GetExtAudioOutDescription,
288                            Unretained(VolumeControl::Get()), address, ext_output_id));
289   }
290 
SetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id,std::string descr)291   void SetExtAudioOutDescription(const RawAddress& address, uint8_t ext_output_id,
292                                  std::string descr) override {
293     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
294       bluetooth::log::verbose(
295               "call ignored, due to already started cleanup procedure or service "
296               "being not read");
297       return;
298     }
299 
300     do_in_main_thread(Bind(&VolumeControl::SetExtAudioOutDescription,
301                            Unretained(VolumeControl::Get()), address, ext_output_id, descr));
302   }
303 
GetExtAudioInState(const RawAddress & address,uint8_t ext_input_id)304   void GetExtAudioInState(const RawAddress& address, uint8_t ext_input_id) override {
305     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
306       bluetooth::log::verbose(
307               "call ignored, due to already started cleanup procedure or service "
308               "being not read");
309       return;
310     }
311 
312     do_in_main_thread(Bind(&VolumeControl::GetExtAudioInState, Unretained(VolumeControl::Get()),
313                            address, ext_input_id));
314   }
315 
GetExtAudioInStatus(const RawAddress & address,uint8_t ext_input_id)316   void GetExtAudioInStatus(const RawAddress& address, uint8_t ext_input_id) override {
317     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
318       bluetooth::log::verbose(
319               "call ignored, due to already started cleanup procedure or service "
320               "being not read");
321       return;
322     }
323 
324     do_in_main_thread(Bind(&VolumeControl::GetExtAudioInStatus, Unretained(VolumeControl::Get()),
325                            address, ext_input_id));
326   }
327 
GetExtAudioInType(const RawAddress & address,uint8_t ext_input_id)328   void GetExtAudioInType(const RawAddress& address, uint8_t ext_input_id) override {
329     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
330       bluetooth::log::verbose(
331               "call ignored, due to already started cleanup procedure or service "
332               "being not read");
333       return;
334     }
335 
336     do_in_main_thread(Bind(&VolumeControl::GetExtAudioInType, Unretained(VolumeControl::Get()),
337                            address, ext_input_id));
338   }
339 
GetExtAudioInGainProps(const RawAddress & address,uint8_t ext_input_id)340   void GetExtAudioInGainProps(const RawAddress& address, uint8_t ext_input_id) override {
341     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
342       bluetooth::log::verbose(
343               "call ignored, due to already started cleanup procedure or service "
344               "being not read");
345       return;
346     }
347 
348     do_in_main_thread(Bind(&VolumeControl::GetExtAudioInGainProps, Unretained(VolumeControl::Get()),
349                            address, ext_input_id));
350   }
351 
GetExtAudioInDescription(const RawAddress & address,uint8_t ext_input_id)352   void GetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id) override {
353     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
354       bluetooth::log::verbose(
355               "call ignored, due to already started cleanup procedure or service "
356               "being not read");
357       return;
358     }
359 
360     do_in_main_thread(Bind(&VolumeControl::GetExtAudioInDescription,
361                            Unretained(VolumeControl::Get()), address, ext_input_id));
362   }
363 
SetExtAudioInDescription(const RawAddress & address,uint8_t ext_input_id,std::string descr)364   bool SetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id,
365                                 std::string descr) override {
366     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
367       bluetooth::log::verbose(
368               "call ignored, due to already started cleanup procedure or service "
369               "being not read");
370       return false;
371     }
372 
373     do_in_main_thread(Bind(&VolumeControl::SetExtAudioInDescription,
374                            Unretained(VolumeControl::Get()), address, ext_input_id, descr));
375     return true;
376   }
377 
SetExtAudioInGainSetting(const RawAddress & address,uint8_t ext_input_id,int8_t gain_setting)378   bool SetExtAudioInGainSetting(const RawAddress& address, uint8_t ext_input_id,
379                                 int8_t gain_setting) override {
380     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
381       bluetooth::log::verbose(
382               "call ignored, due to already started cleanup procedure or service being not read");
383       return false;
384     }
385 
386     do_in_main_thread(Bind(&VolumeControl::SetExtAudioInGainSetting,
387                            Unretained(VolumeControl::Get()), address, ext_input_id, gain_setting));
388     return true;
389   }
390 
SetExtAudioInGainMode(const RawAddress & address,uint8_t ext_input_id,::GainMode gain_mode)391   bool SetExtAudioInGainMode(const RawAddress& address, uint8_t ext_input_id,
392                              ::GainMode gain_mode) override {
393     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
394       bluetooth::log::verbose(
395               "call ignored, due to already started cleanup procedure or service being not read");
396       return false;
397     }
398 
399     do_in_main_thread(Bind(&VolumeControl::SetExtAudioInGainMode, Unretained(VolumeControl::Get()),
400                            address, ext_input_id, gain_mode));
401     return true;
402   }
403 
SetExtAudioInMute(const RawAddress & address,uint8_t ext_input_id,::Mute mute)404   bool SetExtAudioInMute(const RawAddress& address, uint8_t ext_input_id, ::Mute mute) override {
405     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
406       bluetooth::log::verbose(
407               "call ignored, due to already started cleanup procedure or service being not read");
408       return false;
409     }
410 
411     do_in_main_thread(Bind(&VolumeControl::SetExtAudioInMute, Unretained(VolumeControl::Get()),
412                            address, ext_input_id, mute));
413     return true;
414   }
415 
Cleanup(void)416   void Cleanup(void) override {
417     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
418       bluetooth::log::verbose(
419               "call ignored, due to already started cleanup procedure or service "
420               "being not read");
421       return;
422     }
423 
424     initialized = false;
425     do_in_main_thread(Bind(&VolumeControl::CleanUp));
426   }
427 
428 private:
429   VolumeControlCallbacks* callbacks_;
430 };
431 
432 } /* namespace */
433 
btif_volume_control_get_interface(void)434 VolumeControlInterface* btif_volume_control_get_interface(void) {
435   if (!vc_instance) {
436     vc_instance.reset(new VolumeControlInterfaceImpl());
437   }
438 
439   return vc_instance.get();
440 }
441