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

Commit 07a75898 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Gracefully handled IP changes"

parents 48d69928 b3b37dba
Loading
Loading
Loading
Loading
+62 −5
Original line number Diff line number Diff line
@@ -98,6 +98,8 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.NetUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.telephony.Rlog;

@@ -1690,6 +1692,41 @@ public class DataNetwork extends StateMachine {
        }
    }

    /**
     * Check if the new link properties are compatible with the old link properties. For example,
     * if IP changes, that's considered incompatible.
     *
     * @param oldLinkProperties Old link properties.
     * @param newLinkProperties New Link properties.
     *
     * @return {@code true} if the new link properties is compatible with the old link properties.
     */
    private boolean isLinkPropertiesCompatible(@NonNull LinkProperties oldLinkProperties,
            @NonNull LinkProperties newLinkProperties) {
        if (Objects.equals(oldLinkProperties, newLinkProperties)) return true;

        if (!LinkPropertiesUtils.isIdenticalAddresses(oldLinkProperties, newLinkProperties)) {
            // If the same address type was removed and added we need to cleanup.
            LinkPropertiesUtils.CompareOrUpdateResult<Integer, LinkAddress> result =
                    new LinkPropertiesUtils.CompareOrUpdateResult<>(
                            oldLinkProperties.getLinkAddresses(),
                            newLinkProperties.getLinkAddresses(),
                            linkAddress -> Objects.hash(linkAddress.getAddress(),
                                    linkAddress.getPrefixLength(), linkAddress.getScope()));
            log("isLinkPropertiesCompatible: old=" + oldLinkProperties
                    + " new=" + newLinkProperties + " result=" + result);
            for (LinkAddress added : result.added) {
                for (LinkAddress removed : result.removed) {
                    if (NetUtils.addressTypeMatches(removed.getAddress(), added.getAddress())) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    /**
     * Check if there are immutable capabilities changed. The connectivity service is not able
     * to handle immutable capabilities changed, but in very rare scenarios, immutable capabilities
@@ -2209,10 +2246,29 @@ public class DataNetwork extends StateMachine {
        }

        if (!linkProperties.equals(mLinkProperties)) {
            // If the new link properties is not compatible (e.g. IP changes, interface changes),
            // then we should de-register the network agent and re-create a new one.
            if ((isConnected() || isHandoverInProgress())
                    && !isLinkPropertiesCompatible(linkProperties, mLinkProperties)) {
                logl("updateDataNetwork: Incompatible link properties detected. Re-create the "
                        + "network agent. Changed from " + mLinkProperties + " to "
                        + linkProperties);

                mLinkProperties = linkProperties;

                // Abandon the network agent because we are going to create a new one.
                mNetworkAgent.abandon();
                // Update the link properties first so the new network agent would be created with
                // the new link properties.
                mLinkProperties = linkProperties;
                mNetworkAgent = createNetworkAgent();
                mNetworkAgent.markConnected();
            } else {
                mLinkProperties = linkProperties;
                log("sendLinkProperties " + mLinkProperties);
                mNetworkAgent.sendLinkProperties(mLinkProperties);
            }
        }

        updateNetworkCapabilities();
    }
@@ -2361,8 +2417,9 @@ public class DataNetwork extends StateMachine {
    private void onTearDown(@TearDownReason int reason) {
        logl("onTearDown: reason=" + tearDownReasonToString(reason));

        // track frequent networkUnwanted call of IMS and INTERNET
        if ((isConnected())
        // track frequent NetworkAgent.onNetworkUnwanted() call of IMS and INTERNET
        if (reason == TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED
                && isConnected()
                && (mNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                || mNetworkCapabilities.hasCapability(
                        NetworkCapabilities.NET_CAPABILITY_INTERNET))) {
+6 −10
Original line number Diff line number Diff line
@@ -2242,21 +2242,17 @@ public class DataNetworkController extends Handler {
    }

    /**
     * There have been several bugs where a RECONNECT loop kicks off where a DataConnection
     * connects to the Network, ConnectivityService indicates that the Network is unwanted,
     * and then the DataConnection reconnects. By the time we get the bug report it's too late
     * because there have already been hundreds of RECONNECTS.  This is meant to capture the issue
     * when it first starts.
     *
     * The unwanted counter is configured to only take an anomaly report in extreme cases.
     * This is to avoid having the anomaly message show up on several devices.
     *
     * There have been several bugs where a RECONNECT loop kicks off where a data network
     * is brought up, but connectivity service indicates that the network is unwanted so telephony
     * tears down the network. But later telephony bring up the data network again and becomes an
     * infinite loop. By the time we get the bug report it's too late because there have already
     * been hundreds of bring up/tear down. This is meant to capture the issue when it first starts.
     */
    private void onTrackNetworkUnwanted() {
        if (mNetworkUnwantedCounter.addOccurrence()) {
            reportAnomaly("Network Unwanted called "
                            + mNetworkUnwantedCounter.getFrequencyString(),
                    "9f3bc55b-bfa6-4e26-afaa-5031426a66d2");
                    "9f3bc55b-bfa6-4e26-afaa-5031426a66d3");
        }
    }

+280 −0
Original line number Diff line number Diff line
@@ -95,7 +95,9 @@ import java.util.concurrent.Executor;
@TestableLooper.RunWithLooper
public class DataNetworkTest extends TelephonyTest {
    private static final String IPV4_ADDRESS = "10.0.2.15";
    private static final String IPV4_ADDRESS1 = "10.0.2.16";
    private static final String IPV6_ADDRESS = "2607:fb90:a620:651d:eabe:f8da:c107:44be";
    private static final String IPV6_ADDRESS1 = "2607:fb90:a620:651d:eabe:f8da:c107:44bf";

    private static final int ADMIN_UID1 = 1234;
    private static final int ADMIN_UID2 = 5678;
@@ -1340,4 +1342,282 @@ public class DataNetworkTest extends TelephonyTest {
        // Internet priority is 20
        assertThat(mDataNetworkUT.getPriority()).isEqualTo(20);
    }

    @Test
    public void testIpChangedV4() throws Exception {
        testCreateDataNetwork();

        DataCallResponse response = new DataCallResponse.Builder()
                .setCause(0)
                .setRetryDurationMillis(-1L)
                .setId(123)
                .setLinkStatus(2)
                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                .setInterfaceName("ifname")
                .setAddresses(Arrays.asList(
                        new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS1), 32),
                        new LinkAddress(IPV6_ADDRESS + "/64")))
                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                        InetAddresses.parseNumericAddress("fd00:976a::9")))
                .setGatewayAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("10.0.2.15"),
                        InetAddresses.parseNumericAddress("fe80::2")))
                .setPcscfAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                .setMtuV4(1234)
                .setMtuV6(5678)
                .setPduSessionId(1)
                .setQosBearerSessions(new ArrayList<>())
                .setTrafficDescriptors(Collections.emptyList())
                .build();

        // IP changes
        mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                        List.of(response), null)).sendToTarget();
        processAllMessages();

        ArgumentCaptor<LinkProperties> linkPropertiesCaptor =
                ArgumentCaptor.forClass(LinkProperties.class);

        // Agent re-created, so register should be called twice.
        verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
                anyInt());
        // The new agent should have the new IP address.
        assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                InetAddresses.parseNumericAddress(IPV4_ADDRESS1),
                InetAddresses.parseNumericAddress(IPV6_ADDRESS));

        assertThat(linkPropertiesCaptor.getValue()).isEqualTo(mDataNetworkUT.getLinkProperties());
    }

    @Test
    public void testIpChangedV6() throws Exception {
        testCreateDataNetwork();

        DataCallResponse response = new DataCallResponse.Builder()
                .setCause(0)
                .setRetryDurationMillis(-1L)
                .setId(123)
                .setLinkStatus(2)
                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                .setInterfaceName("ifname")
                .setAddresses(Arrays.asList(new LinkAddress(IPV6_ADDRESS1 + "/64")))
                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                        InetAddresses.parseNumericAddress("fd00:976a::9")))
                .setGatewayAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("10.0.2.15"),
                        InetAddresses.parseNumericAddress("fe80::2")))
                .setPcscfAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                .setMtuV4(1234)
                .setMtuV6(5678)
                .setPduSessionId(1)
                .setQosBearerSessions(new ArrayList<>())
                .setTrafficDescriptors(Collections.emptyList())
                .build();

        // IP changes
        mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                        List.of(response), null)).sendToTarget();
        processAllMessages();

        ArgumentCaptor<LinkProperties> linkPropertiesCaptor =
                ArgumentCaptor.forClass(LinkProperties.class);

        // Agent re-created, so register should be called twice.
        verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
                anyInt());
        // The new agent should have the new IP address.
        assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                InetAddresses.parseNumericAddress(IPV6_ADDRESS1));

        assertThat(linkPropertiesCaptor.getValue()).isEqualTo(mDataNetworkUT.getLinkProperties());
    }

    @Test
    public void testIpChangedFromV4ToV6() throws Exception {
        doAnswer(invocation -> {
            final Message msg = (Message) invocation.getArguments()[10];

            DataCallResponse response = new DataCallResponse.Builder()
                    .setCause(0)
                    .setRetryDurationMillis(-1L)
                    .setId(123)
                    .setLinkStatus(2)
                    .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                    .setInterfaceName("ifname")
                    .setAddresses(Arrays.asList(
                            new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS), 32)))
                    .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                            InetAddresses.parseNumericAddress("fd00:976a::9")))
                    .setGatewayAddresses(Arrays.asList(
                            InetAddresses.parseNumericAddress("10.0.2.15"),
                            InetAddresses.parseNumericAddress("fe80::2")))
                    .setPcscfAddresses(Arrays.asList(
                            InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                            InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                            InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                    .setMtuV4(1234)
                    .setMtuV6(5678)
                    .setPduSessionId(1)
                    .setQosBearerSessions(new ArrayList<>())
                    .setTrafficDescriptors(Collections.emptyList())
                    .build();
            msg.getData().putParcelable("data_call_response", response);
            msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
            msg.sendToTarget();
            return null;
        }).when(mMockedWwanDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class),
                anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
                any(Message.class));

        NetworkRequestList networkRequestList = new NetworkRequestList();
        networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .build(), mPhone));

        mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
                mInternetDataProfile, networkRequestList,
                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                DataAllowedReason.NORMAL, mDataNetworkCallback);
        replaceInstance(DataNetwork.class, "mDataCallSessionStats",
                mDataNetworkUT, mDataCallSessionStats);
        processAllMessages();

        DataCallResponse response = new DataCallResponse.Builder()
                .setCause(0)
                .setRetryDurationMillis(-1L)
                .setId(123)
                .setLinkStatus(2)
                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                .setInterfaceName("ifname")
                .setAddresses(Arrays.asList(new LinkAddress(IPV6_ADDRESS + "/64")))
                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                        InetAddresses.parseNumericAddress("fd00:976a::9")))
                .setGatewayAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("10.0.2.15"),
                        InetAddresses.parseNumericAddress("fe80::2")))
                .setPcscfAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                .setMtuV4(1234)
                .setMtuV6(5678)
                .setPduSessionId(1)
                .setQosBearerSessions(new ArrayList<>())
                .setTrafficDescriptors(Collections.emptyList())
                .build();

        // IP changes
        mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                        List.of(response), null)).sendToTarget();
        processAllMessages();

        // Agent should not be re-created, so register should be called ony once.
        verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
                anyInt());

        // The network should have IPv6 address now
        assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
                InetAddresses.parseNumericAddress(IPV6_ADDRESS));
    }

    @Test
    public void testIpChangedV4Removed() throws Exception {
        testCreateDataNetwork();

        DataCallResponse response = new DataCallResponse.Builder()
                .setCause(0)
                .setRetryDurationMillis(-1L)
                .setId(123)
                .setLinkStatus(2)
                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                .setInterfaceName("ifname")
                .setAddresses(Arrays.asList(new LinkAddress(IPV6_ADDRESS + "/64")))
                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                        InetAddresses.parseNumericAddress("fd00:976a::9")))
                .setGatewayAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("10.0.2.15"),
                        InetAddresses.parseNumericAddress("fe80::2")))
                .setPcscfAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                .setMtuV4(1234)
                .setMtuV6(5678)
                .setPduSessionId(1)
                .setQosBearerSessions(new ArrayList<>())
                .setTrafficDescriptors(Collections.emptyList())
                .build();

        // IP changes
        mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                        List.of(response), null)).sendToTarget();
        processAllMessages();

        // Agent should not be re-created, so register should be called ony once.
        verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
                anyInt());

        // The network should have IPv6 address now
        assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
                InetAddresses.parseNumericAddress(IPV6_ADDRESS));
    }

    @Test
    public void testIpChangedV6Removed() throws Exception {
        testCreateDataNetwork();

        DataCallResponse response = new DataCallResponse.Builder()
                .setCause(0)
                .setRetryDurationMillis(-1L)
                .setId(123)
                .setLinkStatus(2)
                .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
                .setInterfaceName("ifname")
                .setAddresses(Arrays.asList(new LinkAddress(
                        InetAddresses.parseNumericAddress(IPV4_ADDRESS), 32)))
                .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
                        InetAddresses.parseNumericAddress("fd00:976a::9")))
                .setGatewayAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("10.0.2.15"),
                        InetAddresses.parseNumericAddress("fe80::2")))
                .setPcscfAddresses(Arrays.asList(
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
                        InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
                        InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
                .setMtuV4(1234)
                .setMtuV6(5678)
                .setPduSessionId(1)
                .setQosBearerSessions(new ArrayList<>())
                .setTrafficDescriptors(Collections.emptyList())
                .build();

        // IP changes
        mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
                new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                        List.of(response), null)).sendToTarget();
        processAllMessages();

        // Agent should not be re-created, so register should be called ony once.
        verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
                anyInt());

        // The network should have IPv6 address now
        assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
                InetAddresses.parseNumericAddress(IPV4_ADDRESS));
    }
}