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

Commit a47e091d authored by Rongxuan Liu's avatar Rongxuan Liu
Browse files

[le audio] Add counter metrics for le audio health status

Enable le audio health status with counter metrics

Bug: 301448525
Test: atest bluetooth_le_audio_client_test bluetooth_le_audio_test
Test: manual test with allow list
Change-Id: I17f0b291efeb779b82e56774adb58d81235dd78d
parent ac7e63ce
Loading
Loading
Loading
Loading
+40 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.bluetooth.le_audio;

import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;

import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;

@@ -34,6 +35,7 @@ import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastSettings;
import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothLeAudio;
@@ -67,6 +69,7 @@ import android.util.Pair;

import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -1729,20 +1732,28 @@ public class LeAudioService extends ProfileService {
    }

    private void handleDeviceHealthAction(BluetoothDevice device, int action) {
        // To implement
        if (DBG) {
            Log.d(
                    TAG,
                    "handleDeviceHealthAction: "
                    "handleDeviceHealthAction: device: "
                            + device
                            + " action: "
                            + action
                            + ", not implemented");
        }
        if (action == LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE) {
            MetricsLogger.getInstance()
                    .count(
                            mAdapterService.isLeAudioAllowed(device)
                                    ? BluetoothProtoEnums
                                            .LE_AUDIO_ALLOWLIST_DEVICE_HEALTH_STATUS_BAD
                                    : BluetoothProtoEnums
                                            .LE_AUDIO_NONALLOWLIST_DEVICE_HEALTH_STATUS_BAD,
                            1);
        }
    }

    private void handleGroupHealthAction(int groupId, int action) {
        // To implement
        if (DBG) {
            Log.d(
                    TAG,
@@ -1752,6 +1763,31 @@ public class LeAudioService extends ProfileService {
                            + action
                            + ", not implemented");
        }
        BluetoothDevice device = getLeadDeviceForTheGroup(groupId);
        switch (action) {
            case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE:
                MetricsLogger.getInstance()
                        .count(
                                mAdapterService.isLeAudioAllowed(device)
                                        ? BluetoothProtoEnums
                                                .LE_AUDIO_ALLOWLIST_GROUP_HEALTH_STATUS_BAD
                                        : BluetoothProtoEnums
                                                .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_BAD,
                                1);
                break;
            case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING:
                MetricsLogger.getInstance()
                        .count(
                                mAdapterService.isLeAudioAllowed(device)
                                        ? BluetoothProtoEnums
                                                .LE_AUDIO_ALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD
                                        : BluetoothProtoEnums
                                                .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD,
                                1);
                break;
            default:
                break;
        }
    }

    private void handleGroupTransitToActive(int groupId) {
@@ -2197,7 +2233,7 @@ public class LeAudioService extends ProfileService {
                == LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION) {
            handleDeviceHealthAction(stackEvent.device, stackEvent.valueInt1);
        } else if (stackEvent.type
                == LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION) {
                == LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION) {
            handleGroupHealthAction(stackEvent.valueInt1, stackEvent.valueInt2);
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) {
            int broadcastId = stackEvent.valueInt1;
+2 −2
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ public class LeAudioStackEvent {
                return "{group_id: " + value + "}";
            case EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION:
                switch (value) {
                    case HEALTH_RECOMMENDATION_ACTION_NONE:
                    case HEALTH_RECOMMENDATION_ACTION_DISABLE:
                        return "ACTION_DISABLE";
                    case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING:
                        return "ACTION_CONSIDER_DISABLING";
@@ -236,7 +236,7 @@ public class LeAudioStackEvent {
                return "{state:" + broadcastStateToString(value) + "}";
            case EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION:
                switch (value) {
                    case HEALTH_RECOMMENDATION_ACTION_NONE:
                    case HEALTH_RECOMMENDATION_ACTION_DISABLE:
                        return "ACTION_DISABLE";
                    case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING:
                        return "ACTION_CONSIDER_DISABLING";
+1 −47
Original line number Diff line number Diff line
@@ -963,6 +963,7 @@ cc_test {
        "le_audio/devices.cc",
        "le_audio/le_audio_client_test.cc",
        "le_audio/le_audio_health_status.cc",
        "le_audio/le_audio_health_status_test.cc",
        "le_audio/le_audio_log_history.cc",
        "le_audio/le_audio_set_configuration_provider_json.cc",
        "le_audio/le_audio_types.cc",
@@ -1036,53 +1037,6 @@ cc_test {
    cflags: ["-Wno-unused-parameter"],
}

// health status unit tests for host
cc_test {
    name: "bluetooth_le_audio_health_status_test",
    test_suites: ["device-tests"],
    defaults: [
        "bluetooth_gtest_x86_asan_workaround",
        "fluoride_bta_defaults",
        "mts_defaults",
    ],
    host_supported: true,
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/bta/include",
    ],
    srcs: [
        ":TestCommonMockFunctions",
        //":TestMockBtif",
        "le_audio/le_audio_health_status.cc",
        "le_audio/le_audio_health_status_test.cc",
    ],
    shared_libs: [
        "libcrypto",
        "liblog",
    ],
    static_libs: [
        "crypto_toolbox_for_tests",
        "libbluetooth-types",
        "libbluetooth_gd",
        "libbt-common",
        "libbt_shim_bridge",
        "libbt_shim_ffi",
        "libchrome",
        "libgmock",
        "libosi",
    ],
    sanitize: {
        cfi: true,
        scs: true,
        address: true,
        all_undefined: true,
        integer_overflow: true,
        diag: {
            undefined: true,
        },
    },
}

cc_test {
    name: "bluetooth_test_broadcaster_state_machine",
    test_suites: ["device-tests"],
+6 −8
Original line number Diff line number Diff line
@@ -416,7 +416,7 @@ class LeAudioClientImpl : public LeAudioClient {

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

    group_add_node(group_id, address);
@@ -471,7 +471,7 @@ class LeAudioClientImpl : public LeAudioClient {

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

    LOG_ERROR(
@@ -2461,8 +2461,7 @@ class LeAudioClientImpl : public LeAudioClient {
    LOG_ERROR("%s, %s", ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
              error_string.c_str());
    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForDevice(leAudioDevice->address_,
                                                  stat);
      leAudioHealthStatus_->AddStatisticForDevice(leAudioDevice, stat);
    }
    DisconnectDevice(leAudioDevice);
  }
@@ -2839,7 +2838,7 @@ class LeAudioClientImpl : public LeAudioClient {
    leAudioDevice->notify_connected_after_read_ = true;
    if (leAudioHealthStatus_) {
      leAudioHealthStatus_->AddStatisticForDevice(
          leAudioDevice->address_, LeAudioHealthDeviceStatType::VALID_DB);
          leAudioDevice, LeAudioHealthDeviceStatType::VALID_DB);
    }

    /* If already known group id */
@@ -4979,8 +4978,7 @@ class LeAudioClientImpl : public LeAudioClient {

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

        groupStateMachine_->ProcessHciNotifCisEstablished(group, leAudioDevice,
@@ -5166,7 +5164,7 @@ class LeAudioClientImpl : public LeAudioClient {

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

        if (!group) {
+32 −18
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ using bluetooth::Uuid;
using namespace bluetooth::le_audio;

using le_audio::LeAudioCodecConfiguration;
using le_audio::LeAudioDeviceGroup;
using le_audio::LeAudioHealthStatus;
using le_audio::LeAudioSinkAudioHalClient;
using le_audio::LeAudioSourceAudioHalClient;
@@ -2651,7 +2652,16 @@ class UnicastTestHealthStatus : public UnicastTest {
  void SetUp() override {
    use_health_status = true;
    UnicastTest::SetUp();
    group_ = new LeAudioDeviceGroup(group_id_);
  }

  void TearDown() override {
    delete group_;
    UnicastTest::TearDown();
  }

  const int group_id_ = 0;
  LeAudioDeviceGroup* group_ = nullptr;
};

RawAddress GetTestAddress(uint8_t index) {
@@ -2969,7 +2979,6 @@ TEST_F(UnicastTestHealthStatus,

TEST_F(UnicastTestHealthStatus, ConnectOneEarbudDisable_withHealthStatus) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = 0;
  int conn_id = 1;

  SetSampleDatabaseEarbudsValid(
@@ -2984,7 +2993,10 @@ TEST_F(UnicastTestHealthStatus, ConnectOneEarbudDisable_withHealthStatus) {
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  LeAudioClient::Get()->GroupSetActive(group_id);
  LeAudioClient::Get()->GroupSetActive(group_id_);
  auto device = std::make_shared<LeAudioDevice>(
      test_address0, DeviceConnectState::DISCONNECTED);
  group_->AddNode(device);
  SyncOnMainLoop();

  auto health_status = LeAudioHealthStatus::Get();
@@ -2992,30 +3004,30 @@ TEST_F(UnicastTestHealthStatus, ConnectOneEarbudDisable_withHealthStatus) {
  /* Inject stream error */
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnHealthBasedGroupRecommendationAction(
                  group_id, LeAudioHealthBasedAction::DISABLE))
                  group_id_, LeAudioHealthBasedAction::DISABLE))
      .Times(1);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);

  /* Do not act on disconnect */
  ON_CALL(mock_gatt_interface_, Close(_)).WillByDefault(DoAll(Return()));
  ON_CALL(mock_btm_interface_, AclDisconnectFromHandle(_, _))
      .WillByDefault(DoAll(Return()));

  state_machine_callbacks_->OnStateTransitionTimeout(group_id);
  state_machine_callbacks_->OnStateTransitionTimeout(group_id_);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnHealthBasedGroupRecommendationAction(
                  group_id, LeAudioHealthBasedAction::DISABLE))
                  group_id_, LeAudioHealthBasedAction::DISABLE))
      .Times(0);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}
@@ -3023,7 +3035,6 @@ TEST_F(UnicastTestHealthStatus, ConnectOneEarbudDisable_withHealthStatus) {
TEST_F(UnicastTestHealthStatus,
       ConnectOneEarbudConsiderDisabling_withHealthStatus) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = 0;
  int conn_id = 1;

  SetSampleDatabaseEarbudsValid(
@@ -3038,7 +3049,10 @@ TEST_F(UnicastTestHealthStatus,
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  LeAudioClient::Get()->GroupSetActive(group_id);
  LeAudioClient::Get()->GroupSetActive(group_id_);
  auto device = std::make_shared<LeAudioDevice>(
      test_address0, DeviceConnectState::DISCONNECTED);
  group_->AddNode(device);
  SyncOnMainLoop();

  auto health_status = LeAudioHealthStatus::Get();
@@ -3046,21 +3060,21 @@ TEST_F(UnicastTestHealthStatus,
  /* Inject stream success and error */
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnHealthBasedGroupRecommendationAction(
                  group_id, LeAudioHealthBasedAction::CONSIDER_DISABLING))
                  group_id_, LeAudioHealthBasedAction::CONSIDER_DISABLING))
      .Times(1);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_SUCCESS);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_SUCCESS);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);

  /* Do not act on disconnect */
  ON_CALL(mock_gatt_interface_, Close(_)).WillByDefault(DoAll(Return()));
  ON_CALL(mock_btm_interface_, AclDisconnectFromHandle(_, _))
      .WillByDefault(DoAll(Return()));

  state_machine_callbacks_->OnStateTransitionTimeout(group_id);
  state_machine_callbacks_->OnStateTransitionTimeout(group_id_);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

@@ -3069,9 +3083,9 @@ TEST_F(UnicastTestHealthStatus,
                  1, LeAudioHealthBasedAction::CONSIDER_DISABLING))
      .Times(0);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  health_status->AddStatisticForGroup(
      group_id, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
      group_, LeAudioHealthGroupStatType::STREAM_CREATE_CIS_FAILED);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}
Loading