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

Commit 12da59bf authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Automerger Merge Worker
Browse files

leaudio: Add graceful disconnect support. am: 035e277b am: 7e20d871

parents 07b5b2a9 7e20d871
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -883,6 +883,14 @@ class LeAudioClientImpl : public LeAudioClient {
    BTA_GATTC_CancelOpen(0, address, false);

    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
      auto group = aseGroups_.FindById(leAudioDevice->group_id_);
      if (group &&
          group->GetState() ==
              le_audio::types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        leAudioDevice->closing_stream_for_disconnection_ = true;
        groupStateMachine_->StopStream(group);
        return;
      }
      DisconnectDevice(leAudioDevice);
      return;
    }
@@ -1308,6 +1316,7 @@ class LeAudioClientImpl : public LeAudioClient {

    callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, address);
    leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID;
    leAudioDevice->closing_stream_for_disconnection_ = false;
    leAudioDevice->encrypted_ = false;

    if (leAudioDevice->removing_device_) {
@@ -3238,6 +3247,20 @@ class LeAudioClientImpl : public LeAudioClient {
    }
  }

  void HandlePendingDeviceDisconnection(LeAudioDeviceGroup* group) {
    LOG_DEBUG();
    auto leAudioDevice = group->GetFirstDevice();
    while (leAudioDevice) {
      if (leAudioDevice->closing_stream_for_disconnection_) {
        leAudioDevice->closing_stream_for_disconnection_ = false;
        LOG_DEBUG("Disconnecting group id: %d, address: %s", group->group_id_,
                  leAudioDevice->address_.ToString().c_str());
        DisconnectDevice(leAudioDevice);
      }
      leAudioDevice = group->GetNextDevice(leAudioDevice);
    }
  }

  void StatusReportCb(int group_id, GroupStreamStatus status) {
    LOG(INFO) << __func__ << "status: " << static_cast<int>(status)
              << " audio_sender_state_: " << audio_sender_state_
@@ -3289,7 +3312,10 @@ class LeAudioClientImpl : public LeAudioClient {
          }
        }
        CancelStreamingRequest();
        if (group) {
          HandlePendingAvailableContexts(group);
          HandlePendingDeviceDisconnection(group);
        }
        break;
      }
      case GroupStreamStatus::RELEASING:
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ class LeAudioDevice {
   * This is true only during initial phase of first connection. */
  bool first_connection_;
  bool connecting_actively_;
  bool closing_stream_for_disconnection_;
  uint16_t conn_id_;
  bool encrypted_;
  int group_id_;
@@ -87,6 +88,7 @@ class LeAudioDevice {
        removing_device_(false),
        first_connection_(first_connection),
        connecting_actively_(first_connection),
        closing_stream_for_disconnection_(false),
        conn_id_(GATT_INVALID_CONN_ID),
        encrypted_(false),
        group_id_(group_id),
+74 −14
Original line number Diff line number Diff line
@@ -437,7 +437,7 @@ class UnicastTestNoInit : public Test {
        }));

    global_conn_id = 1;
    ON_CALL(mock_gatt_interface_, Open(_, _, _, _))
    ON_CALL(mock_gatt_interface_, Open(_, _, true, _))
        .WillByDefault(
            Invoke([&](tGATT_IF client_if, const RawAddress& remote_bda,
                       bool is_direct, bool opportunistic) {
@@ -1094,6 +1094,8 @@ class UnicastTestNoInit : public Test {
    tracks_[0].content_type = content_type;

    if (reconfigure_existing_stream) {
      EXPECT_CALL(*mock_unicast_audio_source_, SuspendedForReconfiguration())
          .Times(1);
      EXPECT_CALL(*mock_unicast_audio_source_, ConfirmStreamingRequest())
          .Times(1);
    } else {
@@ -1930,11 +1932,16 @@ TEST_F(UnicastTest, ConnectRemoteDisconnectOneEarbud) {
  /* For remote disconnection, expect stack to try background re-connect */
  EXPECT_CALL(mock_gatt_interface_, Open(gatt_if, test_address0, false, _))
      .Times(1);
  global_conn_id = 1; /* Reset to keep conn_id same during re-connect */

  EXPECT_CALL(mock_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
      .Times(1);
  InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();

  /* For background connect, test needs to Inject Connected Event */
  InjectConnectedEvent(test_address0, 1);
  SyncOnMainLoop();
}

TEST_F(UnicastTest, ConnectTwoEarbudsCsisGrouped) {
@@ -2084,6 +2091,10 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGrouped) {
      framework_encode_preference);
  if (app_register_callback) app_register_callback.Run(gatt_if, GATT_SUCCESS);

  /* For background connect, test needs to Inject Connected Event */
  InjectConnectedEvent(test_address0, 1);
  InjectConnectedEvent(test_address1, 2);

  // We need to wait for the storage callback before verifying stuff
  SyncOnMainLoop();
  ASSERT_TRUE(LeAudioClient::IsLeAudioClientRunning());
@@ -2174,6 +2185,9 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGroupedDifferently) {
      framework_encode_preference);
  if (app_register_callback) app_register_callback.Run(gatt_if, GATT_SUCCESS);

 /* For background connect, test needs to Inject Connected Event */
  InjectConnectedEvent(test_address0, 1);

  // We need to wait for the storage callback before verifying stuff
  SyncOnMainLoop();
  ASSERT_TRUE(LeAudioClient::IsLeAudioClientRunning());
@@ -2862,7 +2876,7 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}

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

@@ -2907,7 +2921,12 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) {
  for (auto& ase : device->ases_) {
    InjectCisDisconnected(group_id, ase.cis_conn_hdl);
  }
  DisconnectLeAudio(device->address_, 1);

  EXPECT_CALL(mock_gatt_interface_, Open(_, device->address_, false, false))
      .Times(1);

  auto conn_id = device->conn_id_;
  InjectDisconnectedEvent(device->conn_id_, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);

@@ -2916,16 +2935,8 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) {
  cis_count_in = 0;
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);

  // Reconnect the disconnected device
  auto rank = 1;
  auto location = codec_spec_conf::kLeAudioLocationFrontLeft;
  if (device->address_ == test_address1) {
    rank = 2;
    location = codec_spec_conf::kLeAudioLocationFrontRight;
  }
  ConnectCsisDevice(device->address_, 3 /*conn_id*/, location, location,
                    group_size, group_id, rank, true /*connect_through_csis*/,
                    false /* New device */);
  InjectConnectedEvent(device->address_, conn_id);
  SyncOnMainLoop();

  // Expect two iso channels to be fed with data
  cis_count_out = 2;
@@ -2933,5 +2944,54 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) {
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}

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

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

  // First earbud
  const RawAddress test_address0 = GetTestAddress(0);
  ConnectCsisDevice(test_address0, 1 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontLeft,
                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
                    group_id, 1 /* rank*/);

  // Second earbud
  const RawAddress test_address1 = GetTestAddress(1);
  ConnectCsisDevice(test_address1, 2 /*conn_id*/,
                    codec_spec_conf::kLeAudioLocationFrontRight,
                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                    group_id, 2 /* rank*/, true /*connect_through_csis*/);

  // Audio sessions are started only when device gets active
  EXPECT_CALL(*mock_unicast_audio_source_, Start(_, _)).Times(1);
  EXPECT_CALL(*mock_audio_sink_, Start(_, _)).Times(1);
  LeAudioClient::Get()->GroupSetActive(group_id);

  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);

  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
  Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
  SyncOnMainLoop();

  // Expect two iso channels 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);

  // Disconnect one device and expect the group to keep on streaming
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
  EXPECT_CALL(mock_gatt_interface_, Open(_, _, _, _)).Times(0);

  DisconnectLeAudio(test_address0, 1);
  DisconnectLeAudio(test_address1, 2);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
}

}  // namespace
}  // namespace le_audio