Merge "Move mDNS code to service-connectivity-t"
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index e1b7016..e48019c 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1203,6 +1203,9 @@
         }
 
         private void handleConnectivityAction(Intent intent) {
+            // CONNECTIVITY_ACTION is not handled since U+ device.
+            if (SdkLevel.isAtLeastU()) return;
+
             final NetworkInfo networkInfo =
                     (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO);
             if (networkInfo == null
@@ -2034,6 +2037,11 @@
                     // broadcasts that result in being passed a
                     // TetherMainSM.CMD_UPSTREAM_CHANGED.
                     handleNewUpstreamNetworkState(null);
+
+                    if (SdkLevel.isAtLeastU()) {
+                        // Need to try DUN immediately if Wi-Fi goes down.
+                        chooseUpstreamType(true);
+                    }
                     break;
                 default:
                     mLog.e("Unknown arg1 value: " + arg1);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 38f1e9c..da81bda 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -666,7 +666,7 @@
 
             // Calling System.gc() or System.runFinalization() doesn't guarantee GCs or finalizers
             // are executed synchronously. The finalizer is called after GC on a separate thread.
-            final int attempts = 100;
+            final int attempts = 600;
             final long waitIntervalMs = 50;
             for (int i = 0; i < attempts; i++) {
                 forceGc();
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 a8d886b..63702f2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -82,6 +82,8 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeFalse;
@@ -179,6 +181,7 @@
 import android.util.ArraySet;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -1337,6 +1340,131 @@
         verifyDisableTryCellWhenTetheringStop(inOrder);
     }
 
+    private void verifyWifiUpstreamAndUnregisterDunCallback(@NonNull final InOrder inOrder,
+            @NonNull final TestNetworkAgent wifi,
+            @NonNull final NetworkCallback currentDunCallack) throws Exception {
+        assertNotNull(currentDunCallack);
+
+        inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
+        inOrder.verify(mCm).unregisterNetworkCallback(eq(currentDunCallack));
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+    }
+
+    @Nullable
+    private NetworkCallback verifyDunUpstream(@NonNull final InOrder inOrder,
+            @NonNull final TestNetworkAgent dun, final boolean needToRequestNetwork)
+            throws Exception {
+        inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
+        ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
+        NetworkCallback dunNetworkCallback = null;
+        if (needToRequestNetwork) {
+            inOrder.verify(mCm).requestNetwork(any(), eq(0), eq(TYPE_MOBILE_DUN), any(),
+                    captor.capture());
+            dunNetworkCallback = captor.getValue();
+        }
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+
+        if (needToRequestNetwork) {
+            assertNotNull(dunNetworkCallback);
+        } else {
+            assertNull(dunNetworkCallback);
+        }
+
+        return dunNetworkCallback;
+    }
+
+    // Overall test coverage:
+    // - verifyChooseDunUpstreamByAutomaticMode: common, test#1, test#2
+    // - testChooseDunUpstreamByAutomaticMode_defaultNetworkWifi: test#3, test#4
+    // - testChooseDunUpstreamByAutomaticMode_loseDefaultNetworkWifi: test#5
+    // - testChooseDunUpstreamByAutomaticMode_defaultNetworkCell: test#5, test#7
+    // - testChooseDunUpstreamByAutomaticMode_loseAndRegainDun: test#8
+    // - testChooseDunUpstreamByAutomaticMode_switchDefaultFromWifiToCell: test#9, test#10
+    //
+    // Overall test cases:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |       |       |       |   -   | --+
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |       |   V   |       |   -   |   |
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |       |   V   |   O   |  Dun  |   +-- chooseDunUpstreamTestCommon
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |   V   |   O   |   O   |  WiFi |   |
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |   V   |   O   |       |  WiFi | --+
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   1   +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   2   +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   3   |   V   |   O   |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |   4   |   V   |       |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |   5   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   6   |       |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   7   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |       |       |       |   -   |
+    // |   8   +-------+-------+-------+-------+
+    // |       |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |   V   |       |   O   |  WiFi |
+    // |   9   +-------+-------+-------+-------+
+    // |       |   V   |       |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   10  +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
+    // Annotation:
+    // 1. "V" means that the given network is connected and it is default network.
+    // 2. "O" means that the given network is connected and it is not default network.
+    //
+
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |       |       |       |   -   | --+
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |       |   V   |       |   -   |   |
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |       |   V   |   O   |  Dun  |   +-- chooseDunUpstreamTestCommon
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |   V   |   O   |   O   |  WiFi |   |
+    // +-------+-------+-------+-------+-------+   |
+    // |   -   |   V   |   O   |       |  WiFi | --+
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   1   +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   2   +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
     private void verifyChooseDunUpstreamByAutomaticMode(boolean configAutomatic) throws Exception {
         // Enable automatic upstream selection.
         TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
@@ -1345,20 +1473,14 @@
         InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
         chooseDunUpstreamTestCommon(configAutomatic, inOrder, mobile, wifi, dun);
 
-        // When default network switch to mobile and wifi is connected (may have low signal),
+        // [1] When default network switch to mobile and wifi is connected (may have low signal),
         // automatic mode would request dun again and choose it as upstream.
         mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
         mLooper.dispatchAll();
-        inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
-        ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
-        inOrder.verify(mCm).requestNetwork(any(), eq(0), eq(TYPE_MOBILE_DUN), any(), any());
-        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(null);
-        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
-        dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
-        mLooper.dispatchAll();
-        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+        verifyDunUpstream(inOrder, dun, true /* needToRequestNetwork */);
 
-        // Lose and regain upstream again.
+        // [2] Lose and regain upstream again.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
         dun.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
         mLooper.dispatchAll();
         inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
@@ -1376,12 +1498,245 @@
         verifyChooseDunUpstreamByAutomaticMode(true /* configAutomatic */);
     }
 
+    // testChooseDunUpstreamByAutomaticMode_* doesn't verify configAutomatic:false because no
+    // matter |configAutomatic| set to true or false, the result always be automatic mode. We
+    // just need one test to make sure this behavior. Don't need to test this configuration
+    // in all tests.
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
     public void testChooseDunUpstreamByAutomaticModeWithConfigDisabled() throws Exception {
         verifyChooseDunUpstreamByAutomaticMode(false /* configAutomatic */);
     }
 
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   3   |   V   |   O   |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |   4   |   V   |       |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    //
+    // See verifyChooseDunUpstreamByAutomaticMode for the annotation.
+    //
+    @Test
+    public void testChooseDunUpstreamByAutomaticMode_defaultNetworkWifi() throws Exception {
+        TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
+        TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
+        TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
+        final InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+        final NetworkCallback dunNetworkCallback1 = setupDunUpstreamTest(
+                true /* configAutomatic */, inOrder);
+
+        // When wifi connected, unregister dun request and choose wifi as upstream.
+        wifi.fakeConnect();
+        mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        verifyWifiUpstreamAndUnregisterDunCallback(inOrder, wifi, dunNetworkCallback1);
+
+        // When default network switch to mobile and wifi is connected (may have low signal),
+        // automatic mode would request dun again and choose it as upstream.
+        mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        final NetworkCallback dunNetworkCallback2 =
+                verifyDunUpstream(inOrder, dun, true /* needToRequestNetwork */);
+
+        // [3] When default network switch to wifi and mobile is still connected,
+        // unregister dun request and choose wifi as upstream.
+        mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        verifyWifiUpstreamAndUnregisterDunCallback(inOrder, wifi, dunNetworkCallback2);
+
+        // [4] When mobile is disconnected, keep wifi as upstream.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        mobile.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+
+        verifyDisableTryCellWhenTetheringStop(inOrder);
+    }
+
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |   V   |       |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |   5   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
+    // See verifyChooseDunUpstreamByAutomaticMode for the annotation.
+    //
+    @Test
+    public void testChooseDunUpstreamByAutomaticMode_loseDefaultNetworkWifi() throws Exception {
+        TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
+        TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
+        final InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+        final NetworkCallback dunNetworkCallback = setupDunUpstreamTest(
+                true /* configAutomatic */, inOrder);
+
+        // When wifi connected, unregister dun request and choose wifi as upstream.
+        wifi.fakeConnect();
+        mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        verifyWifiUpstreamAndUnregisterDunCallback(inOrder, wifi, dunNetworkCallback);
+
+        // [5] When wifi is disconnected, automatic mode would request dun again and choose it
+        // as upstream.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        mCm.makeDefaultNetwork(null, CALLBACKS_FIRST, doDispatchAll);
+        wifi.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        verifyDunUpstream(inOrder, dun, true /* needToRequestNetwork */);
+
+        verifyDisableTryCellWhenTetheringStop(inOrder);
+    }
+
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   6   |       |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |   7   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
+    // See verifyChooseDunUpstreamByAutomaticMode for the annotation.
+    //
+    @Test
+    public void testChooseDunUpstreamByAutomaticMode_defaultNetworkCell() throws Exception {
+        TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
+        TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
+        final InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+        setupDunUpstreamTest(true /* configAutomatic */, inOrder);
+
+        // Pretend dun connected and expect choose dun as upstream.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+
+        // [6] When mobile is connected and default network switch to mobile, keep dun as upstream.
+        mobile.fakeConnect();
+        mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+
+        // [7] When mobile is disconnected, keep dun as upstream.
+        mCm.makeDefaultNetwork(null, CALLBACKS_FIRST, doDispatchAll);
+        mobile.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+
+        verifyDisableTryCellWhenTetheringStop(inOrder);
+    }
+
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |       |       |       |   -   |
+    // |   8   +-------+-------+-------+-------+
+    // |       |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
+    // See verifyChooseDunUpstreamByAutomaticMode for the annotation.
+    //
+    @Test
+    public void testChooseDunUpstreamByAutomaticMode_loseAndRegainDun() throws Exception {
+        TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
+        final InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+        setupDunUpstreamTest(true /* configAutomatic */, inOrder);
+
+        // Pretend dun connected and expect choose dun as upstream.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+
+        // [8] Lose and regain upstream again.
+        dun.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        verifyDunUpstream(inOrder, dun, false /* needToRequestNetwork */);
+
+        verifyDisableTryCellWhenTetheringStop(inOrder);
+    }
+
+    // Test case:
+    // +-------+-------+-------+-------+-------+
+    // | Test  | WiFi  | Cellu |  Dun  | Expec |
+    // | Case  |       | alr   |       | ted   |
+    // |   #   |       |       |       | Upstr |
+    // |       |       |       |       | eam   |
+    // +-------+-------+-------+-------+-------+
+    // |   -   |       |       |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    // |       |   V   |       |   O   |  WiFi |
+    // |   9   +-------+-------+-------+-------+
+    // |       |   V   |       |       |  WiFi |
+    // +-------+-------+-------+-------+-------+
+    // |       |   O   |   V   |       |   -   |
+    // |   10  +-------+-------+-------+-------+
+    // |       |   O   |   V   |   O   |  Dun  |
+    // +-------+-------+-------+-------+-------+
+    //
+    // See verifyChooseDunUpstreamByAutomaticMode for the annotation.
+    //
+    @Test
+    public void testChooseDunUpstreamByAutomaticMode_switchDefaultFromWifiToCell()
+            throws Exception {
+        TestNetworkAgent mobile = new TestNetworkAgent(mCm, buildMobileDualStackUpstreamState());
+        TestNetworkAgent wifi = new TestNetworkAgent(mCm, buildWifiUpstreamState());
+        TestNetworkAgent dun = new TestNetworkAgent(mCm, buildDunUpstreamState());
+        final InOrder inOrder = inOrder(mCm, mUpstreamNetworkMonitor);
+        final NetworkCallback dunNetworkCallback = setupDunUpstreamTest(
+                true /* configAutomatic */, inOrder);
+
+        // Pretend dun connected and expect choose dun as upstream.
+        final Runnable doDispatchAll = () -> mLooper.dispatchAll();
+        dun.fakeConnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(dun.networkId);
+
+        // [9] When wifi is connected and default network switch to wifi, unregister dun request
+        // and choose wifi as upstream. When dun is disconnected, keep wifi as upstream.
+        wifi.fakeConnect();
+        mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        verifyWifiUpstreamAndUnregisterDunCallback(inOrder, wifi, dunNetworkCallback);
+        dun.fakeDisconnect(CALLBACKS_FIRST, doDispatchAll);
+        mLooper.dispatchAll();
+        inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
+
+        // [10] When mobile and mobile are connected and default network switch to mobile
+        // (may have low signal), automatic mode would request dun again and choose it as
+        // upstream.
+        mobile.fakeConnect();
+        mCm.makeDefaultNetwork(mobile, CALLBACKS_FIRST);
+        mLooper.dispatchAll();
+        verifyDunUpstream(inOrder, dun, true /* needToRequestNetwork */);
+
+        verifyDisableTryCellWhenTetheringStop(inOrder);
+    }
+
     @Test
     @IgnoreAfter(Build.VERSION_CODES.TIRAMISU)
     public void testChooseDunUpstreamByLegacyMode() throws Exception {
@@ -1425,8 +1780,7 @@
         verifyDisableTryCellWhenTetheringStop(inOrder);
     }
 
-    private void chooseDunUpstreamTestCommon(final boolean automatic, InOrder inOrder,
-            TestNetworkAgent mobile, TestNetworkAgent wifi, TestNetworkAgent dun) throws Exception {
+    private NetworkCallback setupDunUpstreamTest(final boolean automatic, InOrder inOrder) {
         when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(automatic);
         when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true);
         sendConfigurationChanged();
@@ -1439,8 +1793,14 @@
         inOrder.verify(mUpstreamNetworkMonitor).setTryCell(true);
         ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
         inOrder.verify(mCm).requestNetwork(any(), eq(0), eq(TYPE_MOBILE_DUN), any(),
-                captor.capture());
-        final NetworkCallback dunNetworkCallback1 = captor.getValue();
+                captor.capture() /* DUN network callback */);
+
+        return captor.getValue();
+    }
+
+    private void chooseDunUpstreamTestCommon(final boolean automatic, InOrder inOrder,
+            TestNetworkAgent mobile, TestNetworkAgent wifi, TestNetworkAgent dun) throws Exception {
+        final NetworkCallback dunNetworkCallback = setupDunUpstreamTest(automatic, inOrder);
 
         // Pretend cellular connected and expect the upstream to be set.
         mobile.fakeConnect();
@@ -1458,9 +1818,7 @@
         wifi.fakeConnect();
         mCm.makeDefaultNetwork(wifi, CALLBACKS_FIRST);
         mLooper.dispatchAll();
-        inOrder.verify(mUpstreamNetworkMonitor).setTryCell(false);
-        inOrder.verify(mCm).unregisterNetworkCallback(eq(dunNetworkCallback1));
-        inOrder.verify(mUpstreamNetworkMonitor).setCurrentUpstream(wifi.networkId);
+        verifyWifiUpstreamAndUnregisterDunCallback(inOrder, wifi, dunNetworkCallback);
         dun.fakeDisconnect(BROADCAST_FIRST, doDispatchAll);
         mLooper.dispatchAll();
         inOrder.verify(mUpstreamNetworkMonitor, never()).setCurrentUpstream(any());
@@ -1767,7 +2125,7 @@
                     new ArrayList<Network>(Arrays.asList(networks));
             for (Network upstream : expectedUpstreams) {
                 // throws OOB if no expectations
-                assertEquals(mActualUpstreams.remove(0), upstream);
+                assertEquals(upstream, mActualUpstreams.remove(0));
             }
             assertNoUpstreamChangeCallback();
         }
@@ -1782,14 +2140,14 @@
             for (TetheringConfigurationParcel config : expectedTetherConfig) {
                 // throws OOB if no expectations
                 final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0);
-                assertTetherConfigParcelEqual(actualConfig, config);
+                assertTetherConfigParcelEqual(config, actualConfig);
             }
             assertNoConfigChangeCallback();
         }
 
         public void expectOffloadStatusChanged(final int expectedStatus) {
             assertOffloadStatusChangedCallback();
-            assertEquals(mOffloadStatus.remove(0), new Integer(expectedStatus));
+            assertEquals(Integer.valueOf(expectedStatus), mOffloadStatus.remove(0));
         }
 
         public TetherStatesParcel pollTetherStatesChanged() {
@@ -1880,12 +2238,12 @@
 
         private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual,
                 @NonNull TetheringConfigurationParcel expect) {
-            assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs);
-            assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs);
-            assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs);
-            assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges);
-            assertArrayEquals(actual.provisioningApp, expect.provisioningApp);
-            assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi);
+            assertArrayEquals(expect.tetherableUsbRegexs, actual.tetherableUsbRegexs);
+            assertArrayEquals(expect.tetherableWifiRegexs, actual.tetherableWifiRegexs);
+            assertArrayEquals(expect.tetherableBluetoothRegexs, actual.tetherableBluetoothRegexs);
+            assertArrayEquals(expect.legacyDhcpRanges, actual.legacyDhcpRanges);
+            assertArrayEquals(expect.provisioningApp, actual.provisioningApp);
+            assertEquals(expect.provisioningAppNoUi, actual.provisioningAppNoUi);
         }
     }
 
