Merge "Refine CrossSimCalling updating" into main

This commit is contained in:
Chaohui Wang
2024-09-03 02:58:25 +00:00
committed by Android (Google) Code Review
4 changed files with 54 additions and 55 deletions

View File

@@ -27,6 +27,7 @@ import android.telephony.ims.ImsStateCallback
import android.telephony.ims.RegistrationManager import android.telephony.ims.RegistrationManager
import android.telephony.ims.feature.MmTelFeature import android.telephony.ims.feature.MmTelFeature
import android.util.Log import android.util.Log
import androidx.annotation.VisibleForTesting
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
@@ -53,11 +54,6 @@ interface ImsMmTelRepository {
@AccessNetworkConstants.TransportType transportType: Int, @AccessNetworkConstants.TransportType transportType: Int,
): Flow<Boolean> ): Flow<Boolean>
suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
): Boolean
suspend fun setCrossSimCallingEnabled(enabled: Boolean) suspend fun setCrossSimCallingEnabled(enabled: Boolean)
} }
@@ -143,7 +139,8 @@ class ImsMmTelRepositoryImpl(
override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> = override fun isSupportedFlow(capability: Int, transportType: Int): Flow<Boolean> =
imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) } imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) }
override suspend fun isSupported( @VisibleForTesting
suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int, @AccessNetworkConstants.TransportType transportType: Int,
): Boolean = withContext(Dispatchers.Default) { ): Boolean = withContext(Dispatchers.Default) {

View File

@@ -21,6 +21,7 @@ import android.app.settings.SettingsEnums
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.util.Log
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.R import com.android.settings.R
@@ -34,6 +35,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@@ -43,9 +45,8 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class CrossSimCallingViewModel( class CrossSimCallingViewModel(private val application: Application) :
private val application: Application, AndroidViewModel(application) {
) : AndroidViewModel(application) {
private val subscriptionRepository = SubscriptionRepository(application) private val subscriptionRepository = SubscriptionRepository(application)
private val dataSubscriptionRepository = DataSubscriptionRepository(application) private val dataSubscriptionRepository = DataSubscriptionRepository(application)
@@ -61,38 +62,45 @@ class CrossSimCallingViewModel(
subscriptionRepository.activeSubscriptionIdListFlow(), subscriptionRepository.activeSubscriptionIdListFlow(),
dataSubscriptionRepository.defaultDataSubscriptionIdFlow(), dataSubscriptionRepository.defaultDataSubscriptionIdFlow(),
) { activeSubIds, defaultDataSubId -> ) { activeSubIds, defaultDataSubId ->
activeSubIds to crossSimCallNewEnabled(activeSubIds, defaultDataSubId) updatableSubIdsFlow(activeSubIds) to
crossSimCallNewEnabledFlow(activeSubIds, defaultDataSubId)
} }
.flatMapLatest { (activeSubIds, newEnabledFlow) -> .flatMapLatest { (updatableSubIdsFlow, crossSimCallNewEnabledFlow) ->
newEnabledFlow.map { newEnabled -> activeSubIds to newEnabled } combine(updatableSubIdsFlow, crossSimCallNewEnabledFlow) {
updatableSubIds,
newEnabled ->
updatableSubIds to newEnabled
}
} }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { (activeSubIds, newEnabled) -> .conflate()
updateCrossSimCalling(activeSubIds, newEnabled) .onEach { (updatableSubIds, newEnabled) ->
Log.d(TAG, "updatableSubIds: $updatableSubIds newEnabled: $newEnabled")
updateCrossSimCalling(updatableSubIds, newEnabled)
} }
.launchIn(scope) .launchIn(scope)
} }
} }
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) { private fun updatableSubIdsFlow(activeSubIds: List<Int>): Flow<List<Int>> {
metricsFeatureProvider.action( val updatableSubIdFlows =
application, activeSubIds.map { subId ->
SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, WifiCallingRepository(application, subId).wifiCallingReadyFlow().map { isReady ->
newEnabled, subId.takeIf { isReady && isCrossSimImsAvailable(subId) }
) }
activeSubIds
.filter { subId -> crossSimAvailable(subId) }
.forEach { subId ->
ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
} }
return combine(updatableSubIdFlows) { subIds -> subIds.filterNotNull() }
.distinctUntilChanged()
.conflate()
} }
private suspend fun crossSimAvailable(subId: Int): Boolean = private fun isCrossSimImsAvailable(subId: Int) =
WifiCallingRepository(application, subId).isWifiCallingSupported() && carrierConfigRepository.getBoolean(
carrierConfigRepository.getBoolean( subId,
subId, CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL) CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
)
private fun crossSimCallNewEnabled( private fun crossSimCallNewEnabledFlow(
activeSubscriptionIdList: List<Int>, activeSubscriptionIdList: List<Int>,
defaultDataSubId: Int, defaultDataSubId: Int,
): Flow<Boolean> { ): Flow<Boolean> {
@@ -102,8 +110,27 @@ class CrossSimCallingViewModel(
.filter { subId -> subId != defaultDataSubId } .filter { subId -> subId != defaultDataSubId }
.map { subId -> .map { subId ->
mobileDataRepository.isMobileDataPolicyEnabledFlow( mobileDataRepository.isMobileDataPolicyEnabledFlow(
subId, TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) subId,
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
)
} }
return combine(isMobileDataPolicyEnabledFlows) { true in it } return combine(isMobileDataPolicyEnabledFlows) { true in it }
.distinctUntilChanged()
.conflate()
}
private suspend fun updateCrossSimCalling(subIds: List<Int>, newEnabled: Boolean) {
metricsFeatureProvider.action(
application,
SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
newEnabled,
)
for (subId in subIds) {
ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
}
}
companion object {
private const val TAG = "CrossSimCallingVM"
} }
} }

View File

@@ -29,9 +29,7 @@ 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.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.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
interface IWifiCallingRepository { interface IWifiCallingRepository {
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */ /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
@@ -75,11 +73,4 @@ constructor(
tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
) )
suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) {
imsMmTelRepository.isSupported(
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
)
}
} }

View File

@@ -102,22 +102,6 @@ class WifiCallingRepositoryTest {
assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED) assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED)
} }
@Test
fun isWifiCallingSupported() = runBlocking {
mockImsMmTelRepository.stub {
onBlocking {
isSupported(
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
)
} doReturn true
}
val isSupported = repository.isWifiCallingSupported()
assertThat(isSupported).isTrue()
}
private fun mockUseWfcHomeModeForRoaming(config: Boolean) { private fun mockUseWfcHomeModeForRoaming(config: Boolean) {
mockCarrierConfigManager.stub { mockCarrierConfigManager.stub {
on { on {