Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b9c415a authored by Ling Ma's avatar Ling Ma
Browse files

Separate timer for availability and performance

Add res config: auto_data_switch_performance_stability_time_threshold_millis
as stability check when switch due to RAT/signal strength.

Use alarm manager to schedule timer longer than 1 minute.

Also, minor improvement on switching to default data if it has the same
score as the current, to honor user's preference.

Test: adb shell am broadcast -a com.android.internal.telephony.TestServiceState --ei phone_id 1 --ei data_rat 3
Test: inspect log to confirm
Test: internet browsing + voice call

Fix: 308456021
Change-Id: I061740bf0264ae622bc8774c3d52ae5331b21225
parent bdb00c5b
Loading
Loading
Loading
Loading
+219 −66

File changed.

Preview size limit exceeded, changes collapsed.

+12 −0
Original line number Original line Diff line number Diff line
@@ -1060,6 +1060,16 @@ public class DataConfigManager extends Handler {
                .auto_data_switch_availability_stability_time_threshold_millis);
                .auto_data_switch_availability_stability_time_threshold_millis);
    }
    }


    /**
     * @return Time threshold in ms to define a 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.
     */
    public long getAutoDataSwitchPerformanceStabilityTimeThreshold() {
        return mResources.getInteger(com.android.internal.R.integer
                .auto_data_switch_performance_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:
@@ -1456,6 +1466,8 @@ public class DataConfigManager extends Handler {
                + Arrays.toString(value)));
                + Arrays.toString(value)));
        pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold="
        pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold="
                + getAutoDataSwitchAvailabilityStabilityTimeThreshold());
                + getAutoDataSwitchAvailabilityStabilityTimeThreshold());
        pw.println("getAutoDataSwitchPerformanceStabilityTimeThreshold="
                + getAutoDataSwitchPerformanceStabilityTimeThreshold());
        pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry());
        pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry());
        pw.decreaseIndent();
        pw.decreaseIndent();
        pw.println("Metered APN types=" + mMeteredApnTypes.stream()
        pw.println("Metered APN types=" + mMeteredApnTypes.stream()
+10 −0
Original line number Original line Diff line number Diff line
@@ -424,6 +424,16 @@ public abstract class TelephonyTest {
        field.set(obj, newValue);
        field.set(obj, newValue);
    }
    }


    protected static <T> T getPrivateField(Object object, String fieldName, Class<T> fieldType)
            throws Exception {

        Class<?> clazz = object.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);

        return fieldType.cast(field.get(object));
    }

    protected synchronized void restoreInstance(final Class c, final String instanceName,
    protected synchronized void restoreInstance(final Class c, final String instanceName,
                                                final Object obj) throws Exception {
                                                final Object obj) throws Exception {
        InstanceKey key = new InstanceKey(c, instanceName, obj);
        InstanceKey key = new InstanceKey(c, instanceName, obj);
+71 −14
Original line number Original line Diff line number Diff line
@@ -20,10 +20,15 @@ import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;


import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;
import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;


import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
@@ -32,6 +37,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;


import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Context;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities;
@@ -59,6 +65,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;


import java.util.Map;

@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@TestableLooper.RunWithLooper
public class AutoDataSwitchControllerTest extends TelephonyTest {
public class AutoDataSwitchControllerTest extends TelephonyTest {
@@ -66,7 +74,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
    private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
    private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
    private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
    private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
    private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
    private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
    private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
    private static final int EVENT_STABILITY_CHECK_PASSED = 5;


    private static final int PHONE_1 = 0;
    private static final int PHONE_1 = 0;
    private static final int SUB_1 = 1;
    private static final int SUB_1 = 1;
@@ -79,12 +87,14 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
    private boolean mIsNonTerrestrialNetwork = false;
    private boolean mIsNonTerrestrialNetwork = false;
    // Mocked
    // Mocked
    private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
    private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
    private AlarmManager mMockedAlarmManager;


    // Real
    // Real
    private TelephonyDisplayInfo mGoodTelephonyDisplayInfo;
    private TelephonyDisplayInfo mGoodTelephonyDisplayInfo;
    private TelephonyDisplayInfo mBadTelephonyDisplayInfo;
    private TelephonyDisplayInfo mBadTelephonyDisplayInfo;
    private int mDefaultDataSub;
    private int mDefaultDataSub;
    private AutoDataSwitchController mAutoDataSwitchControllerUT;
    private AutoDataSwitchController mAutoDataSwitchControllerUT;
    private Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        super.setUp(getClass().getSimpleName());
@@ -94,6 +104,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/);
                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/);
        mMockedPhoneSwitcherCallback =
        mMockedPhoneSwitcherCallback =
                mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
                mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
        mMockedAlarmManager = mock(AlarmManager.class);


        doReturn(PHONE_1).when(mPhone).getPhoneId();
        doReturn(PHONE_1).when(mPhone).getPhoneId();
        doReturn(SUB_1).when(mPhone).getSubId();
        doReturn(SUB_1).when(mPhone).getSubId();
@@ -147,6 +158,8 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
        doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
        doReturn(10000L).when(mDataConfigManager)
        doReturn(10000L).when(mDataConfigManager)
                .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
                .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
        doReturn(120000L).when(mDataConfigManager)
                .getAutoDataSwitchPerformanceStabilityTimeThreshold();
        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 -> {
@@ -166,6 +179,11 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
        mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);


        replaceInstance(AutoDataSwitchController.class, "mAlarmManager",
                mAutoDataSwitchControllerUT, mMockedAlarmManager);
        mEventsToAlarmListener = getPrivateField(mAutoDataSwitchControllerUT,
                "mEventsToAlarmListener", Map.class);

        doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
        doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
        doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
        doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
    }
    }