diff --git a/framework-t/src/android/net/IpSecManager.java b/framework-t/src/android/net/IpSecManager.java
index c8469b1..3afa6ef 100644
--- a/framework-t/src/android/net/IpSecManager.java
+++ b/framework-t/src/android/net/IpSecManager.java
@@ -273,7 +273,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            mCloseGuard.open("open");
+            mCloseGuard.open("close");
         }
 
         /** @hide */
diff --git a/framework-t/src/android/net/IpSecTransform.java b/framework-t/src/android/net/IpSecTransform.java
index 68ae5de..c236b6c 100644
--- a/framework-t/src/android/net/IpSecTransform.java
+++ b/framework-t/src/android/net/IpSecTransform.java
@@ -126,7 +126,7 @@
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
                 Log.d(TAG, "Added Transform with Id " + mResourceId);
-                mCloseGuard.open("build");
+                mCloseGuard.open("close");
             } catch (ServiceSpecificException e) {
                 throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
             }
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index 0605abe..51683de 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -491,10 +491,19 @@
             mDeps.makeIpClient(mContext, name, mIpClientCallback);
             mIpClientCallback.awaitIpClientStart();
 
+            if (mIpConfig.getProxySettings() == ProxySettings.STATIC
+                    || mIpConfig.getProxySettings() == ProxySettings.PAC) {
+                mIpClient.setHttpProxy(mIpConfig.getHttpProxy());
+            }
+
             if (sTcpBufferSizes == null) {
                 sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext);
             }
