Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cbb53219 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Automerger Merge Worker
Browse files

Merge changes I426f2a93,If860e8ae,Ic05ca817 am: f1790e9c am: 62e0f05a am:...

Merge changes I426f2a93,If860e8ae,Ic05ca817 am: f1790e9c am: 62e0f05a am: 15f6a24c am: 29b14d40

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/2200598



Change-Id: Ie998bece52ffa9b8c20890d156fbbe0217acfdee
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 12b6a0b3 29b14d40
Loading
Loading
Loading
Loading
+76 −41
Original line number Diff line number Diff line
@@ -996,6 +996,13 @@ public class LeAudioService extends ProfileService {
            return;
        }
        mLeAudioNativeInterface.groupSetActive(groupId);
        if (groupId == LE_AUDIO_GROUP_ID_INVALID) {
            /* Native will clear its states and send us group Inactive.
             * However we would like to notify audio framework that LeAudio is not
             * active anymore and does not want to get more audio data.
             */
            handleGroupTransitToInactive(currentlyActiveGroupId);
        }
    }

    /**
@@ -1112,20 +1119,59 @@ public class LeAudioService extends ProfileService {
    }

    private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) {
        synchronized (mGroupLock) {
            if (DBG) {
            Log.d(TAG, " lost dev: " + descriptor.mLostLeadDeviceWhileStreaming);
                Log.d(TAG, "Clearing lost dev: " + descriptor.mLostLeadDeviceWhileStreaming);
            }

        LeAudioStateMachine sm = mStateMachines.get(descriptor.mLostLeadDeviceWhileStreaming);
            LeAudioStateMachine sm =
                    mStateMachines.get(descriptor.mLostLeadDeviceWhileStreaming);
            if (sm != null) {
                LeAudioStackEvent stackEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
                        new LeAudioStackEvent(
                                LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
                stackEvent.device = descriptor.mLostLeadDeviceWhileStreaming;
                stackEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
                sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent);
            }
            descriptor.mLostLeadDeviceWhileStreaming = null;
        }
    }

    private void handleGroupTransitToActive(int groupId) {
        synchronized (mGroupLock) {
            LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
            if (descriptor == null || descriptor.mIsActive) {
                Log.e(TAG, "no descriptors for group: " + groupId + " or group already active");
                return;
            }

            descriptor.mIsActive = updateActiveDevices(groupId,
                            ACTIVE_CONTEXTS_NONE, descriptor.mActiveContexts, true);

            if (descriptor.mIsActive) {
                notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
            }
        }
    }

    private void handleGroupTransitToInactive(int groupId) {
        synchronized (mGroupLock) {
            LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
            if (descriptor == null || !descriptor.mIsActive) {
                Log.e(TAG, "no descriptors for group: " + groupId + " or group already inactive");
                return;
            }

            descriptor.mIsActive = false;
            updateActiveDevices(groupId, descriptor.mActiveContexts,
                    ACTIVE_CONTEXTS_NONE, descriptor.mIsActive);
            /* Clear lost devices */
            if (DBG) Log.d(TAG, "Clear for group: " + groupId);
            clearLostDevicesWhileStreaming(descriptor);
            notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
        }
    }

    // Suppressed since this is part of a local process
    @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1153,10 +1199,14 @@ public class LeAudioService extends ProfileService {
                        switch (stackEvent.valueInt1) {
                            case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTING:
                            case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED:
                                boolean disconnectDueToUnbond =
                                        (BluetoothDevice.BOND_NONE
                                                == mAdapterService.getBondState(device));
                                if (descriptor != null && (Objects.equals(device,
                                        mActiveAudioOutDevice)
                                        || Objects.equals(device, mActiveAudioInDevice))
                                        && (getConnectedPeerDevices(groupId).size() > 1)) {
                                        && (getConnectedPeerDevices(groupId).size() > 1)
                                        && !disconnectDueToUnbond) {

                                    if (DBG) Log.d(TAG, "Adding to lost devices : " + device);
                                    descriptor.mLostLeadDeviceWhileStreaming = device;
@@ -1278,47 +1328,19 @@ public class LeAudioService extends ProfileService {
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) {
            int groupId = stackEvent.valueInt1;
            int groupStatus = stackEvent.valueInt2;
            boolean notifyGroupStatus = false;

            switch (groupStatus) {
                case LeAudioStackEvent.GROUP_STATUS_ACTIVE: {
                    LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
                    if (descriptor != null) {
                        if (!descriptor.mIsActive) {
                            descriptor.mIsActive = updateActiveDevices(groupId,
                                    ACTIVE_CONTEXTS_NONE, descriptor.mActiveContexts, true);
                            notifyGroupStatus = descriptor.mIsActive;
                        }
                    } else {
                        Log.e(TAG, "no descriptors for group: " + groupId);
                    }
                    handleGroupTransitToActive(groupId);
                    break;
                }
                case LeAudioStackEvent.GROUP_STATUS_INACTIVE: {
                    LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
                    if (descriptor != null) {
                        if (descriptor.mIsActive) {
                            descriptor.mIsActive = false;
                            updateActiveDevices(groupId, descriptor.mActiveContexts,
                                    ACTIVE_CONTEXTS_NONE, descriptor.mIsActive);
                            notifyGroupStatus = true;
                            /* Clear lost devices */
                            if (DBG) Log.d(TAG, "Clear for group: " + groupId);
                            clearLostDevicesWhileStreaming(descriptor);
                        }
                    } else {
                        Log.e(TAG, "no descriptors for group: " + groupId);
                    }
                    handleGroupTransitToInactive(groupId);
                    break;
                }
                default:
                    break;
            }

            if (notifyGroupStatus) {
                notifyGroupStatusChanged(groupId, groupStatus);
            }

        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) {
            int broadcastId = stackEvent.valueInt1;
            boolean success = stackEvent.valueBool1;
@@ -1501,6 +1523,8 @@ public class LeAudioService extends ProfileService {
                return;
            }
            if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
                Log.w(TAG, "Device is not disconnected yet.");
                disconnect(device);
                return;
            }
            removeStateMachine(device);
@@ -1839,6 +1863,10 @@ public class LeAudioService extends ProfileService {

    private void handleGroupNodeAdded(BluetoothDevice device, int groupId) {
        synchronized (mGroupLock) {
            if (DBG) {
                Log.d(TAG, "Device " + device + " added to group " + groupId);
            }

            mDeviceGroupIdMap.put(device, groupId);
            LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
            if (descriptor == null) {
@@ -1863,7 +1891,14 @@ public class LeAudioService extends ProfileService {
    }

    private void handleGroupNodeRemoved(BluetoothDevice device, int groupId) {
        if (DBG) {
            Log.d(TAG, "Removing device " + device + " grom group " + groupId);
        }

        LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
        if (DBG) {
            Log.d(TAG, "Lost lead device is " + descriptor.mLostLeadDeviceWhileStreaming);
        }
        if (Objects.equals(device, descriptor.mLostLeadDeviceWhileStreaming)) {
            clearLostDevicesWhileStreaming(descriptor);
        }
+1 −0
Original line number Diff line number Diff line
@@ -1322,6 +1322,7 @@ public class LeAudioServiceTest {
        verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(),
                        any(BluetoothProfileConnectionInfo.class));

        doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService).getBondState(leadDevice);
        verifyActiveDeviceStateIntent(TIMEOUT_MS, leadDevice);
        injectNoVerifyDeviceDisconnected(leadDevice);

+4 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ cc_library_static {
        "le_audio/devices.cc",
        "le_audio/hal_verifier.cc",
        "le_audio/state_machine.cc",
        "le_audio/storage_helper.cc",
        "le_audio/client_parser.cc",
        "le_audio/client_audio.cc",
        "le_audio/le_audio_utils.cc",
@@ -572,6 +573,8 @@ cc_test {
        "test/common/mock_controller.cc",
        "le_audio/state_machine.cc",
        "le_audio/state_machine_test.cc",
        "le_audio/storage_helper.cc",
        "le_audio/storage_helper_test.cc",
        "le_audio/mock_codec_manager.cc",
    ],
    data: [
@@ -638,6 +641,7 @@ cc_test {
        "le_audio/metrics_collector_test.cc",
        "le_audio/mock_iso_manager.cc",
        "le_audio/mock_state_machine.cc",
        "le_audio/storage_helper.cc",
        "test/common/btm_api_mock.cc",
        "test/common/bta_gatt_api_mock.cc",
        "test/common/bta_gatt_queue_mock.cc",
+16 −1
Original line number Diff line number Diff line
@@ -69,7 +69,22 @@ class LeAudioClient {
  virtual void SetInCall(bool in_call) = 0;

  virtual std::vector<RawAddress> GetGroupDevices(const int group_id) = 0;
  static void AddFromStorage(const RawAddress& addr, bool autoconnect);
  static void AddFromStorage(const RawAddress& addr, bool autoconnect,
                             int sink_audio_location, int source_audio_location,
                             int sink_supported_context_types,
                             int source_supported_context_types,
                             const std::vector<uint8_t>& handles,
                             const std::vector<uint8_t>& sink_pacs,
                             const std::vector<uint8_t>& source_pacs,
                             const std::vector<uint8_t>& ases);
  static bool GetHandlesForStorage(const RawAddress& addr,
                                   std::vector<uint8_t>& out);
  static bool GetSinkPacsForStorage(const RawAddress& addr,
                                    std::vector<uint8_t>& out);
  static bool GetSourcePacsForStorage(const RawAddress& addr,
                                      std::vector<uint8_t>& out);
  static bool GetAsesForStorage(const RawAddress& addr,
                                std::vector<uint8_t>& out);
  static bool IsLeAudioClientRunning();

  static void InitializeAudioSetConfigurationProvider(void);
+183 −13
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@
#include "stack/btm/btm_sec.h"
#include "stack/include/btu.h"  // do_in_main_thread
#include "state_machine.h"
#include "storage_helper.h"

using base::Closure;
using bluetooth::Uuid;
@@ -385,10 +386,14 @@ class LeAudioClientImpl : public LeAudioClient {

  void UpdateContextAndLocations(LeAudioDeviceGroup* group,
                                 LeAudioDevice* leAudioDevice) {
    /* Make sure location and direction are updated for the group. */
    auto location_update = group->ReloadAudioLocations();
    group->ReloadAudioDirections();

    std::optional<AudioContexts> new_group_updated_contexts =
        group->UpdateActiveContextsMap(leAudioDevice->GetAvailableContexts());

    if (new_group_updated_contexts || group->ReloadAudioLocations()) {
    if (new_group_updated_contexts || location_update) {
      callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                              group->snk_audio_locations_.to_ulong(),
                              group->src_audio_locations_.to_ulong(),
@@ -960,15 +965,31 @@ class LeAudioClientImpl : public LeAudioClient {
  }

  /* Restore paired device from storage to recreate groups */
  void AddFromStorage(const RawAddress& address, bool autoconnect) {
  void AddFromStorage(const RawAddress& address, bool autoconnect,
                      int sink_audio_location, int source_audio_location,
                      int sink_supported_context_types,
                      int source_supported_context_types,
                      const std::vector<uint8_t>& handles,
                      const std::vector<uint8_t>& sink_pacs,
                      const std::vector<uint8_t>& source_pacs,
                      const std::vector<uint8_t>& ases) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address);

    LOG(INFO) << __func__ << ", restoring: " << address;
    if (leAudioDevice) {
      LOG_ERROR("Device is already loaded. Nothing to do.");
      return;
    }

    LOG_INFO(
        "restoring: %s, autoconnect %d, sink_audio_location: %d, "
        "source_audio_location: %d, sink_supported_context_types : 0x%04x, "
        "source_supported_context_types 0x%04x ",
        address.ToString().c_str(), autoconnect, sink_audio_location,
        source_audio_location, sink_supported_context_types,
        source_supported_context_types);

    if (!leAudioDevice) {
    leAudioDevices_.Add(address, false);
    leAudioDevice = leAudioDevices_.FindByAddress(address);
    }

    int group_id = DeviceGroups::Get()->GetGroupId(
        address, le_audio::uuid::kCapServiceUuid);
@@ -976,11 +997,71 @@ class LeAudioClientImpl : public LeAudioClient {
      group_add_node(group_id, address);
    }

    leAudioDevice->snk_audio_locations_ = sink_audio_location;
    if (sink_audio_location != 0) {
      leAudioDevice->audio_directions_ |=
          le_audio::types::kLeAudioDirectionSink;
    }

    leAudioDevice->src_audio_locations_ = source_audio_location;
    if (source_audio_location != 0) {
      leAudioDevice->audio_directions_ |=
          le_audio::types::kLeAudioDirectionSource;
    }

    leAudioDevice->SetSupportedContexts(
        (uint16_t)sink_supported_context_types,
        (uint16_t)source_supported_context_types);

    /* Use same as or supported ones for now. */
    leAudioDevice->SetAvailableContexts(
        (uint16_t)sink_supported_context_types,
        (uint16_t)source_supported_context_types);

    if (!DeserializeHandles(leAudioDevice, handles)) {
      LOG_WARN("Could not load Handles");
    }

    if (!DeserializeSinkPacs(leAudioDevice, sink_pacs)) {
      LOG_WARN("Could not load sink pacs");
    }

    if (!DeserializeSourcePacs(leAudioDevice, source_pacs)) {
      LOG_WARN("Could not load source pacs");
    }

    if (!DeserializeAses(leAudioDevice, ases)) {
      LOG_WARN("Could not load ases");
    }

    if (autoconnect) {
      BTA_GATTC_Open(gatt_if_, address, false, false);
    }
  }

  bool GetHandlesForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);
    return SerializeHandles(leAudioDevice, out);
  }

  bool GetSinkPacsForStorage(const RawAddress& addr,
                             std::vector<uint8_t>& out) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);
    return SerializeSinkPacs(leAudioDevice, out);
  }

  bool GetSourcePacsForStorage(const RawAddress& addr,
                               std::vector<uint8_t>& out) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);
    return SerializeSourcePacs(leAudioDevice, out);
  }

  bool GetAsesForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);

    return SerializeAses(leAudioDevice, out);
  }

  void BackgroundConnectIfGroupConnected(LeAudioDevice* leAudioDevice) {
    DLOG(INFO) << __func__ << leAudioDevice->address_;
    auto group = aseGroups_.FindById(leAudioDevice->group_id_);
@@ -1099,7 +1180,7 @@ class LeAudioClientImpl : public LeAudioClient {
   * are dispatched to correct elements e.g. ASEs, PACs, audio locations etc.
   */
  void LeAudioCharValueHandle(uint16_t conn_id, uint16_t hdl, uint16_t len,
                              uint8_t* value) {
                              uint8_t* value, bool notify = false) {
    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByConnId(conn_id);
    struct ase* ase;

@@ -1126,7 +1207,7 @@ class LeAudioClientImpl : public LeAudioClient {
      std::vector<struct le_audio::types::acs_ac_record> pac_recs;

      /* Guard consistency of PAC records structure */
      if (!le_audio::client_parser::pacs::ParsePac(pac_recs, len, value))
      if (!le_audio::client_parser::pacs::ParsePacs(pac_recs, len, value))
        return;

      LOG(INFO) << __func__ << ", Registering sink PACs";
@@ -1147,6 +1228,9 @@ class LeAudioClientImpl : public LeAudioClient {
                                group->src_audio_locations_.to_ulong(),
                                group->GetActiveContexts().to_ulong());
      }
      if (notify) {
        btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
      }
      return;
    }

@@ -1157,7 +1241,7 @@ class LeAudioClientImpl : public LeAudioClient {
      std::vector<struct le_audio::types::acs_ac_record> pac_recs;

      /* Guard consistency of PAC records structure */
      if (!le_audio::client_parser::pacs::ParsePac(pac_recs, len, value))
      if (!le_audio::client_parser::pacs::ParsePacs(pac_recs, len, value))
        return;

      LOG(INFO) << __func__ << ", Registering source PACs";
@@ -1178,6 +1262,10 @@ class LeAudioClientImpl : public LeAudioClient {
                                group->src_audio_locations_.to_ulong(),
                                group->GetActiveContexts().to_ulong());
      }

      if (notify) {
        btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
      }
      return;
    }

@@ -1203,6 +1291,14 @@ class LeAudioClientImpl : public LeAudioClient {
      LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);
      callbacks_->OnSinkAudioLocationAvailable(leAudioDevice->address_,
                                               snk_audio_locations.to_ulong());

      if (notify) {
        btif_storage_set_leaudio_audio_location(
            leAudioDevice->address_,
            leAudioDevice->snk_audio_locations_.to_ulong(),
            leAudioDevice->src_audio_locations_.to_ulong());
      }

      /* Read of source audio locations during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
@@ -1237,6 +1333,14 @@ class LeAudioClientImpl : public LeAudioClient {
      leAudioDevice->src_audio_locations_ = src_audio_locations;

      LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);

      if (notify) {
        btif_storage_set_leaudio_audio_location(
            leAudioDevice->address_,
            leAudioDevice->snk_audio_locations_.to_ulong(),
            leAudioDevice->src_audio_locations_.to_ulong());
      }

      /* Read of source audio locations during initial attribute discovery.
       * Group would be assigned once service search is completed.
       */
@@ -1300,6 +1404,12 @@ class LeAudioClientImpl : public LeAudioClient {
      /* Just store if for now */
      leAudioDevice->SetSupportedContexts(supp_audio_contexts->snk_supp_cont,
                                          supp_audio_contexts->src_supp_cont);

      btif_storage_set_leaudio_supported_context_types(
          leAudioDevice->address_,
          supp_audio_contexts->snk_supp_cont.to_ulong(),
          supp_audio_contexts->src_supp_cont.to_ulong());

    } else if (hdl == leAudioDevice->ctp_hdls_.val_hdl) {
      auto ntf =
          std::make_unique<struct le_audio::client_parser::ascs::ctp_ntf>();
@@ -1913,6 +2023,8 @@ class LeAudioClientImpl : public LeAudioClient {
    }

    leAudioDevice->known_service_handles_ = true;
    btif_storage_leaudio_update_handles_bin(leAudioDevice->address_);

    leAudioDevice->notify_connected_after_read_ = true;

    /* If already known group id */
@@ -3348,6 +3460,16 @@ class LeAudioClientImpl : public LeAudioClient {
      LeAudioDevice* leAudioDevice =
          instance->leAudioDevices_.FindByConnId(conn_id);
      leAudioDevice->notify_connected_after_read_ = false;

      /* Update PACs and ASEs when all is read.*/
      btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
      btif_storage_leaudio_update_ase_bin(leAudioDevice->address_);

      btif_storage_set_leaudio_audio_location(
          leAudioDevice->address_,
          leAudioDevice->snk_audio_locations_.to_ulong(),
          leAudioDevice->src_audio_locations_.to_ulong());

      instance->connectionReady(leAudioDevice);
    }
  }
@@ -3732,7 +3854,7 @@ void le_audio_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
    case BTA_GATTC_NOTIF_EVT:
      instance->LeAudioCharValueHandle(
          p_data->notify.conn_id, p_data->notify.handle, p_data->notify.len,
          static_cast<uint8_t*>(p_data->notify.value));
          static_cast<uint8_t*>(p_data->notify.value), true);

      if (!p_data->notify.is_notify)
        BTA_GATTC_SendIndConfirm(p_data->notify.conn_id, p_data->notify.handle);
@@ -3897,13 +4019,61 @@ DeviceGroupsCallbacksImpl deviceGroupsCallbacksImpl;

}  // namespace

void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect) {
void LeAudioClient::AddFromStorage(
    const RawAddress& addr, bool autoconnect, int sink_audio_location,
    int source_audio_location, int sink_supported_context_types,
    int source_supported_context_types, const std::vector<uint8_t>& handles,
    const std::vector<uint8_t>& sink_pacs,
    const std::vector<uint8_t>& source_pacs, const std::vector<uint8_t>& ases) {
  if (!instance) {
    LOG(ERROR) << "Not initialized yet";
    return;
  }

  instance->AddFromStorage(addr, autoconnect);
  instance->AddFromStorage(addr, autoconnect, sink_audio_location,
                           source_audio_location, sink_supported_context_types,
                           source_supported_context_types, handles, sink_pacs,
                           source_pacs, ases);
}

bool LeAudioClient::GetHandlesForStorage(const RawAddress& addr,
                                         std::vector<uint8_t>& out) {
  if (!instance) {
    LOG_ERROR("Not initialized yet");
    return false;
  }

  return instance->GetHandlesForStorage(addr, out);
}

bool LeAudioClient::GetSinkPacsForStorage(const RawAddress& addr,
                                          std::vector<uint8_t>& out) {
  if (!instance) {
    LOG_ERROR("Not initialized yet");
    return false;
  }

  return instance->GetSinkPacsForStorage(addr, out);
}

bool LeAudioClient::GetSourcePacsForStorage(const RawAddress& addr,
                                            std::vector<uint8_t>& out) {
  if (!instance) {
    LOG_ERROR("Not initialized yet");
    return false;
  }

  return instance->GetSourcePacsForStorage(addr, out);
}

bool LeAudioClient::GetAsesForStorage(const RawAddress& addr,
                                      std::vector<uint8_t>& out) {
  if (!instance) {
    LOG_ERROR("Not initialized yet");
    return false;
  }

  return instance->GetAsesForStorage(addr, out);
}

bool LeAudioClient::IsLeAudioClientRunning(void) { return instance != nullptr; }
Loading