Loading src/java/com/android/internal/telephony/data/AutoDataSwitchController.java +116 −49 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.AsyncResult; import android.os.AsyncResult; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; Loading @@ -44,6 +45,7 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyDisplayInfo; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.LocalLog; Loading Loading @@ -100,6 +102,38 @@ public class AutoDataSwitchController extends Handler { EVALUATION_REASON_VOICE_CALL_END}) EVALUATION_REASON_VOICE_CALL_END}) public @interface AutoDataSwitchEvaluationReason {} public @interface AutoDataSwitchEvaluationReason {} /** * Defines the switch type for considering a subscription as out of service before switching * data, in milliseconds. * If one SIM has service while the other is out of service for this duration, * data will be switched to the SIM with service. */ private static final int STABILITY_CHECK_AVAILABILITY_SWITCH = 0; /** * Defines the switch type for considering the RAT and signal strength advantage of a * subscription to be stable before switching data, in milliseconds. * Each RAT and signal strength is assigned a score. If one SIM's score is higher * than the other SIM's score for this duration, data will be switched to that SIM. */ private static final int STABILITY_CHECK_PERFORMANCE_SWITCH = 1; /** * Defines the switch type for switching data back to the default SIM when both SIMs are out of * service, in milliseconds. * If the current data is on the backup SIM and both SIMs remain out of service, * data will be switched back to the default SIM. */ private static final int STABILITY_CHECK_AVAILABILITY_SWITCH_BACK = 2; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "STABILITY_CHECK_", value = {STABILITY_CHECK_AVAILABILITY_SWITCH, STABILITY_CHECK_PERFORMANCE_SWITCH, STABILITY_CHECK_AVAILABILITY_SWITCH_BACK, }) public @interface PreSwitchStabilityCheckType {} /** stability check type to timer in milliseconds. */ private static final Map<Integer, Long> STABILITY_CHECK_TIMER_MAP = new ArrayMap<>(); private static final String LOG_TAG = "ADSC"; private static final String LOG_TAG = "ADSC"; /** Event for service state changed. */ /** Event for service state changed. */ Loading Loading @@ -160,11 +194,22 @@ public class AutoDataSwitchController extends Handler { /** /** * Event extras for checking environment stability. * Event extras for checking environment stability. * @param targetPhoneId The target phone Id to switch to when the stability check pass. * @param targetPhoneId The target phone Id to switch to when the stability check pass. * @param isForPerformance Whether the switch is due to RAT/signal strength performance. * @param switchType Whether the switch is due to OOS, RAT/signal strength performance, or * switch back. * @param needValidation Whether ping test needs to pass. * @param needValidation Whether ping test needs to pass. */ */ private record StabilityEventExtra(int targetPhoneId, boolean isForPerformance, private record StabilityEventExtra(int targetPhoneId, boolean needValidation) {} @PreSwitchStabilityCheckType int switchType, boolean needValidation) { @Override public String toString() { return "StabilityEventExtra{" + "targetPhoneId=" + targetPhoneId + ", switchType=" + switchTypeToString(switchType) + ", needValidation=" + needValidation + "}"; } } /** /** * Event extras for evaluating switch environment. * Event extras for evaluating switch environment. Loading @@ -174,18 +219,6 @@ public class AutoDataSwitchController extends Handler { private boolean mDefaultNetworkIsOnNonCellular = false; private boolean mDefaultNetworkIsOnNonCellular = false; /** {@code true} if we've displayed the notification the first time auto switch occurs **/ /** {@code true} if we've displayed the notification the first time auto switch occurs **/ private boolean mDisplayedNotification = false; private boolean mDisplayedNotification = false; /** * Configurable time threshold in ms to define an internet connection status to be stable(e.g. * out of service, in service, wifi is the default active network.etc), while -1 indicates auto * switch feature disabled. */ private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1; /** * Configurable time threshold in ms to define an internet connection performance status to be * stable (e.g. LTE + 4 signal strength, UMTS + 2 signal strength), while -1 indicates * auto switch feature based on RAT/SS is disabled. */ private long mAutoDataSwitchPerformanceStabilityTimeThreshold = -1; /** /** * The tolerated gap of score for auto data switch decision, larger than which the device will * The tolerated gap of score for auto data switch decision, larger than which the device will * switch to the SIM with higher score. If 0, the device will always switch to the higher score * switch to the SIM with higher score. If 0, the device will always switch to the higher score Loading Loading @@ -462,10 +495,14 @@ public class AutoDataSwitchController extends Handler { mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance(); mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance(); mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired(); mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired(); mAllowNddsRoaming = dataConfig.doesAutoDataSwitchAllowRoaming(); mAllowNddsRoaming = dataConfig.doesAutoDataSwitchAllowRoaming(); mAutoDataSwitchAvailabilityStabilityTimeThreshold = STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_AVAILABILITY_SWITCH, dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold(); dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold()); mAutoDataSwitchPerformanceStabilityTimeThreshold = STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_PERFORMANCE_SWITCH, dataConfig.getAutoDataSwitchPerformanceStabilityTimeThreshold(); dataConfig.getAutoDataSwitchPerformanceStabilityTimeThreshold()); STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_AVAILABILITY_SWITCH_BACK, dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() >= 0 ? dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() : dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold()); mAutoDataSwitchValidationMaxRetry = mAutoDataSwitchValidationMaxRetry = dataConfig.getAutoDataSwitchValidationMaxRetry(); dataConfig.getAutoDataSwitchValidationMaxRetry(); } } Loading Loading @@ -628,7 +665,7 @@ public class AutoDataSwitchController extends Handler { */ */ public void evaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { public void evaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { long delayMs = reason == EVALUATION_REASON_RETRY_VALIDATION long delayMs = reason == EVALUATION_REASON_RETRY_VALIDATION ? mAutoDataSwitchAvailabilityStabilityTimeThreshold ? STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_AVAILABILITY_SWITCH) << mAutoSwitchValidationFailedCount << mAutoSwitchValidationFailedCount : 0; : 0; if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) { if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) { Loading @@ -645,7 +682,7 @@ public class AutoDataSwitchController extends Handler { */ */ private void onEvaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { private void onEvaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { // auto data switch feature is disabled. // auto data switch feature is disabled. if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return; if (STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_AVAILABILITY_SWITCH) < 0) return; int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); // check is valid DSDS // check is valid DSDS if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return; if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return; Loading @@ -669,7 +706,7 @@ public class AutoDataSwitchController extends Handler { log(debugMessage.toString()); log(debugMessage.toString()); if (res.targetPhoneId != INVALID_PHONE_INDEX) { if (res.targetPhoneId != INVALID_PHONE_INDEX) { mSelectedTargetPhoneId = res.targetPhoneId; mSelectedTargetPhoneId = res.targetPhoneId; startStabilityCheck(res.targetPhoneId, res.isForPerformance, res.needValidation); startStabilityCheck(res.targetPhoneId, res.switchType, res.needValidation); } else { } else { cancelAnyPendingSwitch(); cancelAnyPendingSwitch(); } } Loading @@ -690,8 +727,7 @@ public class AutoDataSwitchController extends Handler { log(debugMessage.append( log(debugMessage.append( ", immediately back to default as user turns off default").toString()); ", immediately back to default as user turns off default").toString()); return; return; } else if (!(internetEvaluation = backupDataPhone.getDataNetworkController() } else if (!(internetEvaluation = getInternetEvaluation(backupDataPhone)) .getInternetEvaluation(false/*ignoreExistingNetworks*/)) .isSubsetOf(DataEvaluation.DataDisallowedReason.NOT_IN_SERVICE)) { .isSubsetOf(DataEvaluation.DataDisallowedReason.NOT_IN_SERVICE)) { mSelectedTargetPhoneId = INVALID_PHONE_INDEX; mSelectedTargetPhoneId = INVALID_PHONE_INDEX; mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone( mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone( Loading @@ -703,7 +739,7 @@ public class AutoDataSwitchController extends Handler { } } boolean backToDefault = false; boolean backToDefault = false; boolean isForPerformance = false; int switchType = STABILITY_CHECK_AVAILABILITY_SWITCH; boolean needValidation = true; boolean needValidation = true; if (isNddsRoamingEnabled()) { if (isNddsRoamingEnabled()) { Loading Loading @@ -747,7 +783,7 @@ public class AutoDataSwitchController extends Handler { .append(defaultScore).append(" versus current ") .append(defaultScore).append(" versus current ") .append(currentScore); .append(currentScore); backToDefault = true; backToDefault = true; isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; needValidation = mRequirePingTestBeforeSwitch; needValidation = mRequirePingTestBeforeSwitch; } } } else { } else { Loading @@ -759,6 +795,7 @@ public class AutoDataSwitchController extends Handler { } else { } else { debugMessage.append(", back to default as both phones are unusable."); debugMessage.append(", back to default as both phones are unusable."); backToDefault = true; backToDefault = true; switchType = STABILITY_CHECK_AVAILABILITY_SWITCH_BACK; needValidation = false; needValidation = false; } } } } Loading @@ -782,7 +819,7 @@ public class AutoDataSwitchController extends Handler { .append(defaultScore).append(" versus current ") .append(defaultScore).append(" versus current ") .append(currentScore); .append(currentScore); backToDefault = true; backToDefault = true; isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; needValidation = mRequirePingTestBeforeSwitch; needValidation = mRequirePingTestBeforeSwitch; } } } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) { } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) { Loading @@ -795,7 +832,7 @@ public class AutoDataSwitchController extends Handler { if (backToDefault) { if (backToDefault) { log(debugMessage.toString()); log(debugMessage.toString()); mSelectedTargetPhoneId = defaultDataPhoneId; mSelectedTargetPhoneId = defaultDataPhoneId; startStabilityCheck(DEFAULT_PHONE_INDEX, isForPerformance, needValidation); startStabilityCheck(DEFAULT_PHONE_INDEX, switchType, needValidation); } else { } else { // cancel any previous attempts of switching back to default phone // cancel any previous attempts of switching back to default phone cancelAnyPendingSwitch(); cancelAnyPendingSwitch(); Loading @@ -812,9 +849,9 @@ public class AutoDataSwitchController extends Handler { @NonNull private StabilityEventExtra evaluateAnyCandidateToUse(int defaultPhoneId, @NonNull private StabilityEventExtra evaluateAnyCandidateToUse(int defaultPhoneId, @NonNull StringBuilder debugMessage) { @NonNull StringBuilder debugMessage) { Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId); Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId); boolean isForPerformance = false; int switchType = STABILITY_CHECK_AVAILABILITY_SWITCH; StabilityEventExtra invalidResult = new StabilityEventExtra(INVALID_PHONE_INDEX, StabilityEventExtra invalidResult = new StabilityEventExtra(INVALID_PHONE_INDEX, isForPerformance, mRequirePingTestBeforeSwitch); switchType, mRequirePingTestBeforeSwitch); if (defaultDataPhone == null) { if (defaultDataPhone == null) { debugMessage.append(", no candidate as no sim loaded"); debugMessage.append(", no candidate as no sim loaded"); Loading Loading @@ -874,7 +911,7 @@ public class AutoDataSwitchController extends Handler { debugMessage.append(" with ").append(defaultScore) debugMessage.append(" with ").append(defaultScore) .append(" versus candidate higher score ").append(candidateScore); .append(" versus candidate higher score ").append(candidateScore); secondaryDataPhone = PhoneFactory.getPhone(phoneId); secondaryDataPhone = PhoneFactory.getPhone(phoneId); isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; } else { } else { debugMessage.append(", candidate's score ").append(candidateScore) debugMessage.append(", candidate's score ").append(candidateScore) .append(" doesn't justify the switch given the current ") .append(" doesn't justify the switch given the current ") Loading @@ -895,7 +932,7 @@ public class AutoDataSwitchController extends Handler { debugMessage.append(" with higher score ").append(candidateScore) debugMessage.append(" with higher score ").append(candidateScore) .append(" versus current ").append(defaultScore); .append(" versus current ").append(defaultScore); secondaryDataPhone = PhoneFactory.getPhone(phoneId); secondaryDataPhone = PhoneFactory.getPhone(phoneId); isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; } else { } else { debugMessage.append(", but its score ").append(candidateScore) debugMessage.append(", but its score ").append(candidateScore) .append(" doesn't meet the bar to switch given the current ") .append(" doesn't meet the bar to switch given the current ") Loading @@ -909,15 +946,14 @@ public class AutoDataSwitchController extends Handler { } } if (secondaryDataPhone != null) { if (secondaryDataPhone != null) { DataEvaluation evaluation = getInternetEvaluation(secondaryDataPhone); // check internet data is allowed on the candidate // check internet data is allowed on the candidate DataEvaluation internetEvaluation = secondaryDataPhone.getDataNetworkController() if (!evaluation.containsDisallowedReasons()) { .getInternetEvaluation(false/*ignoreExistingNetworks*/); if (!internetEvaluation.containsDisallowedReasons()) { return new StabilityEventExtra(phoneId, return new StabilityEventExtra(phoneId, isForPerformance, mRequirePingTestBeforeSwitch); switchType, mRequirePingTestBeforeSwitch); } else { } else { debugMessage.append(", but candidate's data is not allowed ") debugMessage.append(", but candidate's data is not allowed ") .append(internetEvaluation); .append(evaluation); } } } } } } Loading @@ -925,11 +961,32 @@ public class AutoDataSwitchController extends Handler { return invalidResult; return invalidResult; } } /** * Get internet evaluation base on phone's satellite/terrestrial env. * @param phone the target phone * @return internet evaluation. */ @NonNull private DataEvaluation getInternetEvaluation(@NonNull Phone phone) { NetworkRequest.Builder reqBuilder = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); if (phone.getServiceState().isUsingNonTerrestrialNetwork()) { // When satellite, RCS requests are restricted. reqBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } return phone.getDataNetworkController().evaluateNetworkRequest( new TelephonyNetworkRequest(reqBuilder.build(), phone, sFeatureFlags), DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); } /** /** * @return {@code true} If the feature of switching base on RAT and signal strength is enabled. * @return {@code true} If the feature of switching base on RAT and signal strength is enabled. */ */ private boolean isRatSignalStrengthBasedSwitchEnabled() { private boolean isRatSignalStrengthBasedSwitchEnabled() { return mScoreTolerance >= 0 && mAutoDataSwitchPerformanceStabilityTimeThreshold >= 0 return mScoreTolerance >= 0 && STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_PERFORMANCE_SWITCH) >= 0 && sFeatureFlags.autoDataSwitchEnhanced(); && sFeatureFlags.autoDataSwitchEnhanced(); } } Loading @@ -943,29 +1000,27 @@ public class AutoDataSwitchController extends Handler { /** /** * Called when the current environment suits auto data switch. * Called when the current environment suits auto data switch. * Start pre-switch validation if the current environment suits auto data switch for * Start pre-switch validation if the current environment suits auto data switch for * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS. * {@link #STABILITY_CHECK_TIMER_MAP} MS. * @param targetPhoneId the target phone Id. * @param targetPhoneId the target phone Id. * @param isForPerformance {@code true} entails longer stability check. * @param switchType {@code true} determines stability check timer. * @param needValidation {@code true} if validation is needed. * @param needValidation {@code true} if validation is needed. */ */ private void startStabilityCheck(int targetPhoneId, boolean isForPerformance, private void startStabilityCheck(int targetPhoneId, @PreSwitchStabilityCheckType int switchType, boolean needValidation) { boolean needValidation) { StabilityEventExtra eventExtras = (StabilityEventExtra) StabilityEventExtra eventExtras = (StabilityEventExtra) mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED, mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED, new StabilityEventExtra(INVALID_PHONE_INDEX, false /*need validation*/, new StabilityEventExtra(INVALID_PHONE_INDEX, -1 /*invalid switch type*/, false /*isForPerformance*/)); false /*isForPerformance*/)); long delayMs = -1; long delayMs = -1; // Check if already scheduled one with that combination of extras. // Check if already scheduled one with that combination of extras. if (eventExtras.targetPhoneId != targetPhoneId if (eventExtras.targetPhoneId != targetPhoneId || eventExtras.needValidation != needValidation || eventExtras.needValidation != needValidation || eventExtras.isForPerformance != isForPerformance) { || eventExtras.switchType != switchType) { eventExtras = eventExtras = new StabilityEventExtra(targetPhoneId, isForPerformance, needValidation); new StabilityEventExtra(targetPhoneId, switchType, needValidation); // Reset with new timer. // Reset with new timer. delayMs = isForPerformance delayMs = STABILITY_CHECK_TIMER_MAP.get(switchType); ? mAutoDataSwitchPerformanceStabilityTimeThreshold : mAutoDataSwitchAvailabilityStabilityTimeThreshold; scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs); scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs); } } log("startStabilityCheck: " log("startStabilityCheck: " Loading Loading @@ -1158,6 +1213,17 @@ public class AutoDataSwitchController extends Handler { return phoneId >= 0 && phoneId < mPhonesSignalStatus.length; return phoneId >= 0 && phoneId < mPhonesSignalStatus.length; } } /** Auto data switch stability check type to string. */ @NonNull public static String switchTypeToString(@PreSwitchStabilityCheckType int switchType) { return switch (switchType) { case STABILITY_CHECK_AVAILABILITY_SWITCH -> "AVAILABILITY_SWITCH"; case STABILITY_CHECK_PERFORMANCE_SWITCH -> "PERFORMANCE_SWITCH"; case STABILITY_CHECK_AVAILABILITY_SWITCH_BACK -> "AVAILABILITY_SWITCH_BACK"; default -> "Unknown(" + switchType + ")"; }; } /** /** * Log debug messages. * Log debug messages. * @param s debug messages * @param s debug messages Loading Loading @@ -1198,8 +1264,9 @@ public class AutoDataSwitchController extends Handler { pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount); + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount); pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch); pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch); pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold=" pw.println("STABILITY_CHECK_TIMER_MAP:"); + mAutoDataSwitchAvailabilityStabilityTimeThreshold); STABILITY_CHECK_TIMER_MAP.forEach((key, value) -> pw.println(switchTypeToString(key) + ": " + value)); pw.println("mSelectedTargetPhoneId=" + mSelectedTargetPhoneId); pw.println("mSelectedTargetPhoneId=" + mSelectedTargetPhoneId); pw.increaseIndent(); pw.increaseIndent(); for (PhoneSignalStatus status: mPhonesSignalStatus) { for (PhoneSignalStatus status: mPhonesSignalStatus) { Loading src/java/com/android/internal/telephony/data/CellularNetworkValidator.java +15 −5 Original line number Original line Diff line number Diff line Loading @@ -273,7 +273,8 @@ public class CellularNetworkValidator { mNetworkCallback = new ConnectivityNetworkCallback(subId); mNetworkCallback = new ConnectivityNetworkCallback(subId); mConnectivityManager.requestNetwork(createNetworkRequest(), mNetworkCallback, mHandler); mConnectivityManager.requestNetwork( createNetworkRequest(subId), mNetworkCallback, mHandler); mHandler.postDelayed(() -> onValidationTimeout(subId), timeoutInMs); mHandler.postDelayed(() -> onValidationTimeout(subId), timeoutInMs); } } Loading Loading @@ -314,13 +315,22 @@ public class CellularNetworkValidator { return mState != STATE_IDLE; return mState != STATE_IDLE; } } private NetworkRequest createNetworkRequest() { private NetworkRequest createNetworkRequest(int subId) { return new NetworkRequest.Builder() NetworkRequest.Builder req = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() .setSubscriptionId(mSubId).build()) .setSubscriptionId(subId).build()); .build(); // Satellite is considered valid as long as it can serve restricted requests. Phone target = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); boolean isSatellite = target != null && target.getServiceState().isUsingNonTerrestrialNetwork(); if (isSatellite) { req.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE) .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } return req.build(); } } private synchronized void reportValidationResult(boolean passed, int subId) { private synchronized void reportValidationResult(boolean passed, int subId) { Loading src/java/com/android/internal/telephony/data/DataConfigManager.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -1161,6 +1161,19 @@ public class DataConfigManager extends Handler { .auto_data_switch_performance_stability_time_threshold_millis); .auto_data_switch_performance_stability_time_threshold_millis); } } /** * Defines the threshold for switching data back to the default SIM when both SIMs are out of * service, in milliseconds. * If the current data is on the backup SIM and both SIMs remain out of service for this * duration, data will be switched back to the default SIM. * A value of 0 means an immediate switch. If the value is negative, the threshold defined by * {@link #getAutoDataSwitchAvailabilityStabilityTimeThreshold()} will be used instead. */ public long getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() { return mResources.getInteger(com.android.internal.R.integer .auto_data_switch_availability_switchback_stability_time_threshold_millis); } /** /** * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}. * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}. * The config string will have the following form, with values in bytes: * The config string will have the following form, with values in bytes: Loading src/java/com/android/internal/telephony/data/DataNetworkController.java +5 −1 Original line number Original line Diff line number Diff line Loading @@ -1593,7 +1593,8 @@ public class DataNetworkController extends Handler { * @return The data evaluation result. * @return The data evaluation result. */ */ @NonNull @NonNull private DataEvaluation evaluateNetworkRequest( @VisibleForTesting public DataEvaluation evaluateNetworkRequest( @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) { @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) { DataEvaluation evaluation = new DataEvaluation(reason); DataEvaluation evaluation = new DataEvaluation(reason); int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability( int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability( Loading Loading @@ -2289,6 +2290,9 @@ public class DataNetworkController extends Handler { } } // When the device is on satellite, internet with restricted capabilities always honor // When the device is on satellite, internet with restricted capabilities always honor // soft disallowed reasons and not respected as restricted request // soft disallowed reasons and not respected as restricted request // Note - ping test are performed with restricted request on satellite assuming they cannot // bypass any checks. If below is removed, reevaluate the ping request in // CellularNetworkValidator and the getInternetEvaluation in AutoDataSwitchController return !(mServiceState.isUsingNonTerrestrialNetwork() return !(mServiceState.isUsingNonTerrestrialNetwork() && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); } } Loading tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java +8 −2 Original line number Original line Diff line number Diff line Loading @@ -142,7 +142,9 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { .when(phone).isUserDataEnabled(); .when(phone).isUserDataEnabled(); } } mDataEvaluation = new DataEvaluation(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); mDataEvaluation = new DataEvaluation(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); doReturn(mDataEvaluation).when(mDataNetworkController).getInternetEvaluation(anyBoolean()); doReturn(mDataEvaluation).when(mDataNetworkController).evaluateNetworkRequest( any(TelephonyNetworkRequest.class), eq(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY)); doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService) doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService) .getActiveSubIdList(true); .getActiveSubIdList(true); doAnswer(invocation -> { doAnswer(invocation -> { Loading @@ -167,6 +169,8 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { .getAutoDataSwitchAvailabilityStabilityTimeThreshold(); .getAutoDataSwitchAvailabilityStabilityTimeThreshold(); doReturn(120000L).when(mDataConfigManager) doReturn(120000L).when(mDataConfigManager) .getAutoDataSwitchPerformanceStabilityTimeThreshold(); .getAutoDataSwitchPerformanceStabilityTimeThreshold(); doReturn(150000L).when(mDataConfigManager) .getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold(); doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry(); doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry(); doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance(); doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance(); doAnswer(invocation -> { doAnswer(invocation -> { Loading Loading @@ -250,7 +254,9 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason .NO_SUITABLE_DATA_PROFILE); .NO_SUITABLE_DATA_PROFILE); doReturn(mDataEvaluation) doReturn(mDataEvaluation) .when(mDataNetworkController).getInternetEvaluation(anyBoolean()); .when(mDataNetworkController).evaluateNetworkRequest( any(TelephonyNetworkRequest.class), eq(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY)); mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED); mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED); processAllFutureMessages(); processAllFutureMessages(); Loading Loading
src/java/com/android/internal/telephony/data/AutoDataSwitchController.java +116 −49 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.AsyncResult; import android.os.AsyncResult; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; Loading @@ -44,6 +45,7 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyDisplayInfo; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.LocalLog; Loading Loading @@ -100,6 +102,38 @@ public class AutoDataSwitchController extends Handler { EVALUATION_REASON_VOICE_CALL_END}) EVALUATION_REASON_VOICE_CALL_END}) public @interface AutoDataSwitchEvaluationReason {} public @interface AutoDataSwitchEvaluationReason {} /** * Defines the switch type for considering a subscription as out of service before switching * data, in milliseconds. * If one SIM has service while the other is out of service for this duration, * data will be switched to the SIM with service. */ private static final int STABILITY_CHECK_AVAILABILITY_SWITCH = 0; /** * Defines the switch type for considering the RAT and signal strength advantage of a * subscription to be stable before switching data, in milliseconds. * Each RAT and signal strength is assigned a score. If one SIM's score is higher * than the other SIM's score for this duration, data will be switched to that SIM. */ private static final int STABILITY_CHECK_PERFORMANCE_SWITCH = 1; /** * Defines the switch type for switching data back to the default SIM when both SIMs are out of * service, in milliseconds. * If the current data is on the backup SIM and both SIMs remain out of service, * data will be switched back to the default SIM. */ private static final int STABILITY_CHECK_AVAILABILITY_SWITCH_BACK = 2; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "STABILITY_CHECK_", value = {STABILITY_CHECK_AVAILABILITY_SWITCH, STABILITY_CHECK_PERFORMANCE_SWITCH, STABILITY_CHECK_AVAILABILITY_SWITCH_BACK, }) public @interface PreSwitchStabilityCheckType {} /** stability check type to timer in milliseconds. */ private static final Map<Integer, Long> STABILITY_CHECK_TIMER_MAP = new ArrayMap<>(); private static final String LOG_TAG = "ADSC"; private static final String LOG_TAG = "ADSC"; /** Event for service state changed. */ /** Event for service state changed. */ Loading Loading @@ -160,11 +194,22 @@ public class AutoDataSwitchController extends Handler { /** /** * Event extras for checking environment stability. * Event extras for checking environment stability. * @param targetPhoneId The target phone Id to switch to when the stability check pass. * @param targetPhoneId The target phone Id to switch to when the stability check pass. * @param isForPerformance Whether the switch is due to RAT/signal strength performance. * @param switchType Whether the switch is due to OOS, RAT/signal strength performance, or * switch back. * @param needValidation Whether ping test needs to pass. * @param needValidation Whether ping test needs to pass. */ */ private record StabilityEventExtra(int targetPhoneId, boolean isForPerformance, private record StabilityEventExtra(int targetPhoneId, boolean needValidation) {} @PreSwitchStabilityCheckType int switchType, boolean needValidation) { @Override public String toString() { return "StabilityEventExtra{" + "targetPhoneId=" + targetPhoneId + ", switchType=" + switchTypeToString(switchType) + ", needValidation=" + needValidation + "}"; } } /** /** * Event extras for evaluating switch environment. * Event extras for evaluating switch environment. Loading @@ -174,18 +219,6 @@ public class AutoDataSwitchController extends Handler { private boolean mDefaultNetworkIsOnNonCellular = false; private boolean mDefaultNetworkIsOnNonCellular = false; /** {@code true} if we've displayed the notification the first time auto switch occurs **/ /** {@code true} if we've displayed the notification the first time auto switch occurs **/ private boolean mDisplayedNotification = false; private boolean mDisplayedNotification = false; /** * Configurable time threshold in ms to define an internet connection status to be stable(e.g. * out of service, in service, wifi is the default active network.etc), while -1 indicates auto * switch feature disabled. */ private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1; /** * Configurable time threshold in ms to define an internet connection performance status to be * stable (e.g. LTE + 4 signal strength, UMTS + 2 signal strength), while -1 indicates * auto switch feature based on RAT/SS is disabled. */ private long mAutoDataSwitchPerformanceStabilityTimeThreshold = -1; /** /** * The tolerated gap of score for auto data switch decision, larger than which the device will * The tolerated gap of score for auto data switch decision, larger than which the device will * switch to the SIM with higher score. If 0, the device will always switch to the higher score * switch to the SIM with higher score. If 0, the device will always switch to the higher score Loading Loading @@ -462,10 +495,14 @@ public class AutoDataSwitchController extends Handler { mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance(); mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance(); mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired(); mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired(); mAllowNddsRoaming = dataConfig.doesAutoDataSwitchAllowRoaming(); mAllowNddsRoaming = dataConfig.doesAutoDataSwitchAllowRoaming(); mAutoDataSwitchAvailabilityStabilityTimeThreshold = STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_AVAILABILITY_SWITCH, dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold(); dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold()); mAutoDataSwitchPerformanceStabilityTimeThreshold = STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_PERFORMANCE_SWITCH, dataConfig.getAutoDataSwitchPerformanceStabilityTimeThreshold(); dataConfig.getAutoDataSwitchPerformanceStabilityTimeThreshold()); STABILITY_CHECK_TIMER_MAP.put(STABILITY_CHECK_AVAILABILITY_SWITCH_BACK, dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() >= 0 ? dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() : dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold()); mAutoDataSwitchValidationMaxRetry = mAutoDataSwitchValidationMaxRetry = dataConfig.getAutoDataSwitchValidationMaxRetry(); dataConfig.getAutoDataSwitchValidationMaxRetry(); } } Loading Loading @@ -628,7 +665,7 @@ public class AutoDataSwitchController extends Handler { */ */ public void evaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { public void evaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { long delayMs = reason == EVALUATION_REASON_RETRY_VALIDATION long delayMs = reason == EVALUATION_REASON_RETRY_VALIDATION ? mAutoDataSwitchAvailabilityStabilityTimeThreshold ? STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_AVAILABILITY_SWITCH) << mAutoSwitchValidationFailedCount << mAutoSwitchValidationFailedCount : 0; : 0; if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) { if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) { Loading @@ -645,7 +682,7 @@ public class AutoDataSwitchController extends Handler { */ */ private void onEvaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { private void onEvaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) { // auto data switch feature is disabled. // auto data switch feature is disabled. if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return; if (STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_AVAILABILITY_SWITCH) < 0) return; int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId(); // check is valid DSDS // check is valid DSDS if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return; if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return; Loading @@ -669,7 +706,7 @@ public class AutoDataSwitchController extends Handler { log(debugMessage.toString()); log(debugMessage.toString()); if (res.targetPhoneId != INVALID_PHONE_INDEX) { if (res.targetPhoneId != INVALID_PHONE_INDEX) { mSelectedTargetPhoneId = res.targetPhoneId; mSelectedTargetPhoneId = res.targetPhoneId; startStabilityCheck(res.targetPhoneId, res.isForPerformance, res.needValidation); startStabilityCheck(res.targetPhoneId, res.switchType, res.needValidation); } else { } else { cancelAnyPendingSwitch(); cancelAnyPendingSwitch(); } } Loading @@ -690,8 +727,7 @@ public class AutoDataSwitchController extends Handler { log(debugMessage.append( log(debugMessage.append( ", immediately back to default as user turns off default").toString()); ", immediately back to default as user turns off default").toString()); return; return; } else if (!(internetEvaluation = backupDataPhone.getDataNetworkController() } else if (!(internetEvaluation = getInternetEvaluation(backupDataPhone)) .getInternetEvaluation(false/*ignoreExistingNetworks*/)) .isSubsetOf(DataEvaluation.DataDisallowedReason.NOT_IN_SERVICE)) { .isSubsetOf(DataEvaluation.DataDisallowedReason.NOT_IN_SERVICE)) { mSelectedTargetPhoneId = INVALID_PHONE_INDEX; mSelectedTargetPhoneId = INVALID_PHONE_INDEX; mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone( mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone( Loading @@ -703,7 +739,7 @@ public class AutoDataSwitchController extends Handler { } } boolean backToDefault = false; boolean backToDefault = false; boolean isForPerformance = false; int switchType = STABILITY_CHECK_AVAILABILITY_SWITCH; boolean needValidation = true; boolean needValidation = true; if (isNddsRoamingEnabled()) { if (isNddsRoamingEnabled()) { Loading Loading @@ -747,7 +783,7 @@ public class AutoDataSwitchController extends Handler { .append(defaultScore).append(" versus current ") .append(defaultScore).append(" versus current ") .append(currentScore); .append(currentScore); backToDefault = true; backToDefault = true; isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; needValidation = mRequirePingTestBeforeSwitch; needValidation = mRequirePingTestBeforeSwitch; } } } else { } else { Loading @@ -759,6 +795,7 @@ public class AutoDataSwitchController extends Handler { } else { } else { debugMessage.append(", back to default as both phones are unusable."); debugMessage.append(", back to default as both phones are unusable."); backToDefault = true; backToDefault = true; switchType = STABILITY_CHECK_AVAILABILITY_SWITCH_BACK; needValidation = false; needValidation = false; } } } } Loading @@ -782,7 +819,7 @@ public class AutoDataSwitchController extends Handler { .append(defaultScore).append(" versus current ") .append(defaultScore).append(" versus current ") .append(currentScore); .append(currentScore); backToDefault = true; backToDefault = true; isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; needValidation = mRequirePingTestBeforeSwitch; needValidation = mRequirePingTestBeforeSwitch; } } } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) { } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) { Loading @@ -795,7 +832,7 @@ public class AutoDataSwitchController extends Handler { if (backToDefault) { if (backToDefault) { log(debugMessage.toString()); log(debugMessage.toString()); mSelectedTargetPhoneId = defaultDataPhoneId; mSelectedTargetPhoneId = defaultDataPhoneId; startStabilityCheck(DEFAULT_PHONE_INDEX, isForPerformance, needValidation); startStabilityCheck(DEFAULT_PHONE_INDEX, switchType, needValidation); } else { } else { // cancel any previous attempts of switching back to default phone // cancel any previous attempts of switching back to default phone cancelAnyPendingSwitch(); cancelAnyPendingSwitch(); Loading @@ -812,9 +849,9 @@ public class AutoDataSwitchController extends Handler { @NonNull private StabilityEventExtra evaluateAnyCandidateToUse(int defaultPhoneId, @NonNull private StabilityEventExtra evaluateAnyCandidateToUse(int defaultPhoneId, @NonNull StringBuilder debugMessage) { @NonNull StringBuilder debugMessage) { Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId); Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId); boolean isForPerformance = false; int switchType = STABILITY_CHECK_AVAILABILITY_SWITCH; StabilityEventExtra invalidResult = new StabilityEventExtra(INVALID_PHONE_INDEX, StabilityEventExtra invalidResult = new StabilityEventExtra(INVALID_PHONE_INDEX, isForPerformance, mRequirePingTestBeforeSwitch); switchType, mRequirePingTestBeforeSwitch); if (defaultDataPhone == null) { if (defaultDataPhone == null) { debugMessage.append(", no candidate as no sim loaded"); debugMessage.append(", no candidate as no sim loaded"); Loading Loading @@ -874,7 +911,7 @@ public class AutoDataSwitchController extends Handler { debugMessage.append(" with ").append(defaultScore) debugMessage.append(" with ").append(defaultScore) .append(" versus candidate higher score ").append(candidateScore); .append(" versus candidate higher score ").append(candidateScore); secondaryDataPhone = PhoneFactory.getPhone(phoneId); secondaryDataPhone = PhoneFactory.getPhone(phoneId); isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; } else { } else { debugMessage.append(", candidate's score ").append(candidateScore) debugMessage.append(", candidate's score ").append(candidateScore) .append(" doesn't justify the switch given the current ") .append(" doesn't justify the switch given the current ") Loading @@ -895,7 +932,7 @@ public class AutoDataSwitchController extends Handler { debugMessage.append(" with higher score ").append(candidateScore) debugMessage.append(" with higher score ").append(candidateScore) .append(" versus current ").append(defaultScore); .append(" versus current ").append(defaultScore); secondaryDataPhone = PhoneFactory.getPhone(phoneId); secondaryDataPhone = PhoneFactory.getPhone(phoneId); isForPerformance = true; switchType = STABILITY_CHECK_PERFORMANCE_SWITCH; } else { } else { debugMessage.append(", but its score ").append(candidateScore) debugMessage.append(", but its score ").append(candidateScore) .append(" doesn't meet the bar to switch given the current ") .append(" doesn't meet the bar to switch given the current ") Loading @@ -909,15 +946,14 @@ public class AutoDataSwitchController extends Handler { } } if (secondaryDataPhone != null) { if (secondaryDataPhone != null) { DataEvaluation evaluation = getInternetEvaluation(secondaryDataPhone); // check internet data is allowed on the candidate // check internet data is allowed on the candidate DataEvaluation internetEvaluation = secondaryDataPhone.getDataNetworkController() if (!evaluation.containsDisallowedReasons()) { .getInternetEvaluation(false/*ignoreExistingNetworks*/); if (!internetEvaluation.containsDisallowedReasons()) { return new StabilityEventExtra(phoneId, return new StabilityEventExtra(phoneId, isForPerformance, mRequirePingTestBeforeSwitch); switchType, mRequirePingTestBeforeSwitch); } else { } else { debugMessage.append(", but candidate's data is not allowed ") debugMessage.append(", but candidate's data is not allowed ") .append(internetEvaluation); .append(evaluation); } } } } } } Loading @@ -925,11 +961,32 @@ public class AutoDataSwitchController extends Handler { return invalidResult; return invalidResult; } } /** * Get internet evaluation base on phone's satellite/terrestrial env. * @param phone the target phone * @return internet evaluation. */ @NonNull private DataEvaluation getInternetEvaluation(@NonNull Phone phone) { NetworkRequest.Builder reqBuilder = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); if (phone.getServiceState().isUsingNonTerrestrialNetwork()) { // When satellite, RCS requests are restricted. reqBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } return phone.getDataNetworkController().evaluateNetworkRequest( new TelephonyNetworkRequest(reqBuilder.build(), phone, sFeatureFlags), DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); } /** /** * @return {@code true} If the feature of switching base on RAT and signal strength is enabled. * @return {@code true} If the feature of switching base on RAT and signal strength is enabled. */ */ private boolean isRatSignalStrengthBasedSwitchEnabled() { private boolean isRatSignalStrengthBasedSwitchEnabled() { return mScoreTolerance >= 0 && mAutoDataSwitchPerformanceStabilityTimeThreshold >= 0 return mScoreTolerance >= 0 && STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_PERFORMANCE_SWITCH) >= 0 && sFeatureFlags.autoDataSwitchEnhanced(); && sFeatureFlags.autoDataSwitchEnhanced(); } } Loading @@ -943,29 +1000,27 @@ public class AutoDataSwitchController extends Handler { /** /** * Called when the current environment suits auto data switch. * Called when the current environment suits auto data switch. * Start pre-switch validation if the current environment suits auto data switch for * Start pre-switch validation if the current environment suits auto data switch for * {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS. * {@link #STABILITY_CHECK_TIMER_MAP} MS. * @param targetPhoneId the target phone Id. * @param targetPhoneId the target phone Id. * @param isForPerformance {@code true} entails longer stability check. * @param switchType {@code true} determines stability check timer. * @param needValidation {@code true} if validation is needed. * @param needValidation {@code true} if validation is needed. */ */ private void startStabilityCheck(int targetPhoneId, boolean isForPerformance, private void startStabilityCheck(int targetPhoneId, @PreSwitchStabilityCheckType int switchType, boolean needValidation) { boolean needValidation) { StabilityEventExtra eventExtras = (StabilityEventExtra) StabilityEventExtra eventExtras = (StabilityEventExtra) mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED, mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED, new StabilityEventExtra(INVALID_PHONE_INDEX, false /*need validation*/, new StabilityEventExtra(INVALID_PHONE_INDEX, -1 /*invalid switch type*/, false /*isForPerformance*/)); false /*isForPerformance*/)); long delayMs = -1; long delayMs = -1; // Check if already scheduled one with that combination of extras. // Check if already scheduled one with that combination of extras. if (eventExtras.targetPhoneId != targetPhoneId if (eventExtras.targetPhoneId != targetPhoneId || eventExtras.needValidation != needValidation || eventExtras.needValidation != needValidation || eventExtras.isForPerformance != isForPerformance) { || eventExtras.switchType != switchType) { eventExtras = eventExtras = new StabilityEventExtra(targetPhoneId, isForPerformance, needValidation); new StabilityEventExtra(targetPhoneId, switchType, needValidation); // Reset with new timer. // Reset with new timer. delayMs = isForPerformance delayMs = STABILITY_CHECK_TIMER_MAP.get(switchType); ? mAutoDataSwitchPerformanceStabilityTimeThreshold : mAutoDataSwitchAvailabilityStabilityTimeThreshold; scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs); scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs); } } log("startStabilityCheck: " log("startStabilityCheck: " Loading Loading @@ -1158,6 +1213,17 @@ public class AutoDataSwitchController extends Handler { return phoneId >= 0 && phoneId < mPhonesSignalStatus.length; return phoneId >= 0 && phoneId < mPhonesSignalStatus.length; } } /** Auto data switch stability check type to string. */ @NonNull public static String switchTypeToString(@PreSwitchStabilityCheckType int switchType) { return switch (switchType) { case STABILITY_CHECK_AVAILABILITY_SWITCH -> "AVAILABILITY_SWITCH"; case STABILITY_CHECK_PERFORMANCE_SWITCH -> "PERFORMANCE_SWITCH"; case STABILITY_CHECK_AVAILABILITY_SWITCH_BACK -> "AVAILABILITY_SWITCH_BACK"; default -> "Unknown(" + switchType + ")"; }; } /** /** * Log debug messages. * Log debug messages. * @param s debug messages * @param s debug messages Loading Loading @@ -1198,8 +1264,9 @@ public class AutoDataSwitchController extends Handler { pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount); + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount); pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch); pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch); pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold=" pw.println("STABILITY_CHECK_TIMER_MAP:"); + mAutoDataSwitchAvailabilityStabilityTimeThreshold); STABILITY_CHECK_TIMER_MAP.forEach((key, value) -> pw.println(switchTypeToString(key) + ": " + value)); pw.println("mSelectedTargetPhoneId=" + mSelectedTargetPhoneId); pw.println("mSelectedTargetPhoneId=" + mSelectedTargetPhoneId); pw.increaseIndent(); pw.increaseIndent(); for (PhoneSignalStatus status: mPhonesSignalStatus) { for (PhoneSignalStatus status: mPhonesSignalStatus) { Loading
src/java/com/android/internal/telephony/data/CellularNetworkValidator.java +15 −5 Original line number Original line Diff line number Diff line Loading @@ -273,7 +273,8 @@ public class CellularNetworkValidator { mNetworkCallback = new ConnectivityNetworkCallback(subId); mNetworkCallback = new ConnectivityNetworkCallback(subId); mConnectivityManager.requestNetwork(createNetworkRequest(), mNetworkCallback, mHandler); mConnectivityManager.requestNetwork( createNetworkRequest(subId), mNetworkCallback, mHandler); mHandler.postDelayed(() -> onValidationTimeout(subId), timeoutInMs); mHandler.postDelayed(() -> onValidationTimeout(subId), timeoutInMs); } } Loading Loading @@ -314,13 +315,22 @@ public class CellularNetworkValidator { return mState != STATE_IDLE; return mState != STATE_IDLE; } } private NetworkRequest createNetworkRequest() { private NetworkRequest createNetworkRequest(int subId) { return new NetworkRequest.Builder() NetworkRequest.Builder req = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() .setSubscriptionId(mSubId).build()) .setSubscriptionId(subId).build()); .build(); // Satellite is considered valid as long as it can serve restricted requests. Phone target = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); boolean isSatellite = target != null && target.getServiceState().isUsingNonTerrestrialNetwork(); if (isSatellite) { req.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE) .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } return req.build(); } } private synchronized void reportValidationResult(boolean passed, int subId) { private synchronized void reportValidationResult(boolean passed, int subId) { Loading
src/java/com/android/internal/telephony/data/DataConfigManager.java +13 −0 Original line number Original line Diff line number Diff line Loading @@ -1161,6 +1161,19 @@ public class DataConfigManager extends Handler { .auto_data_switch_performance_stability_time_threshold_millis); .auto_data_switch_performance_stability_time_threshold_millis); } } /** * Defines the threshold for switching data back to the default SIM when both SIMs are out of * service, in milliseconds. * If the current data is on the backup SIM and both SIMs remain out of service for this * duration, data will be switched back to the default SIM. * A value of 0 means an immediate switch. If the value is negative, the threshold defined by * {@link #getAutoDataSwitchAvailabilityStabilityTimeThreshold()} will be used instead. */ public long getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() { return mResources.getInteger(com.android.internal.R.integer .auto_data_switch_availability_switchback_stability_time_threshold_millis); } /** /** * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}. * Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}. * The config string will have the following form, with values in bytes: * The config string will have the following form, with values in bytes: Loading
src/java/com/android/internal/telephony/data/DataNetworkController.java +5 −1 Original line number Original line Diff line number Diff line Loading @@ -1593,7 +1593,8 @@ public class DataNetworkController extends Handler { * @return The data evaluation result. * @return The data evaluation result. */ */ @NonNull @NonNull private DataEvaluation evaluateNetworkRequest( @VisibleForTesting public DataEvaluation evaluateNetworkRequest( @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) { @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) { DataEvaluation evaluation = new DataEvaluation(reason); DataEvaluation evaluation = new DataEvaluation(reason); int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability( int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability( Loading Loading @@ -2289,6 +2290,9 @@ public class DataNetworkController extends Handler { } } // When the device is on satellite, internet with restricted capabilities always honor // When the device is on satellite, internet with restricted capabilities always honor // soft disallowed reasons and not respected as restricted request // soft disallowed reasons and not respected as restricted request // Note - ping test are performed with restricted request on satellite assuming they cannot // bypass any checks. If below is removed, reevaluate the ping request in // CellularNetworkValidator and the getInternetEvaluation in AutoDataSwitchController return !(mServiceState.isUsingNonTerrestrialNetwork() return !(mServiceState.isUsingNonTerrestrialNetwork() && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); && networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)); } } Loading
tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java +8 −2 Original line number Original line Diff line number Diff line Loading @@ -142,7 +142,9 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { .when(phone).isUserDataEnabled(); .when(phone).isUserDataEnabled(); } } mDataEvaluation = new DataEvaluation(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); mDataEvaluation = new DataEvaluation(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY); doReturn(mDataEvaluation).when(mDataNetworkController).getInternetEvaluation(anyBoolean()); doReturn(mDataEvaluation).when(mDataNetworkController).evaluateNetworkRequest( any(TelephonyNetworkRequest.class), eq(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY)); doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService) doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService) .getActiveSubIdList(true); .getActiveSubIdList(true); doAnswer(invocation -> { doAnswer(invocation -> { Loading @@ -167,6 +169,8 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { .getAutoDataSwitchAvailabilityStabilityTimeThreshold(); .getAutoDataSwitchAvailabilityStabilityTimeThreshold(); doReturn(120000L).when(mDataConfigManager) doReturn(120000L).when(mDataConfigManager) .getAutoDataSwitchPerformanceStabilityTimeThreshold(); .getAutoDataSwitchPerformanceStabilityTimeThreshold(); doReturn(150000L).when(mDataConfigManager) .getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold(); doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry(); doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry(); doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance(); doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance(); doAnswer(invocation -> { doAnswer(invocation -> { Loading Loading @@ -250,7 +254,9 @@ public class AutoDataSwitchControllerTest extends TelephonyTest { mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason .NO_SUITABLE_DATA_PROFILE); .NO_SUITABLE_DATA_PROFILE); doReturn(mDataEvaluation) doReturn(mDataEvaluation) .when(mDataNetworkController).getInternetEvaluation(anyBoolean()); .when(mDataNetworkController).evaluateNetworkRequest( any(TelephonyNetworkRequest.class), eq(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY)); mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED); mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED); processAllFutureMessages(); processAllFutureMessages(); Loading