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