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
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -47,6 +48,11 @@ interface ImsMmTelRepository {
|
|||||||
|
|
||||||
fun imsReadyFlow(): Flow<Boolean>
|
fun imsReadyFlow(): Flow<Boolean>
|
||||||
|
|
||||||
|
fun isSupportedFlow(
|
||||||
|
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
|
||||||
|
@AccessNetworkConstants.TransportType transportType: Int,
|
||||||
|
): Flow<Boolean>
|
||||||
|
|
||||||
suspend fun isSupported(
|
suspend fun isSupported(
|
||||||
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
|
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
|
||||||
@AccessNetworkConstants.TransportType transportType: Int,
|
@AccessNetworkConstants.TransportType transportType: Int,
|
||||||
@@ -55,6 +61,11 @@ interface ImsMmTelRepository {
|
|||||||
suspend fun setCrossSimCallingEnabled(enabled: Boolean)
|
suspend fun setCrossSimCallingEnabled(enabled: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A repository for the IMS MMTel.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the [subId] is invalid.
|
||||||
|
*/
|
||||||
class ImsMmTelRepositoryImpl(
|
class ImsMmTelRepositoryImpl(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val subId: Int,
|
private val subId: Int,
|
||||||
@@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl(
|
|||||||
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
|
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
|
||||||
}.catch { e ->
|
}.catch { e ->
|
||||||
Log.w(TAG, "[$subId] error while imsReadyFlow", e)
|
Log.w(TAG, "[$subId] error while imsReadyFlow", e)
|
||||||
|
emit(false)
|
||||||
}.conflate().flowOn(Dispatchers.Default)
|
}.conflate().flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
|
override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
|
||||||
|
imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
|
||||||
|
|
||||||
override suspend fun isSupported(
|
override suspend fun isSupported(
|
||||||
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
|
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
|
||||||
@AccessNetworkConstants.TransportType transportType: Int,
|
@AccessNetworkConstants.TransportType transportType: Int,
|
||||||
|
@@ -20,24 +20,17 @@ import android.content.Context
|
|||||||
import android.telephony.AccessNetworkConstants
|
import android.telephony.AccessNetworkConstants
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
|
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.ImsMmTelManager.WiFiCallingMode
|
||||||
import android.telephony.ims.feature.MmTelFeature
|
import android.telephony.ims.feature.MmTelFeature
|
||||||
import android.telephony.ims.stub.ImsRegistrationImplBase
|
import android.telephony.ims.stub.ImsRegistrationImplBase
|
||||||
import androidx.lifecycle.LifecycleOwner
|
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.ImsMmTelRepository
|
||||||
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
|
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.settings.network.telephony.telephonyManager
|
||||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
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
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
interface IWifiCallingRepository {
|
interface IWifiCallingRepository {
|
||||||
@@ -50,11 +43,11 @@ class WifiCallingRepository
|
|||||||
constructor(
|
constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val subId: Int,
|
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 {
|
) : IWifiCallingRepository {
|
||||||
private val telephonyManager = context.telephonyManager(subId)
|
private val telephonyManager = context.telephonyManager(subId)
|
||||||
|
|
||||||
private val provisioningRepository = ProvisioningRepository(context)
|
|
||||||
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
|
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
|
||||||
|
|
||||||
@WiFiCallingMode
|
@WiFiCallingMode
|
||||||
@@ -76,28 +69,12 @@ constructor(
|
|||||||
wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
|
wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
fun wifiCallingReadyFlow(): Flow<Boolean> =
|
||||||
fun wifiCallingReadyFlow(): Flow<Boolean> {
|
imsFeatureRepository.isReadyFlow(
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
|
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
|
||||||
return context.subscriptionsChangedFlow().flatMapLatest {
|
tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
|
||||||
combine(
|
transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
|
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
|
||||||
imsMmTelRepository.isSupported(
|
imsMmTelRepository.isSupported(
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
@@ -55,7 +55,8 @@ class WifiCallingRepositoryTest {
|
|||||||
on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN
|
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
|
@Test
|
||||||
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {
|
fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() {
|
||||||
|
Reference in New Issue
Block a user