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