-            provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes);
+            if (!TextUtils.isEmpty(sTcpBufferSizes)) {
+                mIpClient.setTcpBufferSizes(sTcpBufferSizes);
+            }
+
+            mIpClient.startProvisioning(createProvisioningConfiguration(mIpConfig));
         }
 
         void onIpLayerStarted(@NonNull final LinkProperties linkProperties) {
@@ -635,20 +644,6 @@
             mRequestIds.clear();
         }
 
-        private static void provisionIpClient(@NonNull final IpClientManager ipClient,
-                @NonNull final IpConfiguration config, @NonNull final String tcpBufferSizes) {
-            if (config.getProxySettings() == ProxySettings.STATIC ||
-                    config.getProxySettings() == ProxySettings.PAC) {
-                ipClient.setHttpProxy(config.getHttpProxy());
-            }
-
-            if (!TextUtils.isEmpty(tcpBufferSizes)) {
-                ipClient.setTcpBufferSizes(tcpBufferSizes);
-            }
-
-            ipClient.startProvisioning(createProvisioningConfiguration(config));
-        }
-
         private static ProvisioningConfiguration createProvisioningConfiguration(
                 @NonNull final IpConfiguration config) {
             if (config.getIpAssignment() == IpAssignment.STATIC) {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 0e04a80..61b597a 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2391,7 +2391,7 @@
             getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons));
         }
         private void assertNoBlockedStatusCallback() {
-            super.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS,
+            super.assertNoCallback(NO_CALLBACK_TIMEOUT_MS,
                     c -> c instanceof CallbackEntry.BlockedStatus);
         }
     }
