Use InterfaceSet for upstream interfaces.

Allows using different upstream interfaces for IPv4 and IPv6.

Bug: 38218697
Bug: 64382985
Bug: 64976379
Bug: 64995262
Bug: 64380515
Test: runtest frameworks-net, manual (with aosp/644099 applied)

Change-Id: I3db63f7aa5255a0229253187def7590a386c5133
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java
new file mode 100644
index 0000000..8012838
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceSetTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceSetTest {
+    @Test
+    public void testNullNamesIgnored() {
+        final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
+        assertEquals(2, set.ifnames.size());
+        assertTrue(set.ifnames.contains("if1"));
+        assertTrue(set.ifnames.contains("if2"));
+    }
+
+    @Test
+    public void testToString() {
+        final InterfaceSet set = new InterfaceSet("if1", "if2");
+        final String setString = set.toString();
+        assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
+    }
+
+    @Test
+    public void testToString_Empty() {
+        final InterfaceSet set = new InterfaceSet(null, null);
+        assertEquals("[]", set.toString());
+    }
+
+    @Test
+    public void testEquals() {
+        assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
+        assertEquals(new InterfaceSet(null, null), new InterfaceSet());
+        assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
+        assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
+        assertFalse(new InterfaceSet().equals(null));
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index c727bd2..d010f14 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -29,7 +29,6 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
@@ -225,7 +224,8 @@
         }
     }
 
-    private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6) {
+    private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
+            boolean with464xlat) {
         final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null);
         info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
@@ -245,6 +245,15 @@
                     NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
         }
 
+        if (with464xlat) {
+            final LinkProperties stackedLink = new LinkProperties();
+            stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
+            stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+
+            prop.addStackedLink(stackedLink);
+        }
+
 
         final NetworkCapabilities capabilities = new NetworkCapabilities()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
@@ -252,11 +261,19 @@
     }
 
     private static NetworkState buildMobileIPv4UpstreamState() {
-        return buildMobileUpstreamState(true, false);
+        return buildMobileUpstreamState(true, false, false);
+    }
+
+    private static NetworkState buildMobileIPv6UpstreamState() {
+        return buildMobileUpstreamState(false, true, false);
     }
 
     private static NetworkState buildMobileDualStackUpstreamState() {
-        return buildMobileUpstreamState(true, true);
+        return buildMobileUpstreamState(true, true, false);
+    }
+
+    private static NetworkState buildMobile464xlatUpstreamState() {
+        return buildMobileUpstreamState(false, true, true);
     }
 
     @Before
@@ -521,7 +538,7 @@
 
         for (TetherInterfaceStateMachine tism :
                 mTetheringDependencies.ipv6CoordinatorNotifyList) {
-            NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true);
+            NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
             tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
                     upstreamState.linkProperties.isIPv6Provisioned()
                             ? ipv6OnlyState.linkProperties
@@ -549,6 +566,19 @@
     }
 
     @Test
+    public void workingMobileUsbTethering_IPv6() throws Exception {
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
     public void workingMobileUsbTethering_DualStack() throws Exception {
         NetworkState upstreamState = buildMobileDualStackUpstreamState();
         runUsbTethering(upstreamState);
@@ -562,6 +592,52 @@
         verify(mNetd, times(1)).tetherApplyDnsInterfaces();
     }
 
+    @Test
+    public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
+        NetworkState upstreamState = buildMobile464xlatUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
+        // Setup IPv6
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        // Then 464xlat comes up
+        upstreamState = buildMobile464xlatUpstreamState();
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
+
+        // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
+        mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+                Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstreamState);
+        mLooper.dispatchAll();
+
+        // Forwarding is added for 464xlat
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
+        // Forwarding was not re-added for v6 (still times(1))
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+    }
 
     @Test
     public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index 5f3fc54..7c77cf5 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -43,6 +43,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
@@ -371,7 +372,7 @@
      */
     private void dispatchTetherConnectionChanged(String upstreamIface) {
         mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                upstreamIface);
+                new InterfaceSet(upstreamIface));
         mLooper.dispatchAll();
     }