From daa509874fdb48ba3650482397c3b081c97afd15 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 23 Feb 2024 18:26:11 +0800 Subject: [PATCH] Fix Wi-Fi calling option does not appear After turning on non-DDS sim. This is because IMS feature takes time to provisioning, and IMS takes time to become ready. Before this fix, we do not observe the status. After this fix, observe the readiness to fix. Fix: 325414275 Test: manual - turn DSDS on - off Test: unit tests Change-Id: If10abdb331da1ff8310e10681d055d7fbe04eaee --- .../network/ims/WifiCallingQueryImsState.java | 4 + .../network/telephony/MobileNetworkUtils.java | 4 + .../telephony/SubscriptionRepository.kt | 6 +- .../WifiCallingPreferenceController.kt | 16 ++-- .../ims/ImsFeatureProvisionedFlow.kt | 83 +++++++++++++++++++ .../telephony/ims/ImsMmTelRepository.kt | 63 ++++++++++++++ .../wificalling/WifiCallingRepository.kt | 38 +++++++++ .../WifiCallingPreferenceControllerTest.kt | 3 +- .../ims/ImsFeatureProvisionedFlowTest.kt | 75 +++++++++++++++++ .../telephony/ims/ImsMmTelRepositoryTest.kt | 53 ++++++++++++ 10 files changed, 333 insertions(+), 12 deletions(-) create mode 100644 src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt diff --git a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java index efa93e5abe5..00d162bf522 100644 --- a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java +++ b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java @@ -27,6 +27,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settings.network.telephony.wificalling.WifiCallingRepository; + /** * Controller class for querying Wifi calling status */ @@ -92,7 +94,9 @@ public class WifiCallingQueryImsState extends ImsQueryController { * Check whether Wifi Calling can be perform or not on this subscription * * @return true when Wifi Calling can be performed, otherwise false + * @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead. */ + @Deprecated public boolean isReadyToWifiCalling() { if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { return false; diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index 47515d8d6fe..8a635051bdc 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -80,6 +80,7 @@ import com.android.settings.network.CarrierConfigCache; import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants; +import com.android.settings.network.telephony.wificalling.WifiCallingRepository; import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.graph.SignalDrawable; @@ -928,7 +929,10 @@ public class MobileNetworkUtils { /** * Copied from WifiCallingPreferenceController#isWifiCallingEnabled() + * + * @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead. */ + @Deprecated public static boolean isWifiCallingEnabled(Context context, int subId, @Nullable WifiCallingQueryImsState queryImsState) { if (queryImsState == null) { diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt index 7a14d6bf302..ee4ac1e8efb 100644 --- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt +++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt @@ -18,12 +18,16 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.SubscriptionManager +import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onEach + +private const val TAG = "SubscriptionRepository" fun Context.subscriptionsChangedFlow() = callbackFlow { val subscriptionManager = getSystemService(SubscriptionManager::class.java)!! @@ -40,4 +44,4 @@ fun Context.subscriptionsChangedFlow() = callbackFlow { ) awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) } -}.conflate().flowOn(Dispatchers.Default) +}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default) diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt index 698341cdddd..b0ea6a678b2 100644 --- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt @@ -45,7 +45,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( context: Context, key: String, private val callStateFlowFactory: (subId: Int) -> Flow = context::callStateFlow, - private val wifiCallingRepository: (subId: Int) -> WifiCallingRepository = { subId -> + private val wifiCallingRepositoryFactory: (subId: Int) -> WifiCallingRepository = { subId -> WifiCallingRepository(context, subId) }, ) : TelephonyBasePreferenceController(context, key) { @@ -80,15 +80,11 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { - viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - val isVisible = withContext(Dispatchers.Default) { - MobileNetworkUtils.isWifiCallingEnabled(mContext, mSubId, null) - } - preference.isVisible = isVisible - callingPreferenceCategoryController.updateChildVisible(preferenceKey, isVisible) + wifiCallingRepositoryFactory(mSubId).wifiCallingReadyFlow() + .collectLatestWithLifecycle(viewLifecycleOwner) { + preference.isVisible = it + callingPreferenceCategoryController.updateChildVisible(preferenceKey, it) } - } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -122,7 +118,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor( } private fun getSummaryForWfcMode(): String { - val resId = when (wifiCallingRepository(mSubId).getWiFiCallingMode()) { + val resId = when (wifiCallingRepositoryFactory(mSubId).getWiFiCallingMode()) { ImsMmTelManager.WIFI_MODE_WIFI_ONLY -> com.android.internal.R.string.wfc_mode_wifi_only_summary diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt new file mode 100644 index 00000000000..676949845fc --- /dev/null +++ b/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 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.settings.network.telephony.ims + +import android.telephony.ims.ProvisioningManager +import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback +import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability +import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech +import android.util.Log +import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onEach + +private const val TAG = "ImsFeatureProvisioned" + +fun imsFeatureProvisionedFlow( + subId: Int, + @MmTelCapability capability: Int, + @ImsRegistrationTech tech: Int, +): Flow = imsFeatureProvisionedFlow( + subId = subId, + capability = capability, + tech = tech, + provisioningManager = ProvisioningManager.createForSubscriptionId(subId), +) + +@VisibleForTesting +fun imsFeatureProvisionedFlow( + subId: Int, + @MmTelCapability capability: Int, + @ImsRegistrationTech tech: Int, + provisioningManager : ProvisioningManager, +): Flow = callbackFlow { + val callback = object : FeatureProvisioningCallback() { + override fun onFeatureProvisioningChanged( + receivedCapability: Int, + receivedTech: Int, + isProvisioned: Boolean, + ) { + if (capability == receivedCapability && tech == receivedTech) trySend(isProvisioned) + } + + override fun onRcsFeatureProvisioningChanged( + capability: Int, + tech: Int, + isProvisioned: Boolean, + ) { + } + } + + provisioningManager.registerFeatureProvisioningChangedCallback( + Dispatchers.Default.asExecutor(), + callback, + ) + trySend(provisioningManager.getProvisioningStatusForCapability(capability, tech)) + + awaitClose { provisioningManager.unregisterFeatureProvisioningChangedCallback(callback) } +}.catch { e -> + Log.w(TAG, "[$subId] error while imsFeatureProvisionedFlow", e) +}.conflate().onEach { + Log.d(TAG, "[$subId] changed: capability=$capability tech=$tech isProvisioned=$it") +}.flowOn(Dispatchers.Default) diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt index 1d288d4296b..822c20a2e0f 100644 --- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt @@ -17,14 +17,33 @@ package com.android.settings.network.telephony.ims import android.content.Context +import android.telephony.AccessNetworkConstants import android.telephony.ims.ImsManager import android.telephony.ims.ImsMmTelManager import android.telephony.ims.ImsMmTelManager.WiFiCallingMode +import android.telephony.ims.ImsStateCallback +import android.telephony.ims.feature.MmTelFeature import android.util.Log +import kotlin.coroutines.resume +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext interface ImsMmTelRepository { @WiFiCallingMode fun getWiFiCallingMode(useRoamingMode: Boolean): Int + fun imsReadyFlow(): Flow + suspend fun isSupported( + @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, + @AccessNetworkConstants.TransportType transportType: Int, + ): Boolean } class ImsMmTelRepositoryImpl( @@ -45,6 +64,50 @@ class ImsMmTelRepositoryImpl( ImsMmTelManager.WIFI_MODE_UNKNOWN } + override fun imsReadyFlow(): Flow = callbackFlow { + val callback = object : ImsStateCallback() { + override fun onAvailable() { + Log.d(TAG, "[$subId] IMS onAvailable") + trySend(true) + } + + override fun onError() { + Log.d(TAG, "[$subId] IMS onError") + trySend(false) + } + + override fun onUnavailable(reason: Int) { + Log.d(TAG, "[$subId] IMS onUnavailable") + trySend(false) + } + } + + imsMmTelManager.registerImsStateCallback(Dispatchers.Default.asExecutor(), callback) + + awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) } + }.catch { e -> + Log.w(TAG, "[$subId] error while imsReadyFlow", e) + }.conflate().flowOn(Dispatchers.Default) + + override suspend fun isSupported( + @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, + @AccessNetworkConstants.TransportType transportType: Int, + ): Boolean = withContext(Dispatchers.Default) { + suspendCancellableCoroutine { continuation -> + try { + imsMmTelManager.isSupported( + capability, + transportType, + Dispatchers.Default.asExecutor(), + continuation::resume, + ) + } catch (e: Exception) { + continuation.resume(false) + Log.w(TAG, "[$subId] isSupported failed", e) + } + }.also { Log.d(TAG, "[$subId] isSupported = $it") } + } + private companion object { private const val TAG = "ImsMmTelRepository" } diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index 3d841d5c704..ac95404e78e 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -17,12 +17,24 @@ package com.android.settings.network.telephony.wificalling import android.content.Context +import android.telephony.AccessNetworkConstants import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL +import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.ims.ImsMmTelManager.WiFiCallingMode +import android.telephony.ims.feature.MmTelFeature +import android.telephony.ims.stub.ImsRegistrationImplBase import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl +import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow +import com.android.settings.network.telephony.subscriptionsChangedFlow +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map class WifiCallingRepository( private val context: Context, @@ -44,4 +56,30 @@ class WifiCallingRepository( carrierConfigManager .getConfigForSubId(subId, KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL) .getBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL) + + @OptIn(ExperimentalCoroutinesApi::class) + fun wifiCallingReadyFlow(): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + return context.subscriptionsChangedFlow().flatMapLatest { + combine( + imsFeatureProvisionedFlow( + subId = subId, + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, + ), + isWifiCallingSupportedFlow(), + ) { imsFeatureProvisioned, isWifiCallingSupported -> + imsFeatureProvisioned && isWifiCallingSupported + } + } + } + + private fun isWifiCallingSupportedFlow(): Flow { + return imsMmTelRepository.imsReadyFlow().map { imsReady -> + imsReady && imsMmTelRepository.isSupported( + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + ) + } + } } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt index f947f815552..92776df3dd2 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.kt @@ -62,6 +62,7 @@ class WifiCallingPreferenceControllerTest { private val mockWifiCallingRepository = mock { on { getWiFiCallingMode() } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN + on { wifiCallingReadyFlow() } doReturn flowOf(true) } private val callingPreferenceCategoryController = @@ -71,7 +72,7 @@ class WifiCallingPreferenceControllerTest { context = context, key = TEST_KEY, callStateFlowFactory = { flowOf(callState) }, - wifiCallingRepository = { mockWifiCallingRepository }, + wifiCallingRepositoryFactory = { mockWifiCallingRepository }, ).init(subId = SUB_ID, callingPreferenceCategoryController) @Before diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt new file mode 100644 index 00000000000..75f933ab7a2 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 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.settings.network.telephony.ims + +import android.telephony.ims.ProvisioningManager +import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback +import android.telephony.ims.feature.MmTelFeature +import android.telephony.ims.stub.ImsRegistrationImplBase +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +class ImsFeatureProvisionedFlowTest { + + private var callback: FeatureProvisioningCallback? = null + + private val mockProvisioningManager = mock { + on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer { + callback = it.arguments[1] as FeatureProvisioningCallback + callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, true) + } + } + + @Test + fun imsFeatureProvisionedFlow_sendInitialValue() = runBlocking { + val flow = imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager) + + val state = flow.first() + + assertThat(state).isTrue() + } + + @Test + fun imsFeatureProvisionedFlow_changed(): Unit = runBlocking { + val listDeferred = async { + imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager) + .toListWithTimeout() + } + delay(100) + + callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, false) + + assertThat(listDeferred.await().last()).isFalse() + } + + private companion object { + const val SUB_ID = 1 + const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE + const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt index 106a82f1751..24b081a704f 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt @@ -17,14 +17,26 @@ package com.android.settings.network.telephony.ims import android.content.Context +import android.telephony.AccessNetworkConstants import android.telephony.ims.ImsMmTelManager +import android.telephony.ims.ImsStateCallback +import android.telephony.ims.feature.MmTelFeature import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.toListWithTimeout import com.google.common.truth.Truth.assertThat +import java.util.function.Consumer +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.stub @@ -32,10 +44,21 @@ import org.mockito.kotlin.stub class ImsMmTelRepositoryTest { private val context: Context = ApplicationProvider.getApplicationContext() + private var stateCallback: ImsStateCallback? = null + private val mockImsMmTelManager = mock { on { isVoWiFiSettingEnabled } doReturn true on { getVoWiFiRoamingModeSetting() } doReturn ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED on { getVoWiFiModeSetting() } doReturn ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED + on { registerImsStateCallback(any(), any()) } doAnswer { + stateCallback = it.arguments[1] as ImsStateCallback + stateCallback?.onAvailable() + } + on { isSupported(eq(CAPABILITY), eq(TRANSPORT), any(), any()) } doAnswer { + @Suppress("UNCHECKED_CAST") + val consumer = it.arguments[3] as Consumer + consumer.accept(true) + } } private val repository = ImsMmTelRepositoryImpl(context, SUB_ID, mockImsMmTelManager) @@ -76,7 +99,37 @@ class ImsMmTelRepositoryTest { assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_UNKNOWN) } + @Test + fun imsReadyFlow_sendInitialValue() = runBlocking { + val flow = repository.imsReadyFlow() + + val state = flow.first() + + assertThat(state).isTrue() + } + + @Test + fun imsReadyFlow_changed(): Unit = runBlocking { + val listDeferred = async { + repository.imsReadyFlow().toListWithTimeout() + } + delay(100) + + stateCallback?.onUnavailable(ImsStateCallback.REASON_IMS_SERVICE_NOT_READY) + + assertThat(listDeferred.await().last()).isFalse() + } + + @Test + fun isSupported() = runBlocking { + val isSupported = repository.isSupported(CAPABILITY, TRANSPORT) + + assertThat(isSupported).isTrue() + } + private companion object { const val SUB_ID = 1 + const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE + const val TRANSPORT = AccessNetworkConstants.TRANSPORT_TYPE_WLAN } }