Merge "Add a test for upstream selection."
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 40b7c55..0611086 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -78,6 +78,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -165,6 +166,7 @@
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
 import com.android.testutils.MiscAsserts;
 
 import org.junit.After;
@@ -174,6 +176,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -501,6 +504,7 @@
         prop.setInterfaceName(interfaceName);
 
         if (withIPv4) {
+            prop.addLinkAddress(new LinkAddress("10.1.2.3/15"));
             prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
                     InetAddresses.parseNumericAddress("10.0.0.1"),
                     interfaceName, RTN_UNICAST));
@@ -830,9 +834,7 @@
         mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
     }
 
-    private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
-        initTetheringUpstream(upstreamState);
-
+    private void prepareUsbTethering() {
         // Emulate pressing the USB tethering button in Settings UI.
         final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
         mTethering.startTethering(request, null);
@@ -847,7 +849,8 @@
     @Test
     public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
         UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
-        prepareUsbTethering(upstreamState);
+        initTetheringUpstream(upstreamState);
+        prepareUsbTethering();
 
         // This should produce no activity of any kind.
         verifyNoMoreInteractions(mNetd);
@@ -942,7 +945,8 @@
     }
 
     private void runUsbTethering(UpstreamNetworkState upstreamState) {
-        prepareUsbTethering(upstreamState);
+        initTetheringUpstream(upstreamState);
+        prepareUsbTethering();
         sendUsbBroadcast(true, true, true, TETHERING_USB);
         mLooper.dispatchAll();
     }
@@ -1083,6 +1087,73 @@
         verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
     }
 
+    @Test
+    public void testAutomaticUpstreamSelection() throws Exception {
+        // Enable automatic upstream selection.
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
+        sendConfigurationChanged();
+        mLooper.dispatchAll();
+
+        InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+
+        // Start USB tethering with no current upstream.
+        prepareUsbTethering();
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).startObserveAllNetworks();
+        inOrder.verify(mUpstreamNetworkMonitor).registerMobileNetworkRequest();
+
+        // Pretend cellular connected and expect the upstream to be set.
+        TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
+        mobile.fakeConnect();
+        mCm.makeDefaultNetwork(mobile);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
+
+        // Switch upstreams a few times.
+        // TODO: there may be a race where if the effects of the CONNECTIVITY_ACTION happen before
+        // UpstreamNetworkMonitor gets onCapabilitiesChanged on CALLBACK_DEFAULT_INTERNET, the
+        // upstream does not change. Extend TestConnectivityManager to simulate this condition and
+        // write a test for this.
+        TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
+        wifi.fakeConnect();
+        mCm.makeDefaultNetwork(wifi);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
+
+        mCm.makeDefaultNetwork(mobile);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
+
+        // Wifi disconnecting should not have any affect since it's not the current upstream.
+        wifi.fakeDisconnect();
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+
+        // Lose and regain upstream.
+        assertTrue(mUpstreamNetworkMonitor.getCurrentPreferredUpstream().linkProperties
+                .hasIPv4Address());
+        mobile.fakeDisconnect();
+        mCm.makeDefaultNetwork(null);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
+
+        mobile = new TestNetworkAgent(mCm, buildMobile464xlatUpstreamState());
+        mobile.fakeConnect();
+        mCm.makeDefaultNetwork(mobile);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(mobile.networkId);
+
+        // Check the IP addresses to ensure that the upstream is indeed not the same as the previous
+        // mobile upstream, even though the netId is (unrealistically) the same.
+        assertFalse(mUpstreamNetworkMonitor.getCurrentPreferredUpstream().linkProperties
+                .hasIPv4Address());
+        mobile.fakeDisconnect();
+        mCm.makeDefaultNetwork(null);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
+    }
+
     private void runNcmTethering() {
         prepareNcmTethering();
         sendUsbBroadcast(true, true, true, TETHERING_NCM);