@@ -2979,7 +2979,7 @@
             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
                     entry -> cellNetwork.equals(entry.getNetwork()));
             // The network should not validate again.
-            wifiCb.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS, c -> isValidatedCaps(c));
+            wifiCb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS, c -> isValidatedCaps(c));
         } finally {
             resetAvoidBadWifi(previousAvoidBadWifi);
             mHttpServer.stop();
@@ -3151,7 +3151,7 @@
      */
     private void assertNoCallbackExceptCapOrLpChange(
             @NonNull final TestableNetworkCallback cb) {
-        cb.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS,
+        cb.assertNoCallback(NO_CALLBACK_TIMEOUT_MS,
                 c -> !(c instanceof CallbackEntry.CapabilitiesChanged
                         || c instanceof CallbackEntry.LinkPropertiesChanged));
     }
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 7e91478..b924f65 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -57,8 +57,8 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.OutcomeReceiver
-import android.os.SystemProperties
 import android.os.Process
+import android.os.SystemProperties
 import android.platform.test.annotations.AppModeFull
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.net.module.util.ArrayTrackRecord
@@ -77,16 +77,10 @@
 import com.android.testutils.assertThrows
 import com.android.testutils.runAsShell
 import com.android.testutils.waitForIdle
-import org.junit.After
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
 import java.io.IOException
 import java.net.Inet6Address
-import java.util.Random
 import java.net.Socket
+import java.util.Random
 import java.util.concurrent.CompletableFuture
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeUnit
@@ -99,6 +93,12 @@
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
+import org.junit.After
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
 
 private const val TAG = "EthernetManagerTest"
 // This timeout does not affect the test duration for passing tests. It needs to be long enough to
@@ -530,12 +530,10 @@
         eventuallyExpect(Lost::class) { n?.equals(it.network) ?: true }
 
     private fun TestableNetworkCallback.assertNeverLost(n: Network? = null) =
-        assertNoCallbackThat() {
-            it is Lost && (n?.equals(it.network) ?: true)
-        }
+        assertNoCallback { it is Lost && (n?.equals(it.network) ?: true) }
 
     private fun TestableNetworkCallback.assertNeverAvailable(n: Network? = null) =
-        assertNoCallbackThat { it is Available && (n?.equals(it.network) ?: true) }
+        assertNoCallback { it is Available && (n?.equals(it.network) ?: true) }
 
     private fun TestableNetworkCallback.expectCapabilitiesWithInterfaceName(name: String) =
         expect<CapabilitiesChanged> { it.caps.networkSpecifier == EthernetNetworkSpecifier(name) }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 7d64c26..51f54a4 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2806,7 +2806,8 @@
             // for any other request.
             generalCb.expectLosing(net2);
             net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS);
