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

Commit b6dc5780 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes Ia5ad66b0,If0d7fc83,Idf5f22c9

* changes:
  leaudio: Improve cleanup on Bluetooth stack off
  leaudio: Fix cleaning autoconnect flag
  leaudio: Add graceful disconnect support.
parents 249161b4 ab4a7d4b
Loading
Loading
Loading
Loading
+38 −3
Original line number Diff line number Diff line
@@ -883,6 +883,18 @@ class LeAudioClientImpl : public LeAudioClient {
    BTA_GATTC_CancelOpen(0, address, false);

    if (leAudioDevice->conn_id_ != GATT_INVALID_CONN_ID) {
      /* User is disconnecting the device, we shall remove the autoconnect flag
       */
      btif_storage_set_leaudio_autoconnect(address, false);

      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 +1320,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_) {
@@ -2559,10 +2572,15 @@ class LeAudioClientImpl : public LeAudioClient {

  void Cleanup(base::Callback<void()> cleanupCb) {
    if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);

    if (active_group_id_ != bluetooth::groups::kGroupUnknown) {
      /* Bluetooth turned off while streaming */
      StopAudio();
      ClientAudioIntefraceRelease();
    }
    groupStateMachine_->Cleanup();
    leAudioDevices_.Cleanup();
    aseGroups_.Cleanup();
    StopAudio();
    leAudioDevices_.Cleanup();
    if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);

    std::move(cleanupCb).Run();
@@ -3233,6 +3251,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_
@@ -3284,7 +3316,10 @@ class LeAudioClientImpl : public LeAudioClient {
          }
        }
        CancelStreamingRequest();
        if (group) {
          HandlePendingAvailableContexts(group);
          HandlePendingDeviceDisconnection(group);
        }
        break;
      }
      case GroupStreamStatus::RELEASING:
+42 −2
Original line number Diff line number Diff line
@@ -102,7 +102,42 @@ int LeAudioDeviceGroup::NumOfConnected(types::LeAudioContextType context_type) {
      });
}

void LeAudioDeviceGroup::Cleanup(void) { leAudioDevices_.clear(); }
void LeAudioDeviceGroup::Cleanup(void) {
  /* Bluetooth is off while streaming - disconnect CISes and remove CIG */
  if (GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
    if (!stream_conf.sink_streams.empty()) {
      for (auto [cis_handle, audio_location] : stream_conf.sink_streams) {
        bluetooth::hci::IsoManager::GetInstance()->DisconnectCis(
            cis_handle, HCI_ERR_PEER_USER);

        if (stream_conf.source_streams.empty()) {
          continue;
        }
        uint16_t cis_hdl = cis_handle;
        stream_conf.source_streams.erase(
            std::remove_if(
                stream_conf.source_streams.begin(),
                stream_conf.source_streams.end(),
                [cis_hdl](auto& pair) { return pair.first == cis_hdl; }),
            stream_conf.source_streams.end());
      }
    }

    if (!stream_conf.source_streams.empty()) {
      for (auto [cis_handle, audio_location] : stream_conf.source_streams) {
        bluetooth::hci::IsoManager::GetInstance()->DisconnectCis(
            cis_handle, HCI_ERR_PEER_USER);
      }
    }
  }

  /* Note: CIG will stay in the controller. We cannot remove it here, because
   * Cises are not yet disconnected.
   * When user start Bluetooth, HCI Reset should remove it
   */

  leAudioDevices_.clear();
}

void LeAudioDeviceGroup::Deactivate(void) {
  for (auto* leAudioDevice = GetFirstActiveDevice(); leAudioDevice;
@@ -1900,6 +1935,11 @@ void LeAudioDevices::Dump(int fd, int group_id) {
  }
}

void LeAudioDevices::Cleanup(void) { leAudioDevices_.clear(); }
void LeAudioDevices::Cleanup(void) {
  for (auto const& device : leAudioDevices_) {
    device->DisconnectAcl();
  }
  leAudioDevices_.clear();
}

}  // namespace le_audio
+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),
+87 −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) {
@@ -1963,6 +1970,13 @@ TEST_F(UnicastTest, ConnectTwoEarbudsCsisGrouped) {
                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                    group_id, 2 /* rank*/, true /*connect_through_csis*/);

  Mock::VerifyAndClearExpectations(&mock_btif_storage_);

  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, false))
      .Times(1);
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, false))
      .Times(1);

  // Verify grouping information
  std::vector<RawAddress> devs =
      LeAudioClient::Get()->GetGroupDevices(group_id);
@@ -1999,12 +2013,18 @@ TEST_F(UnicastTest, ConnectTwoEarbudsCsisGroupUnknownAtConnect) {
                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
                    group_id, 2 /* rank*/, true /*connect_through_csis*/);

  Mock::VerifyAndClearExpectations(&mock_btif_storage_);

  // Verify grouping information
  std::vector<RawAddress> devs =
      LeAudioClient::Get()->GetGroupDevices(group_id);
  ASSERT_NE(std::find(devs.begin(), devs.end(), test_address0), devs.end());
  ASSERT_NE(std::find(devs.begin(), devs.end(), test_address1), devs.end());

  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, false))
      .Times(1);
  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, false))
      .Times(1);
  DisconnectLeAudio(test_address0, 1);
  DisconnectLeAudio(test_address1, 2);
}
@@ -2084,6 +2104,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 +2198,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 +2889,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 +2934,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 +2948,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 +2957,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
+0 −2
Original line number Diff line number Diff line
@@ -151,8 +151,6 @@ class LeAudioClientInterfaceImpl : public LeAudioClientInterface,
    do_in_main_thread(FROM_HERE,
                      Bind(&LeAudioClient::Disconnect,
                           Unretained(LeAudioClient::Get()), address));
    do_in_jni_thread(
        FROM_HERE, Bind(&btif_storage_set_leaudio_autoconnect, address, false));
  }

  void GroupAddNode(const int group_id, const RawAddress& address) override {