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

Commit dd8bcaad authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Fix handling ACL diconnect in transient states

When transitioning to a state other than IDLE state and one of the
devices disconnectes, we should continue enabling the audio stream on
the remaining devices.

Bug: 360267958
Test: atest bluetooth_le_audio_test
Flag: EXEMPT; Fix covered with unit test
Change-Id: I3ed3cb42da38c560a737703d7cfe7e455801b046
parent 19410862
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -400,6 +400,9 @@ public:
  types::BidirectionalPair<types::AudioContexts> GetLatestAvailableContexts(void) const;

  bool IsInTransition(void) const;
  bool IsInTransitionTo(types::AseState state) const {
    return (GetTargetState() == state) && IsInTransition();
  }
  bool IsStreaming(void) const;
  bool IsReleasingOrIdle(void) const;

+6 −0
Original line number Diff line number Diff line
@@ -816,11 +816,17 @@ public:
        SendStreamingStatusCbIfNeeded(group);
        return;
      }

      if (!group->IsInTransitionTo(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
        /* do nothing if not transitioning to IDLE */
        return;
      }
    }

    /* Group is not connected and all the CISes are down.
     * Clean states and destroy HCI group
     */
    log::debug("Clearing inactive group");
    ClearGroup(group, true);
  }

+54 −0
Original line number Diff line number Diff line
@@ -7637,6 +7637,60 @@ TEST_F(StateMachineTest, StreamClearAfterReleaseAndConnectionTimeout) {
  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
}

TEST_F(StateMachineTest, DisconnectGroupMemberWhileEnablingStream) {
  auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 4;
  const auto num_devices = 2;

  /* Scenario
  1. Initiate streaming to 1 device but stay in QOS_CONFIGURED due to started enabling ASEs
  2. Second device is attached and immediately disconnected
  4. Groups should not go to IDLE as the first device is about to stream
  5. Continue streaming with the first device
  */

  // Prepare multiple fake connected devices in a group
  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices,
                                             kContextTypeConversational | kContextTypeMedia);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group);
  PrepareConfigureQosHandler(group);

  auto* leAudioDevice = group->GetFirstDevice();
  auto* firstDevice = leAudioDevice;
  auto* lastDevice = leAudioDevice;

  while (leAudioDevice) {
    lastDevice = leAudioDevice;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }

  InjectInitialIdleNotification(group);

  // Start the configuration up to the ENABLING state
  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
          group, context_type,
          {.sink = types::AudioContexts(context_type),
           .source = types::AudioContexts(context_type)}));
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

  ASSERT_EQ(group->NumOfConnected(), 2);

  // Inject second device disconnection
  InjectAclDisconnected(group, lastDevice);

  // Expect the group to not go to IDLE, as the first device is enabling
  ASSERT_NE(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);

  // Resume the interrupted enabling process
  InjectEnablingStateFroActiveAses(group, firstDevice);
  InjectStreamingStateFroActiveAses(group, firstDevice);

  // Verify we go to STREAMING
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
}

TEST_F(StateMachineTest, VerifyThereIsNoDoubleDataPathRemoval) {
  auto context_type = kContextTypeConversational;
  const auto leaudio_group_id = 4;