@@ -356,6 +374,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {


    @Test
    @Test
    public void testCancelSwitch_onPrimary_rat_signalStrength() {
    public void testCancelSwitch_onPrimary_rat_signalStrength() {
        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
        // 4.1.1 Display info and signal strength on secondary phone became bad,
        // 4.1.1 Display info and signal strength on secondary phone became bad,
        // but primary is still OOS, so still switch to the secondary.
        // but primary is still OOS, so still switch to the secondary.
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
@@ -371,30 +390,39 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        // but primary become service, then don't switch.
        // but primary become service, then don't switch.
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
        processAllFutureMessages();
        processAllFutureMessages();
        clearInvocations(mMockedPhoneSwitcherCallback);
        clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_MODERATE);
        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_MODERATE);
        processAllFutureMessages();
        processAllFutureMessages();
        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedPhoneSwitcherCallback, atLeastOnce())
                .onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
                EVENT_STABILITY_CHECK_PASSED));


        // 4.2 Display info on default phone became good just as the secondary
        // 4.2 Display info on default phone became good just as the secondary
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
        processAllFutureMessages();
        processAllFutureMessages();
        clearInvocations(mMockedPhoneSwitcherCallback);
        clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        displayInfoChanged(PHONE_1, mGoodTelephonyDisplayInfo);
        displayInfoChanged(PHONE_1, mGoodTelephonyDisplayInfo);
        processAllFutureMessages();
        processAllFutureMessages();
        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedPhoneSwitcherCallback, atLeastOnce())
                .onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
                EVENT_STABILITY_CHECK_PASSED));


        // 4.3 Signal strength on default phone became just as good as the secondary
        // 4.3 Signal strength on default phone became just as good as the secondary
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
        processAllFutureMessages();
        processAllFutureMessages();
        clearInvocations(mMockedPhoneSwitcherCallback);
        clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
        processAllFutureMessages();
        processAllFutureMessages();
        verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedPhoneSwitcherCallback, atLeastOnce())
                .onRequireCancelAnyPendingAutoSwitchValidation();
        verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
                EVENT_STABILITY_CHECK_PASSED));
    }
    }


    @Test
    @Test
