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

Commit 03e77b40 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

laudio: Improve removing and disconnecting leaudio device

This patch fixes disconnecting and removing device when device is in the
different connection state.
It makes sure, that device is removed from the background connect when
necessary.

Unit tests has beed added to verify the behavior.

Bug: 273285484
Test: atest bluetooth_le_audio_client_test
Test: atest BluetoothInstrumentationTests
Tag: #feature
Change-Id: Ib05cb66de76897803efa3f9e4c4db77d9a82a964
parent 06a99616
Loading
Loading
Loading
Loading
+107 −54
Original line number Diff line number Diff line
@@ -1105,10 +1105,37 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
    LOG_INFO("%s, state: %s", ADDRESS_TO_LOGGABLE_CSTR(address),
             bluetooth::common::ToString(leAudioDevice->GetConnectionState())
                 .c_str());
    auto connection_state = leAudioDevice->GetConnectionState();
    switch (connection_state) {
      case DeviceConnectState::REMOVING:
      case DeviceConnectState::PENDING_REMOVAL:
        /* Just return, and let device disconnect */
        return;
      case DeviceConnectState::CONNECTED:
      case DeviceConnectState::CONNECTED_AUTOCONNECT_GETTING_READY:
      case DeviceConnectState::CONNECTED_BY_USER_GETTING_READY:
        /* ACL exist in this case, disconnect and mark as removing */
        Disconnect(address);
        [[fallthrough]];
      case DeviceConnectState::DISCONNECTING:
        /* Remove device from the background connect if it is there */
        BTA_GATTC_CancelOpen(gatt_if_, address, false);
        /* Device is disconnecting, just mark it shall be removed after all. */
        leAudioDevice->SetConnectionState(DeviceConnectState::REMOVING);
        return;
      case DeviceConnectState::CONNECTING_BY_USER:
        BTA_GATTC_CancelOpen(gatt_if_, address, true);
        [[fallthrough]];
      case DeviceConnectState::CONNECTING_AUTOCONNECT:
        /* Cancel background conection */
        BTA_GATTC_CancelOpen(gatt_if_, address, false);
        break;
      case DeviceConnectState::DISCONNECTED:
        /* Do nothing, just remove device  */
        break;
    }

    /* Remove the group assignment if not yet removed. It might happen that the
@@ -1267,24 +1294,27 @@ class LeAudioClientImpl : public LeAudioClient {
    return SerializeAses(leAudioDevice, out);
  }

  void BackgroundConnectIfGroupConnected(LeAudioDevice* leAudioDevice) {
  void BackgroundConnectIfNeeded(LeAudioDevice* leAudioDevice) {
    DLOG(INFO) << __func__ << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_);
    auto group = aseGroups_.FindById(leAudioDevice->group_id_);
    if (!group) {
      DLOG(INFO) << __func__ << " Device is not yet part of the group. ";
      LOG_INFO(" Device %s is not yet part of the group %d. ",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
               leAudioDevice->group_id_);
      return;
    }

    if (!group->IsAnyDeviceConnected()) {
      DLOG(INFO) << __func__ << " group: " << leAudioDevice->group_id_
                 << " is not connected";
    if (!leAudioDevice->autoconnect_flag_ && !group->IsAnyDeviceConnected()) {
      LOG_DEBUG("Device %s not in the background connect",
                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      return;
    }

    DLOG(INFO) << __func__ << " Add "
               << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_)
               << " to background connect to connected group: "
               << leAudioDevice->group_id_;
    LOG_INFO(
        "Add %s added to background connect. autoconnect flag: %d "
        "group_connected: %d",
        ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
        leAudioDevice->group_id_, group->IsAnyDeviceConnected());

    leAudioDevice->SetConnectionState(
        DeviceConnectState::CONNECTING_AUTOCONNECT);
@@ -1302,28 +1332,31 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    /* cancel pending direct connect */
    if (leAudioDevice->GetConnectionState() ==
        DeviceConnectState::CONNECTING_BY_USER) {
      BTA_GATTC_CancelOpen(gatt_if_, address, true);
    }

    /* Removes all registrations for connection */
    BTA_GATTC_CancelOpen(0, address, false);
    auto connection_state = leAudioDevice->GetConnectionState();
    LOG_INFO("%s, state: %s", ADDRESS_TO_LOGGABLE_CSTR(address),
             bluetooth::common::ToString(connection_state).c_str());

    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
    switch (connection_state) {
      case DeviceConnectState::CONNECTING_BY_USER:
        /* Timeout happen on the Java layer. Device probably not in the range.
         * Cancel just direct connection and keep background if it is there.
         */
        BTA_GATTC_CancelOpen(gatt_if_, address, true);
        break;
      case DeviceConnectState::CONNECTED: {
        /* User is disconnecting the device, we shall remove the autoconnect
         * flag for this device and all others
         */
        LOG_INFO("Removing autoconnect flag for group_id %d",
                 leAudioDevice->group_id_);

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

        if (leAudioDevice->autoconnect_flag_) {
          /* Removes device from background connect */
          BTA_GATTC_CancelOpen(gatt_if_, address, false);
          btif_storage_set_leaudio_autoconnect(address, false);
          leAudioDevice->autoconnect_flag_ = false;
        }
        auto group = aseGroups_.FindById(leAudioDevice->group_id_);

        if (group) {
          /* Remove devices from auto connect mode */
@@ -1346,14 +1379,34 @@ class LeAudioClientImpl : public LeAudioClient {
          groupStateMachine_->StopStream(group);
          return;
        }
      }
        [[fallthrough]];
      case DeviceConnectState::CONNECTED_BY_USER_GETTING_READY:
        /* Timeout happen on the Java layer before native got ready with the
         * device */
        DisconnectDevice(leAudioDevice);
        return;
      case DeviceConnectState::CONNECTED_AUTOCONNECT_GETTING_READY:
        /* Java is not aware about autoconnect actions,
         * therefore this should not happen.
         */
        LOG_WARN("Should not happen - disconnect device");
        DisconnectDevice(leAudioDevice);
        return;
      case DeviceConnectState::DISCONNECTED:
      case DeviceConnectState::DISCONNECTING:
      case DeviceConnectState::CONNECTING_AUTOCONNECT:
      case DeviceConnectState::PENDING_REMOVAL:
      case DeviceConnectState::REMOVING:
        LOG_WARN("%s, invalid state %s", ADDRESS_TO_LOGGABLE_CSTR(address),
                 bluetooth::common::ToString(connection_state).c_str());
        break;
    }

    /* If this is a device which is a part of the group which is connected,
     * lets start backgroup connect
     */
    BackgroundConnectIfGroupConnected(leAudioDevice);
    BackgroundConnectIfNeeded(leAudioDevice);
  }

  void DisconnectDevice(LeAudioDevice* leAudioDevice,
+1 −1
Original line number Diff line number Diff line
@@ -2157,7 +2157,7 @@ void LeAudioDeviceGroup::Dump(int fd, int active_group_id) {

/* LeAudioDevice Class methods implementation */
void LeAudioDevice::SetConnectionState(DeviceConnectState state) {
  LOG_DEBUG(" %s --> %s",
  LOG_DEBUG("%s, %s --> %s", ADDRESS_TO_LOGGABLE_CSTR(address_),
            bluetooth::common::ToString(connection_state_).c_str(),
            bluetooth::common::ToString(state).c_str());
  connection_state_ = state;
+212 −1
Original line number Diff line number Diff line
@@ -2266,7 +2266,7 @@ class UnicastTestNoInit : public Test {
  gatt::MockBtaGattQueue mock_gatt_queue_;
  tBTA_GATTC_CBACK* gatt_callback;
  const uint8_t gatt_if = 0xfe;
  uint8_t global_conn_id = 1;
  uint16_t global_conn_id = 1;
  le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks_;
  std::map<int, LeAudioDeviceGroup*> streaming_groups;

@@ -3164,6 +3164,217 @@ TEST_F(UnicastTest, RemoveTwoEarbudsCsisGrouped) {
  Mock::VerifyAndClearExpectations(&mock_btif_storage_);
}

TEST_F(UnicastTest, RemoveDeviceWhenConnected) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  uint16_t conn_id = 1;

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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)));
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
      .Times(1);
  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);

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

  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, false))
      .Times(1);
  EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
  EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);

  LeAudioClient::Get()->RemoveDevice(test_address0);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_btif_storage_);
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
}

