Refine CrossSimCalling updating

Currently, this setting depends on whether wifi calling is supported,
since wifi calling could takes some time to provision after sim is
turned on, this state could be wrong when set cross sim calling.

Use wifiCallingReadyFlow() to retrieve the latest state, and update
setting when state changes.

Fix: 352736998
Fix: 348529996
Flag: EXEMPT bug fix
Test: manual - by turn on / off sim
Change-Id: Id4b099e0c5d7cf47b007f37e6f278d1c46e58659
This commit is contained in:
Chaohui Wang
2024-09-02 17:54:41 +08:00
parent 651c9b90c2
commit 355ee0a1ee
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 {