| /* |
| * Copyright 2023 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 androidx.core.telecom.test |
| |
| import android.Manifest |
| import android.os.Build |
| import android.telecom.Call |
| import android.telecom.DisconnectCause |
| import android.util.Log |
| import androidx.annotation.RequiresApi |
| import androidx.core.telecom.CallAttributesCompat |
| import androidx.core.telecom.CallControlResult |
| import androidx.core.telecom.CallsManager |
| import androidx.core.telecom.internal.InCallServiceCompat |
| import androidx.core.telecom.internal.utils.Utils |
| import androidx.core.telecom.test.utils.BaseTelecomTest |
| import androidx.core.telecom.test.utils.MockInCallService |
| import androidx.core.telecom.test.utils.TestUtils |
| import androidx.test.ext.junit.runners.AndroidJUnit4 |
| import androidx.test.filters.LargeTest |
| import androidx.test.filters.SdkSuppress |
| import androidx.test.rule.GrantPermissionRule |
| import kotlinx.coroutines.launch |
| import kotlinx.coroutines.runBlocking |
| import org.junit.After |
| import org.junit.Assert |
| import org.junit.Before |
| import org.junit.Rule |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| |
| /** |
| * This test class can be used to verify the [InCallServiceCompat] functionality. |
| * |
| * Note: [Call] is package-private so we still need to leverage Telecom to create calls on our |
| * behalf for testing. The call properties and extras fields aren't mutable so we need to ensure |
| * that we wait for them to become available before accessing them. |
| */ |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O) |
| @RequiresApi(Build.VERSION_CODES.O) |
| @RunWith(AndroidJUnit4::class) |
| class InCallServiceCompatTest : BaseTelecomTest() { |
| private lateinit var inCallServiceCompat: InCallServiceCompat |
| |
| /** |
| * Grant READ_PHONE_NUMBERS permission as part of testing |
| * [InCallServiceCompat#resolveCallExtensionsType]. |
| */ |
| @get:Rule |
| val readPhoneNumbersRule: GrantPermissionRule = |
| GrantPermissionRule.grant(Manifest.permission.READ_PHONE_NUMBERS)!! |
| |
| companion object { |
| /** |
| * Logging for within the test class. |
| */ |
| internal val TAG = InCallServiceCompatTest::class.simpleName |
| } |
| |
| @Before |
| fun setUp() { |
| Utils.resetUtils() |
| inCallServiceCompat = InCallServiceCompat() |
| } |
| |
| @After |
| fun onDestroy() { |
| Utils.resetUtils() |
| } |
| |
| /** |
| * Assert that EXTRAS is the extension type for calls made using the V1.5 ConnectionService + |
| * Extensions Library (Auto). The call should have the [CallsManager.EXTRA_VOIP_API_VERSION] |
| * defined in the extras. |
| * |
| * The contents of the call detail extras need to be modified to test calls using the V1.5 |
| * ConnectionService + Extensions library (until E2E testing can be supported for it). This |
| * requires us to manually insert the [CallsManager.EXTRA_VOIP_API_VERSION] key into the bundle. |
| * |
| * Note: This portion of the logic in [InCallServiceCompat.onCallAdded] is not yet supported so |
| * for this test, we just configure the call and directly invoke |
| * [InCallServiceCompat.resolveCallExtensionsType] ensuring that it returns EXTRAS properly. |
| */ |
| @LargeTest |
| @Test(timeout = 10000) |
| fun testResolveCallExtension_Extra() { |
| setUpBackwardsCompatTest() |
| val voipApiExtra = Pair(CallsManager.EXTRA_VOIP_API_VERSION, true) |
| addAndVerifyCallExtensionType( |
| TestUtils.OUTGOING_CALL_ATTRIBUTES, |
| InCallServiceCompat.EXTRAS, |
| extraToInclude = voipApiExtra) |
| } |
| |
| /** |
| * Assert that CAPABILITY_EXCHANGE is the extension type for calls that either have the |
| * [CallsManager.PROPERTY_IS_TRANSACTIONAL] (V) defined as a property or the phone account |
| * supports transactional ops (U+) and that capability exchange between the VOIP app and |
| * associated ICS is successful. This is signaled from the ICS side when the feature setup is |
| * completed via CapabilityExchange#featureSetupComplete. |
| * |
| * For pre-U devices, the call extras would define the |
| * [CallsManager.EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED] key. |
| * |
| * Note: The version codes for V is not available so we need to enforce a strict manual check |
| * to ensure the V test path is not executed by incompatible devices. |
| */ |
| @LargeTest |
| @Test(timeout = 10000) |
| fun testResolveCallExtension_CapabilityExchange() { |
| // Add EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED for pre-U testing |
| val backwardsCompatExtra = configureCapabilityExchangeTypeTest() |
| addAndVerifyCallExtensionType( |
| TestUtils.OUTGOING_CALL_ATTRIBUTES, |
| InCallServiceCompat.CAPABILITY_EXCHANGE, |
| // Waiting is not required for U+ testing |
| waitForCallDetailExtras = !TestUtils.buildIsAtLeastU(), |
| extraToInclude = backwardsCompatExtra, |
| ) |
| } |
| |
| /** |
| * Assert that NONE is the extension type for calls with phone accounts that do not support |
| * transactional ops and that capability exchange does not succeed in this case. Note that the |
| * caller must have had the read phone numbers permission. |
| * |
| * Note: Ensure that all extras are cleared before asserting extension type so that the phone |
| * account can be checked. For backwards compatibility tests, calls define the |
| * [CallsManager.EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED] key in the details extras so this |
| * needs to be disregarded. |
| * |
| * We need to ensure that all extras/properties are ignored for testing so that the phone |
| * account can be checked to see if it supports transactional ops. In jetpack, this can only be |
| * verified on pre-U devices as those phone accounts are registered in Telecom without |
| * transactional ops. Keep in mind that because these calls are set up for backwards |
| * compatibility, they will have the [CallsManager.EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED] |
| * extra in the details (which will need to be ignored during testing). |
| */ |
| @LargeTest |
| @Test(timeout = 10000) |
| fun testResolveCallExtension_TransactionalOpsNotSupported() { |
| // Phone accounts that don't use the v2 APIs don't support transactional ops. |
| setUpBackwardsCompatTest() |
| addAndVerifyCallExtensionType( |
| TestUtils.OUTGOING_CALL_ATTRIBUTES, |
| InCallServiceCompat.NONE, |
| waitForCallDetailExtras = true |
| ) |
| } |
| |
| /*********************************************************************************************** |
| * Helpers |
| *********************************************************************************************/ |
| |
| /** |
| * Helper to add a call via CallsManager#addCall and verify the extension type depending on |
| * the APIs that are leveraged. |
| * |
| * Note: The connection extras are not added into the call until the connection is successfully |
| * created. This is usually the case when the call moves from the CONNECTING state into either |
| * the DIALING/RINGING state. This would be the case for [CallsManager.EXTRA_VOIP_API_VERSION] |
| * (handled by auto) as well as for [CallsManager.EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED] |
| * (see JetpackConnectionService#createSelfManagedConnection). Keep in mind that these extras |
| * would not be available in [InCalLService#onCallAdded], but after |
| * [Call#handleCreateConnectionSuccess] is invoked and the connection service extras are |
| * propagated into the call details via [Call#putConnectionServiceExtras]. |
| * |
| * @param callAttributesCompat for the call. |
| * @param expectedType for call extension type. |
| * @param waitForCallDetailExtras used for waiting on the call details extras to be non-null. |
| * @param extraToInclude as part of the call extras. |
| */ |
| private fun addAndVerifyCallExtensionType( |
| callAttributesCompat: CallAttributesCompat, |
| @InCallServiceCompat.Companion.CapabilityExchangeType expectedType: Int, |
| waitForCallDetailExtras: Boolean = true, |
| extraToInclude: Pair<String, Boolean>? = null |
| ) { |
| runBlocking { |
| assertWithinTimeout_addCall(callAttributesCompat) { |
| launch { |
| try { |
| // Enforce waiting logic to ensure that the call details extras are populated. |
| val call = configureCallWithSanitizedExtras( |
| waitForCallDetailExtras, extraToInclude) |
| |
| Log.i(TAG, "Service bounded invoking resolveCallExtensionsType") |
| |
| // Assert call extension type. |
| val ics = MockInCallService.getService() |
| Assert.assertEquals(expectedType, ics?.resolveCallExtensionsType(call)) |
| } finally { |
| // Always send disconnect signal if possible. |
| Assert.assertEquals( |
| CallControlResult.Success(), |
| disconnect(DisconnectCause(DisconnectCause.LOCAL))) |
| } |
| } |
| } |
| } |
| } |
| |
| private fun configureCapabilityExchangeTypeTest(): Pair<String, Boolean>? { |
| if (TestUtils.buildIsAtLeastU()) { |
| Log.w(CallCompatTest.TAG, "Setting up v2 tests for U+ device") |
| setUpV2Test() |
| } else { |
| Log.w(CallCompatTest.TAG, "Setting up backwards compatibility tests for pre-U device") |
| setUpBackwardsCompatTest() |
| } |
| |
| // Add EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED for pre-U testing |
| return if (!TestUtils.buildIsAtLeastU()) |
| Pair(CallsManager.EXTRA_VOIP_BACKWARDS_COMPATIBILITY_SUPPORTED, true) |
| else null |
| } |
| |
| /** |
| * Helper to retrieve the call from MockInCallService and wait for any call detail extras |
| * to be populated, if needed. |
| */ |
| private suspend fun configureCallWithSanitizedExtras( |
| waitForCallDetailExtras: Boolean, |
| extraToInclude: Pair<String, Boolean>? = null |
| ): Call { |
| val call = TestUtils.waitOnInCallServiceToReachXCalls(1) |
| Assert.assertNotNull("The returned Call object is <NULL>", call!!) |
| |
| // Enforce waiting logic to ensure that the call details extras are populated. |
| if (waitForCallDetailExtras) { |
| TestUtils.waitOnCallExtras(call) |
| } |
| |
| val callDetails = call.details |
| // Clear out extras to isolate the testing scenarios. |
| call.details.extras?.clear() |
| // Add extraToInclude for testing. |
| if (extraToInclude != null) { |
| callDetails.extras?.putBoolean(extraToInclude.first, extraToInclude.second) |
| } |
| return call |
| } |
| } |