TEST_F(UnicastTest, RemoveDeviceWhenConnecting) {
  const RawAddress test_address0 = GetTestAddress(0);
  uint16_t conn_id = 1;

  /* Prepare  mock to not inject connect event so the device can stay in
   * CONNECTING state*/
  ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_DIRECT_CONNECTION, _))
      .WillByDefault(DoAll(Return()));

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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(0);
  ConnectLeAudio(test_address0);

  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, true))
      .Times(1);
  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
      .Times(1);

  LeAudioClient::Get()->RemoveDevice(test_address0);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
}

TEST_F(UnicastTest, RemoveDeviceWhenGettingConnectionReady) {
  const RawAddress test_address0 = GetTestAddress(0);
  uint16_t conn_id = 1;

  /* Prepare  mock to not inject Service Search Complete*/
  ON_CALL(mock_gatt_interface_, ServiceSearchRequest(_, _))
      .WillByDefault(DoAll(Return()));

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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(0);
  ConnectLeAudio(test_address0);

  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
  EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);
  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
      .Times(1);

  LeAudioClient::Get()->RemoveDevice(test_address0);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_btif_storage_);
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
}

TEST_F(UnicastTest, DisconnectDeviceWhenConnected) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  uint16_t conn_id = 1;

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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)));
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
      .Times(1);
  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);

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

  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, false))
      .Times(1);
  EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
  EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);

  LeAudioClient::Get()->Disconnect(test_address0);
  SyncOnMainLoop();

  Mock::VerifyAndClearExpectations(&mock_btif_storage_);
  Mock::VerifyAndClearExpectations(&mock_gatt_queue_);
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
}

