Loading system/bta/le_audio/le_audio_client_test.cc +59 −4 Original line number Diff line number Diff line Loading @@ -563,6 +563,40 @@ class UnicastTestNoInit : public Test { return true; }); ON_CALL(mock_state_machine_, AttachToStream(_, _)) .WillByDefault([this](LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { if (group->GetState() != types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { return false; } auto* stream_conf = &group->stream_conf; for (auto& ase : leAudioDevice->ases_) { if (!ase.active) continue; // And also skip the ase establishment procedure which should // be tested as part of the state machine unit tests ase.data_path_state = types::AudioStreamDataPathState::DATA_PATH_ESTABLISHED; ase.cis_conn_hdl = iso_con_counter_++; ase.active = true; ase.state = types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING; /* Copied from state_machine.cc Enabling->Streaming*/ if (ase.direction == le_audio::types::kLeAudioDirectionSource) { stream_conf->source_streams.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); } else { stream_conf->sink_streams.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); } } return true; }); ON_CALL(mock_state_machine_, StartStream(_, _)) .WillByDefault([this](LeAudioDeviceGroup* group, types::LeAudioContextType context_type) { Loading Loading @@ -681,8 +715,10 @@ class UnicastTestNoInit : public Test { if (ases_pair.sink) { ases_pair.sink->data_path_state = types::AudioStreamDataPathState::CIS_ASSIGNED; ases_pair.sink->active = false; } if (ases_pair.source) { ases_pair.source->active = false; ases_pair.source->data_path_state = types::AudioStreamDataPathState::CIS_ASSIGNED; } Loading Loading @@ -965,7 +1001,8 @@ class UnicastTestNoInit : public Test { uint32_t sink_audio_allocation, uint32_t source_audio_allocation, uint8_t group_size, int group_id, uint8_t rank, bool connect_through_csis = false) { bool connect_through_csis = false, bool new_device = true) { SetSampleDatabaseEarbudsValid(conn_id, addr, sink_audio_allocation, source_audio_allocation, true, /*add_csis*/ true, /*add_cas*/ Loading @@ -976,9 +1013,11 @@ class UnicastTestNoInit : public Test { OnConnectionState(ConnectionState::CONNECTED, addr)) .Times(1); if (new_device) { EXPECT_CALL(mock_client_callbacks_, OnGroupNodeStatus(addr, group_id, GroupNodeStatus::ADDED)) .Times(1); } if (connect_through_csis) { // Add it the way CSIS would do: add to group and then connect Loading Loading @@ -2843,6 +2882,22 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) { cis_count_out = 1; 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 */); // Expect two iso channels to be fed with data cis_count_out = 2; cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); } } // namespace Loading system/bta/le_audio/state_machine.cc +1 −1 Original line number Diff line number Diff line Loading @@ -1657,6 +1657,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING; CisCreateForDevice(leAudioDevice); return; } Loading Loading @@ -1716,7 +1717,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ CisCreateForDevice(leAudioDevice); return; } Loading system/bta/le_audio/state_machine_test.cc +139 −0 Original line number Diff line number Diff line Loading @@ -2290,5 +2290,144 @@ TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, static_cast<types::LeAudioContextType>(context_type))); } static void InjectCisDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { bluetooth::hci::iso_manager::cis_disconnected_evt event; auto* ase = leAudioDevice->GetFirstActiveAse(); while (ase) { event.reason = 0x08; event.cig_id = group->group_id_; event.cis_conn_hdl = ase->cis_conn_hdl; LeAudioGroupStateMachine::Get()->ProcessHciNotifCisDisconnected( group, leAudioDevice, &event); ase = leAudioDevice->GetNextActiveAse(ase); } } static void InjectAclDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { LeAudioGroupStateMachine::Get()->ProcessHciNotifAclDisconnected( group, leAudioDevice); } TEST_F(StateMachineTest, testAttachDeviceToTheStream) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; // 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(2); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); InjectInitialIdleNotification(group); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream( group, static_cast<types::LeAudioContextType>(context_type)); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); // Inject CIS and ACL disconnection of first device InjectCisDisconnected(group, lastDevice); InjectAclDisconnected(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); lastDevice->conn_id_ = 3; group->UpdateActiveContextsMap(); auto* stream_conf = &group->stream_conf; /* Second device got reconnect. Try to get it to the stream seamlessly * Code take from client.cc */ le_audio::types::AudioLocations sink_group_audio_locations = 0; uint8_t sink_num_of_active_ases = 0; for (auto [cis_handle, audio_location] : stream_conf->sink_streams) { sink_group_audio_locations |= audio_location; sink_num_of_active_ases++; } le_audio::types::AudioLocations source_group_audio_locations = 0; uint8_t source_num_of_active_ases = 0; for (auto [cis_handle, audio_location] : stream_conf->source_streams) { source_group_audio_locations |= audio_location; source_num_of_active_ases++; } for (auto& ent : stream_conf->conf->confs) { if (ent.direction == le_audio::types::kLeAudioDirectionSink) { /* Sink*/ if (!lastDevice->ConfigureAses( ent, group->GetCurrentContextType(), &sink_num_of_active_ases, sink_group_audio_locations, source_group_audio_locations, true)) { LOG(INFO) << __func__ << " Could not set sink configuration of " << stream_conf->conf->name; return; } } else { /* Source*/ if (!lastDevice->ConfigureAses( ent, group->GetCurrentContextType(), &source_num_of_active_ases, sink_group_audio_locations, source_group_audio_locations, true)) { LOG(INFO) << __func__ << " Could not set source configuration of " << stream_conf->conf->name; return; } } } EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); } } // namespace internal } // namespace le_audio Loading
system/bta/le_audio/le_audio_client_test.cc +59 −4 Original line number Diff line number Diff line Loading @@ -563,6 +563,40 @@ class UnicastTestNoInit : public Test { return true; }); ON_CALL(mock_state_machine_, AttachToStream(_, _)) .WillByDefault([this](LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { if (group->GetState() != types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { return false; } auto* stream_conf = &group->stream_conf; for (auto& ase : leAudioDevice->ases_) { if (!ase.active) continue; // And also skip the ase establishment procedure which should // be tested as part of the state machine unit tests ase.data_path_state = types::AudioStreamDataPathState::DATA_PATH_ESTABLISHED; ase.cis_conn_hdl = iso_con_counter_++; ase.active = true; ase.state = types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING; /* Copied from state_machine.cc Enabling->Streaming*/ if (ase.direction == le_audio::types::kLeAudioDirectionSource) { stream_conf->source_streams.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); } else { stream_conf->sink_streams.emplace_back( std::make_pair(ase.cis_conn_hdl, *ase.codec_config.audio_channel_allocation)); } } return true; }); ON_CALL(mock_state_machine_, StartStream(_, _)) .WillByDefault([this](LeAudioDeviceGroup* group, types::LeAudioContextType context_type) { Loading Loading @@ -681,8 +715,10 @@ class UnicastTestNoInit : public Test { if (ases_pair.sink) { ases_pair.sink->data_path_state = types::AudioStreamDataPathState::CIS_ASSIGNED; ases_pair.sink->active = false; } if (ases_pair.source) { ases_pair.source->active = false; ases_pair.source->data_path_state = types::AudioStreamDataPathState::CIS_ASSIGNED; } Loading Loading @@ -965,7 +1001,8 @@ class UnicastTestNoInit : public Test { uint32_t sink_audio_allocation, uint32_t source_audio_allocation, uint8_t group_size, int group_id, uint8_t rank, bool connect_through_csis = false) { bool connect_through_csis = false, bool new_device = true) { SetSampleDatabaseEarbudsValid(conn_id, addr, sink_audio_allocation, source_audio_allocation, true, /*add_csis*/ true, /*add_cas*/ Loading @@ -976,9 +1013,11 @@ class UnicastTestNoInit : public Test { OnConnectionState(ConnectionState::CONNECTED, addr)) .Times(1); if (new_device) { EXPECT_CALL(mock_client_callbacks_, OnGroupNodeStatus(addr, group_id, GroupNodeStatus::ADDED)) .Times(1); } if (connect_through_csis) { // Add it the way CSIS would do: add to group and then connect Loading Loading @@ -2843,6 +2882,22 @@ TEST_F(UnicastTest, TwoEarbuds2ndDisconnect) { cis_count_out = 1; 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 */); // Expect two iso channels to be fed with data cis_count_out = 2; cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920); } } // namespace Loading
system/bta/le_audio/state_machine.cc +1 −1 Original line number Diff line number Diff line Loading @@ -1657,6 +1657,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING; CisCreateForDevice(leAudioDevice); return; } Loading Loading @@ -1716,7 +1717,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine { if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { /* We are here because of the reconnection of the single device. */ CisCreateForDevice(leAudioDevice); return; } Loading
system/bta/le_audio/state_machine_test.cc +139 −0 Original line number Diff line number Diff line Loading @@ -2290,5 +2290,144 @@ TEST_F(StateMachineTest, testConfigureDataPathForAdsp) { ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream( group, static_cast<types::LeAudioContextType>(context_type))); } static void InjectCisDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { bluetooth::hci::iso_manager::cis_disconnected_evt event; auto* ase = leAudioDevice->GetFirstActiveAse(); while (ase) { event.reason = 0x08; event.cig_id = group->group_id_; event.cis_conn_hdl = ase->cis_conn_hdl; LeAudioGroupStateMachine::Get()->ProcessHciNotifCisDisconnected( group, leAudioDevice, &event); ase = leAudioDevice->GetNextActiveAse(ase); } } static void InjectAclDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) { LeAudioGroupStateMachine::Get()->ProcessHciNotifAclDisconnected( group, leAudioDevice); } TEST_F(StateMachineTest, testAttachDeviceToTheStream) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; const auto num_devices = 2; // 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(2); EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2); InjectInitialIdleNotification(group); // Start the configuration and stream Media content LeAudioGroupStateMachine::Get()->StartStream( group, static_cast<types::LeAudioContextType>(context_type)); // Check if group has transitioned to a proper state ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); // Inject CIS and ACL disconnection of first device InjectCisDisconnected(group, lastDevice); InjectAclDisconnected(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); lastDevice->conn_id_ = 3; group->UpdateActiveContextsMap(); auto* stream_conf = &group->stream_conf; /* Second device got reconnect. Try to get it to the stream seamlessly * Code take from client.cc */ le_audio::types::AudioLocations sink_group_audio_locations = 0; uint8_t sink_num_of_active_ases = 0; for (auto [cis_handle, audio_location] : stream_conf->sink_streams) { sink_group_audio_locations |= audio_location; sink_num_of_active_ases++; } le_audio::types::AudioLocations source_group_audio_locations = 0; uint8_t source_num_of_active_ases = 0; for (auto [cis_handle, audio_location] : stream_conf->source_streams) { source_group_audio_locations |= audio_location; source_num_of_active_ases++; } for (auto& ent : stream_conf->conf->confs) { if (ent.direction == le_audio::types::kLeAudioDirectionSink) { /* Sink*/ if (!lastDevice->ConfigureAses( ent, group->GetCurrentContextType(), &sink_num_of_active_ases, sink_group_audio_locations, source_group_audio_locations, true)) { LOG(INFO) << __func__ << " Could not set sink configuration of " << stream_conf->conf->name; return; } } else { /* Source*/ if (!lastDevice->ConfigureAses( ent, group->GetCurrentContextType(), &source_num_of_active_ases, sink_group_audio_locations, source_group_audio_locations, true)) { LOG(INFO) << __func__ << " Could not set source configuration of " << stream_conf->conf->name; return; } } } EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_, lastDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _)) .Times(AtLeast(3)); LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice); // Check if group keeps streaming ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING); } } // namespace internal } // namespace le_audio