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