TEST_F(UnicastTest, DisconnectDeviceWhenConnecting) {
  const RawAddress test_address0 = GetTestAddress(0);
  uint16_t conn_id = 1;

  /* Prepare  mock to not inject connect event so the device can stay in
   * CONNECTING state*/
  ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_DIRECT_CONNECTION, _))
      .WillByDefault(DoAll(Return()));

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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(0);
  ConnectLeAudio(test_address0);

  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  /* Prepare on call mock on Close - to not trigger Inject Disconnection, as it
   * is done in default mock.
   */
  ON_CALL(mock_gatt_interface_, Close(_)).WillByDefault(DoAll(Return()));
  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, true))
      .Times(1);

  LeAudioClient::Get()->Disconnect(test_address0);

  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
}

TEST_F(UnicastTest, DisconnectDeviceWhenGettingConnectionReady) {
  const RawAddress test_address0 = GetTestAddress(0);
  uint16_t conn_id = global_conn_id;

  /* Prepare  mock to not inject Service Search Complete*/
  ON_CALL(mock_gatt_interface_, ServiceSearchRequest(_, _))
      .WillByDefault(DoAll(Return()));

  SetSampleDatabaseEarbudsValid(
      conn_id, test_address0, codec_spec_conf::kLeAudioLocationStereo,
      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
      default_channel_cnt, 0x0004,
      /* source sample freq 16khz */ 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(0);
  ConnectLeAudio(test_address0);

  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);

  EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
  EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);
  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
      .Times(0);

  LeAudioClient::Get()->Disconnect(test_address0);
}

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