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

Commit bfbc6439 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Make use of leAudioHealthStatus module

Bug: 279233743
Test: atest bluetooth_le_audio_client_test
Test: atest BluetoothInstrumentationTests
Tag: #feature
Change-Id: Id12397823821fe4f6a38f812fe21e263bdc8c8d6
parent 0ab4a727
Loading
Loading
Loading
Loading
+147 −39
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include "gatt/bta_gattc_int.h"
#include "gd/common/strings.h"
#include "internal_include/stack_config.h"
#include "le_audio_health_status.h"
#include "le_audio_set_configuration_provider.h"
#include "le_audio_types.h"
#include "le_audio_utils.h"
@@ -68,6 +69,7 @@ using bluetooth::le_audio::ConnectionState;
using bluetooth::le_audio::GroupNodeStatus;
using bluetooth::le_audio::GroupStatus;
using bluetooth::le_audio::GroupStreamStatus;
using bluetooth::le_audio::LeAudioHealthBasedAction;
using le_audio::CodecManager;
using le_audio::ContentControlIdKeeper;
using le_audio::DeviceConnectState;
@@ -77,6 +79,10 @@ using le_audio::LeAudioDeviceGroup;
using le_audio::LeAudioDeviceGroups;
using le_audio::LeAudioDevices;
using le_audio::LeAudioGroupStateMachine;
using le_audio::LeAudioHealthDeviceStatType;
using le_audio::LeAudioHealthGroupStatType;
using le_audio::LeAudioHealthStatus;
using le_audio::LeAudioRecommendationActionCb;
using le_audio::LeAudioSinkAudioHalClient;
using le_audio::LeAudioSourceAudioHalClient;
using le_audio::types::ase;
@@ -159,6 +165,10 @@ std::ostream& operator<<(std::ostream& os, const AudioState& audio_state) {
namespace {
void le_audio_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);

static void le_audio_health_status_callback(const RawAddress& addr,
                                            int group_id,
                                            LeAudioHealthBasedAction action);

inline uint8_t bits_to_bytes_per_sample(uint8_t bits_per_sample) {
  // 24 bit audio stream is sent as unpacked, each sample takes 4 bytes.
  if (bits_per_sample == 24) return 4;
@@ -263,6 +273,13 @@ class LeAudioClientImpl : public LeAudioClient {
      reconnection_mode_ = BTM_BLE_BKG_CONNECT_ALLOW_LIST;
    }

    if (bluetooth::common::InitFlags::IsLeAudioHealthBasedActionsEnabled()) {
      LOG_INFO("Loading health status module");
      leAudioHealthStatus_ = LeAudioHealthStatus::Get();
      leAudioHealthStatus_->RegisterCallback(
          base::BindRepeating(le_audio_health_status_callback));
    }

    BTA_GATTC_AppRegister(
        le_audio_gattc_callback,
        base::Bind(
@@ -413,6 +430,11 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForDevice(
          address, LeAudioHealthDeviceStatType::VALID_CSIS);
    }

    group_add_node(group_id, address);
  }

@@ -437,6 +459,10 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->RemoveStatistics(address, group->group_id_);
    }

    if (leAudioDevice->HaveActiveAse()) {
      SetDeviceAsRemovePendingAndStopGroup(leAudioDevice);
      return;
@@ -459,6 +485,11 @@ class LeAudioClientImpl : public LeAudioClient {
    bool check_if_recovery_needed =
        group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;

    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForGroup(
          group_id, LeAudioHealthGroupStatType::STREAM_CREATE_SIGNALING_FAILED);
    }

    LOG_ERROR(
        " State not achieved on time for group: group id %d, current state %s, "
        "target state: %s, check_if_recovery_needed: %d",
@@ -2231,9 +2262,9 @@ class LeAudioClientImpl : public LeAudioClient {
    }

    if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) {
      LOG_ERROR("device %s not a valid group member",
                ADDRESS_TO_LOGGABLE_CSTR(address));
      DisconnectDevice(leAudioDevice);
      disconnectInvalidDevice(leAudioDevice,
                              ", device not a valid group member",
                              LeAudioHealthDeviceStatType::INVALID_CSIS);
      return;
    }
  }
@@ -2484,6 +2515,19 @@ class LeAudioClientImpl : public LeAudioClient {
          leAudioDevice->conn_id_,
          &le_audio::uuid::kPublishedAudioCapabilityServiceUuid);
  }

  void disconnectInvalidDevice(LeAudioDevice* leAudioDevice,
                               std::string error_string,
                               LeAudioHealthDeviceStatType stat) {
    LOG_ERROR("%s, %s", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
              error_string.c_str());
    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForDevice(leAudioDevice->address_,
                                                  stat);
    }
    DisconnectDevice(leAudioDevice);
  }

  /* This method is called after connection beginning to identify and initialize
   * a le audio device. Any missing mandatory attribute will result in reverting
   * and cleaning up device.
@@ -2560,9 +2604,9 @@ class LeAudioClientImpl : public LeAudioClient {
    }

    if (!pac_svc || !ase_svc) {
      LOG(ERROR) << "No mandatory le audio services found";

      DisconnectDevice(leAudioDevice);
      disconnectInvalidDevice(
          leAudioDevice, "No mandatory le audio services found (pacs or ascs)",
          LeAudioHealthDeviceStatType::INVALID_DB);
      return;
    }

@@ -2577,15 +2621,17 @@ class LeAudioClientImpl : public LeAudioClient {
        hdl_pair.ccc_hdl = find_ccc_handle(charac);

        if (hdl_pair.ccc_hdl == 0) {
          LOG(ERROR) << __func__ << ", snk pac char doesn't have ccc";

          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", snk pac char doesn't have ccc",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        hdl_pair)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", cound not subscribe for snk pac char",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2607,15 +2653,17 @@ class LeAudioClientImpl : public LeAudioClient {
        hdl_pair.ccc_hdl = find_ccc_handle(charac);

        if (hdl_pair.ccc_hdl == 0) {
          LOG(ERROR) << __func__ << ", src pac char doesn't have ccc";

          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", src pac char doesn't have ccc",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        hdl_pair)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", could not subscribe for src pac char",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2635,16 +2683,17 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->snk_audio_locations_hdls_.ccc_hdl =
            find_ccc_handle(charac);

        if (leAudioDevice->snk_audio_locations_hdls_.ccc_hdl == 0)
          LOG(INFO) << __func__
                    << ", snk audio locations char doesn't have"
                       "ccc";
        if (leAudioDevice->snk_audio_locations_hdls_.ccc_hdl == 0) {
          LOG_INFO(", snk audio locations char doesn't have ccc");
        }

        if (leAudioDevice->snk_audio_locations_hdls_.ccc_hdl != 0 &&
            !subscribe_for_notification(
                conn_id, leAudioDevice->address_,
                leAudioDevice->snk_audio_locations_hdls_)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(
              leAudioDevice, ", could not subscribe for snk locations char",
              LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2662,16 +2711,17 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->src_audio_locations_hdls_.ccc_hdl =
            find_ccc_handle(charac);

        if (leAudioDevice->src_audio_locations_hdls_.ccc_hdl == 0)
          LOG(INFO) << __func__
                    << ", snk audio locations char doesn't have"
                       "ccc";
        if (leAudioDevice->src_audio_locations_hdls_.ccc_hdl == 0) {
          LOG_INFO(", src audio locations char doesn't have ccc");
        }

        if (leAudioDevice->src_audio_locations_hdls_.ccc_hdl != 0 &&
            !subscribe_for_notification(
                conn_id, leAudioDevice->address_,
                leAudioDevice->src_audio_locations_hdls_)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(
              leAudioDevice, ", could not subscribe for src locations char",
              LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2689,15 +2739,17 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->audio_avail_hdls_.ccc_hdl = find_ccc_handle(charac);

        if (leAudioDevice->audio_avail_hdls_.ccc_hdl == 0) {
          LOG(ERROR) << __func__ << ", audio avails char doesn't have ccc";

          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", audio avails char doesn't have ccc",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        leAudioDevice->audio_avail_hdls_)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", could not subscribe for audio avails char",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2714,13 +2766,17 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->audio_supp_cont_hdls_.val_hdl = charac.value_handle;
        leAudioDevice->audio_supp_cont_hdls_.ccc_hdl = find_ccc_handle(charac);

        if (leAudioDevice->audio_supp_cont_hdls_.ccc_hdl == 0)
          LOG(INFO) << __func__ << ", audio avails char doesn't have ccc";
        if (leAudioDevice->audio_supp_cont_hdls_.ccc_hdl == 0) {
          LOG_INFO(", audio avails char doesn't have ccc");
        }

        if (leAudioDevice->audio_supp_cont_hdls_.ccc_hdl != 0 &&
            !subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        leAudioDevice->audio_supp_cont_hdls_)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(
              leAudioDevice,
              ", could not subscribe for audio supported ctx char",
              LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2744,15 +2800,16 @@ class LeAudioClientImpl : public LeAudioClient {
          charac.uuid == le_audio::uuid::kSourceAudioStreamEndpointUuid) {
        uint16_t ccc_handle = find_ccc_handle(charac);
        if (ccc_handle == 0) {
          LOG(ERROR) << __func__ << ", audio avails char doesn't have ccc";

          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice, ", ASE char doesn't have ccc",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }
        struct le_audio::types::hdl_pair hdls(charac.value_handle, ccc_handle);
        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        hdls)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", could not subscribe ASE char",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2775,15 +2832,16 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->ctp_hdls_.ccc_hdl = find_ccc_handle(charac);

        if (leAudioDevice->ctp_hdls_.ccc_hdl == 0) {
          LOG(ERROR) << __func__ << ", ase ctp doesn't have ccc";

          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice, ", ASE ctp doesn't have ccc",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
                                        leAudioDevice->ctp_hdls_)) {
          DisconnectDevice(leAudioDevice);
          disconnectInvalidDevice(leAudioDevice,
                                  ", could not subscribe ASE char",
                                  LeAudioHealthDeviceStatType::INVALID_DB);
          return;
        }

@@ -2816,6 +2874,10 @@ class LeAudioClientImpl : public LeAudioClient {
    btif_storage_leaudio_update_handles_bin(leAudioDevice->address_);

    leAudioDevice->notify_connected_after_read_ = true;
    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForDevice(
          leAudioDevice->address_, LeAudioHealthDeviceStatType::VALID_DB);
    }

    /* If already known group id */
    if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) {
@@ -3658,6 +3720,10 @@ class LeAudioClientImpl : public LeAudioClient {
    aseGroups_.Dump(fd, active_group_id_);
    dprintf(fd, "\n  Not grouped devices:\n");
    leAudioDevices_.Dump(fd, bluetooth::groups::kGroupUnknown);

    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->DebugDump(fd);
    }
  }

  void Cleanup(base::Callback<void()> cleanupCb) {
@@ -3675,6 +3741,10 @@ class LeAudioClientImpl : public LeAudioClient {
    if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);

    std::move(cleanupCb).Run();

    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->Cleanup();
    }
  }

  AudioReconfigurationResult UpdateConfigAndCheckIfReconfigurationIsNeeded(
@@ -4884,6 +4954,22 @@ class LeAudioClientImpl : public LeAudioClient {
    }
  }

  void LeAudioHealthSendRecommendation(const RawAddress& address, int group_id,
                                       LeAudioHealthBasedAction action) {
    LOG_DEBUG("%s, %d, %s", ADDRESS_TO_LOGGABLE_CSTR(address), group_id,
              ToString(action).c_str());

    if (address != RawAddress::kEmpty &&
        leAudioDevices_.FindByAddress(address)) {
      callbacks_->OnHealthBasedRecommendationAction(address, action);
    }

    if (group_id != bluetooth::groups::kGroupUnknown &&
        aseGroups_.FindById(group_id)) {
      callbacks_->OnHealthBasedGroupRecommendationAction(group_id, action);
    }
  }

  void IsoCigEventsCb(uint16_t event_type, void* data) {
    switch (event_type) {
      case bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl: {
@@ -4943,6 +5029,12 @@ class LeAudioClientImpl : public LeAudioClient {
          group->SetTransportLatency(le_audio::types::kLeAudioDirectionSource,
                                     event->trans_lat_stom);

        if (leAudioHealthStatus_ && (event->status != HCI_SUCCESS)) {
          leAudioHealthStatus_->AddStatisticForGroup(
              leAudioDevice->group_id_,
              LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
        }

        groupStateMachine_->ProcessHciNotifCisEstablished(group, leAudioDevice,
                                                          event);
      } break;
@@ -5191,6 +5283,12 @@ class LeAudioClientImpl : public LeAudioClient {

        le_audio::MetricsCollector::Get()->OnStreamStarted(
            active_group_id_, configuration_context_type_);

        if (leAudioHealthStatus_) {
          leAudioHealthStatus_->AddStatisticForGroup(
              group_id, LeAudioHealthGroupStatType::STREAM_CREATE_SUCCESS);
        }

        break;
      case GroupStreamStatus::SUSPENDED:
        stream_setup_end_timestamp_ = 0;
@@ -5297,6 +5395,9 @@ class LeAudioClientImpl : public LeAudioClient {
  static constexpr uint64_t kAutoConnectAfterOwnDisconnectDelayMs = 1000;
  static constexpr uint64_t kCsisGroupMemberDelayMs = 5000;

  /* LeAudioHealthStatus */
  LeAudioHealthStatus* leAudioHealthStatus_ = nullptr;

  static constexpr char kNotifyUpperLayerAboutGroupBeingInIdleDuringCall[] =
      "persist.bluetooth.leaudio.notify.idle.during.call";

@@ -5365,6 +5466,13 @@ class LeAudioClientImpl : public LeAudioClient {
  }
};

void le_audio_health_status_callback(const RawAddress& addr, int group_id,
                                     LeAudioHealthBasedAction action) {
  if (instance) {
    instance->LeAudioHealthSendRecommendation(addr, group_id, action);
  }
}

/* This is a generic callback method for gatt client which handles every client
 * application events.
 */
+300 −1

File changed.

Preview size limit exceeded, changes collapsed.

+4 −0
Original line number Diff line number Diff line
@@ -68,6 +68,10 @@ class InitFlags final {
    return init_flags::leaudio_targeted_announcement_reconnection_mode_is_enabled();
  }

  inline static bool IsLeAudioHealthBasedActionsEnabled() {
    return init_flags::leaudio_enable_health_based_actions_is_enabled();
  }

  inline static bool UseRsiFromCachedInquiryResults() {
    return init_flags::use_rsi_from_cached_inqiry_results_is_enabled();
  }
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,12 @@ TEST(InitFlagsTest, test_leaudio_targeted_announcement_reconnection_mode) {
  ASSERT_TRUE(InitFlags::IsTargetedAnnouncementReconnectionMode());
}

TEST(InitFlagsTest, test_leaudio_enable_health_based_actions) {
  const char* input[] = {"INIT_leaudio_enable_health_based_actions=true", nullptr};
  InitFlags::Load(input);
  ASSERT_TRUE(InitFlags::IsLeAudioHealthBasedActionsEnabled());
}

TEST(InitFlagsTest, test_enable_debug_logging_for_all) {
  const char* input[] = {"INIT_default_log_level=5", nullptr};
  InitFlags::Load(input);
+1 −0
Original line number Diff line number Diff line
@@ -384,6 +384,7 @@ init_flags!(
        hfp_dynamic_version = true,
        irk_rotation,
        leaudio_targeted_announcement_reconnection_mode = true,
        leaudio_enable_health_based_actions,
        pass_phy_update_callback = true,
        pbap_pse_dynamic_version_upgrade = false,
        periodic_advertising_adi = true,
Loading