Merge "DSDA: Supporting ECC for call sequencing" into main
diff --git a/src/com/android/server/telecom/CallAudioWatchdog.java b/src/com/android/server/telecom/CallAudioWatchdog.java
index dcfc80f..4ca237a 100644
--- a/src/com/android/server/telecom/CallAudioWatchdog.java
+++ b/src/com/android/server/telecom/CallAudioWatchdog.java
@@ -28,6 +28,7 @@
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Handler;
+import android.os.Process;
import android.telecom.Log;
import android.telecom.Logging.EventManager;
import android.telecom.PhoneAccountHandle;
@@ -288,6 +289,11 @@
&& config.getAudioAttributes().getUsage()
== AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ // Skip if the client's pid is same as myself
+ if (config.getClientPid() == Process.myPid()) {
+ continue;
+ }
+
// If an audio session is idle, we don't count it as playing. It must be in a
// started state.
boolean isPlaying = config.getPlayerState() == PLAYER_STATE_STARTED;
diff --git a/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java b/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java
index 4adc8d0..37bc065 100644
--- a/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java
+++ b/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java
@@ -260,9 +260,7 @@
}
private void removeCallFromCallsManager(Call call, DisconnectCause cause) {
- if (cause.getCode() != DisconnectCause.REJECTED) {
- mCallsManager.markCallAsDisconnected(call, cause);
- }
+ mCallsManager.markCallAsDisconnected(call, cause);
mCallsManager.removeCall(call);
}
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalCallSequencingAdapterTest.java b/tests/src/com/android/server/telecom/tests/TransactionalCallSequencingAdapterTest.java
new file mode 100644
index 0000000..6449ea7
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/TransactionalCallSequencingAdapterTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.OutcomeReceiver;
+import android.telecom.CallException;
+import android.telecom.DisconnectCause;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.TransactionalCallSequencingAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+
+/**
+ * Unit tests for {@link TransactionalCallSequencingAdapter}.
+ *
+ * These tests verify the behavior of the TransactionalCallSequencingAdapter, focusing on
+ * how it interacts with the TransactionManager and CallsManager, particularly in the
+ * context of asynchronous operations and feature flag configurations (e.g., setting
+ * rejected calls to a disconnected state).
+ */
+public class TransactionalCallSequencingAdapterTest extends TelecomTestCase {
+
+ private static final String CALL_ID_1 = "1";
+ private static final DisconnectCause REJECTED_DISCONNECT_CAUSE =
+ new DisconnectCause(DisconnectCause.REJECTED);
+
+ @Mock private Call mMockCall1;
+ @Mock private Context mMockContext;
+ @Mock private CallsManager mCallsManager;
+ @Mock private TransactionManager mTransactionManager;
+
+ private TransactionalCallSequencingAdapter mAdapter;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ when(mMockCall1.getId()).thenReturn(CALL_ID_1);
+ when(mMockContext.getResources()).thenReturn(Mockito.mock(Resources.class));
+ mAdapter = new TransactionalCallSequencingAdapter(
+ mTransactionManager, mCallsManager, true);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Tests the scenario where an incoming call is rejected and the onSetDisconnect is called.
+ * Verifies that {@link CallsManager#markCallAsDisconnected} *is* called and that the
+ * {@link OutcomeReceiver} receives the correct result, handling the asynchronous nature of
+ * the operation.
+ */
+ @Test
+ public void testOnSetDisconnected() {
+ // GIVEN -a new incoming call that is rejected
+
+ // Create a CompletableFuture to control the asynchronous operation.
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+
+ // Mock the TransactionManager's addTransaction method.
+ setupAddTransactionMock(future);
+
+ // Create a mock OutcomeReceiver to verify interactions.
+ OutcomeReceiver<CallTransactionResult, CallException> resultReceiver =
+ mock(OutcomeReceiver.class);
+
+ // WHEN - Call onSetDisconnected and get the result future.
+ mAdapter.onSetDisconnected(
+ mMockCall1,
+ REJECTED_DISCONNECT_CAUSE,
+ mock(CallTransaction.class),
+ resultReceiver);
+
+ // Simulate the asynchronous operation completing.
+ completeAddTransactionSuccessfully(future);
+
+ // THEN - Verify that markCallAsDisconnected and the receiver's onResult were called.
+ verifyMarkCallAsDisconnectedAndReceiverResult(resultReceiver);
+ }
+ /**
+ * Sets up the mock behavior for {@link TransactionManager#addTransaction}.
+ *
+ * @param future The CompletableFuture to be returned by the mocked method.
+ */
+ private void setupAddTransactionMock(CompletableFuture<Boolean> future) {
+ when(mTransactionManager.addTransaction(any(), any())).thenAnswer(invocation -> {
+ return future; // Return the provided future.
+ });
+ }
+ /**
+ * Simulates the successful completion of the asynchronous operation tracked by the given
+ * future. Captures the {@link OutcomeReceiver} passed to
+ * {@link TransactionManager#addTransaction}, completes the future, and invokes
+ * {@link OutcomeReceiver#onResult} with a successful result.
+ *
+ * @param future The CompletableFuture to complete.
+ */
+ private void completeAddTransactionSuccessfully(CompletableFuture<Boolean> future) {
+ // Capture the OutcomeReceiver passed to addTransaction.
+ ArgumentCaptor<OutcomeReceiver<CallTransactionResult, CallException>> captor =
+ ArgumentCaptor.forClass(OutcomeReceiver.class);
+ verify(mTransactionManager).addTransaction(any(CallTransaction.class), captor.capture());
+
+ // Complete the future to signal the end of the asynchronous operation.
+ future.complete(true);
+
+ // Create a successful CallTransactionResult.
+ CallTransactionResult callTransactionResult = new CallTransactionResult(
+ CallTransactionResult.RESULT_SUCCEED,
+ "EndCallTransaction: RESULT_SUCCEED");
+
+ // Invoke onResult on the captured OutcomeReceiver.
+ captor.getValue().onResult(callTransactionResult);
+
+ }
+ /**
+ * Verifies that {@link CallsManager#markCallAsDisconnected} and the provided
+ * {@link OutcomeReceiver}'s {@code onResult} method were called. Also waits for the future
+ * to complete.
+ *
+ * @param resultReceiver The mock OutcomeReceiver.
+ */
+ private void verifyMarkCallAsDisconnectedAndReceiverResult(
+ OutcomeReceiver<CallTransactionResult, CallException> resultReceiver) {
+ verify(mCallsManager, times(1)).markCallAsDisconnected(
+ mMockCall1,
+ REJECTED_DISCONNECT_CAUSE);
+ verify(resultReceiver).onResult(any());
+ }
+}
\ No newline at end of file