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 #include <base/functional/bind.h>
19 #include <base/strings/string_number_conversions.h>
20 #include <base/strings/string_util.h>
21 #include <bluetooth/log.h>
22 #include <hardware/bt_gatt_types.h>
23 #include <hardware/bt_vc.h>
24
25 #include <mutex>
26 #include <string>
27 #include <vector>
28
29 #include "bta/le_audio/le_audio_types.h"
30 #include "bta_csis_api.h"
31 #include "bta_gatt_api.h"
32 #include "bta_gatt_queue.h"
33 #include "bta_vc_api.h"
34 #include "devices.h"
35 #include "internal_include/bt_trace.h"
36 #include "os/log.h"
37 #include "osi/include/osi.h"
38 #include "stack/btm/btm_sec.h"
39 #include "stack/include/bt_types.h"
40 #include "types/bluetooth/uuid.h"
41 #include "types/raw_address.h"
42
43 using base::Closure;
44 using bluetooth::Uuid;
45 using bluetooth::csis::CsisClient;
46 using bluetooth::vc::ConnectionState;
47 using namespace bluetooth::vc::internal;
48 using namespace bluetooth;
49
50 namespace {
51 class VolumeControlImpl;
52 VolumeControlImpl* instance;
53 std::mutex instance_mutex;
54
55 /**
56 * Overview:
57 *
58 * This is Volume Control Implementation class which realize Volume Control
59 * Profile (VCP)
60 *
61 * Each connected peer device supporting Volume Control Service (VCS) is on the
62 * list of devices (volume_control_devices_). When VCS is discovered on the peer
63 * device, Android does search for all the instances Volume Offset Service
64 * (VOCS). Note that AIS and VOCS are optional.
65 *
66 * Once all the mandatory characteristis for all the services are discovered,
67 * Fluoride calls ON_CONNECTED callback.
68 *
69 * It is assumed that whenever application changes general audio options in this
70 * profile e.g. Volume up/down, mute/unmute etc, profile configures all the
71 * devices which are active Le Audio devices.
72 *
73 * Peer devices has at maximum one instance of VCS and 0 or more instance of
74 * VOCS. Android gets access to External Audio Outputs using appropriate ID.
75 * Also each of the External Device has description
76 * characteristic and Type which gives the application hint what it is a device.
77 * Examples of such devices:
78 * External Output: 1 instance to controller ballance between set of devices
79 * External Output: each of 5.1 speaker set etc.
80 */
81 class VolumeControlImpl : public VolumeControl {
82 public:
83 ~VolumeControlImpl() override = default;
84
VolumeControlImpl(bluetooth::vc::VolumeControlCallbacks * callbacks,const base::Closure & initCb)85 VolumeControlImpl(bluetooth::vc::VolumeControlCallbacks* callbacks,
86 const base::Closure& initCb)
87 : gatt_if_(0), callbacks_(callbacks), latest_operation_id_(0) {
88 BTA_GATTC_AppRegister(
89 gattc_callback_static,
90 base::Bind(
91 [](const base::Closure& initCb, uint8_t client_id, uint8_t status) {
92 if (status != GATT_SUCCESS) {
93 log::error(
94 "Can't start Volume Control profile - no gatt clients "
95 "left!");
96 return;
97 }
98 instance->gatt_if_ = client_id;
99 initCb.Run();
100 },
101 initCb),
102 true);
103 }
104
StartOpportunisticConnect(const RawAddress & address)105 void StartOpportunisticConnect(const RawAddress& address) {
106 /* Oportunistic works only for direct connect,
107 * but in fact this is background connect
108 */
109 log::info(": {}", address);
110 BTA_GATTC_Open(gatt_if_, address, BTM_BLE_DIRECT_CONNECTION, true);
111 }
112
Connect(const RawAddress & address)113 void Connect(const RawAddress& address) override {
114 log::info(": {}", address);
115
116 auto device = volume_control_devices_.FindByAddress(address);
117 if (!device) {
118 if (!BTM_IsLinkKeyKnown(address, BT_TRANSPORT_LE)) {
119 log::error("Connecting {} when not bonded", address);
120 callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, address);
121 return;
122 }
123 volume_control_devices_.Add(address, true);
124 } else {
125 device->connecting_actively = true;
126
127 if (device->IsConnected()) {
128 log::warn("address={}, connection_id={} already connected.", address,
129 device->connection_id);
130
131 if (device->IsReady()) {
132 callbacks_->OnConnectionState(ConnectionState::CONNECTED,
133 device->address);
134 } else {
135 OnGattConnected(GATT_SUCCESS, device->connection_id, gatt_if_,
136 device->address, BT_TRANSPORT_LE, GATT_MAX_MTU_SIZE);
137 }
138 return;
139 }
140 }
141
142 StartOpportunisticConnect(address);
143 }
144
AddFromStorage(const RawAddress & address)145 void AddFromStorage(const RawAddress& address) {
146 log::info("{}", address);
147 volume_control_devices_.Add(address, false);
148 StartOpportunisticConnect(address);
149 }
150
OnGattConnected(tGATT_STATUS status,uint16_t connection_id,tGATT_IF,RawAddress address,tBT_TRANSPORT transport,uint16_t)151 void OnGattConnected(tGATT_STATUS status, uint16_t connection_id,
152 tGATT_IF /*client_if*/, RawAddress address,
153 tBT_TRANSPORT transport, uint16_t /*mtu*/) {
154 log::info("{}, conn_id=0x{:04x}, transport={}, status={}(0x{:02x})",
155 address, connection_id, bt_transport_text(transport),
156 gatt_status_text(status), status);
157
158 if (transport != BT_TRANSPORT_LE) {
159 log::warn("Only LE connection is allowed (transport {})",
160 bt_transport_text(transport));
161 BTA_GATTC_Close(connection_id);
162 return;
163 }
164
165 VolumeControlDevice* device =
166 volume_control_devices_.FindByAddress(address);
167 if (!device) {
168 log::error("Skipping unknown device, address={}", address);
169 return;
170 }
171
172 if (status != GATT_SUCCESS) {
173 log::info("Failed to connect to Volume Control device");
174 device_cleanup_helper(device, device->connecting_actively);
175 return;
176 }
177
178 device->connection_id = connection_id;
179
180 /* Make sure to remove device from background connect.
181 * It will be added back if needed, when device got disconnected
182 */
183 BTA_GATTC_CancelOpen(gatt_if_, address, false);
184
185 if (device->IsEncryptionEnabled()) {
186 OnEncryptionComplete(address, BTM_SUCCESS);
187 return;
188 }
189
190 if (!device->EnableEncryption()) {
191 log::error("Link key is not known for {}, disconnect profile", address);
192 device->Disconnect(gatt_if_);
193 }
194 }
195
OnEncryptionComplete(const RawAddress & address,uint8_t success)196 void OnEncryptionComplete(const RawAddress& address, uint8_t success) {
197 VolumeControlDevice* device =
198 volume_control_devices_.FindByAddress(address);
199 if (!device) {
200 log::error("Skipping unknown device {}", address);
201 return;
202 }
203
204 if (success != BTM_SUCCESS) {
205 log::error("encryption failed status: {}", int{success});
206 // If the encryption failed, do not remove the device.
207 // Disconnect only, since the Android will try to re-enable encryption
208 // after disconnection
209 device_cleanup_helper(device, device->connecting_actively);
210 return;
211 }
212
213 log::info("{} status: {}", address, success);
214
215 if (device->HasHandles()) {
216 device->EnqueueInitialRequests(gatt_if_, chrc_read_callback_static,
217 OnGattWriteCccStatic);
218
219 } else {
220 BTA_GATTC_ServiceSearchRequest(device->connection_id, kVolumeControlUuid);
221 }
222 }
223
ClearDeviceInformationAndStartSearch(VolumeControlDevice * device)224 void ClearDeviceInformationAndStartSearch(VolumeControlDevice* device) {
225 if (!device) {
226 log::error("Device is null");
227 return;
228 }
229
230 log::info("address={}", device->address);
231 if (device->known_service_handles_ == false) {
232 log::info("Device already is waiting for new services");
233 return;
234 }
235
236 std::vector<RawAddress> devices = {device->address};
237 device->DeregisterNotifications(gatt_if_);
238
239 RemovePendingVolumeControlOperations(devices,
240 bluetooth::groups::kGroupUnknown);
241 device->ResetHandles();
242 BTA_GATTC_ServiceSearchRequest(device->connection_id, kVolumeControlUuid);
243 }
244
OnServiceChangeEvent(const RawAddress & address)245 void OnServiceChangeEvent(const RawAddress& address) {
246 VolumeControlDevice* device =
247 volume_control_devices_.FindByAddress(address);
248 if (!device) {
249 log::error("Skipping unknown device {}", address);
250 return;
251 }
252
253 ClearDeviceInformationAndStartSearch(device);
254 }
255
OnServiceDiscDoneEvent(const RawAddress & address)256 void OnServiceDiscDoneEvent(const RawAddress& address) {
257 VolumeControlDevice* device =
258 volume_control_devices_.FindByAddress(address);
259 if (!device) {
260 log::error("Skipping unknown device {}", address);
261 return;
262 }
263
264 if (device->known_service_handles_ == false) {
265 BTA_GATTC_ServiceSearchRequest(device->connection_id, kVolumeControlUuid);
266 }
267 }
268
OnServiceSearchComplete(uint16_t connection_id,tGATT_STATUS status)269 void OnServiceSearchComplete(uint16_t connection_id, tGATT_STATUS status) {
270 VolumeControlDevice* device =
271 volume_control_devices_.FindByConnId(connection_id);
272 if (!device) {
273 log::error("Skipping unknown device, connection_id=0x{:x}",
274 connection_id);
275 return;
276 }
277
278 /* Known device, nothing to do */
279 if (device->IsReady()) return;
280
281 if (status != GATT_SUCCESS) {
282 /* close connection and report service discovery complete with error */
283 log::error("Service discovery failed");
284 device_cleanup_helper(device, device->connecting_actively);
285 return;
286 }
287
288 if (!device->IsEncryptionEnabled()) {
289 log::warn("Device not yet bonded - waiting for encryption");
290 return;
291 }
292
293 bool success = device->UpdateHandles();
294 if (!success) {
295 log::error("Incomplete service database");
296 device_cleanup_helper(device, device->connecting_actively);
297 return;
298 }
299
300 device->EnqueueInitialRequests(gatt_if_, chrc_read_callback_static,
301 OnGattWriteCccStatic);
302 }
303
OnCharacteristicValueChanged(uint16_t conn_id,tGATT_STATUS status,uint16_t handle,uint16_t len,uint8_t * value,void * data,bool is_notification)304 void OnCharacteristicValueChanged(uint16_t conn_id, tGATT_STATUS status,
305 uint16_t handle, uint16_t len,
306 uint8_t* value, void* data,
307 bool is_notification) {
308 VolumeControlDevice* device = volume_control_devices_.FindByConnId(conn_id);
309 if (!device) {
310 log::info("unknown conn_id=0x{:x}", conn_id);
311 return;
312 }
313
314 if (status != GATT_SUCCESS) {
315 log::info("status=0x{:02x}", static_cast<int>(status));
316 if (status == GATT_DATABASE_OUT_OF_SYNC) {
317 log::info("Database out of sync for {}", device->address);
318 ClearDeviceInformationAndStartSearch(device);
319 }
320 return;
321 }
322
323 if (handle == device->volume_state_handle) {
324 OnVolumeControlStateReadOrNotified(device, len, value, is_notification);
325 verify_device_ready(device, handle);
326 return;
327 }
328 if (handle == device->volume_flags_handle) {
329 OnVolumeControlFlagsChanged(device, len, value);
330 verify_device_ready(device, handle);
331 return;
332 }
333
334 const gatt::Service* service = BTA_GATTC_GetOwningService(conn_id, handle);
335 if (service == nullptr) return;
336
337 VolumeOffset* offset =
338 device->audio_offsets.FindByServiceHandle(service->handle);
339 if (offset != nullptr) {
340 if (handle == offset->state_handle) {
341 OnExtAudioOutStateChanged(device, offset, len, value);
342 } else if (handle == offset->audio_location_handle) {
343 OnExtAudioOutLocationChanged(device, offset, len, value);
344 } else if (handle == offset->audio_descr_handle) {
345 OnOffsetOutputDescChanged(device, offset, len, value);
346 } else {
347 log::error("unknown offset handle=0x{:x}", handle);
348 return;
349 }
350
351 verify_device_ready(device, handle);
352 return;
353 }
354
355 log::error("unknown handle=0x{:x}", handle);
356 }
357
OnNotificationEvent(uint16_t conn_id,uint16_t handle,uint16_t len,uint8_t * value)358 void OnNotificationEvent(uint16_t conn_id, uint16_t handle, uint16_t len,
359 uint8_t* value) {
360 log::info("handle=0x{:x}", handle);
361 OnCharacteristicValueChanged(conn_id, GATT_SUCCESS, handle, len, value,
362 nullptr, true);
363 }
364
VolumeControlReadCommon(uint16_t conn_id,uint16_t handle)365 void VolumeControlReadCommon(uint16_t conn_id, uint16_t handle) {
366 BtaGattQueue::ReadCharacteristic(conn_id, handle, chrc_read_callback_static,
367 nullptr);
368 }
369
HandleAutonomusVolumeChange(VolumeControlDevice * device,bool is_volume_change,bool is_mute_change)370 void HandleAutonomusVolumeChange(VolumeControlDevice* device,
371 bool is_volume_change, bool is_mute_change) {
372 log::debug("{}, is volume change: {}, is mute change: {}", device->address,
373 is_volume_change, is_mute_change);
374
375 if (!is_volume_change && !is_mute_change) {
376 log::error("Autonomous change but volume and mute did not changed.");
377 return;
378 }
379
380 auto csis_api = CsisClient::Get();
381 if (!csis_api) {
382 log::warn("Csis module is not available");
383 callbacks_->OnVolumeStateChanged(device->address, device->volume,
384 device->mute, true);
385 return;
386 }
387
388 auto group_id =
389 csis_api->GetGroupId(device->address, le_audio::uuid::kCapServiceUuid);
390 if (group_id == bluetooth::groups::kGroupUnknown) {
391 log::warn("No group for device {}", device->address);
392 callbacks_->OnVolumeStateChanged(device->address, device->volume,
393 device->mute, true);
394 return;
395 }
396
397 auto devices = csis_api->GetDeviceList(group_id);
398 for (auto it = devices.begin(); it != devices.end();) {
399 auto dev = volume_control_devices_.FindByAddress(*it);
400 if (!dev || !dev->IsConnected() || (dev->address == device->address)) {
401 it = devices.erase(it);
402 } else {
403 it++;
404 }
405 }
406
407 if (devices.empty() && (is_volume_change || is_mute_change)) {
408 log::info("No more devices in the group right now");
409 callbacks_->OnGroupVolumeStateChanged(group_id, device->volume,
410 device->mute, true);
411 return;
412 }
413
414 if (is_volume_change) {
415 std::vector<uint8_t> arg({device->volume});
416 PrepareVolumeControlOperation(devices, group_id, true,
417 kControlPointOpcodeSetAbsoluteVolume, arg);
418 }
419
420 if (is_mute_change) {
421 std::vector<uint8_t> arg;
422 uint8_t opcode =
423 device->mute ? kControlPointOpcodeMute : kControlPointOpcodeUnmute;
424 PrepareVolumeControlOperation(devices, group_id, true, opcode, arg);
425 }
426
427 StartQueueOperation();
428 }
429
OnVolumeControlStateReadOrNotified(VolumeControlDevice * device,uint16_t len,uint8_t * value,bool is_notification)430 void OnVolumeControlStateReadOrNotified(VolumeControlDevice* device,
431 uint16_t len, uint8_t* value,
432 bool is_notification) {
433 if (len != 3) {
434 log::info("malformed len=0x{:x}", len);
435 return;
436 }
437
438 uint8_t vol;
439 uint8_t mute;
440 uint8_t* pp = value;
441 STREAM_TO_UINT8(vol, pp);
442 STREAM_TO_UINT8(mute, pp);
443 STREAM_TO_UINT8(device->change_counter, pp);
444
445 bool is_volume_change = (device->volume != vol);
446 device->volume = vol;
447
448 bool is_mute_change = (device->mute != mute);
449 device->mute = mute;
450
451 log::info("volume 0x{:x} mute 0x{:x} change_counter 0x{:x}", device->volume,
452 device->mute, device->change_counter);
453
454 if (!device->IsReady()) {
455 log::info("Device: {} is not ready yet.", device->address);
456 return;
457 }
458
459 /* This is just a read, send single notification */
460 if (!is_notification) {
461 callbacks_->OnVolumeStateChanged(device->address, device->volume,
462 device->mute, false);
463 return;
464 }
465
466 auto addr = device->address;
467 auto op = find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
468 [addr](auto& operation) {
469 auto it = find(operation.devices_.begin(),
470 operation.devices_.end(), addr);
471 return it != operation.devices_.end();
472 });
473 if (op == ongoing_operations_.end()) {
474 log::debug("Could not find operation id for device: {}. Autonomus change",
475 device->address);
476 HandleAutonomusVolumeChange(device, is_volume_change, is_mute_change);
477 return;
478 }
479
480 /* Received notification from the device we do expect */
481 auto it = find(op->devices_.begin(), op->devices_.end(), device->address);
482 op->devices_.erase(it);
483 if (!op->devices_.empty()) {
484 log::debug("wait for more responses for operation_id: {}",
485 op->operation_id_);
486 return;
487 }
488
489 if (op->IsGroupOperation()) {
490 callbacks_->OnGroupVolumeStateChanged(op->group_id_, device->volume,
491 device->mute, op->is_autonomous_);
492 } else {
493 /* op->is_autonomous_ will always be false,
494 since we only make it true for group operations */
495 callbacks_->OnVolumeStateChanged(device->address, device->volume,
496 device->mute, false);
497 }
498
499 ongoing_operations_.erase(op);
500 StartQueueOperation();
501 }
502
OnVolumeControlFlagsChanged(VolumeControlDevice * device,uint16_t len,uint8_t * value)503 void OnVolumeControlFlagsChanged(VolumeControlDevice* device, uint16_t len,
504 uint8_t* value) {
505 device->flags = *value;
506
507 log::info("flags 0x{:x}", device->flags);
508 }
509
OnExtAudioOutStateChanged(VolumeControlDevice * device,VolumeOffset * offset,uint16_t len,uint8_t * value)510 void OnExtAudioOutStateChanged(VolumeControlDevice* device,
511 VolumeOffset* offset, uint16_t len,
512 uint8_t* value) {
513 if (len != 3) {
514 log::info("malformed len=0x{:x}", len);
515 return;
516 }
517
518 uint8_t* pp = value;
519 STREAM_TO_UINT16(offset->offset, pp);
520 STREAM_TO_UINT8(offset->change_counter, pp);
521
522 log::info("{}", base::HexEncode(value, len));
523 log::info("id: 0x{:x} offset: 0x{:x} counter: 0x{:x}", offset->id,
524 offset->offset, offset->change_counter);
525
526 if (!device->IsReady()) {
527 log::info("Device: {} is not ready yet.", device->address);
528 return;
529 }
530
531 callbacks_->OnExtAudioOutVolumeOffsetChanged(device->address, offset->id,
532 offset->offset);
533 }
534
OnExtAudioOutLocationChanged(VolumeControlDevice * device,VolumeOffset * offset,uint16_t len,uint8_t * value)535 void OnExtAudioOutLocationChanged(VolumeControlDevice* device,
536 VolumeOffset* offset, uint16_t len,
537 uint8_t* value) {
538 if (len != 4) {
539 log::info("malformed len=0x{:x}", len);
540 return;
541 }
542
543 uint8_t* pp = value;
544 STREAM_TO_UINT32(offset->location, pp);
545
546 log::info("{}", base::HexEncode(value, len));
547 log::info("id 0x{:x}location 0x{:x}", offset->id, offset->location);
548
549 if (!device->IsReady()) {
550 log::info("Device: {} is not ready yet.", device->address);
551 return;
552 }
553
554 callbacks_->OnExtAudioOutLocationChanged(device->address, offset->id,
555 offset->location);
556 }
557
OnExtAudioOutCPWrite(uint16_t connection_id,tGATT_STATUS status,uint16_t handle,void *)558 void OnExtAudioOutCPWrite(uint16_t connection_id, tGATT_STATUS status,
559 uint16_t handle, void* /*data*/) {
560 VolumeControlDevice* device =
561 volume_control_devices_.FindByConnId(connection_id);
562 if (!device) {
563 log::error("Skipping unknown device disconnect, connection_id=0x{:x}",
564 connection_id);
565 return;
566 }
567
568 log::info("Offset Control Point write response handle0x{:x} status: 0x{:x}",
569 handle, (int)(status));
570
571 /* TODO Design callback API to notify about changes */
572 }
573
OnOffsetOutputDescChanged(VolumeControlDevice * device,VolumeOffset * offset,uint16_t len,uint8_t * value)574 void OnOffsetOutputDescChanged(VolumeControlDevice* device,
575 VolumeOffset* offset, uint16_t len,
576 uint8_t* value) {
577 std::string description = std::string(value, value + len);
578 if (!base::IsStringUTF8(description)) description = "<invalid utf8 string>";
579
580 log::info("{}", description);
581
582 if (!device->IsReady()) {
583 log::info("Device: {} is not ready yet.", device->address);
584 return;
585 }
586
587 callbacks_->OnExtAudioOutDescriptionChanged(device->address, offset->id,
588 std::move(description));
589 }
590
OnGattWriteCcc(uint16_t connection_id,tGATT_STATUS status,uint16_t handle,uint16_t len,const uint8_t * value,void *)591 void OnGattWriteCcc(uint16_t connection_id, tGATT_STATUS status,
592 uint16_t handle, uint16_t len, const uint8_t* value,
593 void* /*data*/) {
594 VolumeControlDevice* device =
595 volume_control_devices_.FindByConnId(connection_id);
596 if (!device) {
597 log::info("unknown connection_id=0x{:x}", connection_id);
598 BtaGattQueue::Clean(connection_id);
599 return;
600 }
601
602 if (status != GATT_SUCCESS) {
603 if (status == GATT_DATABASE_OUT_OF_SYNC) {
604 log::info("Database out of sync for {}, conn_id: 0x{:04x}",
605 device->address, connection_id);
606 ClearDeviceInformationAndStartSearch(device);
607 } else {
608 log::error(
609 "Failed to register for notification: 0x{:04x}, status 0x{:02x}",
610 handle, status);
611 device_cleanup_helper(device, true);
612 }
613 return;
614 }
615
616 log::info("Successfully registered on ccc: 0x{:04x}, device: {}", handle,
617 device->address);
618
619 verify_device_ready(device, handle);
620 }
621
OnGattWriteCccStatic(uint16_t connection_id,tGATT_STATUS status,uint16_t handle,uint16_t len,const uint8_t * value,void * data)622 static void OnGattWriteCccStatic(uint16_t connection_id, tGATT_STATUS status,
623 uint16_t handle, uint16_t len,
624 const uint8_t* value, void* data) {
625 if (!instance) {
626 log::error("No instance={}", handle);
627 return;
628 }
629
630 instance->OnGattWriteCcc(connection_id, status, handle, len, value, data);
631 }
632
Dump(int fd)633 void Dump(int fd) {
634 dprintf(fd, "APP ID: %d\n", gatt_if_);
635 volume_control_devices_.DebugDump(fd);
636 }
637
Disconnect(const RawAddress & address)638 void Disconnect(const RawAddress& address) override {
639 log::info("{}", address);
640
641 VolumeControlDevice* device =
642 volume_control_devices_.FindByAddress(address);
643 if (!device) {
644 log::warn("Device not connected to profile {}", address);
645 callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, address);
646 return;
647 }
648
649 log::info("GAP_EVT_CONN_CLOSED: {}", device->address);
650 device->connecting_actively = false;
651 device_cleanup_helper(device, true);
652 }
653
Remove(const RawAddress & address)654 void Remove(const RawAddress& address) override {
655 log::info("{}", address);
656
657 /* Removes all registrations for connection. */
658 BTA_GATTC_CancelOpen(gatt_if_, address, false);
659
660 Disconnect(address);
661 volume_control_devices_.Remove(address);
662 }
663
OnGattDisconnected(uint16_t connection_id,tGATT_IF,RawAddress remote_bda,tGATT_DISCONN_REASON reason)664 void OnGattDisconnected(uint16_t connection_id, tGATT_IF /*client_if*/,
665 RawAddress remote_bda, tGATT_DISCONN_REASON reason) {
666 VolumeControlDevice* device =
667 volume_control_devices_.FindByConnId(connection_id);
668 if (!device) {
669 log::error("Skipping unknown device disconnect, connection_id=0x{:x}",
670 connection_id);
671 return;
672 }
673
674 if (!device->IsConnected()) {
675 log::error(
676 "Skipping disconnect of the already disconnected device, "
677 "connection_id=0x{:x}",
678 connection_id);
679 return;
680 }
681
682 log::info("{}", remote_bda);
683
684 bool notify = device->IsReady() || device->connecting_actively;
685 device_cleanup_helper(device, notify);
686
687 if (reason != GATT_CONN_TERMINATE_LOCAL_HOST &&
688 device->connecting_actively) {
689 StartOpportunisticConnect(remote_bda);
690 }
691 }
692
RemoveDeviceFromOperationList(const RawAddress & addr)693 void RemoveDeviceFromOperationList(const RawAddress& addr) {
694 if (ongoing_operations_.empty()) {
695 return;
696 }
697
698 for (auto& op : ongoing_operations_) {
699 auto it = find(op.devices_.begin(), op.devices_.end(), addr);
700 if (it == op.devices_.end()) {
701 continue;
702 }
703 op.devices_.erase(it);
704 }
705
706 // Remove operations with no devices
707 ongoing_operations_.erase(
708 std::remove_if(ongoing_operations_.begin(), ongoing_operations_.end(),
709 [](auto& op) { return op.devices_.empty(); }),
710 ongoing_operations_.end());
711 }
712
RemoveDeviceFromOperationList(const RawAddress & addr,int operation_id)713 void RemoveDeviceFromOperationList(const RawAddress& addr, int operation_id) {
714 auto op = find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
715 [operation_id](auto& operation) {
716 return operation.operation_id_ == operation_id;
717 });
718
719 if (op == ongoing_operations_.end()) {
720 log::error("Could not find operation id: {}", operation_id);
721 return;
722 }
723
724 auto it = find(op->devices_.begin(), op->devices_.end(), addr);
725 if (it != op->devices_.end()) {
726 op->devices_.erase(it);
727 if (op->devices_.empty()) {
728 ongoing_operations_.erase(op);
729 StartQueueOperation();
730 }
731 return;
732 }
733 }
734
RemovePendingVolumeControlOperations(std::vector<RawAddress> & devices,int group_id)735 void RemovePendingVolumeControlOperations(std::vector<RawAddress>& devices,
736 int group_id) {
737 log::debug("");
738 for (auto op = ongoing_operations_.begin();
739 op != ongoing_operations_.end();) {
740 // We only remove operations that don't affect the mute field.
741 if (op->IsStarted() ||
742 (op->opcode_ != kControlPointOpcodeSetAbsoluteVolume &&
743 op->opcode_ != kControlPointOpcodeVolumeUp &&
744 op->opcode_ != kControlPointOpcodeVolumeDown)) {
745 op++;
746 continue;
747 }
748 if (group_id != bluetooth::groups::kGroupUnknown &&
749 op->group_id_ == group_id) {
750 log::debug("Removing operation {}", op->operation_id_);
751 op = ongoing_operations_.erase(op);
752 continue;
753 }
754 for (auto const& addr : devices) {
755 auto it = find(op->devices_.begin(), op->devices_.end(), addr);
756 if (it != op->devices_.end()) {
757 log::debug("Removing {} from operation", *it);
758 op->devices_.erase(it);
759 }
760 }
761 if (op->devices_.empty()) {
762 op = ongoing_operations_.erase(op);
763 log::debug("Removing operation {}", op->operation_id_);
764 } else {
765 op++;
766 }
767 }
768 }
769
OnWriteControlResponse(uint16_t connection_id,tGATT_STATUS status,uint16_t handle,void * data)770 void OnWriteControlResponse(uint16_t connection_id, tGATT_STATUS status,
771 uint16_t handle, void* data) {
772 VolumeControlDevice* device =
773 volume_control_devices_.FindByConnId(connection_id);
774 if (!device) {
775 log::error("Skipping unknown device disconnect, connection_id=0x{:x}",
776 connection_id);
777 return;
778 }
779
780 log::info("Write response handle: 0x{:x} status: 0x{:x}", handle,
781 (int)(status));
782
783 if (status == GATT_SUCCESS) return;
784
785 /* In case of error, remove device from the tracking operation list */
786 RemoveDeviceFromOperationList(device->address, PTR_TO_INT(data));
787
788 if (status == GATT_DATABASE_OUT_OF_SYNC) {
789 log::info("Database out of sync for {}", device->address);
790 ClearDeviceInformationAndStartSearch(device);
791 }
792 }
793
operation_timeout_callback(void * data)794 static void operation_timeout_callback(void* data) {
795 if (!instance) {
796 log::warn("There is no instance.");
797 return;
798 }
799 instance->OperationMonitorTimeoutFired(PTR_TO_INT(data));
800 }
801
OperationMonitorTimeoutFired(int operation_id)802 void OperationMonitorTimeoutFired(int operation_id) {
803 auto op = find_if(
804 ongoing_operations_.begin(), ongoing_operations_.end(),
805 [operation_id](auto& it) { return it.operation_id_ == operation_id; });
806
807 if (op == ongoing_operations_.end()) {
808 log::error("Could not find operation_id: {}", operation_id);
809 return;
810 }
811
812 log::warn("Operation {} is taking too long for devices:", operation_id);
813 for (const auto& addr : op->devices_) {
814 log::warn("{},", addr);
815 }
816 alarm_set_on_mloop(op->operation_timeout_, kOperationMonitorTimeoutMs,
817 operation_timeout_callback, INT_TO_PTR(operation_id));
818 }
819
StartQueueOperation(void)820 void StartQueueOperation(void) {
821 log::info("");
822 if (ongoing_operations_.empty()) {
823 return;
824 };
825
826 auto op = &ongoing_operations_.front();
827
828 log::info("Current operation_id: {}", op->operation_id_);
829
830 if (op->IsStarted()) {
831 log::info("Operation {} is started, wait until it is complete",
832 op->operation_id_);
833 return;
834 }
835
836 op->Start();
837
838 alarm_set_on_mloop(op->operation_timeout_, kOperationMonitorTimeoutMs,
839 operation_timeout_callback,
840 INT_TO_PTR(op->operation_id_));
841 devices_control_point_helper(
842 op->devices_, op->opcode_,
843 op->arguments_.size() == 0 ? nullptr : &(op->arguments_),
844 op->operation_id_);
845 }
846
PrepareVolumeControlOperation(std::vector<RawAddress> devices,int group_id,bool is_autonomous,uint8_t opcode,std::vector<uint8_t> & arguments)847 void PrepareVolumeControlOperation(std::vector<RawAddress> devices,
848 int group_id, bool is_autonomous,
849 uint8_t opcode,
850 std::vector<uint8_t>& arguments) {
851 log::debug(
852 "num of devices: {}, group_id: {}, is_autonomous: {} opcode: {}, arg "
853 "size: {}",
854 devices.size(), group_id, is_autonomous, opcode, arguments.size());
855
856 if (std::find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
857 [opcode, &devices, &arguments](const VolumeOperation& op) {
858 if (op.opcode_ != opcode) return false;
859 if (!std::equal(op.arguments_.begin(),
860 op.arguments_.end(), arguments.begin()))
861 return false;
862 // Filter out all devices which have the exact operation
863 // already scheduled
864 devices.erase(
865 std::remove_if(devices.begin(), devices.end(),
866 [&op](auto d) {
867 return find(op.devices_.begin(),
868 op.devices_.end(),
869 d) != op.devices_.end();
870 }),
871 devices.end());
872 return devices.empty();
873 }) == ongoing_operations_.end()) {
874 ongoing_operations_.emplace_back(latest_operation_id_++, group_id,
875 is_autonomous, opcode, arguments,
876 devices);
877 }
878 }
879
MuteUnmute(std::variant<RawAddress,int> addr_or_group_id,bool mute)880 void MuteUnmute(std::variant<RawAddress, int> addr_or_group_id, bool mute) {
881 std::vector<uint8_t> arg;
882
883 uint8_t opcode = mute ? kControlPointOpcodeMute : kControlPointOpcodeUnmute;
884
885 if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
886 VolumeControlDevice* dev = volume_control_devices_.FindByAddress(
887 std::get<RawAddress>(addr_or_group_id));
888 if (dev != nullptr) {
889 log::debug("Address: {}: isReady: {}", dev->address, dev->IsReady());
890 if (dev->IsReady() && (dev->mute != mute)) {
891 std::vector<RawAddress> devices = {dev->address};
892 PrepareVolumeControlOperation(
893 devices, bluetooth::groups::kGroupUnknown, false, opcode, arg);
894 }
895 }
896 } else {
897 /* Handle group change */
898 auto group_id = std::get<int>(addr_or_group_id);
899 log::debug("group: {}", group_id);
900 auto csis_api = CsisClient::Get();
901 if (!csis_api) {
902 log::error("Csis is not there");
903 return;
904 }
905
906 auto devices = csis_api->GetDeviceList(group_id);
907 if (devices.empty()) {
908 log::error("group id: {} has no devices", group_id);
909 return;
910 }
911
912 bool muteNotChanged = false;
913 bool deviceNotReady = false;
914
915 for (auto it = devices.begin(); it != devices.end();) {
916 auto dev = volume_control_devices_.FindByAddress(*it);
917 if (!dev) {
918 it = devices.erase(it);
919 continue;
920 }
921
922 if (!dev->IsReady() || (dev->mute == mute)) {
923 it = devices.erase(it);
924 muteNotChanged =
925 muteNotChanged ? muteNotChanged : (dev->mute == mute);
926 deviceNotReady = deviceNotReady ? deviceNotReady : !dev->IsReady();
927 continue;
928 }
929 it++;
930 }
931
932 if (devices.empty()) {
933 log::debug(
934 "No need to update mute for group id: {} . muteNotChanged: {}, "
935 "deviceNotReady: {}",
936 group_id, muteNotChanged, deviceNotReady);
937 return;
938 }
939
940 PrepareVolumeControlOperation(devices, group_id, false, opcode, arg);
941 }
942
943 StartQueueOperation();
944 }
945
Mute(std::variant<RawAddress,int> addr_or_group_id)946 void Mute(std::variant<RawAddress, int> addr_or_group_id) override {
947 log::debug("");
948 MuteUnmute(addr_or_group_id, true /* mute */);
949 }
950
UnMute(std::variant<RawAddress,int> addr_or_group_id)951 void UnMute(std::variant<RawAddress, int> addr_or_group_id) override {
952 log::debug("");
953 MuteUnmute(addr_or_group_id, false /* mute */);
954 }
955
SetVolume(std::variant<RawAddress,int> addr_or_group_id,uint8_t volume)956 void SetVolume(std::variant<RawAddress, int> addr_or_group_id,
957 uint8_t volume) override {
958 std::vector<uint8_t> arg({volume});
959 uint8_t opcode = kControlPointOpcodeSetAbsoluteVolume;
960
961 if (std::holds_alternative<RawAddress>(addr_or_group_id)) {
962 log::debug("Address: {}:", std::get<RawAddress>(addr_or_group_id));
963 VolumeControlDevice* dev = volume_control_devices_.FindByAddress(
964 std::get<RawAddress>(addr_or_group_id));
965 if (dev != nullptr) {
966 log::debug("Address: {}: isReady: {}", dev->address, dev->IsReady());
967 if (dev->IsReady() && (dev->volume != volume)) {
968 std::vector<RawAddress> devices = {dev->address};
969 RemovePendingVolumeControlOperations(
970 devices, bluetooth::groups::kGroupUnknown);
971 PrepareVolumeControlOperation(
972 devices, bluetooth::groups::kGroupUnknown, false, opcode, arg);
973 }
974 }
975 } else {
976 /* Handle group change */
977 auto group_id = std::get<int>(addr_or_group_id);
978 log::debug("group_id: {}, vol: {}", group_id, volume);
979 auto csis_api = CsisClient::Get();
980 if (!csis_api) {
981 log::error("Csis is not there");
982 return;
983 }
984
985 auto devices = csis_api->GetDeviceList(group_id);
986 if (devices.empty()) {
987 log::error("group id: {} has no devices", group_id);
988 return;
989 }
990
991 bool volumeNotChanged = false;
992 bool deviceNotReady = false;
993
994 for (auto it = devices.begin(); it != devices.end();) {
995 auto dev = volume_control_devices_.FindByAddress(*it);
996 if (!dev) {
997 it = devices.erase(it);
998 continue;
999 }
1000
1001 if (!dev->IsReady() || (dev->volume == volume)) {
1002 it = devices.erase(it);
1003 volumeNotChanged =
1004 volumeNotChanged ? volumeNotChanged : (dev->volume == volume);
1005 deviceNotReady = deviceNotReady ? deviceNotReady : !dev->IsReady();
1006 continue;
1007 }
1008
1009 it++;
1010 }
1011
1012 if (devices.empty()) {
1013 log::debug(
1014 "No need to update volume for group id: {} . volumeNotChanged: {}, "
1015 "deviceNotReady: {}",
1016 group_id, volumeNotChanged, deviceNotReady);
1017 return;
1018 }
1019
1020 RemovePendingVolumeControlOperations(devices, group_id);
1021 PrepareVolumeControlOperation(devices, group_id, false, opcode, arg);
1022 }
1023
1024 StartQueueOperation();
1025 }
1026
1027 /* Methods to operate on Volume Control Offset Service (VOCS) */
GetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id)1028 void GetExtAudioOutVolumeOffset(const RawAddress& address,
1029 uint8_t ext_output_id) override {
1030 VolumeControlDevice* device =
1031 volume_control_devices_.FindByAddress(address);
1032 if (!device) {
1033 log::error("no such device!");
1034 return;
1035 }
1036
1037 device->GetExtAudioOutVolumeOffset(ext_output_id, chrc_read_callback_static,
1038 nullptr);
1039 }
1040
SetExtAudioOutVolumeOffset(const RawAddress & address,uint8_t ext_output_id,int16_t offset_val)1041 void SetExtAudioOutVolumeOffset(const RawAddress& address,
1042 uint8_t ext_output_id,
1043 int16_t offset_val) override {
1044 std::vector<uint8_t> arg(2);
1045 uint8_t* ptr = arg.data();
1046 UINT16_TO_STREAM(ptr, offset_val);
1047 ext_audio_out_control_point_helper(
1048 address, ext_output_id, kVolumeOffsetControlPointOpcodeSet, &arg);
1049 }
1050
GetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id)1051 void GetExtAudioOutLocation(const RawAddress& address,
1052 uint8_t ext_output_id) override {
1053 VolumeControlDevice* device =
1054 volume_control_devices_.FindByAddress(address);
1055 if (!device) {
1056 log::error("no such device!");
1057 return;
1058 }
1059
1060 device->GetExtAudioOutLocation(ext_output_id, chrc_read_callback_static,
1061 nullptr);
1062 }
1063
SetExtAudioOutLocation(const RawAddress & address,uint8_t ext_output_id,uint32_t location)1064 void SetExtAudioOutLocation(const RawAddress& address, uint8_t ext_output_id,
1065 uint32_t location) override {
1066 VolumeControlDevice* device =
1067 volume_control_devices_.FindByAddress(address);
1068 if (!device) {
1069 log::error("no such device!");
1070 return;
1071 }
1072
1073 device->SetExtAudioOutLocation(ext_output_id, location);
1074 }
1075
GetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id)1076 void GetExtAudioOutDescription(const RawAddress& address,
1077 uint8_t ext_output_id) override {
1078 VolumeControlDevice* device =
1079 volume_control_devices_.FindByAddress(address);
1080 if (!device) {
1081 log::error("no such device!");
1082 return;
1083 }
1084
1085 device->GetExtAudioOutDescription(ext_output_id, chrc_read_callback_static,
1086 nullptr);
1087 }
1088
SetExtAudioOutDescription(const RawAddress & address,uint8_t ext_output_id,std::string descr)1089 void SetExtAudioOutDescription(const RawAddress& address,
1090 uint8_t ext_output_id,
1091 std::string descr) override {
1092 VolumeControlDevice* device =
1093 volume_control_devices_.FindByAddress(address);
1094 if (!device) {
1095 log::error("no such device!");
1096 return;
1097 }
1098
1099 device->SetExtAudioOutDescription(ext_output_id, descr);
1100 }
1101
CleanUp()1102 void CleanUp() {
1103 log::info("");
1104 volume_control_devices_.Disconnect(gatt_if_);
1105 volume_control_devices_.Clear();
1106 ongoing_operations_.clear();
1107 BTA_GATTC_AppDeregister(gatt_if_);
1108 }
1109
1110 private:
1111 tGATT_IF gatt_if_;
1112 bluetooth::vc::VolumeControlCallbacks* callbacks_;
1113 VolumeControlDevices volume_control_devices_;
1114
1115 /* Used to track volume control operations */
1116 std::list<VolumeOperation> ongoing_operations_;
1117 int latest_operation_id_;
1118
1119 static constexpr uint64_t kOperationMonitorTimeoutMs = 3000;
1120
verify_device_ready(VolumeControlDevice * device,uint16_t handle)1121 void verify_device_ready(VolumeControlDevice* device, uint16_t handle) {
1122 if (device->IsReady()) return;
1123
1124 // VerifyReady sets the device_ready flag if all remaining GATT operations
1125 // are completed
1126 if (device->VerifyReady(handle)) {
1127 log::info("Outstanding reads completed.");
1128
1129 callbacks_->OnDeviceAvailable(device->address,
1130 device->audio_offsets.Size());
1131 callbacks_->OnConnectionState(ConnectionState::CONNECTED,
1132 device->address);
1133
1134 // once profile connected we can notify current states
1135 callbacks_->OnVolumeStateChanged(device->address, device->volume,
1136 device->mute, false);
1137
1138 for (auto const& offset : device->audio_offsets.volume_offsets) {
1139 callbacks_->OnExtAudioOutVolumeOffsetChanged(device->address, offset.id,
1140 offset.offset);
1141 }
1142
1143 device->EnqueueRemainingRequests(gatt_if_, chrc_read_callback_static,
1144 OnGattWriteCccStatic);
1145 }
1146 }
1147
device_cleanup_helper(VolumeControlDevice * device,bool notify)1148 void device_cleanup_helper(VolumeControlDevice* device, bool notify) {
1149 device->Disconnect(gatt_if_);
1150
1151 RemoveDeviceFromOperationList(device->address);
1152
1153 if (notify)
1154 callbacks_->OnConnectionState(ConnectionState::DISCONNECTED,
1155 device->address);
1156 }
1157
devices_control_point_helper(std::vector<RawAddress> & devices,uint8_t opcode,const std::vector<uint8_t> * arg,int operation_id=-1)1158 void devices_control_point_helper(std::vector<RawAddress>& devices,
1159 uint8_t opcode,
1160 const std::vector<uint8_t>* arg,
1161 int operation_id = -1) {
1162 volume_control_devices_.ControlPointOperation(
1163 devices, opcode, arg,
1164 [](uint16_t connection_id, tGATT_STATUS status, uint16_t handle,
1165 uint16_t len, const uint8_t* value, void* data) {
1166 if (instance)
1167 instance->OnWriteControlResponse(connection_id, status, handle,
1168 data);
1169 },
1170 INT_TO_PTR(operation_id));
1171 }
1172
ext_audio_out_control_point_helper(const RawAddress & address,uint8_t ext_output_id,uint8_t opcode,const std::vector<uint8_t> * arg)1173 void ext_audio_out_control_point_helper(const RawAddress& address,
1174 uint8_t ext_output_id, uint8_t opcode,
1175 const std::vector<uint8_t>* arg) {
1176 log::info("{} id=0x{:x} op=0x{:x}", address, ext_output_id, opcode);
1177 VolumeControlDevice* device =
1178 volume_control_devices_.FindByAddress(address);
1179 if (!device) {
1180 log::error("no such device!");
1181 return;
1182 }
1183 device->ExtAudioOutControlPointOperation(
1184 ext_output_id, opcode, arg,
1185 [](uint16_t connection_id, tGATT_STATUS status, uint16_t handle,
1186 uint16_t len, const uint8_t* value, void* data) {
1187 if (instance)
1188 instance->OnExtAudioOutCPWrite(connection_id, status, handle, data);
1189 },
1190 nullptr);
1191 }
1192
gattc_callback(tBTA_GATTC_EVT event,tBTA_GATTC * p_data)1193 void gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
1194 log::info("event = {}", static_cast<int>(event));
1195
1196 if (p_data == nullptr) return;
1197
1198 switch (event) {
1199 case BTA_GATTC_OPEN_EVT: {
1200 tBTA_GATTC_OPEN& o = p_data->open;
1201 OnGattConnected(o.status, o.conn_id, o.client_if, o.remote_bda,
1202 o.transport, o.mtu);
1203
1204 } break;
1205
1206 case BTA_GATTC_CLOSE_EVT: {
1207 tBTA_GATTC_CLOSE& c = p_data->close;
1208 OnGattDisconnected(c.conn_id, c.client_if, c.remote_bda, c.reason);
1209 } break;
1210
1211 case BTA_GATTC_SEARCH_CMPL_EVT:
1212 OnServiceSearchComplete(p_data->search_cmpl.conn_id,
1213 p_data->search_cmpl.status);
1214 break;
1215
1216 case BTA_GATTC_NOTIF_EVT: {
1217 tBTA_GATTC_NOTIFY& n = p_data->notify;
1218 if (!n.is_notify || n.len > GATT_MAX_ATTR_LEN) {
1219 log::error("rejected BTA_GATTC_NOTIF_EVT. is_notify={}, len={}",
1220 n.is_notify, static_cast<int>(n.len));
1221 break;
1222 }
1223 OnNotificationEvent(n.conn_id, n.handle, n.len, n.value);
1224 } break;
1225
1226 case BTA_GATTC_ENC_CMPL_CB_EVT: {
1227 uint8_t encryption_status;
1228 if (BTM_IsEncrypted(p_data->enc_cmpl.remote_bda, BT_TRANSPORT_LE)) {
1229 encryption_status = BTM_SUCCESS;
1230 } else {
1231 encryption_status = BTM_FAILED_ON_SECURITY;
1232 }
1233 OnEncryptionComplete(p_data->enc_cmpl.remote_bda, encryption_status);
1234 } break;
1235
1236 case BTA_GATTC_SRVC_CHG_EVT:
1237 OnServiceChangeEvent(p_data->remote_bda);
1238 break;
1239
1240 case BTA_GATTC_SRVC_DISC_DONE_EVT:
1241 OnServiceDiscDoneEvent(p_data->remote_bda);
1242 break;
1243
1244 default:
1245 break;
1246 }
1247 }
1248
gattc_callback_static(tBTA_GATTC_EVT event,tBTA_GATTC * p_data)1249 static void gattc_callback_static(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
1250 if (instance) instance->gattc_callback(event, p_data);
1251 }
1252
chrc_read_callback_static(uint16_t conn_id,tGATT_STATUS status,uint16_t handle,uint16_t len,uint8_t * value,void * data)1253 static void chrc_read_callback_static(uint16_t conn_id, tGATT_STATUS status,
1254 uint16_t handle, uint16_t len,
1255 uint8_t* value, void* data) {
1256 if (instance)
1257 instance->OnCharacteristicValueChanged(conn_id, status, handle, len,
1258 value, data, false);
1259 }
1260 };
1261 } // namespace
1262
Initialize(bluetooth::vc::VolumeControlCallbacks * callbacks,const base::Closure & initCb)1263 void VolumeControl::Initialize(bluetooth::vc::VolumeControlCallbacks* callbacks,
1264 const base::Closure& initCb) {
1265 std::scoped_lock<std::mutex> lock(instance_mutex);
1266 if (instance) {
1267 log::error("Already initialized!");
1268 return;
1269 }
1270
1271 instance = new VolumeControlImpl(callbacks, initCb);
1272 }
1273
IsVolumeControlRunning()1274 bool VolumeControl::IsVolumeControlRunning() { return instance; }
1275
Get(void)1276 VolumeControl* VolumeControl::Get(void) {
1277 log::assert_that(instance != nullptr, "assert failed: instance != nullptr");
1278 return instance;
1279 };
1280
AddFromStorage(const RawAddress & address)1281 void VolumeControl::AddFromStorage(const RawAddress& address) {
1282 if (!instance) {
1283 log::error("Not initialized yet");
1284 return;
1285 }
1286
1287 instance->AddFromStorage(address);
1288 };
1289
CleanUp()1290 void VolumeControl::CleanUp() {
1291 std::scoped_lock<std::mutex> lock(instance_mutex);
1292 if (!instance) {
1293 log::error("Not initialized!");
1294 return;
1295 }
1296
1297 VolumeControlImpl* ptr = instance;
1298 instance = nullptr;
1299
1300 ptr->CleanUp();
1301
1302 delete ptr;
1303 };
1304
DebugDump(int fd)1305 void VolumeControl::DebugDump(int fd) {
1306 std::scoped_lock<std::mutex> lock(instance_mutex);
1307 dprintf(fd, "Volume Control Manager:\n");
1308 if (instance) instance->Dump(fd);
1309 dprintf(fd, "\n");
1310 }
1311