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

Commit 579fb61e authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk
Browse files

le_audio: Handle removing streaming device from group

Removing device which participates in streaming is special case and
needs additional steps to be done before removing it from group.
LE Audio streaming group needs to be stopped and reconfigured then the
device can be removed from group.

Tag: #feature
Bug: 255245714
Test: atest bluetooth_le_audio_test bluetooth_le_audio_client_test
Merged-In: I4d59f4830959098ab20c882a8f1c706301ade24c
Change-Id: I4d59f4830959098ab20c882a8f1c706301ade24c
(cherry picked from commit a3bc94ef)
parent 30ac6955
Loading
Loading
Loading
Loading
+39 −2
Original line number Diff line number Diff line
@@ -361,6 +361,16 @@ class LeAudioClientImpl : public LeAudioClient {
    group_add_node(group_id, address);
  }

  /* If device participates in streaming the group, it has to be stopped and
   * group needs to be reconfigured if needed to new configuration without
   * considering this removing device.
   */
  void SetDeviceAsRemovePendingAndStopGroup(LeAudioDevice* leAudioDevice) {
    LOG_INFO("device %s", leAudioDevice->address_.ToString().c_str());
    leAudioDevice->SetConnectionState(DeviceConnectState::PENDING_REMOVAL);
    GroupStop(leAudioDevice->group_id_);
  }

  void OnGroupMemberAddedCb(const RawAddress& address, int group_id) {
    LOG(INFO) << __func__ << " address: " << address
              << " group_id: " << group_id;
@@ -388,8 +398,9 @@ class LeAudioClientImpl : public LeAudioClient {

    LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address);
    if (!leAudioDevice) return;
    if (leAudioDevice->group_id_ == bluetooth::groups::kGroupUnknown) {
      LOG(INFO) << __func__ << " device already not assigned to the group.";
    if (leAudioDevice->group_id_ != group_id) {
      LOG_WARN("Device: %s not assigned to the group.",
               leAudioDevice->address_.ToString().c_str());
      return;
    }

@@ -401,6 +412,11 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (leAudioDevice->HaveActiveAse()) {
      SetDeviceAsRemovePendingAndStopGroup(leAudioDevice);
      return;
    }

    group_remove_node(group, address);
  }

@@ -690,6 +706,11 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (leAudioDevice->HaveActiveAse()) {
      SetDeviceAsRemovePendingAndStopGroup(leAudioDevice);
      return;
    }

    group_remove_node(group, address, true);
  }

@@ -3993,6 +4014,21 @@ class LeAudioClientImpl : public LeAudioClient {
    }
  }

  void HandlePendingDeviceRemove(LeAudioDeviceGroup* group) {
    for (auto device = group->GetFirstDevice(); device != nullptr;
         device = group->GetNextDevice(device)) {
      if (device->GetConnectionState() == DeviceConnectState::PENDING_REMOVAL) {
        if (device->closing_stream_for_disconnection_) {
          device->closing_stream_for_disconnection_ = false;
          LOG_INFO("Disconnecting group id: %d, address: %s", group->group_id_,
                   device->address_.ToString().c_str());
          DisconnectDevice(device);
        }
        group_remove_node(group, device->address_, true);
      }
    }
  }

  void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) {
    LOG_DEBUG();
    auto leAudioDevice = group->GetFirstDevice();
@@ -4172,6 +4208,7 @@ class LeAudioClientImpl : public LeAudioClient {
        if (group) {
          NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_);
          HandlePendingAvailableContextsChange(group);
          HandlePendingDeviceRemove(group);
          HandlePendingDeviceDisconnection(group);
        }
        break;
+3 −0
Original line number Diff line number Diff line
@@ -71,6 +71,9 @@ std::ostream& operator<<(std::ostream& os, const DeviceConnectState& state) {
    case DeviceConnectState::DISCONNECTING:
      char_value_ = "DISCONNECTING";
      break;
    case DeviceConnectState::PENDING_REMOVAL:
      char_value_ = "PENDING_REMOVAL";
      break;
    case DeviceConnectState::CONNECTING_BY_USER:
      char_value_ = "CONNECTING_BY_USER";
      break;
+4 −0
Original line number Diff line number Diff line
@@ -46,6 +46,10 @@ enum class DeviceConnectState : uint8_t {
  REMOVING,
  /* Disconnecting */
  DISCONNECTING,
  /* Device will be removed after scheduled action is finished: One of such
   * action is taking Stream to IDLE
   */
  PENDING_REMOVAL,
  /* 2 states below are used when user creates connection. Connect API is
     called. */
  CONNECTING_BY_USER,
+69 −0
Original line number Diff line number Diff line
@@ -2845,6 +2845,75 @@ TEST_F(UnicastTest, GroupingAddRemove) {
  ASSERT_NE(std::find(devs.begin(), devs.end(), test_address1), devs.end());
}

TEST_F(UnicastTest, RemoveNodeWhileStreaming) {
  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,
      /* 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)));

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

  // Start streaming
  constexpr uint8_t cis_count_out = 1;
  constexpr uint8_t cis_count_in = 0;

  constexpr int gmcs_ccid = 1;
  constexpr int gtbs_ccid = 2;

  // Audio sessions are started only when device gets active
  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
  LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
  LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
  LeAudioClient::Get()->GroupSetActive(group_id);

  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, {{gmcs_ccid}}))
      .Times(1);

  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);

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

  // Verify Data transfer on one audio source cis
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);

  EXPECT_CALL(mock_groups_module_, RemoveDevice(test_address0, group_id))
      .Times(1);
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
  EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _))
      .Times(0);
  EXPECT_CALL(
      mock_audio_hal_client_callbacks_,
      OnGroupNodeStatus(test_address0, group_id, GroupNodeStatus::REMOVED));
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
      .Times(0);

  LeAudioClient::Get()->GroupRemoveNode(group_id, test_address0);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_groups_module_);
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}

TEST_F(UnicastTest, GroupingAddTwiceNoRemove) {
  // Earbud connects without known grouping
  uint8_t group_id0 = bluetooth::groups::kGroupUnknown;