Create ImsFeatureRepository
To be shared with video calling and VoLTE features.
Bug: 233327342
Flag: EXEMPT bug fix
Test: manual - on Mobile Settings
Test: atest ImsFeatureRepositoryTest
Change-Id: Ic7bcb532c4bd32c6f7ac4af1eebdd8a70a86ff29
diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
new file mode 100644
index 0000000..ba33257
--- /dev/null
+++ b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.content.Context
+import android.telephony.AccessNetworkConstants.TransportType
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech
+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
+
+/**
+ * A repository for the IMS feature.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class ImsFeatureRepository(
+ private val context: Context,
+ private val subId: Int,
+ private val provisioningRepository: ProvisioningRepository = ProvisioningRepository(context),
+ private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+) {
+ /**
+ * A cold flow that determines the provisioning status for the specified IMS MmTel capability,
+ * and whether or not the requested MmTel capability is supported by the carrier on the
+ * specified network transport.
+ *
+ * @return true if the feature is provisioned and supported, false otherwise.
+ */
+ fun isReadyFlow(
+ @MmTelCapability capability: Int,
+ @ImsRegistrationTech tech: Int,
+ @TransportType transportType: Int,
+ ): Flow<Boolean> =
+ context.subscriptionsChangedFlow().flatMapLatest {
+ combine(
+ provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech),
+ imsMmTelRepository.isSupportedFlow(capability, transportType),
+ ) { imsFeatureProvisioned, isSupported ->
+ imsFeatureProvisioned && isSupported
+ }
+ }
+}
diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
index 9bc10e5..c5d1200 100644
--- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
+++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
@@ -47,6 +48,11 @@
fun imsReadyFlow(): Flow<Boolean>
+ fun isSupportedFlow(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
+ @AccessNetworkConstants.TransportType transportType: Int,
+ ): Flow<Boolean>
+
suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
@@ -55,6 +61,11 @@
suspend fun setCrossSimCallingEnabled(enabled: Boolean)
}
+/**
+ * A repository for the IMS MMTel.
+ *
+ * @throws IllegalArgumentException if the [subId] is invalid.
+ */
class ImsMmTelRepositoryImpl(
context: Context,
private val subId: Int,
@@ -126,8 +137,12 @@
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
}.catch { e ->
Log.w(TAG, "[$subId] error while imsReadyFlow", e)
+ emit(false)
}.conflate().flowOn(Dispatchers.Default)
+ override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
+ imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
+
override suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
index 04e687c..6af0559 100644
--- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
+++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt
@@ -20,24 +20,17 @@
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.ims.ImsMmTelManager.WiFiCallingMode
import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.lifecycle.LifecycleOwner
+import com.android.settings.network.telephony.ims.ImsFeatureRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
-import com.android.settings.network.telephony.ims.ProvisioningRepository
-import com.android.settings.network.telephony.subscriptionsChangedFlow
import com.android.settings.network.telephony.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers
-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
import kotlinx.coroutines.withContext
interface IWifiCallingRepository {
@@ -50,11 +43,11 @@
constructor(
private val context: Context,
private val subId: Int,
- private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
+ private val imsFeatureRepository: ImsFeatureRepository = ImsFeatureRepository(context, subId),
+ private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId),
) : IWifiCallingRepository {
private val telephonyManager = context.telephonyManager(subId)
- private val provisioningRepository = ProvisioningRepository(context)
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
@WiFiCallingMode
@@ -76,28 +69,12 @@
wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
}
- @OptIn(ExperimentalCoroutinesApi::class)
- fun wifiCallingReadyFlow(): Flow<Boolean> {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
- return context.subscriptionsChangedFlow().flatMapLatest {
- combine(
- provisioningRepository.imsFeatureProvisionedFlow(
- subId = subId,
- capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
- ),
- isWifiCallingSupportedFlow(),
- ) { imsFeatureProvisioned, isWifiCallingSupported ->
- imsFeatureProvisioned && isWifiCallingSupported
- }
- }
- }
-
- private fun isWifiCallingSupportedFlow(): Flow<Boolean> {
- return imsMmTelRepository.imsReadyFlow().map { imsReady ->
- imsReady && isWifiCallingSupported()
- }
- }
+ fun wifiCallingReadyFlow(): Flow<Boolean> =
+ imsFeatureRepository.isReadyFlow(
+ capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ )
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
imsMmTelRepository.isSupported(
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
new file mode 100644
index 0000000..3f72b2c
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.content.Context
+import android.telephony.AccessNetworkConstants
+import android.telephony.ims.feature.MmTelFeature
+import android.telephony.ims.stub.ImsRegistrationImplBase
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class ImsFeatureRepositoryTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val mockProvisioningRepository = mock<ProvisioningRepository>()
+ private val mockImsMmTelRepository = mock<ImsMmTelRepository>()
+
+ @Test
+ fun isReadyFlow_notProvisioned_returnFalse() = runBlocking {
+ mockProvisioningRepository.stub {
+ onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn
+ flowOf(false)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ provisioningRepository = mockProvisioningRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isFalse()
+ }
+
+ @Test
+ fun isReadyFlow_notSupported_returnFalse() = runBlocking {
+ mockImsMmTelRepository.stub {
+ onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(false)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ imsMmTelRepository = mockImsMmTelRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isFalse()
+ }
+
+ @Test
+ fun isReadyFlow_provisionedAndSupported_returnFalse() = runBlocking {
+ mockProvisioningRepository.stub {
+ onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn flowOf(true)
+ }
+ mockImsMmTelRepository.stub {
+ onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(true)
+ }
+
+ val repository =
+ ImsFeatureRepository(
+ context = context,
+ subId = SUB_ID,
+ provisioningRepository = mockProvisioningRepository,
+ imsMmTelRepository = mockImsMmTelRepository,
+ )
+
+ val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first()
+
+ assertThat(isReady).isTrue()
+ }
+
+ private companion object {
+ const val SUB_ID = 10
+ const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+ const val TRANSPORT_TYPE = AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
index 0144f66..f0a23eb 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt
@@ -55,7 +55,8 @@
on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
}
- private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository)
+ private val repository =
+ WifiCallingRepository(context, SUB_ID, imsMmTelRepository = mockImsMmTelRepository)
@Test
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {