Loading system/bta/le_audio/client.cc +48 −31 Original line number Original line Diff line number Diff line Loading @@ -4543,11 +4543,32 @@ class LeAudioClientImpl : public LeAudioClient { BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts( BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts( LeAudioDeviceGroup* group, int remote_direction) { LeAudioDeviceGroup* group, int remote_direction) { auto remote_other_direction = (remote_direction == le_audio::types::kLeAudioDirectionSink ? le_audio::types::kLeAudioDirectionSource : le_audio::types::kLeAudioDirectionSink); auto other_direction_hal = (remote_other_direction == le_audio::types::kLeAudioDirectionSource ? audio_receiver_state_ : audio_sender_state_); auto is_streaming_other_direction = (other_direction_hal == AudioState::STARTED) || (other_direction_hal == AudioState::READY_TO_START); auto is_releasing_for_reconfiguration = (((audio_receiver_state_ == AudioState::RELEASING) || (audio_sender_state_ == AudioState::RELEASING)) && group->IsPendingConfiguration() && IsDirectionAvailableForCurrentConfiguration(group, remote_other_direction)); // Inject conversational when ringtone is played - this is required for all // Inject conversational when ringtone is played - this is required for all // the VoIP applications which are not using the telecom API. // the VoIP applications which are not using the telecom API. if ((remote_direction == le_audio::types::kLeAudioDirectionSink) && constexpr AudioContexts possible_voip_contexts = local_metadata_context_types_.source.test( LeAudioContextType::RINGTONE | LeAudioContextType::CONVERSATIONAL; LeAudioContextType::RINGTONE)) { if (local_metadata_context_types_.source.test_any(possible_voip_contexts) && ((remote_direction == le_audio::types::kLeAudioDirectionSink) || (remote_direction == le_audio::types::kLeAudioDirectionSource && is_streaming_other_direction))) { /* Simulate, we are already in the call. Sending RINGTONE when there is /* Simulate, we are already in the call. Sending RINGTONE when there is * no incoming call to accept or reject on TBS could confuse the remote * no incoming call to accept or reject on TBS could confuse the remote * device and interrupt the stream establish procedure. * device and interrupt the stream establish procedure. Loading @@ -4574,24 +4595,6 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioContextType::CONVERSATIONAL); LeAudioContextType::CONVERSATIONAL); } } auto remote_other_direction = (remote_direction == le_audio::types::kLeAudioDirectionSink ? le_audio::types::kLeAudioDirectionSource : le_audio::types::kLeAudioDirectionSink); auto other_direction_hal = (remote_other_direction == le_audio::types::kLeAudioDirectionSource ? audio_receiver_state_ : audio_sender_state_); auto is_streaming_other_direction = (other_direction_hal == AudioState::STARTED) || (other_direction_hal == AudioState::READY_TO_START); auto is_releasing_for_reconfiguration = (((audio_receiver_state_ == AudioState::RELEASING) || (audio_sender_state_ == AudioState::RELEASING)) && group->IsPendingConfiguration() && IsDirectionAvailableForCurrentConfiguration(group, remote_other_direction)); BidirectionalPair<AudioContexts> remote_metadata = { BidirectionalPair<AudioContexts> remote_metadata = { .sink = local_metadata_context_types_.source, .sink = local_metadata_context_types_.source, .source = local_metadata_context_types_.sink}; .source = local_metadata_context_types_.sink}; Loading @@ -4601,6 +4604,9 @@ class LeAudioClientImpl : public LeAudioClient { remote_metadata.sink.unset(LeAudioContextType::RINGTONE); remote_metadata.sink.unset(LeAudioContextType::RINGTONE); } } auto is_ongoing_call_on_other_direction = is_streaming_other_direction && (IsInVoipCall() || IsInCall()); LOG_DEBUG("local_metadata_context_types_.source= %s", LOG_DEBUG("local_metadata_context_types_.source= %s", ToString(local_metadata_context_types_.source).c_str()); ToString(local_metadata_context_types_.source).c_str()); LOG_DEBUG("local_metadata_context_types_.sink= %s", LOG_DEBUG("local_metadata_context_types_.sink= %s", Loading @@ -4617,6 +4623,8 @@ class LeAudioClientImpl : public LeAudioClient { (is_streaming_other_direction ? "True" : "False")); (is_streaming_other_direction ? "True" : "False")); LOG_DEBUG("is_releasing_for_reconfiguration= %s", LOG_DEBUG("is_releasing_for_reconfiguration= %s", (is_releasing_for_reconfiguration ? "True" : "False")); (is_releasing_for_reconfiguration ? "True" : "False")); LOG_DEBUG("is_ongoing_call_on_other_direction=%s", (is_ongoing_call_on_other_direction ? "True" : "False")); if (remote_metadata.get(remote_other_direction) if (remote_metadata.get(remote_other_direction) .test_any(kLeAudioContextAllBidir) && .test_any(kLeAudioContextAllBidir) && Loading @@ -4636,6 +4644,14 @@ class LeAudioClientImpl : public LeAudioClient { LOG_DEBUG( LOG_DEBUG( "Aligning the other direction remote metadata to add this direction " "Aligning the other direction remote metadata to add this direction " "context"); "context"); if (is_ongoing_call_on_other_direction) { /* Other direction is streaming and is in call */ remote_metadata.get(remote_direction) .unset_all(kLeAudioContextAllBidir); remote_metadata.get(remote_direction) .set(LeAudioContextType::CONVERSATIONAL); } else { if (!is_streaming_other_direction) { if (!is_streaming_other_direction) { // Do not take the obsolete metadata // Do not take the obsolete metadata remote_metadata.get(remote_other_direction).clear(); remote_metadata.get(remote_other_direction).clear(); Loading @@ -4648,6 +4664,7 @@ class LeAudioClientImpl : public LeAudioClient { .set_all(remote_metadata.get(remote_direction) & .set_all(remote_metadata.get(remote_direction) & ~kLeAudioContextAllRemoteSinkOnly); ~kLeAudioContextAllRemoteSinkOnly); } } } LOG_DEBUG("remote_metadata.source= %s", LOG_DEBUG("remote_metadata.source= %s", ToString(remote_metadata.source).c_str()); ToString(remote_metadata.source).c_str()); LOG_DEBUG("remote_metadata.sink= %s", LOG_DEBUG("remote_metadata.sink= %s", Loading system/bta/le_audio/le_audio_client_test.cc +89 −1 Original line number Original line Diff line number Diff line Loading @@ -5377,6 +5377,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); // SetInCall is used by GTBS - and only then we can expect CCID to be set. LeAudioClient::Get()->SetInCall(true); // Conversational is a bidirectional scenario so expect GTBS CCID // Conversational is a bidirectional scenario so expect GTBS CCID // in the metadata for both directions. Can be called twice when one // in the metadata for both directions. Can be called twice when one // direction resume after the other and metadata is updated. // direction resume after the other and metadata is updated. Loading @@ -5396,8 +5399,10 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { cis_count_out = 2; cis_count_out = 2; cis_count_in = 2; cis_count_in = 2; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40); TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40); LeAudioClient::Get()->SetInCall(false); // Stop // Stop StopStreaming(group_id); StopStreaming(group_id, true); // Switch back to MEDIA // Switch back to MEDIA ccids = {.sink = {gmcs_ccid}, .source = {}}; ccids = {.sink = {gmcs_ccid}, .source = {}}; Loading @@ -5417,6 +5422,87 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); } } TEST_F(UnicastTest, TwoEarbudsVoipStreamingVerifyMetadataUpdate) { uint8_t group_size = 2; int group_id = 2; /* * Scenario * 1. Configure stream for the VOIP * 2. Verify CONVERSATIONAL metadata and context is used. * 3. Resume LocalSink * 4. Make sure there is no change of the metadata and context */ // Report working CSIS ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) .WillByDefault(Return(true)); // First earbud const RawAddress test_address0 = GetTestAddress(0); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true)) .Times(1); 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); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true)) .Times(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*/); constexpr int gtbs_ccid = 2; ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) .WillByDefault(Invoke([&](int group_id) { return 2; })); LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */); LeAudioClient::Get()->GroupSetActive(group_id); SyncOnMainLoop(); // VOIP not using Telecom API has no ccids. types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {}, .source = {}}; EXPECT_CALL( mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, _, ccids)) .Times(AtLeast(1)); UpdateLocalSourceMetadata(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH); UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC); LocalAudioSourceResume(); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); // Verify Data transfer are sending. The LocalSink is not yet resumed. uint8_t cis_count_out = 2; uint8_t cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 0); types::BidirectionalPair<types::AudioContexts> contexts = { .sink = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL), .source = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)}; EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, contexts, ccids)) .Times(AtLeast(1)); LocalAudioSinkResume(); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); } TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { uint8_t group_size = 2; uint8_t group_size = 2; int group_id = 2; int group_id = 2; Loading Loading @@ -5509,6 +5595,8 @@ TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { conversiational_contexts, ccids)) conversiational_contexts, ccids)) .Times(AtLeast(1)); .Times(AtLeast(1)); LeAudioClient::Get()->SetInCall(true); LocalAudioSourceResume(true); LocalAudioSourceResume(true); SyncOnMainLoop(); SyncOnMainLoop(); Loading Loading
system/bta/le_audio/client.cc +48 −31 Original line number Original line Diff line number Diff line Loading @@ -4543,11 +4543,32 @@ class LeAudioClientImpl : public LeAudioClient { BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts( BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts( LeAudioDeviceGroup* group, int remote_direction) { LeAudioDeviceGroup* group, int remote_direction) { auto remote_other_direction = (remote_direction == le_audio::types::kLeAudioDirectionSink ? le_audio::types::kLeAudioDirectionSource : le_audio::types::kLeAudioDirectionSink); auto other_direction_hal = (remote_other_direction == le_audio::types::kLeAudioDirectionSource ? audio_receiver_state_ : audio_sender_state_); auto is_streaming_other_direction = (other_direction_hal == AudioState::STARTED) || (other_direction_hal == AudioState::READY_TO_START); auto is_releasing_for_reconfiguration = (((audio_receiver_state_ == AudioState::RELEASING) || (audio_sender_state_ == AudioState::RELEASING)) && group->IsPendingConfiguration() && IsDirectionAvailableForCurrentConfiguration(group, remote_other_direction)); // Inject conversational when ringtone is played - this is required for all // Inject conversational when ringtone is played - this is required for all // the VoIP applications which are not using the telecom API. // the VoIP applications which are not using the telecom API. if ((remote_direction == le_audio::types::kLeAudioDirectionSink) && constexpr AudioContexts possible_voip_contexts = local_metadata_context_types_.source.test( LeAudioContextType::RINGTONE | LeAudioContextType::CONVERSATIONAL; LeAudioContextType::RINGTONE)) { if (local_metadata_context_types_.source.test_any(possible_voip_contexts) && ((remote_direction == le_audio::types::kLeAudioDirectionSink) || (remote_direction == le_audio::types::kLeAudioDirectionSource && is_streaming_other_direction))) { /* Simulate, we are already in the call. Sending RINGTONE when there is /* Simulate, we are already in the call. Sending RINGTONE when there is * no incoming call to accept or reject on TBS could confuse the remote * no incoming call to accept or reject on TBS could confuse the remote * device and interrupt the stream establish procedure. * device and interrupt the stream establish procedure. Loading @@ -4574,24 +4595,6 @@ class LeAudioClientImpl : public LeAudioClient { LeAudioContextType::CONVERSATIONAL); LeAudioContextType::CONVERSATIONAL); } } auto remote_other_direction = (remote_direction == le_audio::types::kLeAudioDirectionSink ? le_audio::types::kLeAudioDirectionSource : le_audio::types::kLeAudioDirectionSink); auto other_direction_hal = (remote_other_direction == le_audio::types::kLeAudioDirectionSource ? audio_receiver_state_ : audio_sender_state_); auto is_streaming_other_direction = (other_direction_hal == AudioState::STARTED) || (other_direction_hal == AudioState::READY_TO_START); auto is_releasing_for_reconfiguration = (((audio_receiver_state_ == AudioState::RELEASING) || (audio_sender_state_ == AudioState::RELEASING)) && group->IsPendingConfiguration() && IsDirectionAvailableForCurrentConfiguration(group, remote_other_direction)); BidirectionalPair<AudioContexts> remote_metadata = { BidirectionalPair<AudioContexts> remote_metadata = { .sink = local_metadata_context_types_.source, .sink = local_metadata_context_types_.source, .source = local_metadata_context_types_.sink}; .source = local_metadata_context_types_.sink}; Loading @@ -4601,6 +4604,9 @@ class LeAudioClientImpl : public LeAudioClient { remote_metadata.sink.unset(LeAudioContextType::RINGTONE); remote_metadata.sink.unset(LeAudioContextType::RINGTONE); } } auto is_ongoing_call_on_other_direction = is_streaming_other_direction && (IsInVoipCall() || IsInCall()); LOG_DEBUG("local_metadata_context_types_.source= %s", LOG_DEBUG("local_metadata_context_types_.source= %s", ToString(local_metadata_context_types_.source).c_str()); ToString(local_metadata_context_types_.source).c_str()); LOG_DEBUG("local_metadata_context_types_.sink= %s", LOG_DEBUG("local_metadata_context_types_.sink= %s", Loading @@ -4617,6 +4623,8 @@ class LeAudioClientImpl : public LeAudioClient { (is_streaming_other_direction ? "True" : "False")); (is_streaming_other_direction ? "True" : "False")); LOG_DEBUG("is_releasing_for_reconfiguration= %s", LOG_DEBUG("is_releasing_for_reconfiguration= %s", (is_releasing_for_reconfiguration ? "True" : "False")); (is_releasing_for_reconfiguration ? "True" : "False")); LOG_DEBUG("is_ongoing_call_on_other_direction=%s", (is_ongoing_call_on_other_direction ? "True" : "False")); if (remote_metadata.get(remote_other_direction) if (remote_metadata.get(remote_other_direction) .test_any(kLeAudioContextAllBidir) && .test_any(kLeAudioContextAllBidir) && Loading @@ -4636,6 +4644,14 @@ class LeAudioClientImpl : public LeAudioClient { LOG_DEBUG( LOG_DEBUG( "Aligning the other direction remote metadata to add this direction " "Aligning the other direction remote metadata to add this direction " "context"); "context"); if (is_ongoing_call_on_other_direction) { /* Other direction is streaming and is in call */ remote_metadata.get(remote_direction) .unset_all(kLeAudioContextAllBidir); remote_metadata.get(remote_direction) .set(LeAudioContextType::CONVERSATIONAL); } else { if (!is_streaming_other_direction) { if (!is_streaming_other_direction) { // Do not take the obsolete metadata // Do not take the obsolete metadata remote_metadata.get(remote_other_direction).clear(); remote_metadata.get(remote_other_direction).clear(); Loading @@ -4648,6 +4664,7 @@ class LeAudioClientImpl : public LeAudioClient { .set_all(remote_metadata.get(remote_direction) & .set_all(remote_metadata.get(remote_direction) & ~kLeAudioContextAllRemoteSinkOnly); ~kLeAudioContextAllRemoteSinkOnly); } } } LOG_DEBUG("remote_metadata.source= %s", LOG_DEBUG("remote_metadata.source= %s", ToString(remote_metadata.source).c_str()); ToString(remote_metadata.source).c_str()); LOG_DEBUG("remote_metadata.sink= %s", LOG_DEBUG("remote_metadata.sink= %s", Loading
system/bta/le_audio/le_audio_client_test.cc +89 −1 Original line number Original line Diff line number Diff line Loading @@ -5377,6 +5377,9 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); // SetInCall is used by GTBS - and only then we can expect CCID to be set. LeAudioClient::Get()->SetInCall(true); // Conversational is a bidirectional scenario so expect GTBS CCID // Conversational is a bidirectional scenario so expect GTBS CCID // in the metadata for both directions. Can be called twice when one // in the metadata for both directions. Can be called twice when one // direction resume after the other and metadata is updated. // direction resume after the other and metadata is updated. Loading @@ -5396,8 +5399,10 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { cis_count_out = 2; cis_count_out = 2; cis_count_in = 2; cis_count_in = 2; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40); TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40); LeAudioClient::Get()->SetInCall(false); // Stop // Stop StopStreaming(group_id); StopStreaming(group_id, true); // Switch back to MEDIA // Switch back to MEDIA ccids = {.sink = {gmcs_ccid}, .source = {}}; ccids = {.sink = {gmcs_ccid}, .source = {}}; Loading @@ -5417,6 +5422,87 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) { Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); } } TEST_F(UnicastTest, TwoEarbudsVoipStreamingVerifyMetadataUpdate) { uint8_t group_size = 2; int group_id = 2; /* * Scenario * 1. Configure stream for the VOIP * 2. Verify CONVERSATIONAL metadata and context is used. * 3. Resume LocalSink * 4. Make sure there is no change of the metadata and context */ // Report working CSIS ON_CALL(mock_csis_client_module_, IsCsisClientRunning()) .WillByDefault(Return(true)); // First earbud const RawAddress test_address0 = GetTestAddress(0); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true)) .Times(1); 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); EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true)) .Times(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*/); constexpr int gtbs_ccid = 2; ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) .WillByDefault(Invoke([&](int group_id) { return 2; })); LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */); LeAudioClient::Get()->GroupSetActive(group_id); SyncOnMainLoop(); // VOIP not using Telecom API has no ccids. types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {}, .source = {}}; EXPECT_CALL( mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, _, ccids)) .Times(AtLeast(1)); UpdateLocalSourceMetadata(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH); UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC); LocalAudioSourceResume(); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_); // Verify Data transfer are sending. The LocalSink is not yet resumed. uint8_t cis_count_out = 2; uint8_t cis_count_in = 0; TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 0); types::BidirectionalPair<types::AudioContexts> contexts = { .sink = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL), .source = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)}; EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::CONVERSATIONAL, contexts, ccids)) .Times(AtLeast(1)); LocalAudioSinkResume(); SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); } TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { uint8_t group_size = 2; uint8_t group_size = 2; int group_id = 2; int group_id = 2; Loading Loading @@ -5509,6 +5595,8 @@ TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) { conversiational_contexts, ccids)) conversiational_contexts, ccids)) .Times(AtLeast(1)); .Times(AtLeast(1)); LeAudioClient::Get()->SetInCall(true); LocalAudioSourceResume(true); LocalAudioSourceResume(true); SyncOnMainLoop(); SyncOnMainLoop(); Loading