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

Commit 15aa243e authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Fix for one CSIP member not streaming (race)

When first set member is moving to streaming state and second set member
updates it available context types, the second member will not be
connected to the stream.

This patch fixes that.

Bug: 330376119
Test: atest bluetooth_le_audio_client_test
Flag: Exempt, regression tested with unit test, new test added
Change-Id: I70a06e693c15b8d808bb2664b942ac6e5e050166
parent f62454a0
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -1892,6 +1892,19 @@ class LeAudioClientImpl : public LeAudioClient {
        return;
      }

      if (group->IsInTransition()) {
        /* Group is in transition.
         * if group is going to stream, schedule attaching the device to the
         * group.
         */

        if (group->GetTargetState() ==
            AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
          AttachToStreamingGroupIfNeeded(leAudioDevice);
        }
        return;
      }

      if (!group->IsStreaming()) {
        /* Group is not streaming. Device does not have to be attach to the
         * stream, and we can update context availability for the group
@@ -1900,11 +1913,6 @@ class LeAudioClientImpl : public LeAudioClient {
        return;
      }

      if (group->IsInTransition()) {
        /* Group is in transition, do not take any actions now.*/
        return;
      }

      if (leAudioDevice->HaveActiveAse()) {
        /* Do nothing, device is streaming */
        return;
+106 −7
Original line number Diff line number Diff line
@@ -652,11 +652,14 @@ class UnicastTestNoInit : public Test {

  void InjectAvailableContextTypes(const RawAddress& test_address,
                                   uint16_t conn_id, AudioContexts sink_ctxs,
                                   AudioContexts source_ctxs) {
                                   AudioContexts source_ctxs,
                                   bool sync_on_mainloop = true) {
    /* 0x0074 is pacs->avail_contexts_char + 1 */
    InjectContextTypes(test_address, conn_id, 0x0074, sink_ctxs, source_ctxs);
    if (sync_on_mainloop) {
      SyncOnMainLoop();
    }
  }

  void SetUpMockGatt() {
    // default action for GetCharacteristic function call
@@ -929,12 +932,17 @@ class UnicastTestNoInit : public Test {
            });

    ON_CALL(mock_state_machine_, AttachToStream(_, _, _))
        .WillByDefault([](LeAudioDeviceGroup* group,
        .WillByDefault([this](LeAudioDeviceGroup* group,
                              LeAudioDevice* leAudioDevice,
                              types::BidirectionalPair<std::vector<uint8_t>>
                                  ccids) {
          if (group->GetState() !=
              types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
            if (group->GetTargetState() ==
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
              attach_to_stream_scheduled = true;
            }

            return false;
          }

@@ -1240,7 +1248,8 @@ class UnicastTestNoInit : public Test {
          // Inject the state
          group->SetTargetState(
              types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
          group->SetState(group->GetTargetState());
          group->SetState(
              types::AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
          streaming_groups[group->group_id_] = group;

          /* Assume CIG is created */
@@ -1248,6 +1257,7 @@ class UnicastTestNoInit : public Test {

          if (block_streaming_state_callback) return true;

          group->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
          do_in_main_thread(
              FROM_HERE,
              base::BindOnce(
@@ -2788,6 +2798,8 @@ class UnicastTestNoInit : public Test {
  std::map<int, LeAudioDeviceGroup*> streaming_groups;
  bool block_streaming_state_callback = false;

  bool attach_to_stream_scheduled = false;

  bluetooth::hci::IsoManager* iso_manager_;
  MockIsoManager* mock_iso_manager_;
  bluetooth::hci::iso_manager::CigCallbacks* cig_callbacks_ = nullptr;
@@ -7093,6 +7105,93 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}

TEST_F(UnicastTest, LateStreamConnectBasedOnContextType) {
  uint8_t group_size = 2;
  int group_id = 2;

  /* Scenario
   * 1. Two devices A and B are connect. Device A has all available context
   * types, Device B has no available context types.
   * 2. Stream creation to Device A has been started
   * 3. Device B notified us with new available Context Types - while A is not
   * yet streaming
   * 4. Make sure AttachToStream was called for Device B
   */

  // Report working CSIS
  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
      .WillByDefault(Return(true));

  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
      .WillByDefault(Invoke([&](int group_id) { return group_size; }));

  const RawAddress test_address0 = GetTestAddress(0);
  const RawAddress test_address1 = GetTestAddress(1);

  // First earbud connects
  ConnectCsisDevice(test_address0, 1 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontLeft,
                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
                    group_id, 1 /* rank*/);

  // Second earbud connects
  ConnectCsisDevice(test_address1, 2 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontRight,
                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                    group_id, 2 /* rank*/, true /*connect_through_csis*/);

  // Start streaming
  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _, _)).Times(1);
  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _, _)).Times(1);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();

  /* Simulate available context type being cleared */
  InjectAvailableContextTypes(test_address1, 2, types::AudioContexts(0),
                              types::AudioContexts(0));

  // Block streaming state
  block_streaming_state_callback = true;

  UpdateLocalSourceMetadata(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC);
  LocalAudioSourceResume(false);

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

  InjectAvailableContextTypes(test_address1, 2,
                              types::kLeAudioContextAllRemoteSinkOnly,
                              types::AudioContexts(0), false);

  // Now simulate group is finally streaming
  auto group = streaming_groups.at(group_id);
  do_in_main_thread(
      FROM_HERE,
      base::BindOnce(
          [](int group_id,
             bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks*
                 state_machine_callbacks,
             LeAudioDeviceGroup* group) {
            group->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

            state_machine_callbacks->StatusReportCb(
                group_id, GroupStreamStatus::STREAMING);
          },
          group_id, base::Unretained(this->state_machine_callbacks_),
          std::move(group)));

  SyncOnMainLoop();

  /* verify AttachToStream was called while stream was not yet created. */
  ASSERT_TRUE(attach_to_stream_scheduled);

  // Expect two iso channel to be fed with data
  uint8_t cis_count_out = 2;
  uint8_t cis_count_in = 0;
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
  SyncOnMainLoop();
}

TEST_F(UnicastTest,
       ReconnectedDeviceNotAttachedToStreamBecauseOfNotAvailableContext) {
  uint8_t group_size = 2;