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

Commit 66359a07 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Gracefully handled IP changes" into tm-dev

parents 886afec7 b4d74ce1
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));
    }
}