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

View File

@@ -21,6 +21,7 @@ import android.app.settings.SettingsEnums
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.android.settings.R
@@ -34,6 +35,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -43,9 +45,8 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.plus
@OptIn(ExperimentalCoroutinesApi::class)
class CrossSimCallingViewModel(
private val application: Application,
) : AndroidViewModel(application) {
class CrossSimCallingViewModel(private val application: Application) :
AndroidViewModel(application) {
private val subscriptionRepository = SubscriptionRepository(application)
private val dataSubscriptionRepository = DataSubscriptionRepository(application)
@@ -61,38 +62,45 @@ class CrossSimCallingViewModel(
subscriptionRepository.activeSubscriptionIdListFlow(),
dataSubscriptionRepository.defaultDataSubscriptionIdFlow(),
) { activeSubIds, defaultDataSubId ->
activeSubIds to crossSimCallNewEnabled(activeSubIds, defaultDataSubId)
updatableSubIdsFlow(activeSubIds) to
crossSimCallNewEnabledFlow(activeSubIds, defaultDataSubId)
}
.flatMapLatest { (activeSubIds, newEnabledFlow) ->
newEnabledFlow.map { newEnabled -> activeSubIds to newEnabled }
.flatMapLatest { (updatableSubIdsFlow, crossSimCallNewEnabledFlow) ->
combine(updatableSubIdsFlow, crossSimCallNewEnabledFlow) {
updatableSubIds,
newEnabled ->
updatableSubIds to newEnabled
}
}
.distinctUntilChanged()
.onEach { (activeSubIds, newEnabled) ->
updateCrossSimCalling(activeSubIds, newEnabled)
.conflate()
.onEach { (updatableSubIds, newEnabled) ->
Log.d(TAG, "updatableSubIds: $updatableSubIds newEnabled: $newEnabled")
updateCrossSimCalling(updatableSubIds, newEnabled)
}
.launchIn(scope)
}
}
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {
metricsFeatureProvider.action(
application,
SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
newEnabled,
)
activeSubIds
.filter { subId -> crossSimAvailable(subId) }
.forEach { subId ->
ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
private fun updatableSubIdsFlow(activeSubIds: List<Int>): Flow<List<Int>> {
val updatableSubIdFlows =
activeSubIds.map { subId ->
WifiCallingRepository(application, subId).wifiCallingReadyFlow().map { isReady ->
subId.takeIf { isReady && isCrossSimImsAvailable(subId) }
}
}
return combine(updatableSubIdFlows) { subIds -> subIds.filterNotNull() }
.distinctUntilChanged()
.conflate()
}
private suspend fun crossSimAvailable(subId: Int): Boolean =
WifiCallingRepository(application, subId).isWifiCallingSupported() &&
carrierConfigRepository.getBoolean(
subId, CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL)
private fun isCrossSimImsAvailable(subId: Int) =
carrierConfigRepository.getBoolean(
subId,
CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
)
private fun crossSimCallNewEnabled(
private fun crossSimCallNewEnabledFlow(
activeSubscriptionIdList: List<Int>,
defaultDataSubId: Int,
): Flow<Boolean> {
@@ -102,8 +110,27 @@ class CrossSimCallingViewModel(
.filter { subId -> subId != defaultDataSubId }
.map { subId ->
mobileDataRepository.isMobileDataPolicyEnabledFlow(
subId, TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
subId,
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
)
}
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.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
interface IWifiCallingRepository {
/** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
@@ -75,11 +73,4 @@ constructor(
tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
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)
}
@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) {
mockCarrierConfigManager.stub {
on {