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

Commit 299bf6a6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "leaudio: Workaround for devices not keeping CCC values" into main

parents f8e62818 99f1b50b
Loading
Loading
Loading
Loading
+74 −25
Original line number Diff line number Diff line
@@ -1961,7 +1961,8 @@ class LeAudioClientImpl : public LeAudioClient {
        le_audio::ConnectionStatus::FAILED);
  }

  void RegisterKnownNotifications(LeAudioDevice* leAudioDevice) {
  void RegisterKnownNotifications(LeAudioDevice* leAudioDevice,
                                  bool gatt_register, bool write_ccc) {
    LOG(INFO) << __func__ << " device: "
              << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_);

@@ -1973,49 +1974,45 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    bool write_ccc = false;

    /* GATTC will ommit not registered previously handles */
    for (auto pac_tuple : leAudioDevice->snk_pacs_) {
      subscribe_for_notification(leAudioDevice->conn_id_,
                                 leAudioDevice->address_,
                                 std::get<0>(pac_tuple), write_ccc);
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          std::get<0>(pac_tuple), gatt_register, write_ccc);
    }
    for (auto pac_tuple : leAudioDevice->src_pacs_) {
      subscribe_for_notification(leAudioDevice->conn_id_,
                                 leAudioDevice->address_,
                                 std::get<0>(pac_tuple), write_ccc);
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          std::get<0>(pac_tuple), gatt_register, write_ccc);
    }

    if (leAudioDevice->snk_audio_locations_hdls_.val_hdl != 0)
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          leAudioDevice->snk_audio_locations_hdls_, write_ccc);
          leAudioDevice->snk_audio_locations_hdls_, gatt_register, write_ccc);
    if (leAudioDevice->src_audio_locations_hdls_.val_hdl != 0)
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          leAudioDevice->src_audio_locations_hdls_, write_ccc);
          leAudioDevice->src_audio_locations_hdls_, gatt_register, write_ccc);

    if (leAudioDevice->audio_avail_hdls_.val_hdl != 0)
      subscribe_for_notification(leAudioDevice->conn_id_,
                                 leAudioDevice->address_,
                                 leAudioDevice->audio_avail_hdls_, write_ccc);
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          leAudioDevice->audio_avail_hdls_, gatt_register, write_ccc);

    if (leAudioDevice->audio_supp_cont_hdls_.val_hdl != 0)
      subscribe_for_notification(
          leAudioDevice->conn_id_, leAudioDevice->address_,
          leAudioDevice->audio_supp_cont_hdls_, write_ccc);
          leAudioDevice->audio_supp_cont_hdls_, gatt_register, write_ccc);

    for (struct ase& ase : leAudioDevice->ases_)
      subscribe_for_notification(leAudioDevice->conn_id_,
                                 leAudioDevice->address_, ase.hdls, write_ccc);
                                 leAudioDevice->address_, ase.hdls,
                                 gatt_register, write_ccc);

    /* Let's register only for control point on the reconnection time.
     * This is mainly to assure that the database did not changed on another
     * side
     */
    subscribe_for_notification(leAudioDevice->conn_id_, leAudioDevice->address_,
                               leAudioDevice->ctp_hdls_, true);
                               leAudioDevice->ctp_hdls_, gatt_register,
                               write_ccc);
  }

  void changeMtuIfPossible(LeAudioDevice* leAudioDevice) {
@@ -2067,7 +2064,18 @@ class LeAudioClientImpl : public LeAudioClient {

    /* If we know services, register for notifications */
    if (leAudioDevice->known_service_handles_) {
      RegisterKnownNotifications(leAudioDevice);
      /* This registration will do subscribtion in local GATT as we
       * assume remote device keeps bonded CCC values.
       */
      RegisterKnownNotifications(leAudioDevice, true, false);

      /* Make sure remote keeps CCC values as per specification.
       * We read only ctp_ccc value. If that one is good, we assume
       * remote keeps CCC values correctly.
       */
      BtaGattQueue::ReadCharacteristic(leAudioDevice->conn_id_,
                                       leAudioDevice->ctp_hdls_.ccc_hdl,
                                       OnGattCtpCccReadRspStatic, NULL);
    }

    /* If we know services and read is not ongoing, this is reconnection and
@@ -2339,15 +2347,17 @@ class LeAudioClientImpl : public LeAudioClient {

  bool subscribe_for_notification(uint16_t conn_id, const RawAddress& address,
                                  struct le_audio::types::hdl_pair handle_pair,
                                  bool gatt_register = true,
                                  bool write_ccc = true) {
    std::vector<uint8_t> value(2);
    uint8_t* ptr = value.data();
    uint16_t handle = handle_pair.val_hdl;
    uint16_t ccc_handle = handle_pair.ccc_hdl;

    LOG_INFO("conn id %d", conn_id);
    if (BTA_GATTC_RegisterForNotifications(gatt_if_, address, handle) !=
        GATT_SUCCESS) {
    LOG_INFO("conn id %d, gatt_register: %b, write_ccc: %b", conn_id,
             gatt_register, write_ccc);
    if (gatt_register && BTA_GATTC_RegisterForNotifications(
                             gatt_if_, address, handle) != GATT_SUCCESS) {
      LOG(ERROR) << __func__ << ", cannot register for notification: "
                 << static_cast<int>(handle);
      return false;
@@ -4881,6 +4891,45 @@ class LeAudioClientImpl : public LeAudioClient {
    return false;
  }

  static void OnGattCtpCccReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
                                        uint16_t hdl, uint16_t len,
                                        uint8_t* value, void* data) {
    if (!instance) return;

    LOG_DEBUG("conn_id: 0x%04x, status: 0x%02x", conn_id, status);

    LeAudioDevice* leAudioDevice =
        instance->leAudioDevices_.FindByConnId(conn_id);

    if (!leAudioDevice) {
      LOG_ERROR("LeAudioDevice not found");
      return;
    }

    if (status == GATT_DATABASE_OUT_OF_SYNC) {
      LOG_INFO("Database out of sync for %s, re-discovering",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      instance->ClearDeviceInformationAndStartSearch(leAudioDevice);
      return;
    }

    if (status != GATT_SUCCESS || len != 2) {
      LOG_ERROR("Could not read CCC for %s, disconnecting",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      instance->Disconnect(leAudioDevice->address_);
      return;
    }

    uint16_t val = *(uint16_t*)value;
    if (val == 0) {
      LOG_INFO("%s forgot CCC values. Re-subscribing",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      instance->RegisterKnownNotifications(leAudioDevice, false, true);
    } else {
      instance->connectionReady(leAudioDevice);
    }
  }

  static void OnGattReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
                                  uint16_t hdl, uint16_t len, uint8_t* value,
                                  void* data) {
+109 −17
Original line number Diff line number Diff line
@@ -89,6 +89,9 @@ constexpr le_audio::types::LeAudioContextType
    kLeAudioDefaultConfigurationContext =
        le_audio::types::LeAudioContextType::UNSPECIFIED;

static tGATT_STATUS gatt_read_ctp_ccc_status_ = GATT_SUCCESS;
static uint8_t ccc_stored_byte_val_ = 0x01;

static constexpr char kNotifyUpperLayerAboutGroupBeingInIdleDuringCall[] =
    "persist.bluetooth.leaudio.notify.idle.during.call";
const char* test_flags[] = {
@@ -1399,6 +1402,9 @@ class UnicastTestNoInit : public Test {
            SupportsBleConnectedIsochronousStreamPeripheral)
        .WillByDefault(Return(true));

    gatt_read_ctp_ccc_status_ = GATT_SUCCESS;
    ccc_stored_byte_val_ = 0x01;

    controller::SetMockControllerInterface(&controller_interface_);
    bluetooth::manager::SetMockBtmInterface(&mock_btm_interface_);
    gatt::SetMockBtaGattInterface(&mock_gatt_interface_);
@@ -2414,6 +2420,14 @@ class UnicastTestNoInit : public Test {
            std::vector<uint8_t> value;
            bool is_ase_sink_request = false;
            bool is_ase_src_request = false;

            if (handle == ascs->ctp_ccc) {
              value = {ccc_stored_byte_val_, 00};
              cb(conn_id, gatt_read_ctp_ccc_status_, handle, value.size(),
                 value.data(), cb_data);
              return;
            }

            uint8_t idx;
            for (idx = 0; idx < max_num_of_ases; idx++) {
              if (handle == ascs->sink_ase_char[idx] + 1) {
@@ -7638,25 +7652,12 @@ TEST_F(UnicastTest, HandleDatabaseOutOfSync) {
  InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);

  // default action for WriteDescriptor function call
  ON_CALL(mock_gatt_queue_, WriteDescriptor(_, _, _, _, _, _))
      .WillByDefault(Invoke([](uint16_t conn_id, uint16_t handle,
                               std::vector<uint8_t> value,
                               tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb,
                               void* cb_data) -> void {
        if (cb)
          do_in_main_thread(
              FROM_HERE,
              base::BindOnce(
                  [](GATT_WRITE_OP_CB cb, uint16_t conn_id, uint16_t handle,
                     uint16_t len, uint8_t* value, void* cb_data) {
                    cb(conn_id, GATT_DATABASE_OUT_OF_SYNC, handle, len, value,
                       cb_data);
                  },
                  cb, conn_id, handle, value.size(), value.data(), cb_data));
      }));
  /* Simulate DATABASE OUT OF SYNC */
  gatt_read_ctp_ccc_status_ = GATT_DATABASE_OUT_OF_SYNC;

  EXPECT_CALL(mock_gatt_queue_, WriteDescriptor(_, _, _, _, _, _)).Times(0);
  ON_CALL(mock_gatt_interface_, ServiceSearchRequest(_, _))
      .WillByDefault(Return());
  EXPECT_CALL(mock_gatt_interface_, ServiceSearchRequest(_, _));
@@ -7664,6 +7665,97 @@ TEST_F(UnicastTest, HandleDatabaseOutOfSync) {
  InjectConnectedEvent(test_address0, 1);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);
}

TEST_F(UnicastTest, TestRemoteDeviceKeepCccValues) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;

  SetSampleDatabaseEarbudsValid(
      1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004, false /*add_csis*/, true /*add_cas*/,
      true /*add_pacs*/, default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/,
      0 /*rank*/);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
      .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
      .WillOnce(DoAll(SaveArg<1>(&group_id)));

  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
      .Times(1);
  InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);

  /* Simulate remote cache is good */
  ccc_stored_byte_val_ = 0x01;

  EXPECT_CALL(mock_gatt_queue_, WriteDescriptor(_, _, _, _, _, _)).Times(0);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
      .Times(1);

  InjectConnectedEvent(test_address0, 1);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}

TEST_F(UnicastTest, TestRemoteDeviceForgetsCccValues) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;

  SetSampleDatabaseEarbudsValid(
      1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004, false /*add_csis*/, true /*add_cas*/,
      true /*add_pacs*/, default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/,
      0 /*rank*/);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
      .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
      .WillOnce(DoAll(SaveArg<1>(&group_id)));

  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
      .Times(1);
  InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);

  /* Simulate remote cache is broken */
  ccc_stored_byte_val_ = 0;
  EXPECT_CALL(mock_gatt_queue_, WriteDescriptor(_, _, _, _, _, _))
      .Times(AtLeast(1));
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
      .Times(1);

  InjectConnectedEvent(test_address0, 1);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}

TEST_F(UnicastTest, SpeakerStreamingTimeout) {