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

Commit b7f9b529 authored by Malcolm Chen's avatar Malcolm Chen
Browse files

Delay releasing validation network request to ensure smooth switch.

Upon data switch when a network is validated, as PhoneSwitcher confirms
the switch, Validator shouldn't release the network request immediately.
Otherwise, it may result in disconnecting the new network first, followed
with bringing it up again. So when releaseAfterValidation is true, we
delay releasing the request by 500 ms.

Bug: 140070796
Test: unittest
Change-Id: Ib24ad282e7caadf91a7b8432885bd28022e5822a
parent da2543ec
Loading
Loading
Loading
Loading
+18 −29
Original line number Diff line number Diff line
@@ -85,8 +85,6 @@ public class CellularNetworkValidator {
    public Handler mHandler = new Handler();
    @VisibleForTesting
    public ConnectivityNetworkCallback mNetworkCallback;
    @VisibleForTesting
    public Runnable mTimeoutCallback;
    private final ValidatedNetworkCache mValidatedNetworkCache = new ValidatedNetworkCache();

    private class ValidatedNetworkCache {
@@ -116,7 +114,7 @@ public class CellularNetworkValidator {
            long mValidationTimeStamp;
        }

        boolean isRecentlyValidated(int subId) {
        synchronized boolean isRecentlyValidated(int subId) {
            long cacheTtl = getValidationCacheTtl(subId);
            String networkIdentity = getValidationNetworkIdentity(subId);
            if (networkIdentity == null || !mValidatedNetworkMap.containsKey(networkIdentity)) {
@@ -128,7 +126,7 @@ public class CellularNetworkValidator {
            return recentlyValidated;
        }

        void storeLastValidationResult(int subId, boolean validated) {
        synchronized void storeLastValidationResult(int subId, boolean validated) {
            String networkIdentity = getValidationNetworkIdentity(subId);
            logd("storeLastValidationResult for subId " + subId
                    + (validated ? " validated." : " not validated."));
@@ -284,23 +282,14 @@ public class CellularNetworkValidator {
        mNetworkCallback = new ConnectivityNetworkCallback(subId);

        mConnectivityManager.requestNetwork(mNetworkRequest, mNetworkCallback, mHandler);
        mHandler.postDelayed(() -> onValidationTimeout(subId), mTimeoutInMs);
    }

        mTimeoutCallback = () -> {
    private synchronized void onValidationTimeout(int subId) {
        logd("timeout on subId " + subId + " validation.");
        // Remember latest validated network.
        mValidatedNetworkCache.storeLastValidationResult(subId, false);
        reportValidationResult(false, subId);
        };

        mHandler.postDelayed(mTimeoutCallback, mTimeoutInMs);
    }

    private void removeTimeoutCallback() {
        // Remove timeout callback.
        if (mTimeoutCallback != null) {
            mHandler.removeCallbacks(mTimeoutCallback);
            mTimeoutCallback = null;
        }
    }

    /**
@@ -309,12 +298,13 @@ public class CellularNetworkValidator {
    public synchronized void stopValidation() {
        if (!isValidating()) {
            logd("No need to stop validation.");
        } else {
            return;
        }
        if (mNetworkCallback != null) {
            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
            mState = STATE_IDLE;
        }

        removeTimeoutCallback();
        mState = STATE_IDLE;
        mHandler.removeCallbacksAndMessages(null);
        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    }

@@ -345,25 +335,24 @@ public class CellularNetworkValidator {
        // If the validation result is not for current subId, do nothing.
        if (mSubId != subId) return;

        removeTimeoutCallback();
        mHandler.removeCallbacksAndMessages(null);

        // Deal with the result only when state is still VALIDATING. This is to avoid
        // receiving multiple callbacks in queue.
        if (mState == STATE_VALIDATING) {
            mValidationCallback.onValidationDone(passed, mSubId);
            if (!mReleaseAfterValidation && passed) {
            mState = STATE_VALIDATED;
            // If validation passed and per request to NOT release after validation, delay cleanup.
            if (!mReleaseAfterValidation && passed) {
                mHandler.postDelayed(()-> stopValidation(), 500);
            } else {
                mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
                mState = STATE_IDLE;
                stopValidation();
            }

            TelephonyMetrics.getInstance().writeNetworkValidate(passed
                    ? TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_PASSED
                    : TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_FAILED);
        }

        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    }

    private synchronized void reportNetworkAvailable(Network network, int subId) {
+0 −1
Original line number Diff line number Diff line
@@ -1271,7 +1271,6 @@ public class PhoneSwitcher extends Handler {
            return;
        }
        confirmSwitch(subId, true);
        mValidator.stopValidation();
    }

    private void onValidationDone(int subId, boolean passed) {
+91 −63
Original line number Diff line number Diff line
@@ -22,10 +22,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import android.content.Context;
@@ -104,99 +106,46 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
    public void testValidateSuccess() {
        int subId = 1;
        int timeout = 1000;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build())
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());
        assertInValidation(subId);

        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));

        assertValidationResult(subId, true);
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidateTimeout() {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build())
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);
        assertInValidation(subId);

        // Wait for timeout
        moveTimeForward(timeout);
        processAllMessages();

        assertValidationResult(subId, false);
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testValidateFailure() {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build())
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());

        assertInValidation(subId);
        mValidatorUT.mNetworkCallback.onUnavailable();

        assertValidationResult(subId, false);
    }

    /**
     * Test that a single phone case results in our phone being active and the RIL called
     */
    @Test
    @SmallTest
    public void testNetworkAvailableNotValidated() {
        int subId = 1;
        int timeout = 100;
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build())
                .build();

        mValidatorUT.validate(subId, timeout, true, mCallback);

        assertTrue(mValidatorUT.isValidating());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());
        assertInValidation(subId);

        mValidatorUT.mNetworkCallback.onAvailable(new Network(100));
        assertInValidation(subId);
@@ -212,7 +161,6 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
    @SmallTest
    public void testSkipRecentlyValidatedNetwork() {
        int subId = 1;
        int slotId = 0;
        int timeout = 1000;
        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
@@ -236,7 +184,6 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
    @SmallTest
    public void testDoNotSkipIfValidationFailed() {
        int subId = 1;
        int slotId = 0;
        int timeout = 1000;
        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
@@ -257,9 +204,8 @@ public class CellularNetworkValidatorTest extends TelephonyTest {

    @Test
    @SmallTest
    public void testDoNotSkipIfCachExpires() {
    public void testDoNotSkipIfCacheExpires() {
        int subId = 1;
        int slotId = 0;
        int timeout = 1000;
        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
@@ -286,7 +232,6 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
    @Test
    @SmallTest
    public void testNetworkCachingOfMultipleSub() {
        int slotId = 0;
        int timeout = 1000;
        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
@@ -413,6 +358,80 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
        verify(mCallback).onNetworkAvailable(network, subId);
    }

    @Test
    @SmallTest
    public void testReleaseRequestAfterValidation_shouldReleaseImmediately() {
        int subId = 1;
        int timeout = 1000;
        mValidatorUT.validate(subId, timeout, true, mCallback);
        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));
        assertValidationResult(subId, true);
    }

    @Test
    @SmallTest
    public void testDoNotReleaseRequestAfterValidation_shouldReleaseLater() {
        int subId = 1;
        int timeout = 1000;
        mValidatorUT.validate(subId, timeout, false, mCallback);
        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));
        verify(mCallback).onValidationDone(true, subId);
        assertInValidation(subId);
        moveTimeForward(1000);
        processAllMessages();
        assertValidationResult(subId, true);
    }

    @Test
    @SmallTest
    public void testDoNotReleaseRequestAfterValidation_validationFails_shouldReleaseImmediately() {
        int subId = 1;
        int timeout = 1000;
        mValidatorUT.validate(subId, timeout, false, mCallback);
        mValidatorUT.mNetworkCallback.onLost(new Network(100));
        assertValidationResult(subId, false);
    }

    @Test
    @SmallTest
    public void testDoNotReleaseRequestAfterValidation_timeout_shouldReleaseImmediately() {
        int subId = 1;
        int timeout = 1000;
        mValidatorUT.validate(subId, timeout, false, mCallback);
        assertInValidation(subId);
        moveTimeForward(timeout);
        processAllMessages();
        assertValidationResult(subId, false);
    }

    @Test
    @SmallTest
    public void testDoNotReleaseRequestAfterValidation_BackToBackRequest() {
        int subId1 = 1;
        int timeout = 1000;
        mValidatorUT.validate(subId1, timeout, false, mCallback);
        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));
        verify(mCallback).onValidationDone(true, subId1);
        assertInValidation(subId1);

        resetStates();
        int subId2 = 2;
        mValidatorUT.validate(subId2, timeout, false, mCallback);
        assertInValidation(subId2);
        mValidatorUT.mNetworkCallback.onCapabilitiesChanged(null, new NetworkCapabilities()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED));
        verify(mCallback).onValidationDone(true, subId2);
        assertInValidation(subId2);
        moveTimeForward(1000);
        processAllMessages();
        assertValidationResult(subId2, true);
        // No callback should be triggered on subId1
        verify(mCallback, never()).onValidationDone(anyBoolean(), eq(subId1));
    }

    private void assertNetworkRecentlyValidated(int subId, boolean shouldBeRecentlyValidated) {
        // Start validation and send network available callback.
        resetStates();
@@ -432,7 +451,7 @@ public class CellularNetworkValidatorTest extends TelephonyTest {
    private void assertValidationResult(int subId, boolean shouldPass) {
        // Verify that validation is over.
        verify(mConnectivityManager).unregisterNetworkCallback(eq(mValidatorUT.mNetworkCallback));
        assertFalse(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertFalse(mValidatorUT.mHandler.hasMessagesOrCallbacks());
        assertFalse(mValidatorUT.isValidating());
        assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                mValidatorUT.getSubIdInValidation());
@@ -443,7 +462,16 @@ public class CellularNetworkValidatorTest extends TelephonyTest {

    private void assertInValidation(int subId) {
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        assertTrue(mValidatorUT.mHandler.hasCallbacks(mValidatorUT.mTimeoutCallback));
        assertTrue(mValidatorUT.mHandler.hasMessagesOrCallbacks());
        assertEquals(subId, mValidatorUT.getSubIdInValidation());
        NetworkRequest expectedRequest = new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build())
                .build();
        verify(mConnectivityManager).requestNetwork(
                eq(expectedRequest), eq(mValidatorUT.mNetworkCallback), any());
        assertTrue(mValidatorUT.isValidating());
    }

+0 −1
Original line number Diff line number Diff line
@@ -959,7 +959,6 @@ public class PhoneSwitcherTest extends TelephonyTest {
        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
        processAllMessages();
        verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
        verify(mCellularNetworkValidator).stopValidation();
    }

    @Test