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

Commit 99f1b50b authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Workaround for devices not keeping CCC values

Android will read ASE control point CCC on reconnection and if it is set
properly then device will be considered as connected.
If not, then Android will write CCC to all the mandatory characteristics
again which will increase time when device will be available in the
system

Bug: 308379423
Tag: #feature
Test: atest bluetooth_le_audio_client_test
Change-Id: I105eb4364b3bbf9e4a056232718f8a77741a6e9c
parent 220708d9
Loading
Loading
Loading
Loading
+74 −25
Original line number Diff line number Diff line
@@ -1955,7 +1955,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_);

@@ -1967,49 +1968,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) {
@@ -2061,7 +2058,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
@@ -2333,15 +2341,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;
@@ -4875,6 +4885,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) {
@@ -7490,25 +7504,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(_, _));
@@ -7516,6 +7517,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) {