Loading src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 } } } src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +15 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.callbackFlow 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 Loading @@ -47,6 +48,11 @@ interface ImsMmTelRepository { 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, Loading @@ -55,6 +61,11 @@ interface ImsMmTelRepository { 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, Loading Loading @@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl( 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, Loading src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +9 −32 Original line number Diff line number Diff line Loading @@ -20,24 +20,17 @@ 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.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 { Loading @@ -50,11 +43,11 @@ class WifiCallingRepository 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 Loading @@ -76,28 +69,12 @@ constructor( 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, fun wifiCallingReadyFlow(): Flow<Boolean> = imsFeatureRepository.isReadyFlow( 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() } } transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, ) suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { imsMmTelRepository.isSupported( Loading tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * 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 } } tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ class WifiCallingRepositoryTest { 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() { Loading Loading
src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 } } }
src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +15 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.callbackFlow 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 Loading @@ -47,6 +48,11 @@ interface ImsMmTelRepository { 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, Loading @@ -55,6 +61,11 @@ interface ImsMmTelRepository { 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, Loading Loading @@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl( 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, Loading
src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +9 −32 Original line number Diff line number Diff line Loading @@ -20,24 +20,17 @@ 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.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 { Loading @@ -50,11 +43,11 @@ class WifiCallingRepository 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 Loading @@ -76,28 +69,12 @@ constructor( 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, fun wifiCallingReadyFlow(): Flow<Boolean> = imsFeatureRepository.isReadyFlow( 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() } } transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, ) suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { imsMmTelRepository.isSupported( Loading
tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * 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 } }
tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ class WifiCallingRepositoryTest { 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() { Loading