Setup backup calling for new UI

Backup calling new handles by CrossSimCallingViewModel.
CrossSimCallingViewModel keep monitoring the active SIMs, and setup
backup calling if needed.

Fix: 340444839
Test: manual - SIMs
Test: manual - Mobile Settings
Test: unit test
Change-Id: I0a3451f1e8c3984b2348cf628fb1d91ce7aaecec
This commit is contained in:
Chaohui Wang
2024-05-23 16:24:41 +08:00
parent 5b0211ea75
commit 59a28a244c
11 changed files with 203 additions and 111 deletions

View File

@@ -28,8 +28,8 @@ import kotlinx.coroutines.flow.merge
* *
* Note: This flow can only notify enabled status changes, cannot provide the latest status. * Note: This flow can only notify enabled status changes, cannot provide the latest status.
*/ */
fun Context.mobileDataEnabledFlow(subId: Int): Flow<Unit> { fun Context.mobileDataEnabledFlow(subId: Int, sendInitialValue: Boolean = true): Flow<Unit> {
val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA) val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA, sendInitialValue)
return when (subId) { return when (subId) {
SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow
else -> { else -> {

View File

@@ -28,7 +28,6 @@ import android.telephony.SubscriptionManager;
import android.util.Log; import android.util.Log;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
@@ -242,15 +241,6 @@ public class ProxySubscriptionManager implements LifecycleObserver {
return mSubscriptionMonitor.getAccessibleSubscriptionInfo(subId); return mSubscriptionMonitor.getAccessibleSubscriptionInfo(subId);
} }
/**
* Gets a list of active, visible subscription Id(s) of the currently active SIM(s).
*
* @return the list of subId's that are active and visible; the length may be 0.
*/
public @NonNull int[] getActiveSubscriptionIdList() {
return mSubscriptionMonitor.getActiveSubscriptionIdList();
}
/** /**
* Clear data cached within proxy * Clear data cached within proxy
*/ */

View File

@@ -19,20 +19,14 @@ package com.android.settings.network.telephony;
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference; import androidx.preference.Preference;
@@ -40,15 +34,11 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference; import androidx.preference.TwoStatePreference;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.datausage.DataUsageUtils; 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.ProxySubscriptionManager;
import com.android.settings.network.SubscriptionsChangeListener; import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** /**
* 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,25 +53,29 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController
implements LifecycleObserver, implements LifecycleObserver,
SubscriptionsChangeListener.SubscriptionsChangeListenerClient { SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
private static final String LOG_TAG = "AutoDataSwitchPrefCtrl";
@Nullable
private TwoStatePreference mPreference; private TwoStatePreference mPreference;
@Nullable
private SubscriptionsChangeListener mChangeListener; private SubscriptionsChangeListener mChangeListener;
@Nullable
private TelephonyManager mManager; private TelephonyManager mManager;
@Nullable
private MobileDataContentObserver mMobileDataContentObserver; private MobileDataContentObserver mMobileDataContentObserver;
@Nullable
private CrossSimCallingViewModel mCrossSimCallingViewModel;
@Nullable
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
private final MetricsFeatureProvider mMetricsFeatureProvider; public AutoDataSwitchPreferenceController(
@NonNull Context context, @NonNull String preferenceKey) {
public AutoDataSwitchPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
} }
void init(int subId) { void init(int subId, @Nullable CrossSimCallingViewModel crossSimCallingViewModel) {
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)
@@ -121,35 +115,15 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH);
} }
private int getOtherSubId(@NonNull int[] subIds) {
if (subIds.length > 1) {
for (int subId : subIds) {
if (subId != mSubId) {
return subId;
}
}
}
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
private boolean isEnabled(int subId) {
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return false;
}
TelephonyManager telephonyManager = mContext.getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
return telephonyManager != null && telephonyManager.isMobileDataPolicyEnabled(
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH);
}
@Override @Override
public boolean setChecked(boolean isChecked) { public boolean setChecked(boolean isChecked) {
mManager.setMobileDataPolicyEnabled( if (mManager != null) {
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, mManager.setMobileDataPolicyEnabled(
isChecked); TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
if (mContext.getResources().getBoolean( isChecked);
R.bool.config_auto_data_switch_enables_cross_sim_calling)) { }
trySetCrossSimCalling(mContext, getActiveSubscriptionIdList(), isChecked /* enabled */); if (mCrossSimCallingViewModel != null) {
mCrossSimCallingViewModel.updateCrossSimCalling();
} }
return true; return true;
} }
@@ -159,40 +133,6 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
return DataUsageUtils.hasMobileData(mContext); return DataUsageUtils.hasMobileData(mContext);
} }
private boolean isCrossSimCallingAllowedByPlatform(Context context, int subId) {
if ((new WifiCallingQueryImsState(context, subId)).isWifiCallingSupported()) {
PersistableBundle bundle = getCarrierConfigForSubId(subId);
return (bundle != null) && bundle.getBoolean(
CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL,
false /*default*/);
}
return false;
}
protected ImsMmTelManager getImsMmTelManager(Context context, int subId) {
ImsManager imsMgr = context.getSystemService(ImsManager.class);
return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId);
}
private void trySetCrossSimCallingPerSub(Context context, int subId, boolean enabled) {
try {
getImsMmTelManager(context, subId).setCrossSimCallingEnabled(enabled);
} catch (ImsException | IllegalArgumentException | NullPointerException exception) {
Log.w(LOG_TAG, "failed to change cross SIM calling configuration to " + enabled
+ " for subID " + subId + "with exception: ", exception);
}
}
private void trySetCrossSimCalling(Context context, int[] subIds, boolean enabled) {
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, enabled);
for (int subId : subIds) {
if (isCrossSimCallingAllowedByPlatform(context, subId)) {
trySetCrossSimCallingPerSub(context, subId, enabled);
}
}
}
@Override @Override
public int getAvailabilityStatus(int subId) { public int getAvailabilityStatus(int subId) {
if (Flags.isDualSimOnboardingEnabled() if (Flags.isDualSimOnboardingEnabled()
@@ -221,20 +161,11 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
updateState(mPreference); updateState(mPreference);
} }
private int[] getActiveSubscriptionIdList() {
return ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionIdList();
}
/** /**
* Trigger displaying preference when Mobile data content changed. * Trigger displaying preference when Mobile data content changed.
*/ */
@VisibleForTesting @VisibleForTesting
public void refreshPreference() { public void refreshPreference() {
if (mContext.getResources().getBoolean(
R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
int[] subIds = getActiveSubscriptionIdList();
trySetCrossSimCalling(mContext, subIds, isEnabled(getOtherSubId(subIds)));
}
if (mScreen != null) { if (mScreen != null) {
super.displayPreference(mScreen); super.displayPreference(mScreen);
} }

View File

@@ -38,6 +38,7 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
@@ -53,6 +54,7 @@ import com.android.settings.network.telephony.cdma.CdmaSubscriptionPreferenceCon
import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceController; import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceController;
import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController; import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController;
import com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController; import com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController;
import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.wifi.WifiPickerTrackerHelper; import com.android.settings.wifi.WifiPickerTrackerHelper;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -240,7 +242,9 @@ 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);
use(AutoDataSwitchPreferenceController.class).init(mSubId); final var crossSimCallingViewModel =
new ViewModelProvider(this).get(CrossSimCallingViewModel.class);
use(AutoDataSwitchPreferenceController.class).init(mSubId, crossSimCallingViewModel);
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

@@ -51,6 +51,8 @@ interface ImsMmTelRepository {
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int, @AccessNetworkConstants.TransportType transportType: Int,
): Boolean ): Boolean
suspend fun setCrossSimCallingEnabled(enabled: Boolean)
} }
class ImsMmTelRepositoryImpl( class ImsMmTelRepositoryImpl(
@@ -130,6 +132,7 @@ class ImsMmTelRepositoryImpl(
@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) {
val logName = "isSupported(capability=$capability,transportType=$transportType)"
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
try { try {
imsMmTelManager.isSupported( imsMmTelManager.isSupported(
@@ -140,9 +143,18 @@ class ImsMmTelRepositoryImpl(
) )
} catch (e: Exception) { } catch (e: Exception) {
continuation.resume(false) continuation.resume(false)
Log.w(TAG, "[$subId] isSupported failed", e) Log.w(TAG, "[$subId] $logName failed", e)
} }
}.also { Log.d(TAG, "[$subId] isSupported = $it") } }.also { Log.d(TAG, "[$subId] $logName = $it") }
}
override suspend fun setCrossSimCallingEnabled(enabled: Boolean) {
try {
imsMmTelManager.setCrossSimCallingEnabled(enabled)
Log.d(TAG, "[$subId] setCrossSimCallingEnabled: $enabled")
} catch (e: Exception) {
Log.e(TAG, "[$subId] failed setCrossSimCallingEnabled to $enabled", e)
}
} }
private companion object { private companion object {

View File

@@ -0,0 +1,119 @@
/*
* 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.wificalling
import android.app.Application
import android.app.settings.SettingsEnums
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.android.settings.R
import com.android.settings.network.mobileDataEnabledFlow
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settings.network.telephony.safeGetConfig
import com.android.settings.network.telephony.subscriptionsChangedFlow
import com.android.settings.network.telephony.telephonyManager
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.plus
@OptIn(ExperimentalCoroutinesApi::class)
class CrossSimCallingViewModel(
private val application: Application,
) : AndroidViewModel(application) {
private val subscriptionManager = application.requireSubscriptionManager()
private val carrierConfigManager =
application.getSystemService(CarrierConfigManager::class.java)!!
private val scope = viewModelScope + Dispatchers.Default
private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
private val updateChannel = Channel<Unit>()
init {
val resources = application.resources
if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
application.subscriptionsChangedFlow()
.flatMapLatest {
val activeSubIds = subscriptionManager.activeSubscriptionIdList.toList()
merge(
activeSubIds.anyMobileDataEnableChangedFlow(),
updateChannel.receiveAsFlow(),
).map {
activeSubIds to crossSimCallNewEnabled(activeSubIds)
}
}
.distinctUntilChanged()
.onEach { (activeSubIds, newEnabled) ->
updateCrossSimCalling(activeSubIds, newEnabled)
}
.launchIn(scope)
}
}
fun updateCrossSimCalling() {
updateChannel.trySend(Unit)
}
private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false)
}.merge()
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 { crossSimAvailable(it) }.forEach { subId ->
ImsMmTelRepositoryImpl(application, subId)
.setCrossSimCallingEnabled(newEnabled)
}
}
private suspend fun crossSimAvailable(subId: Int): Boolean =
WifiCallingRepository(application, subId).isWifiCallingSupported() &&
crossSimImsAvailable(subId)
private fun crossSimImsAvailable(subId: Int): Boolean =
carrierConfigManager.safeGetConfig(
keys = listOf(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL),
subId = subId,
).getBoolean(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false)
private fun crossSimCallNewEnabled(activeSubscriptionIdList: List<Int>): Boolean {
val defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId()
return SubscriptionManager.isValidSubscriptionId(defaultDataSubId) &&
activeSubscriptionIdList.any { subId ->
subId != defaultDataSubId &&
application.telephonyManager(subId).isMobileDataPolicyEnabled(
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
)
}
}
}

View File

@@ -29,17 +29,19 @@ 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.imsFeatureProvisionedFlow import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow
import com.android.settings.network.telephony.subscriptionsChangedFlow import com.android.settings.network.telephony.subscriptionsChangedFlow
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.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
class WifiCallingRepository( class WifiCallingRepository(
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 imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId)
) { ) {
private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!! private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
.createForSubscriptionId(subId) .createForSubscriptionId(subId)
@@ -76,10 +78,14 @@ class WifiCallingRepository(
private fun isWifiCallingSupportedFlow(): Flow<Boolean> { private fun isWifiCallingSupportedFlow(): Flow<Boolean> {
return imsMmTelRepository.imsReadyFlow().map { imsReady -> return imsMmTelRepository.imsReadyFlow().map { imsReady ->
imsReady && imsMmTelRepository.isSupported( imsReady && isWifiCallingSupported()
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
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

@@ -20,8 +20,10 @@ import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.telephony.TelephonyRepository import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel
import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -34,6 +36,7 @@ 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
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)
@@ -42,6 +45,7 @@ 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()
} }
} }
} }
@@ -54,5 +58,4 @@ fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) {
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
enabled = newEnabled, enabled = newEnabled,
) )
//TODO: setup backup calling
} }

View File

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

View File

@@ -43,6 +43,7 @@ import org.mockito.kotlin.doThrow
import org.mockito.kotlin.eq import org.mockito.kotlin.eq
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.stub import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class ImsMmTelRepositoryTest { class ImsMmTelRepositoryTest {
@@ -155,6 +156,13 @@ class ImsMmTelRepositoryTest {
assertThat(isSupported).isTrue() assertThat(isSupported).isTrue()
} }
@Test
fun setCrossSimCallingEnabled() = runBlocking {
repository.setCrossSimCallingEnabled(true)
verify(mockImsMmTelManager).setCrossSimCallingEnabled(true)
}
private companion object { private companion object {
const val SUB_ID = 1 const val SUB_ID = 1
const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE

View File

@@ -17,15 +17,18 @@
package com.android.settings.network.telephony.wificalling package com.android.settings.network.telephony.wificalling
import android.content.Context import android.content.Context
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.TelephonyManager import android.telephony.TelephonyManager
import android.telephony.ims.ImsMmTelManager import android.telephony.ims.ImsMmTelManager
import android.telephony.ims.feature.MmTelFeature
import androidx.core.os.persistableBundleOf import androidx.core.os.persistableBundleOf
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.kotlin.any import org.mockito.kotlin.any
@@ -98,6 +101,22 @@ 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 {