@@ -459,16 +487,18 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
    @Test
    @Test
    public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
    public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
        prepareIdealUsesNonDdsCondition();
        processAllFutureMessages();
        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();


        prepareIdealUsesNonDdsCondition();
        // 4.1 Display info and signal strength on secondary phone became bad just as the default
        // 4.1 Display info and signal strength on secondary phone became bad just as the default
        // Expect no switch since both phone has the same score.
        // Expect switch back since both phone has the same score.
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
        processAllFutureMessages();
        processAllFutureMessages();
        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());
        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
                true/*needValidation*/);


        clearInvocations(mMockedPhoneSwitcherCallback);
        clearInvocations(mMockedPhoneSwitcherCallback);
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
@@ -504,7 +534,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
    }
    }


    @Test
    @Test
    public void testStabilityCheckOverride() {
    public void testStabilityCheckOverride_basic() {
        // Starting stability check for switching to non-DDS
        // Starting stability check for switching to non-DDS
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
        processAllMessages();
        processAllMessages();
@@ -515,9 +545,6 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        // Display info and signal strength on secondary phone became worse than the default.
        // Display info and signal strength on secondary phone became worse than the default.
        // Expect to switch back, and it should override the previous stability check
        // Expect to switch back, and it should override the previous stability check
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
        // process all messages include the delayed message
        // process all messages include the delayed message
        processAllFutureMessages();
        processAllFutureMessages();


@@ -527,6 +554,25 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
                true/*needValidation*/);
                true/*needValidation*/);
    }
    }


    @Test
    public void testStabilityCheckOverride_uses_rat_signalStrength() {
        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
        // Switching due to availability first.
        prepareIdealUsesNonDdsCondition();

        // Verify stability check pending with short timer.
        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());

        // Switching due to performance now, should override to use long timer.
        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);

        // Verify stability check pending with long timer.
        assertThat(mAutoDataSwitchControllerUT.hasMessages(EVENT_STABILITY_CHECK_PASSED)).isFalse();
        verify(mMockedAlarmManager).setExact(anyInt(), anyLong(), anyString(),
                eq(mEventsToAlarmListener.get(
                        EVENT_STABILITY_CHECK_PASSED)), any());
    }

    @Test
    @Test
    public void testValidationFailedRetry() {
    public void testValidationFailedRetry() {
        prepareIdealUsesNonDdsCondition();
        prepareIdealUsesNonDdsCondition();
@@ -643,6 +689,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
    @Test
    @Test
    public void testRatSignalStrengthSkipEvaluation() {
    public void testRatSignalStrengthSkipEvaluation() {
        // Verify the secondary phone is OOS and its score(0) is too low to justify the evaluation
        // Verify the secondary phone is OOS and its score(0) is too low to justify the evaluation
        clearInvocations(mMockedPhoneSwitcherCallback);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
        processAllFutureMessages();
        processAllFutureMessages();
        verify(mMockedPhoneSwitcherCallback, never())
        verify(mMockedPhoneSwitcherCallback, never())
@@ -726,9 +773,19 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
        Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
        msg.obj = new AsyncResult(phoneId, null, null);
        msg.obj = new AsyncResult(phoneId, null, null);
        mAutoDataSwitchControllerUT.sendMessage(msg);
        mAutoDataSwitchControllerUT.sendMessage(msg);
        processAllMessages();
    }
    }
    private void setDefaultDataSubId(int defaultDataSub) {
    private void setDefaultDataSubId(int defaultDataSub) {
        mDefaultDataSub = defaultDataSub;
        mDefaultDataSub = defaultDataSub;
        doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
        doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
    }
    }

    @Override
    public void processAllFutureMessages() {
        if (mFeatureFlags.autoDataSwitchRatSs()
                && mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
            mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED).onAlarm();
        }
        super.processAllFutureMessages();
    }
}
}