Always set cross sim calling

By CrossSimCallingViewModel, in SIMs and per SIM settings page.
To ensure the status is always right, including the case after the
onboarding set up finished.

Fix: 347882381
Fix: 348529996
Flag: EXEMPT bug fix
Test: manual - turn on / off "Automatic data switching" on SIMs
Test: manual - turn off "Automatic data switching" during onboarding
Change-Id: Icc2eacb67850fa7b3aa0fe310cd09a0e0147912b
This commit is contained in:
Chaohui Wang
2024-07-16 17:38:17 +08:00
parent d133ec93ab
commit fb0583404f
6 changed files with 47 additions and 59 deletions

View File

@@ -38,7 +38,6 @@ import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.flags.Flags; import com.android.settings.flags.Flags;
import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.MobileDataContentObserver;
import com.android.settings.network.SubscriptionsChangeListener; import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel;
/** /**
* Controls whether switch mobile data to the non-default SIM if the non-default SIM has better * Controls whether switch mobile data to the non-default SIM if the non-default SIM has better
@@ -63,8 +62,6 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
@Nullable @Nullable
private MobileDataContentObserver mMobileDataContentObserver; private MobileDataContentObserver mMobileDataContentObserver;
@Nullable @Nullable
private CrossSimCallingViewModel mCrossSimCallingViewModel;
@Nullable
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
public AutoDataSwitchPreferenceController( public AutoDataSwitchPreferenceController(
@@ -72,10 +69,9 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
super(context, preferenceKey); super(context, preferenceKey);
} }
void init(int subId, @Nullable CrossSimCallingViewModel crossSimCallingViewModel) { void init(int subId) {
this.mSubId = subId; this.mSubId = subId;
mManager = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId); mManager = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
mCrossSimCallingViewModel = crossSimCallingViewModel;
} }
@OnLifecycleEvent(ON_RESUME) @OnLifecycleEvent(ON_RESUME)
@@ -122,9 +118,6 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
isChecked); isChecked);
} }
if (mCrossSimCallingViewModel != null) {
mCrossSimCallingViewModel.updateCrossSimCalling();
}
return true; return true;
} }

View File

@@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
class DataSubscriptionRepository( class DataSubscriptionRepository(
@@ -53,6 +54,7 @@ class DataSubscriptionRepository(
.onStart { emit(SubscriptionManager.getDefaultDataSubscriptionId()) } .onStart { emit(SubscriptionManager.getDefaultDataSubscriptionId()) }
.distinctUntilChanged() .distinctUntilChanged()
.conflate() .conflate()
.onEach { Log.d(TAG, "defaultDataSubscriptionIdFlow: $it") }
.flowOn(Dispatchers.Default) .flowOn(Dispatchers.Default)
fun activeDataSubscriptionIdFlow(): Flow<Int> = fun activeDataSubscriptionIdFlow(): Flow<Int> =

View File

@@ -247,9 +247,10 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
use(CarrierSettingsVersionPreferenceController.class).init(mSubId); use(CarrierSettingsVersionPreferenceController.class).init(mSubId);
use(BillingCyclePreferenceController.class).init(mSubId); use(BillingCyclePreferenceController.class).init(mSubId);
use(MmsMessagePreferenceController.class).init(mSubId); use(MmsMessagePreferenceController.class).init(mSubId);
final var crossSimCallingViewModel = // CrossSimCallingViewModel is responsible for maintaining the correct cross sim calling
new ViewModelProvider(this).get(CrossSimCallingViewModel.class); // settings (backup calling).
use(AutoDataSwitchPreferenceController.class).init(mSubId, crossSimCallingViewModel); new ViewModelProvider(this).get(CrossSimCallingViewModel.class);
use(AutoDataSwitchPreferenceController.class).init(mSubId);
use(DisabledSubscriptionController.class).init(mSubId); use(DisabledSubscriptionController.class).init(mSubId);
use(DeleteSimProfilePreferenceController.class).init(mSubId); use(DeleteSimProfilePreferenceController.class).init(mSubId);
use(DisableSimFooterPreferenceController.class).init(mSubId); use(DisableSimFooterPreferenceController.class).init(mSubId);

View File

@@ -24,22 +24,22 @@ import android.telephony.TelephonyManager
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
import com.android.settings.network.telephony.CarrierConfigRepository
import com.android.settings.network.telephony.DataSubscriptionRepository
import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.network.telephony.MobileDataRepository
import com.android.settings.network.telephony.SubscriptionRepository import com.android.settings.network.telephony.SubscriptionRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.safeGetConfig
import com.android.settings.network.telephony.telephonyManager
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
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.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.plus import kotlinx.coroutines.plus
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@@ -48,24 +48,23 @@ class CrossSimCallingViewModel(
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
private val subscriptionRepository = SubscriptionRepository(application) private val subscriptionRepository = SubscriptionRepository(application)
private val carrierConfigManager = private val dataSubscriptionRepository = DataSubscriptionRepository(application)
application.getSystemService(CarrierConfigManager::class.java)!! private val mobileDataRepository = MobileDataRepository(application)
private val carrierConfigRepository = CarrierConfigRepository(application)
private val scope = viewModelScope + Dispatchers.Default private val scope = viewModelScope + Dispatchers.Default
private val metricsFeatureProvider = featureFactory.metricsFeatureProvider private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
private val updateChannel = Channel<Unit>()
private val mobileDataRepository = MobileDataRepository(application)
init { init {
val resources = application.resources val resources = application.resources
if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) { if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
subscriptionRepository.activeSubscriptionIdListFlow() combine(
.flatMapLatest { activeSubIds -> subscriptionRepository.activeSubscriptionIdListFlow(),
merge( dataSubscriptionRepository.defaultDataSubscriptionIdFlow(),
activeSubIds.anyMobileDataEnableChangedFlow(), ) { activeSubIds, defaultDataSubId ->
updateChannel.receiveAsFlow(), activeSubIds to crossSimCallNewEnabled(activeSubIds, defaultDataSubId)
).map { }
activeSubIds to crossSimCallNewEnabled(activeSubIds) .flatMapLatest { (activeSubIds, newEnabledFlow) ->
} newEnabledFlow.map { newEnabled -> activeSubIds to newEnabled }
} }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { (activeSubIds, newEnabled) -> .onEach { (activeSubIds, newEnabled) ->
@@ -75,44 +74,36 @@ class CrossSimCallingViewModel(
} }
} }
fun updateCrossSimCalling() {
updateChannel.trySend(Unit)
}
private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false)
}.merge()
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) { private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {
metricsFeatureProvider.action( metricsFeatureProvider.action(
application, application,
SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
newEnabled, newEnabled,
) )
activeSubIds.filter { crossSimAvailable(it) }.forEach { subId -> activeSubIds
ImsMmTelRepositoryImpl(application, subId) .filter { subId -> crossSimAvailable(subId) }
.setCrossSimCallingEnabled(newEnabled) .forEach { subId ->
} ImsMmTelRepositoryImpl(application, subId).setCrossSimCallingEnabled(newEnabled)
}
} }
private suspend fun crossSimAvailable(subId: Int): Boolean = private suspend fun crossSimAvailable(subId: Int): Boolean =
WifiCallingRepository(application, subId).isWifiCallingSupported() && WifiCallingRepository(application, subId).isWifiCallingSupported() &&
crossSimImsAvailable(subId) carrierConfigRepository.getBoolean(
subId, CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL)
private fun crossSimImsAvailable(subId: Int): Boolean = private fun crossSimCallNewEnabled(
carrierConfigManager.safeGetConfig( activeSubscriptionIdList: List<Int>,
keys = listOf(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL), defaultDataSubId: Int,
subId = subId, ): Flow<Boolean> {
).getBoolean(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false) if (!SubscriptionManager.isValidSubscriptionId(defaultDataSubId)) return flowOf(false)
val isMobileDataPolicyEnabledFlows =
private fun crossSimCallNewEnabled(activeSubscriptionIdList: List<Int>): Boolean { activeSubscriptionIdList
val defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId() .filter { subId -> subId != defaultDataSubId }
return SubscriptionManager.isValidSubscriptionId(defaultDataSubId) && .map { subId ->
activeSubscriptionIdList.any { subId -> mobileDataRepository.isMobileDataPolicyEnabledFlow(
subId != defaultDataSubId && subId, TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
application.telephonyManager(subId).isMobileDataPolicyEnabled( }
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH return combine(isMobileDataPolicyEnabledFlows) { true in it }
)
}
} }
} }

View File

@@ -34,7 +34,9 @@ fun AutomaticDataSwitchingPreference(
) { ) {
val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg) val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val crossSimCallingViewModel = viewModel<CrossSimCallingViewModel>() // handles backup calling // CrossSimCallingViewModel is responsible for maintaining the correct cross sim calling
// settings (backup calling).
viewModel<CrossSimCallingViewModel>()
SwitchPreference( SwitchPreference(
object : SwitchPreferenceModel { object : SwitchPreferenceModel {
override val title = stringResource(id = R.string.primary_sim_automatic_data_title) override val title = stringResource(id = R.string.primary_sim_automatic_data_title)
@@ -43,7 +45,6 @@ fun AutomaticDataSwitchingPreference(
override val onCheckedChange: (Boolean) -> Unit = { newEnabled -> override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
coroutineScope.launch(Dispatchers.Default) { coroutineScope.launch(Dispatchers.Default) {
setAutoDataEnabled(newEnabled) setAutoDataEnabled(newEnabled)
crossSimCallingViewModel.updateCrossSimCalling()
} }
} }
} }

View File

@@ -85,7 +85,7 @@ public class AutoDataSwitchPreferenceControllerTest {
return true; return true;
} }
}; };
mController.init(SUB_ID_1, null); mController.init(SUB_ID_1);
} }
@Test @Test