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

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

leaudio: Attach device if it contains available context types am: 30e921f7

parents d1a73a25 30e921f7
Loading
Loading
Loading
Loading
+12 −7
Original line number Diff line number Diff line
@@ -1794,12 +1794,7 @@ class LeAudioClientImpl : public LeAudioClient {
        return;
      }

      auto group_metadata_contexts =
          get_bidirectional(group->GetMetadataContexts());
      auto device_available_contexts = leAudioDevice->GetAvailableContexts();
      if (group_metadata_contexts.test_any(device_available_contexts)) {
      AttachToStreamingGroupIfNeeded(leAudioDevice);
      }

    } else if (hdl == leAudioDevice->audio_supp_cont_hdls_.val_hdl) {
      BidirectionalPair<AudioContexts> supp_audio_contexts;
@@ -2965,10 +2960,20 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    LeAudioDeviceGroup* group = aseGroups_.FindById(active_group_id_);

    auto group_metadata_contexts =
        get_bidirectional(group->GetMetadataContexts());
    auto device_available_contexts = leAudioDevice->GetAvailableContexts();
    if (!group_metadata_contexts.test_any(device_available_contexts)) {
      LOG_INFO("%s does is not have required context type",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      return;
    }

    LOG_INFO("Attaching to group: %d", leAudioDevice->group_id_);

    /* Restore configuration */
    LeAudioDeviceGroup* group = aseGroups_.FindById(active_group_id_);
    auto* stream_conf = &group->stream_conf;

    if (audio_sender_state_ == AudioState::IDLE &&
+102 −0
Original line number Diff line number Diff line
@@ -6002,6 +6002,108 @@ TEST_F(UnicastTest, TwoEarbuds2ndLateConnect) {
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}

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

  /* Scenario
   * 1. Two devices A and B are streaming
   * 2. Device A Release ASE and removes all available context types
   * 3. Device B keeps streaming
   * 4. Device A disconnectes
   * 5. Device A reconnect and should not be attached to the stream
   */

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

  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();

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

  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);

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

  // 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);

  /* Get group and Device A */
  auto group = streaming_groups.at(group_id);
  ASSERT_NE(group, nullptr);
  auto device = group->GetFirstDevice();

  /* Simulate available context type being cleared */
  InjectAvailableContextTypes(device->address_, device->conn_id_,
                              types::AudioContexts(0), types::AudioContexts(0));

  /* Simulate ASE releasing and CIS Disconnection */
  for (auto& ase : device->ases_) {
    /* Releasing state */
    if (!ase.active) {
      continue;
    }

    std::vector<uint8_t> releasing_state = {
        ase.id, static_cast<uint8_t>(
                    types::AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING)};
    InjectNotificationEvent(device->address_, device->conn_id_,
                            ase.hdls.val_hdl, releasing_state);
    SyncOnMainLoop();
    InjectCisDisconnected(group_id, ase.cis_conn_hdl);
    SyncOnMainLoop();
  }

  cis_count_out = 1;
  cis_count_in = 0;
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);

  /* Device A will disconnect, and do not reconnect automatically */
  ON_CALL(mock_gatt_interface_,
          Open(_, device->address_, BTM_BLE_DIRECT_CONNECTION, _))
      .WillByDefault(Return());

  /* Disconnect first device */
  auto conn_id = device->conn_id_;
  InjectDisconnectedEvent(conn_id, GATT_CONN_TERMINATE_PEER_USER);
  SyncOnMainLoop();

  /* For background connect, test needs to Inject Connected Event */
  InjectConnectedEvent(device->address_, conn_id);
  SyncOnMainLoop();

  /* Check single device is streaming */
  cis_count_out = 1;
  cis_count_in = 0;
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
}

TEST_F(UnicastTest, TwoEarbuds2ndReleaseAseRemoveAvailableContextAndBack) {
  uint8_t group_size = 2;
  int group_id = 2;
+10 −0
Original line number Diff line number Diff line
@@ -159,6 +159,16 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
      return false;
    }

    /* This is cautious - mostly needed for unit test only */
    auto group_metadata_contexts =
        get_bidirectional(group->GetMetadataContexts());
    auto device_available_contexts = leAudioDevice->GetAvailableContexts();
    if (!group_metadata_contexts.test_any(device_available_contexts)) {
      LOG_INFO("%s does is not have required context type",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      return false;
    }

    if (!group->Configure(group->GetConfigurationContextType(),
                          group->GetMetadataContexts(), ccids)) {
      LOG_ERROR(" failed to set ASE configuration");
+97 −0
Original line number Diff line number Diff line
@@ -4290,6 +4290,103 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
  ASSERT_NE(ase->retrans_nb, 0);
}

TEST_F(StateMachineTest, testAttachDeviceToTheStreamDeviceNoAvailableContext) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;
  const auto num_devices = 2;

  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);

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

  PrepareConfigureCodecHandler(group);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReleaseHandler(group);

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

  auto expected_devices_written = 0;
  while (leAudioDevice) {
    /* Three Writes:
     * 1: Codec Config
     * 2: Codec QoS
     * 3: Enabling
     */
    lastDevice = leAudioDevice;
    EXPECT_CALL(gatt_queue,
                WriteCharacteristic(leAudioDevice->conn_id_,
                                    leAudioDevice->ctp_hdls_.val_hdl, _,
                                    GATT_WRITE_NO_RSP, _, _))
        .Times(AtLeast(3));
    expected_devices_written++;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }
  ASSERT_EQ(expected_devices_written, num_devices);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);

  InjectInitialIdleNotification(group);

  // Start the configuration and stream Media content
  LeAudioGroupStateMachine::Get()->StartStream(
      group, context_type,
      {.sink = types::AudioContexts(context_type),
       .source = types::AudioContexts(context_type)});

  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);

  // Inject CIS and ACL disconnection of first device
  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
  InjectAclDisconnected(group, lastDevice);

  // Check if group keeps streaming
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // Connect the disconnected device BUT remove MEDIA from available Contex
  // Types
  lastDevice->conn_id_ = 3;
  auto test_context_type = kContextTypeUnspecified | kContextTypeConversational;
  lastDevice->SetAvailableContexts(
      {.sink = test_context_type, .source = test_context_type});
  lastDevice->SetConnectionState(DeviceConnectState::CONNECTED);

  // Make sure ASE with disconnected CIS are not left in STREAMING
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSink,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSource,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);

  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
                                              lastDevice->ctp_hdls_.val_hdl, _,
                                              GATT_WRITE_NO_RSP, _, _))
      .Times(AtLeast(0));

  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0);
  ASSERT_EQ(LeAudioGroupStateMachine::Get()->AttachToStream(
                group, lastDevice, {.sink = {media_ccid}, .source = {}}),
            false);

  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
}

TEST_F(StateMachineTest, testAutonomousConfiguredAndAttachToStream) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;