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