Telecom Transactional APIs Implementation
Transactional APIs are defined as APIs that use
android.os.OutcomeReceivers. The receivers are to be completed by
Telecom for CallControl opertaions and the Client for CallEventCallback
operations. Doing so ensures the client and telecom are in sync with
call states and that operations can be completed on both ends.
bug: 249779561
Test: unit + CTS
Change-Id: Ib4cc7c7b05491e4f61c011a5d7af5bc24125d34d
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index e8c69d4..d9756fa 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -21,13 +21,13 @@
<uses-sdk
android:minSdkVersion="23"
- android:targetSdkVersion="23" />
+ android:targetSdkVersion="33" />
<!-- TODO: Needed because we call BluetoothAdapter.getDefaultAdapter() statically, and
BluetoothAdapter is a final class. -->
<uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
-
<!-- TODO: Needed because we call ActivityManager.getCurrentUser() statically. -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/tests/src/com/android/server/telecom/tests/CallAttributesTests.java b/tests/src/com/android/server/telecom/tests/CallAttributesTests.java
new file mode 100644
index 0000000..acb913e
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallAttributesTests.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.telecom.CallAttributes;
+import android.telecom.PhoneAccountHandle;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class CallAttributesTests extends TelecomTestCase {
+
+ private static final PhoneAccountHandle mHandle = new PhoneAccountHandle(
+ new ComponentName("foo", "bar"), "1");
+ private static final String TEST_NAME = "Larry Page";
+ private static final Uri TEST_URI = Uri.fromParts("tel", "abc", "123");
+ @Mock private Parcel mParcel;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testRequiredAttributes() {
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI).build();
+
+ assertEquals(CallAttributes.DIRECTION_OUTGOING, callAttributes.getDirection());
+ assertEquals(mHandle, callAttributes.getPhoneAccountHandle());
+ }
+
+ @Test
+ public void testInvalidDirectionAttributes() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new CallAttributes.Builder(mHandle, -1, TEST_NAME, TEST_URI).build()
+ );
+ }
+
+ @Test
+ public void testInvalidCallType() {
+ assertThrows(IllegalArgumentException.class, () ->
+ new CallAttributes.Builder(mHandle, CallAttributes.DIRECTION_OUTGOING,
+ TEST_NAME, TEST_URI).setCallType(-1).build()
+ );
+ }
+
+ @Test
+ public void testOptionalAttributes() {
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .build();
+
+ assertEquals(CallAttributes.DIRECTION_OUTGOING, callAttributes.getDirection());
+ assertEquals(mHandle, callAttributes.getPhoneAccountHandle());
+ assertEquals(CallAttributes.SUPPORTS_SET_INACTIVE, callAttributes.getCallCapabilities());
+ assertEquals(CallAttributes.AUDIO_CALL, callAttributes.getCallType());
+ assertEquals(TEST_URI, callAttributes.getAddress());
+ assertEquals(TEST_NAME, callAttributes.getDisplayName());
+ }
+
+ @Test
+ public void testDescribeContents() {
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI).build();
+
+ assertEquals(0, callAttributes.describeContents());
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ // GIVEN
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .build();
+
+ // WHEN
+ callAttributes.writeToParcel(mParcel, 0);
+
+ // THEN
+ verify(mParcel, times(1))
+ .writeParcelable(isA(PhoneAccountHandle.class), isA(Integer.class));
+ verify(mParcel, times(1)).writeCharSequence(isA(CharSequence.class));
+ verify(mParcel, times(1))
+ .writeParcelable(isA(Uri.class), isA(Integer.class));
+ verify(mParcel, times(3)).writeInt(isA(Integer.class));
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallControlTest.java b/tests/src/com/android/server/telecom/tests/CallControlTest.java
new file mode 100644
index 0000000..279fcc8
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallControlTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.os.OutcomeReceiver;
+import android.telecom.CallControl;
+import android.telecom.CallException;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.ClientTransactionalServiceRepository;
+import com.android.internal.telecom.ICallControl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.UUID;
+
+public class CallControlTest extends TelecomTestCase {
+
+ private static final PhoneAccountHandle mHandle = new PhoneAccountHandle(
+ new ComponentName("foo", "bar"), "1");
+
+ @Mock
+ private ICallControl mICallControl;
+ @Mock
+ private ClientTransactionalServiceRepository mRepository;
+ private static final String CALL_ID_1 = UUID.randomUUID().toString();
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testGetCallId() {
+ CallControl control = new CallControl(CALL_ID_1, mICallControl, mRepository, mHandle);
+ assertEquals(CALL_ID_1, control.getCallId().toString());
+ }
+
+ @Test
+ public void testCallControlHitsIllegalStateException() {
+ CallControl control = new CallControl(CALL_ID_1, null, mRepository, mHandle);
+ assertThrows(IllegalStateException.class, () ->
+ control.setInactive(Runnable::run, result -> {
+ }));
+ }
+
+ @Test
+ public void testClose() {
+ // GIVEN
+ CallControl control = new CallControl(CALL_ID_1, mICallControl, mRepository, mHandle);
+
+ // WHEN
+ control.close();
+
+ // THEN
+ verify(mRepository, times(1))
+ .removeCallFromServiceWrapper(mHandle, CALL_ID_1);
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallExceptionTests.java b/tests/src/com/android/server/telecom/tests/CallExceptionTests.java
new file mode 100644
index 0000000..3f8336d
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallExceptionTests.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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 android.telecom.CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.isA;
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telecom.CallException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class CallExceptionTests extends TelecomTestCase {
+
+ @Mock private Parcel mParcel;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testSimpleException() {
+ String message = "test message";
+ CallException exception = new CallException(message);
+ assertTrue(exception.getMessage().contains(message));
+ assertEquals(CallException.CODE_ERROR_UNKNOWN, exception.getCode());
+ }
+
+ @Test
+ public void testExceptionWithCode() {
+ String message = "test message";
+ CallException exception = new CallException(message, CODE_CALL_CANNOT_BE_SET_TO_ACTIVE);
+ assertTrue(exception.getMessage().contains(message));
+ assertEquals(CODE_CALL_CANNOT_BE_SET_TO_ACTIVE, exception.getCode());
+ }
+
+ @Test
+ public void testDescribeContents() {
+ String message = "test message";
+ CallException exception = new CallException(message);
+ assertEquals(0, exception.describeContents());
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ // GIVEN
+ String message = "test message";
+ CallException exception = new CallException(message);
+
+ // WHEN
+ exception.writeToParcel(mParcel, 0);
+
+ // THEN
+ verify(mParcel, times(1)).writeString8(isA(String.class));
+ verify(mParcel, times(1)).writeInt(isA(Integer.class));
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallTest.java b/tests/src/com/android/server/telecom/tests/CallTest.java
index 0492f0a..a7619ad 100644
--- a/tests/src/com/android/server/telecom/tests/CallTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallTest.java
@@ -18,15 +18,18 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -54,6 +57,7 @@
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.TransactionalServiceWrapper;
import com.android.server.telecom.ui.ToastFactory;
import org.junit.After;
@@ -87,6 +91,7 @@
@Mock private Toast mMockToast;
@Mock private PhoneNumberUtilsAdapter mMockPhoneNumberUtilsAdapter;
@Mock private ConnectionServiceWrapper mMockConnectionService;
+ @Mock private TransactionalServiceWrapper mMockTransactionalService;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@@ -372,4 +377,87 @@
assertTrue(call.wasDndCheckComputedForCall());
assertTrue(call.isCallSuppressedByDoNotDisturb());
}
+
+ @Test
+ public void testGetConnectionServiceWrapper() {
+ Call call = new Call(
+ "1", /* callId */
+ mContext,
+ mMockCallsManager,
+ mLock,
+ null /* ConnectionServiceRepository */,
+ mMockPhoneNumberUtilsAdapter,
+ TEST_ADDRESS,
+ null /* GatewayInfo */,
+ null /* connectionManagerPhoneAccountHandle */,
+ SIM_1_HANDLE,
+ Call.CALL_DIRECTION_UNDEFINED,
+ false /* shouldAttachToExistingConnection*/,
+ true /* isConference */,
+ mMockClockProxy,
+ mMockToastProxy);
+
+ assertNull(call.getConnectionServiceWrapper());
+ assertFalse(call.isTransactionalCall());
+ call.setConnectionService(mMockConnectionService);
+ assertEquals(mMockConnectionService, call.getConnectionServiceWrapper());
+ call.setIsTransactionalCall(true);
+ assertTrue(call.isTransactionalCall());
+ assertNull(call.getConnectionServiceWrapper());
+ call.setTransactionServiceWrapper(mMockTransactionalService);
+ assertEquals(mMockTransactionalService, call.getTransactionServiceWrapper());
+ }
+
+ @Test
+ public void testCallEventCallbacksWereCalled() {
+ Call call = new Call(
+ "1", /* callId */
+ mContext,
+ mMockCallsManager,
+ mLock,
+ null /* ConnectionServiceRepository */,
+ mMockPhoneNumberUtilsAdapter,
+ TEST_ADDRESS,
+ null /* GatewayInfo */,
+ null /* connectionManagerPhoneAccountHandle */,
+ SIM_1_HANDLE,
+ Call.CALL_DIRECTION_UNDEFINED,
+ false /* shouldAttachToExistingConnection*/,
+ true /* isConference */,
+ mMockClockProxy,
+ mMockToastProxy);
+
+ // setup
+ call.setIsTransactionalCall(true);
+ assertTrue(call.isTransactionalCall());
+ assertNull(call.getConnectionServiceWrapper());
+ call.setTransactionServiceWrapper(mMockTransactionalService);
+ assertEquals(mMockTransactionalService, call.getTransactionServiceWrapper());
+
+ // assert CallEventCallback#onSetInactive is called
+ call.setState(CallState.ACTIVE, "test");
+ call.hold();
+ verify(mMockTransactionalService, times(1)).onSetInactive(call);
+
+ // assert CallEventCallback#onSetActive is called
+ call.setState(CallState.ON_HOLD, "test");
+ call.unhold();
+ verify(mMockTransactionalService, times(1)).onSetActive(call);
+
+ // assert CallEventCallback#onAnswer is called
+ call.setState(CallState.RINGING, "test");
+ call.answer(0);
+ verify(mMockTransactionalService, times(1)).onAnswer(call, 0);
+
+ // assert CallEventCallback#onReject is called
+ call.setState(CallState.RINGING, "test");
+ call.reject(0);
+ verify(mMockTransactionalService, times(1)).onReject(call, 0);
+
+ // assert CallEventCallback#onDisconnect is called
+ call.setState(CallState.ACTIVE, "test");
+ call.disconnect();
+ verify(mMockTransactionalService, times(1)).onDisconnect(call,
+ call.getDisconnectCause());
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 8fe64e2..59efb56 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -50,6 +50,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.OutcomeReceiver;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -60,6 +61,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telecom.CallException;
import android.telecom.VideoProfile;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.MediumTest;
@@ -122,7 +124,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -1774,6 +1775,88 @@
assertTrue(argumentCaptor.getValue().contains("Unavailable phoneAccountHandle"));
}
+ public class LatchedOutcomeReceiver implements OutcomeReceiver<Boolean,
+ CallException> {
+ CountDownLatch mCountDownLatch;
+ Boolean mIsOnResultExpected;
+
+ public LatchedOutcomeReceiver(CountDownLatch latch, boolean isOnResultExpected){
+ mCountDownLatch = latch;
+ mIsOnResultExpected = isOnResultExpected;
+ }
+
+ @Override
+ public void onResult(Boolean result) {
+ if(mIsOnResultExpected) {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onError(CallException error) {
+ OutcomeReceiver.super.onError(error);
+ if(!mIsOnResultExpected){
+ mCountDownLatch.countDown();
+ }
+ }
+ }
+
+ @SmallTest
+ @Test
+ public void testCanHold() {
+ Call newCall = addSpyCall();
+ when(newCall.isTransactionalCall()).thenReturn(true);
+ when(newCall.can(Connection.CAPABILITY_SUPPORT_HOLD)).thenReturn(false);
+ assertFalse(mCallsManager.canHold(newCall));
+ when(newCall.can(Connection.CAPABILITY_SUPPORT_HOLD)).thenReturn(true);
+ assertTrue(mCallsManager.canHold(newCall));
+ }
+
+ @MediumTest
+ @Test
+ public void testHoldTransactional() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ Call newCall = addSpyCall();
+
+ // case 1: no active call, no need to put the call on hold
+ when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(null);
+ mCallsManager.transactionHoldPotentialActiveCallForNewCall(newCall,
+ new LatchedOutcomeReceiver(latch, true));
+ waitForCountDownLatch(latch);
+
+ // case 2: active call == new call, no need to put the call on hold
+ latch = new CountDownLatch(1);
+ when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(newCall);
+ mCallsManager.transactionHoldPotentialActiveCallForNewCall(newCall,
+ new LatchedOutcomeReceiver(latch, true));
+ waitForCountDownLatch(latch);
+
+ // case 3: cannot hold current active call early check
+ Call cannotHoldCall = addSpyCall(SIM_1_HANDLE, null,
+ CallState.ACTIVE, 0, 0);
+ latch = new CountDownLatch(1);
+ when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(cannotHoldCall);
+ mCallsManager.transactionHoldPotentialActiveCallForNewCall(newCall,
+ new LatchedOutcomeReceiver(latch, false));
+ waitForCountDownLatch(latch);
+
+ // case 4: activeCall != newCall && canHold(activeCall)
+ Call canHoldCall = addSpyCall(SIM_1_HANDLE, null,
+ CallState.ACTIVE, Connection.CAPABILITY_HOLD, 0);
+ latch = new CountDownLatch(1);
+ when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(canHoldCall);
+ mCallsManager.transactionHoldPotentialActiveCallForNewCall(newCall,
+ new LatchedOutcomeReceiver(latch, true));
+ waitForCountDownLatch(latch);
+ }
+
+ public void waitForCountDownLatch(CountDownLatch latch) throws InterruptedException {
+ boolean success = latch.await(5000, TimeUnit.MILLISECONDS);
+ if (!success) {
+ fail("assertOnResultWasReceived success failed");
+ }
+ }
+
private Call addSpyCall() {
return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
}
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index 26f0f6d..efa83ea 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -684,6 +684,75 @@
assertEquals(TEST_LABEL, registeredAccount.getLabel());
}
+ @MediumTest
+ @Test
+ public void testSecurityExceptionIsThrownWhenSelfManagedLacksPermissions() {
+ PhoneAccountHandle handle = makeQuickAccountHandle(
+ new ComponentName("self", "managed"), "selfie1");
+
+ PhoneAccount accountWithoutCapability = new PhoneAccount.Builder(handle, "label")
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .build();
+
+ assertFalse(mRegistrar.hasTransactionalCallCapabilites(accountWithoutCapability));
+
+ try {
+ mRegistrar.registerPhoneAccount(accountWithoutCapability);
+ fail("should not be able to register account");
+ } catch (SecurityException securityException) {
+ // test pass
+ } finally {
+ mRegistrar.unregisterPhoneAccount(handle);
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testSelfManagedPhoneAccountWithTransactionalOperations() {
+ PhoneAccountHandle handle = makeQuickAccountHandle(
+ new ComponentName("self", "managed"), "selfie1");
+
+ PhoneAccount accountWithCapability = new PhoneAccount.Builder(handle, "label")
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
+ PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)
+ .build();
+
+ assertTrue(mRegistrar.hasTransactionalCallCapabilites(accountWithCapability));
+
+ try {
+ mRegistrar.registerPhoneAccount(accountWithCapability);
+ PhoneAccount registeredAccount = mRegistrar.getPhoneAccountUnchecked(handle);
+ assertEquals(TEST_LABEL, registeredAccount.getLabel().toString());
+ } finally {
+ mRegistrar.unregisterPhoneAccount(handle);
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testRegisterPhoneAccountAmendsSelfManagedCapabilityInternally() {
+ // GIVEN
+ PhoneAccountHandle handle = makeQuickAccountHandle(
+ new ComponentName("self", "managed"), "selfie1");
+ PhoneAccount accountWithCapability = new PhoneAccount.Builder(handle, "label")
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)
+ .build();
+
+ assertTrue(mRegistrar.hasTransactionalCallCapabilites(accountWithCapability));
+
+ try {
+ // WHEN
+ mRegistrar.registerPhoneAccount(accountWithCapability);
+ PhoneAccount registeredAccount = mRegistrar.getPhoneAccountUnchecked(handle);
+ // THEN
+ assertEquals(PhoneAccount.CAPABILITY_SELF_MANAGED, (registeredAccount.getCapabilities()
+ & PhoneAccount.CAPABILITY_SELF_MANAGED));
+ } finally {
+ mRegistrar.unregisterPhoneAccount(handle);
+ }
+ }
+
/**
* Tests to ensure that when registering a self-managed PhoneAccount, it cannot also be defined
* as a call provider, connection manager, or sim subscription.
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index a5aa936..e8d4226 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -36,9 +36,11 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.telecom.CallAttributes;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -46,6 +48,7 @@
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telecom.ICallEventCallback;
import com.android.internal.telecom.ITelecomService;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallIntentProcessor;
@@ -57,6 +60,9 @@
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.components.UserCallIntentProcessor;
import com.android.server.telecom.components.UserCallIntentProcessorFactory;
+import com.android.server.telecom.voip.IncomingCallTransaction;
+import com.android.server.telecom.voip.OutgoingCallTransaction;
+import com.android.server.telecom.voip.TransactionManager;
import org.junit.After;
import org.junit.Before;
@@ -67,7 +73,6 @@
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -95,14 +100,18 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.when;
@RunWith(JUnit4.class)
public class TelecomServiceImplTest extends TelecomTestCase {
private static final String CALLING_PACKAGE = TelecomServiceImplTest.class.getPackageName();
+ private static final String TEST_NAME = "Alan Turing";
+ private static final Uri TEST_URI = Uri.fromParts("tel", "abc", "123");
public static final String TEST_PACKAGE = "com.test";
public static final String PACKAGE_NAME = "test";
@@ -176,6 +185,8 @@
@Mock private UserCallIntentProcessor mUserCallIntentProcessor;
private PackageManager mPackageManager;
@Mock private ApplicationInfo mApplicationInfo;
+ @Mock private ICallEventCallback mICallEventCallback;
+ @Mock private TransactionManager mTransactionManager;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@@ -225,6 +236,7 @@
mSubscriptionManagerAdapter,
mSettingsSecureAdapter,
mLock);
+ telecomServiceImpl.setTransactionManager(mTransactionManager);
mTSIBinder = telecomServiceImpl.getBinder();
mComponentContextFixture.setTelecomManager(mTelecomManager);
when(mTelecomManager.getDefaultDialerPackage()).thenReturn(DEFAULT_DIALER_PACKAGE);
@@ -356,6 +368,82 @@
.setUserSelectedOutgoingPhoneAccount(eq(TEL_PA_HANDLE_16), any(UserHandle.class));
}
+ @Test
+ public void testAddCallWithOutgoingCall() throws RemoteException {
+ // GIVEN
+ CallAttributes mOutgoingCallAttributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ mTSIBinder.addCall(mOutgoingCallAttributes, mICallEventCallback, "1", CALLING_PACKAGE);
+
+ // THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(OutgoingCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testAddCallWithIncomingCall() throws RemoteException {
+ // GIVEN
+ CallAttributes mIncomingCallAttributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_INCOMING, TEST_NAME, TEST_URI)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ mTSIBinder.addCall(mIncomingCallAttributes, mICallEventCallback, "1", CALLING_PACKAGE);
+
+ // THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(IncomingCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testAddCallWithManagedPhoneAccount() throws RemoteException {
+ // GIVEN
+ CallAttributes attributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI).build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT)
+ .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ .build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ // THEN
+ try {
+ mTSIBinder.addCall(attributes, mICallEventCallback, "1", CALLING_PACKAGE);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // pass
+ }
+ }
+
@SmallTest
@Test
public void testSetUserSelectedOutgoingPhoneAccountFailure() throws RemoteException {
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
new file mode 100644
index 0000000..9ae05eb
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2022 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.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.isA;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.os.UserHandle;
+import android.telecom.CallAttributes;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallerInfoLookupHelper;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.EndCallTransaction;
+import com.android.server.telecom.voip.HoldCallTransaction;
+import com.android.server.telecom.voip.IncomingCallTransaction;
+import com.android.server.telecom.voip.OutgoingCallTransaction;
+import com.android.server.telecom.voip.HoldActiveCallForNewCallTransaction;
+import com.android.server.telecom.voip.RequestFocusTransaction;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+
+public class TransactionTests extends TelecomTestCase {
+
+ private static final String CALL_ID_1 = "1";
+
+ private static final PhoneAccountHandle mHandle = new PhoneAccountHandle(
+ new ComponentName("foo", "bar"), "1");
+ private static final String TEST_NAME = "Sergey Brin";
+ private static final Uri TEST_URI = Uri.fromParts("tel", "abc", "123");
+
+ @Mock private Call mMockCall1;
+ @Mock private Context mMockContext;
+ @Mock private CallsManager mCallsManager;
+ @Mock private ToastFactory mToastFactory;
+ @Mock private ClockProxy mClockProxy;
+ @Mock private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
+ @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
+
+ private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
+ };
+ private static final Uri TEST_ADDRESS = Uri.parse("tel:555-1212");
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(mMockCall1.getId()).thenReturn(CALL_ID_1);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testEndCallTransactionWithDisconnect() throws Exception {
+ // GIVEN
+ EndCallTransaction transaction =
+ new EndCallTransaction(mCallsManager, true, 0, mMockCall1);
+
+ // WHEN
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .markCallAsDisconnected(mMockCall1, new DisconnectCause(0));
+ verify(mCallsManager, never())
+ .rejectCall(mMockCall1, 0);
+ verify(mCallsManager, times(1))
+ .markCallAsRemoved(mMockCall1);
+ }
+
+ @Test
+ public void testEndCallTransactionWithReject() throws Exception {
+ // GIVEN
+ EndCallTransaction transaction =
+ new EndCallTransaction(mCallsManager, false, 0, mMockCall1);
+
+ // WHEN
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, never())
+ .markCallAsDisconnected(mMockCall1, new DisconnectCause(0));
+ verify(mCallsManager, times(1))
+ .rejectCall(mMockCall1, 0);
+ verify(mCallsManager, times(1))
+ .markCallAsRemoved(mMockCall1);
+ }
+
+ @Test
+ public void testHoldCallTransaction() throws Exception {
+ // GIVEN
+ Call spyCall = createSpyCall(null, CallState.ACTIVE, CALL_ID_1);
+
+ HoldCallTransaction transaction =
+ new HoldCallTransaction(mCallsManager, spyCall);
+
+ // WHEN
+ doAnswer(invocation -> {
+ Call call = invocation.getArgument(0);
+ call.setState(CallState.ON_HOLD, "manual set");
+ return null;
+ }).when(mCallsManager).markCallAsOnHold(spyCall);
+
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .markCallAsOnHold(spyCall);
+
+ assertEquals(CallState.ON_HOLD, spyCall.getState());
+ }
+
+ @Test
+ public void testTransactionalRequestFocus() throws Exception {
+ // GIVEN
+ RequestFocusTransaction transaction =
+ new RequestFocusTransaction(mCallsManager, mMockCall1);
+
+ // WHEN
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .transactionRequestNewFocusCall(eq(mMockCall1), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testTransactionalHoldActiveCallForNewCall() throws Exception {
+ // GIVEN
+ HoldActiveCallForNewCallTransaction transaction =
+ new HoldActiveCallForNewCallTransaction(mCallsManager, mMockCall1);
+
+ // WHEN
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .transactionHoldPotentialActiveCallForNewCall(eq(mMockCall1),
+ isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testOutgoingCallTransaction() throws Exception {
+ // GIVEN
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI).build();
+
+ OutgoingCallTransaction transaction =
+ new OutgoingCallTransaction(CALL_ID_1, mMockContext, callAttributes, mCallsManager);
+
+ // WHEN
+ when(mMockContext.getOpPackageName()).thenReturn("testPackage");
+ when(mMockContext.checkCallingPermission(android.Manifest.permission.CALL_PRIVILEGED))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mCallsManager.isOutgoingCallPermitted(callAttributes.getPhoneAccountHandle()))
+ .thenReturn(true);
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .startOutgoingCall(isA(Uri.class),
+ isA(PhoneAccountHandle.class),
+ isA(Bundle.class),
+ isA(UserHandle.class),
+ isA(Intent.class),
+ nullable(String.class));
+ }
+
+ @Test
+ public void testIncomingCallTransaction() throws Exception {
+ // GIVEN
+ CallAttributes callAttributes = new CallAttributes.Builder(mHandle,
+ CallAttributes.DIRECTION_INCOMING, TEST_NAME, TEST_URI).build();
+
+ IncomingCallTransaction transaction =
+ new IncomingCallTransaction(CALL_ID_1, callAttributes, mCallsManager);
+
+ // WHEN
+ when(mCallsManager.isIncomingCallPermitted(callAttributes.getPhoneAccountHandle()))
+ .thenReturn(true);
+ transaction.processTransaction(null);
+
+ // THEN
+ verify(mCallsManager, times(1))
+ .processIncomingCallIntent(isA(PhoneAccountHandle.class),
+ isA(Bundle.class),
+ isA(Boolean.class));
+ }
+
+ private Call createSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState, String id) {
+ when(mCallsManager.getCallerInfoLookupHelper()).thenReturn(mCallerInfoLookupHelper);
+
+ Call call = new Call(id,
+ mMockContext,
+ mCallsManager,
+ mLock, /* ConnectionServiceRepository */
+ null,
+ mPhoneNumberUtilsAdapter,
+ TEST_ADDRESS,
+ null /* GatewayInfo */,
+ null /* ConnectionManagerAccount */,
+ targetPhoneAccount,
+ Call.CALL_DIRECTION_INCOMING,
+ false /* shouldAttachToExistingConnection*/,
+ false /* isConference */,
+ mClockProxy,
+ mToastFactory);
+
+ Call callSpy = Mockito.spy(call);
+
+ callSpy.setState(initialState, "manual set in test");
+
+ // Mocks some methods to not call the real method.
+ doNothing().when(callSpy).unhold();
+ doNothing().when(callSpy).hold();
+ doNothing().when(callSpy).disconnect();
+
+ return callSpy;
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
new file mode 100644
index 0000000..1ea81e7
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.isA;
+
+
+import android.content.ComponentName;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.ICallControl;
+import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.TransactionalServiceRepository;
+import com.android.server.telecom.TransactionalServiceWrapper;
+import com.android.server.telecom.voip.EndCallTransaction;
+import com.android.server.telecom.voip.HoldCallTransaction;
+import com.android.server.telecom.voip.SerialTransaction;
+import com.android.server.telecom.voip.TransactionManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class TransactionalServiceWrapperTest extends TelecomTestCase {
+
+ private static final PhoneAccountHandle SERVICE_HANDLE = new PhoneAccountHandle(
+ ComponentName.unflattenFromString("com.foo/.Blah"), "Service1");
+
+ private static final String CALL_ID_1 = "1";
+ private static final String CALL_ID_2 = "2";
+
+ TransactionalServiceWrapper mTransactionalServiceWrapper;
+
+ @Mock private Call mMockCall1;
+ @Mock private Call mMockCall2;
+ @Mock private CallsManager mCallsManager;
+ @Mock private TransactionManager mTransactionManager;
+ @Mock private ICallEventCallback mCallEventCallback;
+ @Mock private TransactionalServiceRepository mRepository;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(mMockCall1.getId()).thenReturn(CALL_ID_1);
+ Mockito.when(mMockCall2.getId()).thenReturn(CALL_ID_2);
+
+ mTransactionalServiceWrapper = new TransactionalServiceWrapper(mCallEventCallback,
+ mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
+
+ mTransactionalServiceWrapper.setTransactionManager(mTransactionManager);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testTransactionalServiceWrapperStartState() throws Exception {
+ TransactionalServiceWrapper service =
+ new TransactionalServiceWrapper(mCallEventCallback,
+ mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
+
+ assertEquals(SERVICE_HANDLE, service.getPhoneAccountHandle());
+ assertEquals(1, service.getNumberOfTrackedCalls());
+ }
+
+ @Test
+ public void testTransactionalServiceWrapperCallCount() throws Exception {
+ TransactionalServiceWrapper service =
+ new TransactionalServiceWrapper(mCallEventCallback,
+ mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
+
+ assertEquals(1, service.getNumberOfTrackedCalls());
+ service.trackCall(mMockCall2);
+ assertEquals(2, service.getNumberOfTrackedCalls());
+
+ assertTrue(service.untrackCall(mMockCall2));
+ assertEquals(1, service.getNumberOfTrackedCalls());
+
+ assertTrue(service.untrackCall(mMockCall1));
+ assertFalse(service.untrackCall(mMockCall1));
+ assertEquals(0, service.getNumberOfTrackedCalls());
+ }
+
+ @Test
+ public void testCallControlSetActive() throws RemoteException {
+ // GIVEN
+ mTransactionalServiceWrapper.trackCall(mMockCall1);
+ //when(mCallsManager.getCallObjectFromCallId(CALL_ID_1)).thenReturn(mMockCall1);
+
+ // WHEN
+ ICallControl callControl = mTransactionalServiceWrapper.getICallControl();
+ callControl.setActive(CALL_ID_1, new ResultReceiver(null));
+
+ //THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(SerialTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testCallControlRejectCall() throws RemoteException {
+ // GIVEN
+ mTransactionalServiceWrapper.trackCall(mMockCall1);
+ //when(mCallsManager.getCallObjectFromCallId(CALL_ID_1)).thenReturn(mMockCall1);
+
+ // WHEN
+ ICallControl callControl = mTransactionalServiceWrapper.getICallControl();
+ callControl.rejectCall(CALL_ID_1, new ResultReceiver(null));
+
+ //THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(EndCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testCallControlDisconnectCall() throws RemoteException {
+ // GIVEN
+ mTransactionalServiceWrapper.trackCall(mMockCall1);
+ //when(mCallsManager.getCallObjectFromCallId(CALL_ID_1)).thenReturn(mMockCall1);
+
+ // WHEN
+ ICallControl callControl = mTransactionalServiceWrapper.getICallControl();
+ callControl.disconnect(CALL_ID_1, new DisconnectCause(DisconnectCause.LOCAL),
+ new ResultReceiver(null));
+
+ //THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(EndCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testCallControlSetInactive() throws RemoteException {
+ // GIVEN
+ mTransactionalServiceWrapper.trackCall(mMockCall1);
+ //when(mCallsManager.getCallObjectFromCallId(CALL_ID_1)).thenReturn(mMockCall1);
+
+ // WHEN
+ ICallControl callControl = mTransactionalServiceWrapper.getICallControl();
+ callControl.setInactive(CALL_ID_1, new ResultReceiver(null));
+
+ //THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(HoldCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
index d57a163..58e4a77 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import android.os.OutcomeReceiver;
+import android.telecom.CallException;
import android.util.Log;
import androidx.test.filters.SmallTest;
@@ -121,7 +122,7 @@
subTransactions.add(t2);
subTransactions.add(t3);
CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
- OutcomeReceiver<VoipCallTransactionResult, Exception> outcomeReceiver =
+ OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
resultFuture::complete;
String expectedLog = "t1 success;\nt2 success;\nt3 success;\n";
mTransactionManager.addTransaction(new SerialTransaction(subTransactions), outcomeReceiver);
@@ -145,15 +146,15 @@
subTransactions.add(t2);
subTransactions.add(t3);
CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
- OutcomeReceiver<VoipCallTransactionResult, Exception> outcomeReceiver =
- new OutcomeReceiver<VoipCallTransactionResult, Exception>() {
+ OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+ new OutcomeReceiver<VoipCallTransactionResult, CallException>() {
@Override
public void onResult(VoipCallTransactionResult result) {
}
@Override
- public void onError(Exception e) {
+ public void onError(CallException e) {
exceptionFuture.complete(e.getMessage());
}
};
@@ -179,7 +180,7 @@
subTransactions.add(t2);
subTransactions.add(t3);
CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
- OutcomeReceiver<VoipCallTransactionResult, Exception> outcomeReceiver =
+ OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
resultFuture::complete;
mTransactionManager.addTransaction(new ParallelTransaction(subTransactions),
outcomeReceiver);
@@ -206,15 +207,15 @@
subTransactions.add(t2);
subTransactions.add(t3);
CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
- OutcomeReceiver<VoipCallTransactionResult, Exception> outcomeReceiver =
- new OutcomeReceiver<VoipCallTransactionResult, Exception>() {
+ OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+ new OutcomeReceiver<VoipCallTransactionResult, CallException>() {
@Override
public void onResult(VoipCallTransactionResult result) {
}
@Override
- public void onError(Exception e) {
+ public void onError(CallException e) {
exceptionFuture.complete(e.getMessage());
}
};
@@ -231,7 +232,7 @@
VoipCallTransaction t = new TestVoipCallTransaction("t", 10000L,
TestVoipCallTransaction.SUCCESS);
CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
- OutcomeReceiver<VoipCallTransactionResult, Exception> outcomeReceiver =
+ OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
resultFuture::complete;
mTransactionManager.addTransaction(t, outcomeReceiver);
VoipCallTransactionResult result = resultFuture.get(7000L, TimeUnit.MILLISECONDS);