Fix and test usb function configuration change

Allow stop TETHERING_NCM no matter ncm is used for TETHERING_USB or
TETHERING_NCM.

Add a test:
1. NCM is started for TETHERING_NCM
2. The config changes to use NCM for TETHERING_USB.
3. Then the config changes to use RNDIS for TETHERING_USB.

Also let some logs not only print in dumpsys, but also in logcat to
easiler debug unit test.

Bug: 185649441
Test: atest TetheringTests
Change-Id: Ia8d76a29646f36941cbb16cd68ae6887100ec8ec
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index b52ec86..0556a6a 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1039,7 +1039,7 @@
             final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
             final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
 
-            mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
+            mLog.i(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
                     usbConnected, usbConfigured, rndisEnabled, ncmEnabled));
 
             // There are three types of ACTION_USB_STATE:
@@ -1416,7 +1416,7 @@
 
         // If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
         // available.
-        if (mConfig.isUsingNcm()) return TETHER_ERROR_SERVICE_UNAVAIL;
+        if (mConfig.isUsingNcm() && enable) return TETHER_ERROR_SERVICE_UNAVAIL;
 
         UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
         usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
@@ -2558,7 +2558,7 @@
             return;
         }
 
-        mLog.log("adding IpServer for: " + iface);
+        mLog.i("adding IpServer for: " + iface);
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
@@ -2573,7 +2573,7 @@
         if (tetherState == null) return;
 
         tetherState.ipServer.stop();
-        mLog.log("removing IpServer for: " + iface);
+        mLog.i("removing IpServer for: " + iface);
         mTetherStates.remove(iface);
     }
 
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 383fce1..85f06d9 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2599,12 +2599,57 @@
         reset(mBluetoothAdapter, mBluetoothPan);
     }
 
+    private void runDualStackUsbTethering(final String expectedIface) throws Exception {
+        when(mNetd.interfaceGetList()).thenReturn(new String[] {expectedIface});
+        when(mRouterAdvertisementDaemon.start())
+                .thenReturn(true);
+        final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNetd).interfaceGetList();
+        verify(mNetd).tetherAddForward(expectedIface, TEST_MOBILE_IFNAME);
+        verify(mNetd).ipfwdAddInterfaceForward(expectedIface, TEST_MOBILE_IFNAME);
+
+        verify(mRouterAdvertisementDaemon).start();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
+                any(), any());
+        sendIPv6TetherUpdates(upstreamState);
+        assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
+        verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
+        verify(mNetd).tetherApplyDnsInterfaces();
+    }
+
+    private void forceUsbTetheringUse(final int function) {
+        Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, function);
+        final ContentObserver observer = mTethering.getSettingsObserverForTest();
+        observer.onChange(false /* selfChange */);
+        mLooper.dispatchAll();
+    }
+
+    private void verifyUsbTetheringStopDueToSettingChange(final String iface) {
+        verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        mTethering.interfaceRemoved(iface);
+        sendUsbBroadcast(true, true, -1 /* no functions enabled */);
+        reset(mUsbManager, mNetd, mDhcpServer, mRouterAdvertisementDaemon,
+                mIPv6TetheringCoordinator, mDadProxy);
+    }
+
     @Test
-    public void testUsbTetheringWithNcmFunction() throws Exception {
-        when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
-                TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
+    public void testUsbFunctionConfigurationChange() throws Exception {
+        // Run TETHERING_NCM.
+        runNcmTethering();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
+
+        // Change the USB tethering function to NCM. Because the USB tethering function was set to
+        // RNDIS (the default), tethering is stopped.
+        forceUsbTetheringUse(TETHER_USB_NCM_FUNCTION);
+        verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
+
+        // TODO: move this into setup after allowing configure TEST_NCM_REGEX into
+        // config_tether_usb_regexs and config_tether_ncm_regexs at the same time.
         when(mResources.getStringArray(R.array.config_tether_usb_regexs))
-                .thenReturn(new String[] {TEST_NCM_REGEX});
+                .thenReturn(new String[] {TEST_RNDIS_REGEX, TEST_NCM_REGEX});
         sendConfigurationChanged();
 
         // If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
@@ -2614,30 +2659,16 @@
         mLooper.dispatchAll();
         ncmResult.assertHasResult();
 
-        final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
-        runUsbTethering(upstreamState);
+        // Run TETHERING_USB with ncm configuration.
+        runDualStackUsbTethering(TEST_NCM_IFNAME);
 
-        verify(mNetd).interfaceGetList();
-        verify(mNetd).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mNetd).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
+        // Change configuration to rndis.
+        forceUsbTetheringUse(TETHER_USB_RNDIS_FUNCTION);
+        verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
 
-        verify(mRouterAdvertisementDaemon).start();
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
-                any(), any());
-        sendIPv6TetherUpdates(upstreamState);
-        assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
-        verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
-        verify(mNetd).tetherApplyDnsInterfaces();
-
-        Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS,
-                TETHER_USB_RNDIS_FUNCTION);
-        final ContentObserver observer = mTethering.getSettingsObserverForTest();
-        observer.onChange(false /* selfChange */);
-        mLooper.dispatchAll();
-        // stop TETHERING_USB and TETHERING_NCM
-        verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
-        mTethering.interfaceRemoved(TEST_NCM_IFNAME);
-        sendUsbBroadcast(true, true, -1 /* function */);
+        // Run TETHERING_USB with rndis configuration.
+        runDualStackUsbTethering(TEST_RNDIS_IFNAME);
+        runStopUSBTethering();
     }
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.