-            generalCb.assertNoCallback();
+            // Timeout 0 because after a while LOST will actually arrive
+            generalCb.assertNoCallback(0 /* timeoutMs */);
             net2.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
         } else {
             net2.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
@@ -3006,14 +3007,12 @@
      */
     private class TestNetworkCallback extends TestableNetworkCallback {
         TestNetworkCallback() {
-            super(TEST_CALLBACK_TIMEOUT_MS);
-        }
-
-        @Override
-        public void assertNoCallback() {
-            // TODO: better support this use case in TestableNetworkCallback
-            waitForIdle();
-            assertNoCallback(0 /* timeout */);
+            // In the context of this test, the testable network callbacks should use waitForIdle
+            // before calling assertNoCallback in an effort to detect issues where a callback is
+            // not yet sent but a message currently in the queue of a handler will cause it to
+            // be sent soon.
+            super(TEST_CALLBACK_TIMEOUT_MS, TEST_CALLBACK_TIMEOUT_MS,
+                    ConnectivityServiceTest.this::waitForIdle);
         }
 
         public CallbackEntry.Losing expectLosing(final HasNetwork n, final long timeoutMs) {
@@ -3039,9 +3038,15 @@
 
     // Can't be part of TestNetworkCallback because "cannot be declared static; static methods can
     // only be declared in a static or top level type".
+    static void assertNoCallbacks(final long timeoutMs, TestNetworkCallback ... callbacks) {
+        for (TestNetworkCallback c : callbacks) {
+            c.assertNoCallback(timeoutMs);
+        }
+    }
+
     static void assertNoCallbacks(TestNetworkCallback ... callbacks) {
         for (TestNetworkCallback c : callbacks) {
-            c.assertNoCallback();
+            c.assertNoCallback(); // each callback uses its own timeout
         }
     }
 
@@ -3177,7 +3182,10 @@
         wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         cellNetworkCallback.expectLosing(mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
-        assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
+        // Cell will disconnect after the lingering period. Before that elapses check that
+        // there have been no callbacks.
+        assertNoCallbacks(0 /* timeoutMs */,
+                genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
 
         mWiFiNetworkAgent.disconnect();
         genericNetworkCallback.expect(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -3488,7 +3496,7 @@
         callback.expectLosing(mCellNetworkAgent);
 
         // Let linger run its course.
-        callback.assertNoCallback();
+        callback.assertNoCallback(0 /* timeoutMs */);
         final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
         callback.expect(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs);
 
@@ -5927,7 +5935,7 @@
                 .clearCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
                 .build();
-        final TestableNetworkCallback wifiCallback = new TestableNetworkCallback();
+        final TestNetworkCallback wifiCallback = new TestNetworkCallback();
         mCm.registerNetworkCallback(wifiRequest, wifiCallback);
 
         // Bring up validated cell and unvalidated wifi.
@@ -10507,7 +10515,7 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
-    private void expectNat64PrefixChange(TestableNetworkCallback callback,
+    private void expectNat64PrefixChange(TestNetworkCallback callback,
             TestNetworkAgentWrapper agent, IpPrefix prefix) {
         callback.expectLinkPropertiesThat(agent, x -> Objects.equals(x.getNat64Prefix(), prefix));
     }
@@ -16409,7 +16417,7 @@
             final NetworkCallback[] callbacks = new NetworkCallback[remainingCount];
             doAsUid(otherAppUid, () -> {
                 for (int i = 0; i < remainingCount; ++i) {
-                    callbacks[i] = new TestableNetworkCallback();
+                    callbacks[i] = new TestNetworkCallback();
                     mCm.registerDefaultNetworkCallback(callbacks[i]);
                 }
             });