[automerger skipped] Merge changes from topic "cherrypicker-L79500000960657219:N07500001368857425" into udc-dev am: 60437e59de -s ours

am skip reason: Merged-In I09e780af25149162f16bd75410ddc50f160a0dab with SHA-1 ce44beb7aa is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/23232553

Change-Id: Ib26fee7d18272bf57baa53a82774f70943342671
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Cronet/tests/common/Android.bp b/Cronet/tests/common/Android.bp
index 5d2f6e5..e17081a 100644
--- a/Cronet/tests/common/Android.bp
+++ b/Cronet/tests/common/Android.bp
@@ -26,7 +26,6 @@
 // go with merging NetHttp and Tethering targets.
 android_test {
     name: "NetHttpCoverageTests",
-    defaults: ["CronetTestJavaDefaults"],
     enforce_default_target_sdk_version: true,
     min_sdk_version: "30",
     test_suites: ["general-tests", "mts-tethering"],
diff --git a/Cronet/tests/cts/Android.bp b/Cronet/tests/cts/Android.bp
index 22eccf9..44b3364 100644
--- a/Cronet/tests/cts/Android.bp
+++ b/Cronet/tests/cts/Android.bp
@@ -18,38 +18,10 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-// cronet_test_java_defaults can be used to specify a java_defaults target that
-// either enables or disables Cronet tests. This is used to disable Cronet
-// tests on tm-mainline-prod where the required APIs are not present.
-cronet_test_java_defaults = "CronetTestJavaDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_test_java_defaults may have different values
-// depending on the branch
-
-java_defaults {
-    name: "CronetTestJavaDefaultsEnabled",
-    enabled: true,
-    // TODO(danstahr): move to unconditional static_libs once the T branch is abandoned
-    static_libs: [
-        "truth",
-    ],
-}
-
-java_defaults {
-    name: "CronetTestJavaDefaultsDisabled",
-    enabled: false,
-}
-
-java_defaults {
-    name: "CronetTestJavaDefaults",
-    defaults: [cronet_test_java_defaults],
-}
-
 android_library {
     name: "CtsNetHttpTestsLib",
     defaults: [
         "cts_defaults",
-        "CronetTestJavaDefaults",
     ],
     sdk_version: "test_current",
     min_sdk_version: "30",
@@ -61,10 +33,11 @@
         "androidx.test.ext.junit",
         "ctstestrunner-axt",
         "ctstestserver",
-        "junit",
         "hamcrest-library",
+        "junit",
         "kotlin-test",
         "mockito-target",
+        "truth",
     ],
     libs: [
         "android.test.base",
@@ -79,7 +52,6 @@
     name: "CtsNetHttpTestCases",
     defaults: [
         "cts_defaults",
-        "CronetTestJavaDefaults",
     ],
     sdk_version: "test_current",
     static_libs: ["CtsNetHttpTestsLib"],
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index ecf4b7f..93564e4 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -19,7 +19,6 @@
 
 java_genrule {
     name: "net-http-test-jarjar-rules",
-    defaults: ["CronetTestJavaDefaults"],
     tool_files: [
         ":NetHttpTestsLibPreJarJar{.jar}",
         "jarjar_excludes.txt",
@@ -37,7 +36,6 @@
 
 android_library {
     name: "NetHttpTestsLibPreJarJar",
-    defaults: ["CronetTestJavaDefaults"],
     srcs: [":cronet_aml_javatests_sources"],
     sdk_version: "module_current",
     min_sdk_version: "30",
@@ -46,6 +44,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "junit",
+        "truth",
     ],
     libs: [
         "android.test.base",
@@ -59,7 +58,6 @@
 android_test {
      name: "NetHttpTests",
      defaults: [
-        "CronetTestJavaDefaults",
         "mts-target-sdk-version-current",
      ],
      static_libs: ["NetHttpTestsLibPreJarJar"],
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 4506e5a..253fb00 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -50,22 +50,8 @@
 // as the above target may have different "enabled" values
 // depending on the branch
 
-// cronet_in_tethering_apex_defaults can be used to specify an apex_defaults target that either
-// enables or disables inclusion of Cronet in the Tethering apex. This is used to disable Cronet
-// on tm-mainline-prod. Note: in order for Cronet APIs to work Cronet must also be enabled
-// by the cronet_java_*_defaults in common/TetheringLib/Android.bp.
-cronet_in_tethering_apex_defaults = "CronetInTetheringApexDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_apex_defaults may have different values
-// depending on the branch
-
 apex_defaults {
     name: "CronetInTetheringApexDefaults",
-    defaults: [cronet_in_tethering_apex_defaults],
-}
-
-apex_defaults {
-    name: "CronetInTetheringApexDefaultsEnabled",
     jni_libs: [
         "cronet_aml_components_cronet_android_cronet",
         "//external/cronet/third_party/boringssl:libcrypto",
@@ -83,10 +69,6 @@
     },
 }
 
-apex_defaults {
-    name: "CronetInTetheringApexDefaultsDisabled",
-}
-
 apex {
     name: "com.android.tethering",
     defaults: [
diff --git a/Tethering/apex/in-process b/Tethering/apex/in-process
deleted file mode 100644
index e69de29..0000000
--- a/Tethering/apex/in-process
+++ /dev/null
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index 6b62da9..a4db776 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -17,16 +17,6 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-// Both cronet_java_defaults and cronet_java_prejarjar_defaults can be used to
-// specify a java_defaults target that either enables or disables Cronet. This
-// is used to disable Cronet on tm-mainline-prod.
-// Note: they must either both be enabled or disabled.
-cronet_java_defaults = "CronetJavaDefaultsEnabled"
-cronet_java_prejarjar_defaults = "CronetJavaPrejarjarDefaultsEnabled"
-// This is a placeholder comment to avoid merge conflicts
-// as cronet_defaults may have different values
-// depending on the branch
-
 java_sdk_library {
     name: "framework-tethering",
     defaults: [
@@ -67,44 +57,6 @@
     lint: { strict_updatability_linting: true },
 }
 
-java_defaults {
-    name: "CronetJavaDefaults",
-    defaults: [cronet_java_defaults],
-}
-
-java_defaults {
-    name: "CronetJavaDefaultsEnabled",
-    srcs: [":cronet_aml_api_sources"],
-    libs: [
-        "androidx.annotation_annotation",
-    ],
-    impl_only_static_libs: [
-        "cronet_aml_java",
-    ],
-}
-
-java_defaults {
-  name: "CronetJavaDefaultsDisabled",
-  api_dir: "cronet_disabled/api",
-}
-
-java_defaults {
-  name: "CronetJavaPrejarjarDefaults",
-  defaults: [cronet_java_prejarjar_defaults],
-}
-
-java_defaults {
-  name: "CronetJavaPrejarjarDefaultsDisabled",
-}
-
-java_defaults {
-  name: "CronetJavaPrejarjarDefaultsEnabled",
-  static_libs: [
-    "cronet_aml_api_java",
-    "cronet_aml_java"
-  ],
-}
-
 java_library {
   name: "framework-tethering-pre-jarjar",
   defaults: [
diff --git a/Tethering/tests/integration/base/android/net/TetheringTester.java b/Tethering/tests/integration/base/android/net/TetheringTester.java
index ae39b24..1c0803e 100644
--- a/Tethering/tests/integration/base/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/base/android/net/TetheringTester.java
@@ -628,7 +628,7 @@
         return false;
     }
 
-    private void sendUploadPacket(ByteBuffer packet) throws Exception {
+    public void sendUploadPacket(ByteBuffer packet) throws Exception {
         mDownstreamReader.sendResponse(packet);
     }
 
@@ -680,4 +680,12 @@
 
         return verifyPacketNotNull("Download fail", getDownloadPacket(filter));
     }
+
+    // Send DHCPDISCOVER to DHCP server to see if DHCP server is still alive to handle
+    // the upcoming DHCP packets. This method should be only used when we know the DHCP
+    // server has been created successfully before.
+    public boolean testDhcpServerAlive(final MacAddress mac) throws Exception {
+        sendDhcpDiscover(mac.toByteArray());
+        return getNextDhcpPacket() != null;
+    }
 }
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 55854e2..21927df 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -39,6 +39,7 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -839,4 +840,41 @@
                 REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */,
                 tester, true /* isClat */);
     }
+
+    private static final byte[] ZeroLengthDhcpPacket = new byte[] {
+            // scapy.Ether(
+            //   dst="ff:ff:ff:ff:ff:ff")
+            // scapy.IP(
+            //   dst="255.255.255.255")
+            // scapy.UDP(sport=68, dport=67)
+            /* Ethernet Header */
+            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+            (byte) 0xe0, (byte) 0x4f, (byte) 0x43, (byte) 0xe6, (byte) 0xfb, (byte) 0xd2,
+            (byte) 0x08, (byte) 0x00,
+            /* Ip header */
+            (byte) 0x45, (byte) 0x00, (byte) 0x00, (byte) 0x1c, (byte) 0x00, (byte) 0x01,
+            (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0xb6, (byte) 0x58,
+            (byte) 0x64, (byte) 0x4f, (byte) 0x60, (byte) 0x29, (byte) 0xff, (byte) 0xff,
+            (byte) 0xff, (byte) 0xff,
+            /* UDP header */
+            (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
+            (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf
+    };
+
+    @Test
+    public void testTetherZeroLengthDhcpPacket() throws Exception {
+        final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR),
+                toList(TEST_IP4_DNS));
+        tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */);
+
+        // Send a zero-length DHCP packet to upstream DHCP server.
+        final ByteBuffer packet = ByteBuffer.wrap(ZeroLengthDhcpPacket);
+        tester.sendUploadPacket(packet);
+
+        // Send DHCPDISCOVER packet from another downstream tethered device to verify that upstream
+        // DHCP server has closed the listening socket and stopped reading, then we will not receive
+        // any DHCPOFFER in this case.
+        final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66");
+        assertFalse(tester.testDhcpServerAlive(macAddress));
+    }
 }
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
index b3fb3e4..81d4fbe 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -106,6 +106,7 @@
                 ConntrackMessage.Tuple tuple = ctmsg.tupleOrig;
 
                 if (nlmsghdr.nlmsg_type == (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW)
+                        && tuple != null
                         && tuple.protoNum == IPPROTO_TCP
                         && tuple.srcIp.equals(local.getAddress())
                         && tuple.dstIp.equals(remote.getAddress())
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index f2a3e62..3797a38 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -19,8 +19,8 @@
 #include <netinet/in.h>
 #include <stdint.h>
 
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
 
 #include "bpf_helpers.h"
 
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index f05b93e..85ba58e 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -30,8 +30,8 @@
 #define __kernel_udphdr udphdr
 #include <linux/udp.h>
 
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
 
 #include "bpf_helpers.h"
 #include "bpf_net_helpers.h"
diff --git a/bpf_progs/dscpPolicy.c b/bpf_progs/dscpPolicy.c
index 72f63c6..262b65b 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -27,8 +27,8 @@
 #include <stdint.h>
 #include <string.h>
 
-// The resulting .o needs to load on the Android T beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
 
 #include "bpf_helpers.h"
 #include "dscpPolicy.h"
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 39dff7f..839ca40 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-// The resulting .o needs to load on the Android T Beta 3 bpfloader
-#define BPFLOADER_MIN_VER BPFLOADER_T_BETA3_VERSION
+// The resulting .o needs to load on the Android T bpfloader
+#define BPFLOADER_MIN_VER BPFLOADER_T_VERSION
 
 #include <bpf_helpers.h>
 #include <linux/bpf.h>
diff --git a/framework/Android.bp b/framework/Android.bp
index d7eaf9b..123f02a 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -107,8 +107,11 @@
     name: "framework-connectivity-pre-jarjar",
     defaults: [
         "framework-connectivity-defaults",
-        "CronetJavaPrejarjarDefaults",
-     ],
+    ],
+    static_libs: [
+        "cronet_aml_api_java",
+        "cronet_aml_java",
+    ],
     libs: [
         // This cannot be in the defaults clause above because if it were, it would be used
         // to generate the connectivity stubs. That would create a circular dependency
@@ -120,6 +123,17 @@
     visibility: ["//packages/modules/Connectivity:__subpackages__"]
 }
 
+java_defaults {
+    name: "CronetJavaDefaults",
+    srcs: [":cronet_aml_api_sources"],
+    libs: [
+        "androidx.annotation_annotation",
+    ],
+    impl_only_static_libs: [
+        "cronet_aml_java",
+    ],
+}
+
 java_sdk_library {
     name: "framework-connectivity",
     defaults: [
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index e0926e9..4f7ac30 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -1456,9 +1456,8 @@
      * @hide
      */
     public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
-        Collection<InetAddress> targetPcscfs = target.getPcscfServers();
-        return (mPcscfs.size() == targetPcscfs.size()) ?
-                    mPcscfs.containsAll(targetPcscfs) : false;
+        // list order is important, compare one by one
+        return target.getPcscfServers().equals(mPcscfs);
     }
 
     /**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 3cc9c65..92e9599 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1348,6 +1348,18 @@
     }
 
     /**
+     * Gets the transports as an int. Internal callers only.
+     *
+     * Prefer getTransportTypes/hasTransportType if not immediately collapsing back into a scalar.
+     *
+     * @return a long integer representing the transport types.
+     * @hide
+     */
+    public long getTransportTypesInternal() {
+        return mTransportTypes;
+    }
+
+    /**
      * Sets all the transports set on this {@code NetworkCapability} instance.
      * This overwrites any existing transports.
      *
diff --git a/netd/BpfBaseTest.cpp b/netd/BpfBaseTest.cpp
index 624d216..c979a7b 100644
--- a/netd/BpfBaseTest.cpp
+++ b/netd/BpfBaseTest.cpp
@@ -93,7 +93,7 @@
     ASSERT_EQ(TEST_TAG, tagResult.value().tag);
     ASSERT_EQ(0, close(sock));
     // Check map periodically until sk destroy handler have done its job.
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < 1000; i++) {
         usleep(5000);  // 5ms
         tagResult = cookieTagMap.readValue(cookie);
         if (!tagResult.ok()) {
@@ -101,7 +101,7 @@
             return;
         }
     }
-    FAIL() << "socket tag still exist after 50ms";
+    FAIL() << "socket tag still exist after 5s";
 }
 
 }
diff --git a/service-t/native/libs/libnetworkstats/Android.bp b/service-t/native/libs/libnetworkstats/Android.bp
index f40d388..0dfd0af 100644
--- a/service-t/native/libs/libnetworkstats/Android.bp
+++ b/service-t/native/libs/libnetworkstats/Android.bp
@@ -30,6 +30,7 @@
     ],
     shared_libs: [
         "libbase",
+        "libcutils",
         "liblog",
     ],
     static_libs: [
@@ -81,6 +82,7 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "libcutils",
         "libandroid_net",
     ],
     compile_multilib: "both",
diff --git a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
index 6aa0fb4..c5f9631 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTraceHandler.cpp
@@ -149,6 +149,18 @@
   if (mIsTest) return;  // Don't touch non-hermetic bpf in test.
   if (mStarted) sPoller.Stop();
   mStarted = false;
+
+  // Although this shouldn't be required, there seems to be some cases when we
+  // don't fill enough of a Perfetto Chunk for Perfetto to automatically commit
+  // the traced data. This manually flushes OnStop so we commit at least once.
+  NetworkTraceHandler::Trace([&](NetworkTraceHandler::TraceContext ctx) {
+    perfetto::LockedHandle<NetworkTraceHandler> handle =
+        ctx.GetDataSourceLocked();
+    // Trace is called for all active handlers, only flush our context. Since
+    // handle doesn't have a `.get()`, use `*` and `&` to get what it points to.
+    if (&(*handle) != this) return;
+    ctx.Flush();
+  });
 }
 
 void NetworkTraceHandler::Write(const std::vector<PacketTrace>& packets,
diff --git a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
index 3de9897..d538368 100644
--- a/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
+++ b/service-t/native/libs/libnetworkstats/NetworkTracePoller.cpp
@@ -15,10 +15,12 @@
  */
 
 #define LOG_TAG "NetworkTrace"
+#define ATRACE_TAG ATRACE_TAG_NETWORK
 
 #include "netdbpf/NetworkTracePoller.h"
 
 #include <bpf/BpfUtils.h>
+#include <cutils/trace.h>
 #include <log/log.h>
 #include <perfetto/tracing/platform.h>
 #include <perfetto/tracing/tracing.h>
@@ -133,6 +135,8 @@
     return false;
   }
 
+  ATRACE_INT("NetworkTracePackets", packets.size());
+
   mCallback(packets);
 
   return true;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index f977a27..e7ef510 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -46,6 +46,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_TEST;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -1582,7 +1583,9 @@
         // For a template with wifi network keys, it is possible for a malicious
         // client to track the user locations via querying data usage. Thus, enforce
         // fine location permission check.
-        if (!template.getWifiNetworkKeys().isEmpty()) {
+        // For a template with MATCH_TEST, since the wifi network key is just a placeholder
+        // to identify a specific test network, it is not related to track user location.
+        if (!template.getWifiNetworkKeys().isEmpty() && template.getMatchRule() != MATCH_TEST) {
             final boolean canAccessFineLocation = mLocationPermissionChecker
                     .checkCallersLocationPermission(callingPackage,
                     null /* featureId */,
diff --git a/service/jni/com_android_server_BpfNetMaps.cpp b/service/jni/com_android_server_BpfNetMaps.cpp
index 77cffda..9ced44e 100644
--- a/service/jni/com_android_server_BpfNetMaps.cpp
+++ b/service/jni/com_android_server_BpfNetMaps.cpp
@@ -54,6 +54,10 @@
   if (!isOk(status)) {
     uid_t uid = getuid();
     ALOGE("BpfNetMaps jni init failure as uid=%d", uid);
+    // We probably only ever get called from system_server (ie. AID_SYSTEM)
+    // or from tests, and never from network_stack (ie. AID_NETWORK_STACK).
+    // However, if we ever do add calls from production network_stack code
+    // we do want to make sure this initializes correctly.
     // TODO: Fix tests to not use this jni lib, so we can unconditionally abort()
     if (uid == AID_SYSTEM || uid == AID_NETWORK_STACK) abort();
   }
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 059b716..d966070 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -90,11 +90,6 @@
 
 #undef ALOGF
 
-bool isGsiImage() {
-    // this implementation matches 2 other places in the codebase (same function name too)
-    return !access("/system/system_ext/etc/init/init.gsi.rc", F_OK);
-}
-
 static const char* kClatdDir = "/apex/com.android.tethering/bin/for-system";
 static const char* kClatdBin = "/apex/com.android.tethering/bin/for-system/clatd";
 
@@ -135,14 +130,6 @@
 
 #undef V2
 
-    // HACK: Some old vendor kernels lack ~5.10 backport of 'bpffs selinux genfscon' support.
-    // This is *NOT* supported, but let's allow, at least for now, U+ GSI to boot on them.
-    // (without this hack pixel5 R vendor + U gsi breaks)
-    if (isGsiImage() && !bpf::isAtLeastKernelVersion(5, 10, 0)) {
-        ALOGE("GSI with *BAD* pre-5.10 kernel lacking bpffs selinux genfscon support.");
-        return;
-    }
-
     if (fatal) abort();
 }
 
@@ -485,11 +472,15 @@
 static constexpr int WAITPID_ATTEMPTS = 50;
 static constexpr int WAITPID_RETRY_INTERVAL_US = 100000;
 
-static void stopClatdProcess(int pid) {
-    int err = kill(pid, SIGTERM);
-    if (err) {
-        err = errno;
+static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
+                                                                      jint pid) {
+    if (pid <= 0) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
+        return;
     }
+
+    int err = kill(pid, SIGTERM);
+    if (err) err = errno;
     if (err == ESRCH) {
         ALOGE("clatd child process %d unexpectedly disappeared", pid);
         return;
@@ -518,23 +509,6 @@
     }
 }
 
-static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
-                                                                      jstring iface, jstring pfx96,
-                                                                      jstring v4, jstring v6,
-                                                                      jint pid) {
-    ScopedUtfChars ifaceStr(env, iface);
-    ScopedUtfChars pfx96Str(env, pfx96);
-    ScopedUtfChars v4Str(env, v4);
-    ScopedUtfChars v6Str(env, v6);
-
-    if (pid <= 0) {
-        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid pid");
-        return;
-    }
-
-    stopClatdProcess(pid);
-}
-
 static jlong com_android_server_connectivity_ClatCoordinator_getSocketCookie(
         JNIEnv* env, jclass clazz, jobject sockJavaFd) {
     int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
@@ -579,8 +553,7 @@
          "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/lang/"
          "String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
          (void*)com_android_server_connectivity_ClatCoordinator_startClatd},
-        {"native_stopClatd",
-         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V",
+        {"native_stopClatd", "(I)V",
          (void*)com_android_server_connectivity_ClatCoordinator_stopClatd},
         {"native_getSocketCookie", "(Ljava/io/FileDescriptor;)J",
          (void*)com_android_server_connectivity_ClatCoordinator_getSocketCookie},
diff --git a/service/jni/onload.cpp b/service/jni/onload.cpp
index 3d15d43..ed74430 100644
--- a/service/jni/onload.cpp
+++ b/service/jni/onload.cpp
@@ -22,8 +22,8 @@
 namespace android {
 
 int register_com_android_server_TestNetworkService(JNIEnv* env);
-int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
 int register_com_android_server_BpfNetMaps(JNIEnv* env);
+int register_com_android_server_connectivity_ClatCoordinator(JNIEnv* env);
 int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
 
@@ -38,15 +38,15 @@
         return JNI_ERR;
     }
 
-    if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
-        return JNI_ERR;
-    }
-
-    if (register_com_android_server_BpfNetMaps(env) < 0) {
-        return JNI_ERR;
-    }
-
     if (android::modules::sdklevel::IsAtLeastT()) {
+        if (register_com_android_server_BpfNetMaps(env) < 0) {
+            return JNI_ERR;
+        }
+
+        if (register_com_android_server_connectivity_ClatCoordinator(env) < 0) {
+            return JNI_ERR;
+        }
+
         if (register_android_server_net_NetworkStatsFactory(env) < 0) {
             return JNI_ERR;
         }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8ec6eb9..b17af99 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2663,7 +2663,8 @@
         final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
         for (Network network : getAllNetworks()) {
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-            if (nai != null && nai.everConnected()) {
+            final boolean includeNetwork = (nai != null) && nai.isCreated();
+            if (includeNetwork) {
                 // TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
                 // NetworkCapabilities, which may contain UIDs of apps to which the
                 // network applies. Should the UIDs be cleared so as not to leak or
@@ -3889,9 +3890,9 @@
                     break;
                 }
                 case NetworkAgent.EVENT_UNREGISTER_AFTER_REPLACEMENT: {
-                    if (!nai.isCreated()) {
-                        Log.d(TAG, "unregisterAfterReplacement on uncreated " + nai.toShortString()
-                                + ", tearing down instead");
+                    if (!nai.everConnected()) {
+                        Log.d(TAG, "unregisterAfterReplacement on never-connected "
+                                + nai.toShortString() + ", tearing down instead");
                         teardownUnneededNetwork(nai);
                         break;
                     }
@@ -4476,6 +4477,25 @@
         }
     }
 
+    @VisibleForTesting
+    protected static boolean shouldCreateNetworksImmediately() {
+        // Before U, physical networks are only created when the agent advances to CONNECTED.
+        // In U and above, all networks are immediately created when the agent is registered.
+        return SdkLevel.isAtLeastU();
+    }
+
+    private static boolean shouldCreateNativeNetwork(@NonNull NetworkAgentInfo nai,
+            @NonNull NetworkInfo.State state) {
+        if (nai.isCreated()) return false;
+        if (state == NetworkInfo.State.CONNECTED) return true;
+        if (state != NetworkInfo.State.CONNECTING) {
+            // TODO: throw if no WTFs are observed in the field.
+            Log.wtf(TAG, "Uncreated network in invalid state: " + state);
+            return false;
+        }
+        return nai.isVPN() || shouldCreateNetworksImmediately();
+    }
+
     private static boolean shouldDestroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
         return nai.isCreated() && !nai.isDestroyed();
     }
@@ -7918,7 +7938,7 @@
 
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
-        } else {
+        } else if (networkAgent.everConnected()) {
             updateProxy(newLp, oldLp);
         }
 
@@ -7952,6 +7972,10 @@
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
     }
 
+    private void applyInitialLinkProperties(@NonNull NetworkAgentInfo nai) {
+        updateLinkProperties(nai, new LinkProperties(nai.linkProperties), null);
+    }
+
     /**
      * @param naData captive portal data from NetworkAgent
      * @param apiData captive portal data from capport API
@@ -9714,21 +9738,32 @@
                     + oldInfo.getState() + " to " + state);
         }
 
-        if (!networkAgent.isCreated()
-                && (state == NetworkInfo.State.CONNECTED
-                || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
-
+        if (shouldCreateNativeNetwork(networkAgent, state)) {
             // A network that has just connected has zero requests and is thus a foreground network.
             networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
 
             if (!createNativeNetwork(networkAgent)) return;
+
+            networkAgent.setCreated();
+
+            // If the network is created immediately on register, then apply the LinkProperties now.
+            // Otherwise, this is done further down when the network goes into connected state.
+            // Applying the LinkProperties means that the network is ready to carry traffic -
+            // interfaces and routing rules have been added, DNS servers programmed, etc.
+            // For VPNs, this must be done before the capabilities are updated, because as soon as
+            // that happens, UIDs are routed to the network.
+            if (shouldCreateNetworksImmediately()) {
+                applyInitialLinkProperties(networkAgent);
+            }
+
+            // TODO: should this move earlier? It doesn't seem to have anything to do with whether
+            // a network is created or not.
             if (networkAgent.propagateUnderlyingCapabilities()) {
                 // Initialize the network's capabilities to their starting values according to the
                 // underlying networks. This ensures that the capabilities are correct before
                 // anything happens to the network.
                 updateCapabilitiesForNetwork(networkAgent);
             }
-            networkAgent.setCreated();
             networkAgent.onNetworkCreated();
             updateAllowedUids(networkAgent, null, networkAgent.networkCapabilities);
             updateProfileAllowedNetworks();
@@ -9742,8 +9777,19 @@
             networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
 
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
-            updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
-                    null);
+            if (!shouldCreateNetworksImmediately()) {
+                applyInitialLinkProperties(networkAgent);
+            } else {
+                // The network was created when the agent registered, and the LinkProperties are
+                // already up-to-date. However, updateLinkProperties also makes some changes only
+                // when the network connects. Apply those changes here. On T and below these are
+                // handled by the applyInitialLinkProperties call just above.
+                // TODO: stop relying on updateLinkProperties(..., null) to do this.
+                // If something depends on both LinkProperties and connected state, it should be in
+                // this method as well.
+                networkAgent.clatd.update();
+                updateProxy(networkAgent.linkProperties, null);
+            }
 
             // If a rate limit has been configured and is applicable to this network (network
             // provides internet connectivity), apply it. The tc police filter cannot be attached
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 5d04632..fbe706c 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -237,9 +237,8 @@
         /**
          * Stop clatd.
          */
-        public void stopClatd(String iface, String pfx96, String v4, String v6, int pid)
-                throws IOException {
-            native_stopClatd(iface, pfx96, v4, v6, pid);
+        public void stopClatd(int pid) throws IOException {
+            native_stopClatd(pid);
         }
 
         /**
@@ -843,9 +842,7 @@
         Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
 
         maybeStopBpf(mClatdTracker);
-        mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
-                mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
-                mClatdTracker.pid);
+        mDeps.stopClatd(mClatdTracker.pid);
         untagSocket(mClatdTracker.cookie);
 
         Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
@@ -944,7 +941,6 @@
     private static native int native_startClatd(FileDescriptor tunfd, FileDescriptor readsock6,
             FileDescriptor writesock6, String iface, String pfx96, String v4, String v6)
             throws IOException;
-    private static native void native_stopClatd(String iface, String pfx96, String v4, String v6,
-            int pid) throws IOException;
+    private static native void native_stopClatd(int pid) throws IOException;
     private static native long native_getSocketCookie(FileDescriptor sock) throws IOException;
 }
diff --git a/service/src/com/android/server/connectivity/NetworkDiagnostics.java b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
index 15d0925..4f80d47 100644
--- a/service/src/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/service/src/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,7 @@
 
 import static android.system.OsConstants.*;
 
+import static com.android.net.module.util.NetworkStackConstants.DNS_OVER_TLS_PORT;
 import static com.android.net.module.util.NetworkStackConstants.ICMP_HEADER_LEN;
 import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
@@ -730,7 +731,6 @@
     private class DnsTlsCheck extends DnsUdpCheck {
         private static final int TCP_CONNECT_TIMEOUT_MS = 2500;
         private static final int TCP_TIMEOUT_MS = 2000;
-        private static final int DNS_TLS_PORT = 853;
         private static final int DNS_HEADER_SIZE = 12;
 
         private final String mHostname;
@@ -769,7 +769,8 @@
             final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
 
             mMeasurement.startTime = now();
-            sslSocket.connect(new InetSocketAddress(mTarget, DNS_TLS_PORT), TCP_CONNECT_TIMEOUT_MS);
+            sslSocket.connect(new InetSocketAddress(mTarget, DNS_OVER_TLS_PORT),
+                    TCP_CONNECT_TIMEOUT_MS);
 
             // Synchronous call waiting for the TLS handshake complete.
             sslSocket.startHandshake();
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 12e7d33..2245382 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -30,7 +30,6 @@
         "cts-net-utils",
         "ctstestrunner-axt",
         "modules-utils-build",
-        "ub-uiautomator",
     ],
     libs: [
         "android.test.runner",
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
index b2e81ff..13bbab6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataWarningReceiverTest.java
@@ -19,18 +19,18 @@
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.clearSnoozeTimestamps;
 
 import android.content.pm.PackageManager;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.UiAutomatorUtils;
+import com.android.compatibility.common.util.UiAutomatorUtils2;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -84,7 +84,7 @@
             final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
             uiDevice.openNotification();
             try {
-                final UiObject2 uiObject = UiAutomatorUtils.waitFindObject(
+                final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(
                         By.text("Data warning"));
                 Assume.assumeNotNull(uiObject);
                 uiObject.wait(Until.clickable(true), 10_000L);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index c28ee64..73a6502 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -100,9 +100,6 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -114,6 +111,9 @@
 import android.util.Range;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
 
 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 import com.android.modules.utils.build.SdkLevel;
@@ -154,7 +154,6 @@
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
@@ -809,26 +808,12 @@
                 mOldPrivateDnsSpecifier);
     }
 
-    // TODO: replace with CtsNetUtils.awaitPrivateDnsSetting in Q or above.
     private void expectPrivateDnsHostname(final String hostname) throws Exception {
-        final NetworkRequest request = new NetworkRequest.Builder()
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-                .build();
-        final CountDownLatch latch = new CountDownLatch(1);
-        final NetworkCallback callback = new NetworkCallback() {
-            @Override
-            public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
-                if (network.equals(mNetwork) &&
-                        Objects.equals(lp.getPrivateDnsServerName(), hostname)) {
-                    latch.countDown();
-                }
-            }
-        };
-
-        registerNetworkCallback(request, callback);
-
-        assertTrue("Private DNS hostname was not " + hostname + " after " + TIMEOUT_MS + "ms",
-                latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
+            // Wait for private DNS setting to propagate.
+            mCtsNetUtils.awaitPrivateDnsSetting("Test wait private DNS setting timeout",
+                    network, hostname, false);
+        }
     }
 
     private void setAndVerifyPrivateDns(boolean strictMode) throws Exception {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 50de714..ee2f6bb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2130,7 +2130,12 @@
     @AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps")
     @Test
     public void testSetAirplaneMode() throws Exception{
-        final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
+        // Starting from T, wifi supports airplane mode enhancement which may not disconnect wifi
+        // when airplane mode is on. The actual behavior that the device will have could only be
+        // checked with hidden wifi APIs(see Settings.Secure.WIFI_APM_STATE). Thus, stop verifying
+        // wifi on T+ device.
+        final boolean verifyWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+                && !SdkLevel.isAtLeastT();
         final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
         // store the current state of airplane mode
         final boolean isAirplaneModeEnabled = isAirplaneModeEnabled();
@@ -2141,7 +2146,7 @@
         // Verify that networks are available as expected if wifi or cell is supported. Continue the
         // test if none of them are supported since test should still able to verify the permission
         // mechanism.
-        if (supportWifi) {
+        if (verifyWifi) {
             mCtsNetUtils.ensureWifiConnected();
             registerCallbackAndWaitForAvailable(makeWifiNetworkRequest(), wifiCb);
         }
@@ -2165,7 +2170,7 @@
             // Verify that the enabling airplane mode takes effect as expected to prevent flakiness
             // caused by fast airplane mode switches. Ensure network lost before turning off
             // airplane mode.
-            if (supportWifi) waitForLost(wifiCb);
+            if (verifyWifi) waitForLost(wifiCb);
             if (supportTelephony) waitForLost(telephonyCb);
 
             // Verify we can disable Airplane Mode with correct permission:
@@ -2174,7 +2179,7 @@
             // Verify that turning airplane mode off takes effect as expected.
             // connectToCell only registers a request, it cannot / does not need to be called twice
             mCtsNetUtils.ensureWifiConnected();
-            if (supportWifi) waitForAvailable(wifiCb);
+            if (verifyWifi) waitForAvailable(wifiCb);
             if (supportTelephony) waitForAvailable(telephonyCb);
         } finally {
             // Restore the previous state of airplane mode and permissions:
@@ -2975,13 +2980,13 @@
 
         allowBadWifi();
 
-        final Network cellNetwork = mCtsNetUtils.connectToCell();
-        final Network wifiNetwork = prepareValidatedNetwork();
-
-        registerDefaultNetworkCallback(defaultCb);
-        registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
-
         try {
+            final Network cellNetwork = mCtsNetUtils.connectToCell();
+            final Network wifiNetwork = prepareValidatedNetwork();
+
+            registerDefaultNetworkCallback(defaultCb);
+            registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+
             // Verify wifi is the default network.
             defaultCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
                     entry -> wifiNetwork.equals(entry.getNetwork()));
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 691ab99..17a9ca2 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -18,21 +18,18 @@
 
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkUtils;
 import android.net.cts.util.CtsNetUtils;
 import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.test.AndroidTestCase;
 
-import java.util.ArrayList;
-
 public class MultinetworkApiTest extends AndroidTestCase {
 
     static {
@@ -75,26 +72,8 @@
         super.tearDown();
     }
 
-    private Network[] getTestableNetworks() {
-        final ArrayList<Network> testableNetworks = new ArrayList<Network>();
-        for (Network network : mCM.getAllNetworks()) {
-            final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
-            if (nc != null
-                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-                testableNetworks.add(network);
-            }
-        }
-
-        assertTrue(
-                "This test requires that at least one network be connected. " +
-                "Please ensure that the device is connected to a network.",
-                testableNetworks.size() >= 1);
-        return testableNetworks.toArray(new Network[0]);
-    }
-
     public void testGetaddrinfo() throws ErrnoException {
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             int errno = runGetaddrinfoCheck(network.getNetworkHandle());
             if (errno != 0) {
                 throw new ErrnoException(
@@ -109,7 +88,7 @@
         assertNull(mCM.getProcessDefaultNetwork());
         assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
 
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             mCM.setProcessDefaultNetwork(null);
             assertNull(mCM.getProcessDefaultNetwork());
 
@@ -128,7 +107,7 @@
             mCM.setProcessDefaultNetwork(null);
         }
 
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             NetworkUtils.bindProcessToNetwork(0);
             assertNull(mCM.getBoundNetworkForProcess());
 
@@ -148,7 +127,7 @@
 
     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
     public void testSetsocknetwork() throws ErrnoException {
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             int errno = runSetsocknetwork(network.getNetworkHandle());
             if (errno != 0) {
                 throw new ErrnoException(
@@ -158,7 +137,7 @@
     }
 
     public void testNativeDatagramTransmission() throws ErrnoException {
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             int errno = runDatagramCheck(network.getNetworkHandle());
             if (errno != 0) {
                 throw new ErrnoException(
@@ -181,7 +160,7 @@
 
     public void testNetworkHandle() {
         // Test Network -> NetworkHandle -> Network results in the same Network.
-        for (Network network : getTestableNetworks()) {
+        for (Network network : mCtsNetUtils.getTestableNetworks()) {
             long networkHandle = network.getNetworkHandle();
             Network newNetwork = Network.fromNetworkHandle(networkHandle);
             assertEquals(newNetwork, network);
@@ -203,7 +182,7 @@
     }
 
     public void testResNApi() throws Exception {
-        final Network[] testNetworks = getTestableNetworks();
+        final Network[] testNetworks = mCtsNetUtils.getTestableNetworks();
 
         for (Network network : testNetworks) {
             // Throws AssertionError directly in jni function if test fail.
@@ -229,7 +208,7 @@
         // b/144521720
         try {
             mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
-            for (Network network : getTestableNetworks()) {
+            for (Network network : mCtsNetUtils.getTestableNetworks()) {
               // Wait for private DNS setting to propagate.
               mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
                         network, GOOGLE_PRIVATE_DNS_SERVER, true);
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 869562b..af8938a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -29,9 +29,9 @@
 import android.net.NattKeepalivePacketData
 import android.net.Network
 import android.net.NetworkAgent
-import android.net.NetworkAgentConfig
 import android.net.NetworkAgent.INVALID_NETWORK
 import android.net.NetworkAgent.VALID_NETWORK
+import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED
@@ -46,21 +46,23 @@
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkCapabilities.TRANSPORT_TEST
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkInfo
 import android.net.NetworkProvider
 import android.net.NetworkReleasedException
 import android.net.NetworkRequest
 import android.net.NetworkScore
-import android.net.RouteInfo
 import android.net.QosCallback
-import android.net.QosCallbackException
 import android.net.QosCallback.QosCallbackRegistrationException
+import android.net.QosCallbackException
 import android.net.QosSession
 import android.net.QosSessionAttributes
 import android.net.QosSocketInfo
+import android.net.RouteInfo
 import android.net.SocketKeepalive
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
 import android.net.Uri
 import android.net.VpnManager
 import android.net.VpnTransportInfo
@@ -71,6 +73,7 @@
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.Message
+import android.os.Process
 import android.os.SystemClock
 import android.platform.test.annotations.AppModeFull
 import android.system.OsConstants.IPPROTO_TCP
@@ -89,6 +92,7 @@
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.RecorderCallback.CallbackEntry.Available
 import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
+import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
 import com.android.testutils.RecorderCallback.CallbackEntry.Losing
 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
@@ -178,6 +182,7 @@
     private val agentsToCleanUp = mutableListOf<NetworkAgent>()
     private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
     private var qosTestSocket: Closeable? = null // either Socket or DatagramSocket
+    private val ifacesToCleanUp = mutableListOf<TestNetworkInterface>()
 
     @Before
     fun setUp() {
@@ -189,6 +194,7 @@
     fun tearDown() {
         agentsToCleanUp.forEach { it.unregister() }
         callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
+        ifacesToCleanUp.forEach { it.fileDescriptor.close() }
         qosTestSocket?.close()
         mHandlerThread.quitSafely()
         mHandlerThread.join()
@@ -269,7 +275,7 @@
         removeCapability(NET_CAPABILITY_INTERNET)
         addCapability(NET_CAPABILITY_NOT_SUSPENDED)
         addCapability(NET_CAPABILITY_NOT_ROAMING)
-        addCapability(NET_CAPABILITY_NOT_VPN)
+        if (!transports.contains(TRANSPORT_VPN)) addCapability(NET_CAPABILITY_NOT_VPN)
         if (SdkLevel.isAtLeastS()) {
             addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
         }
@@ -304,7 +310,7 @@
         context: Context = realContext,
         specifier: String? = UUID.randomUUID().toString(),
         initialConfig: NetworkAgentConfig? = null,
-        expectedInitSignalStrengthThresholds: IntArray? = intArrayOf(),
+        expectedInitSignalStrengthThresholds: IntArray = intArrayOf(),
         transports: IntArray = intArrayOf()
     ): Pair<TestableNetworkAgent, TestableNetworkCallback> {
         val callback = TestableNetworkCallback()
@@ -317,8 +323,7 @@
         agent.register()
         agent.markConnected()
         agent.expectCallback<OnNetworkCreated>()
-        agent.expectSignalStrengths(expectedInitSignalStrengthThresholds)
-        agent.expectValidationBypassedStatus()
+        agent.expectPostConnectionCallbacks(expectedInitSignalStrengthThresholds)
         callback.expectAvailableThenValidatedCallbacks(agent.network!!)
         return agent to callback
     }
@@ -336,6 +341,19 @@
         mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
     }
 
+    private fun TestableNetworkAgent.expectPostConnectionCallbacks(
+        thresholds: IntArray = intArrayOf()
+    ) {
+        expectSignalStrengths(thresholds)
+        expectValidationBypassedStatus()
+        assertNoCallback()
+    }
+
+    private fun createTunInterface(): TestNetworkInterface = realContext.getSystemService(
+                TestNetworkManager::class.java)!!.createTunInterface(emptyList()).also {
+            ifacesToCleanUp.add(it)
+    }
+
     fun assertLinkPropertiesEventually(
         n: Network,
         description: String,
@@ -1291,8 +1309,12 @@
         requestNetwork(makeTestNetworkRequest(specifier = specifier6), callback)
         val agent6 = createNetworkAgent(specifier = specifier6)
         val network6 = agent6.register()
-        // No callbacks are sent, so check the LinkProperties to see if the network has connected.
-        assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+        if (SdkLevel.isAtLeastU()) {
+            agent6.expectCallback<OnNetworkCreated>()
+        } else {
+            // No callbacks are sent, so check LinkProperties to wait for the network to be created.
+            assertLinkPropertiesEventuallyNotNull(agent6.network!!)
+        }
 
         // unregisterAfterReplacement tears down the network immediately.
         // Approximately check that this is the case by picking an unregister timeout that's longer
@@ -1301,8 +1323,9 @@
         val timeoutMs = agent6.DEFAULT_TIMEOUT_MS.toInt() + 1_000
         agent6.unregisterAfterReplacement(timeoutMs)
         agent6.expectCallback<OnNetworkUnwanted>()
-        if (!SdkLevel.isAtLeastT()) {
+        if (!SdkLevel.isAtLeastT() || SdkLevel.isAtLeastU()) {
             // Before T, onNetworkDestroyed is called even if the network was never created.
+            // On U+, the network was created by register(). Destroying it sends onNetworkDestroyed.
             agent6.expectCallback<OnNetworkDestroyed>()
         }
         // Poll for LinkProperties becoming null, because when onNetworkUnwanted is called, the
@@ -1375,4 +1398,101 @@
         callback.expect<Available>(agent.network!!)
         callback.eventuallyExpect<Lost> { it.network == agent.network }
     }
+
+    fun doTestNativeNetworkCreation(expectCreatedImmediately: Boolean, transports: IntArray) {
+        val iface = createTunInterface()
+        val ifName = iface.interfaceName
+        val nc = makeTestNetworkCapabilities(ifName, transports).also {
+            if (transports.contains(TRANSPORT_VPN)) {
+                val sessionId = "NetworkAgentTest-${Process.myPid()}"
+                it.transportInfo = VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, sessionId,
+                    /*bypassable=*/ false, /*longLivedTcpConnectionsExpensive=*/ false)
+                it.underlyingNetworks = listOf()
+            }
+        }
+        val lp = LinkProperties().apply {
+            interfaceName = ifName
+            addLinkAddress(LinkAddress("2001:db8::1/64"))
+            addRoute(RouteInfo(IpPrefix("2001:db8::/64"), null /* nextHop */, ifName))
+            addRoute(RouteInfo(IpPrefix("::/0"),
+                    InetAddresses.parseNumericAddress("fe80::abcd"),
+                    ifName))
+        }
+
+        // File a request containing the agent's specifier to receive callbacks and to ensure that
+        // the agent is not torn down due to being unneeded.
+        val request = makeTestNetworkRequest(specifier = ifName)
+        val requestCallback = TestableNetworkCallback()
+        requestNetwork(request, requestCallback)
+
+        val listenCallback = TestableNetworkCallback()
+        registerNetworkCallback(request, listenCallback)
+
+        // Register the NetworkAgent...
+        val agent = createNetworkAgent(realContext, initialNc = nc, initialLp = lp)
+        val network = agent.register()
+
+        // ... and then change the NetworkCapabilities and LinkProperties.
+        nc.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+        agent.sendNetworkCapabilities(nc)
+        lp.addLinkAddress(LinkAddress("192.0.2.2/25"))
+        lp.addRoute(RouteInfo(IpPrefix("192.0.2.0/25"), null /* nextHop */, ifName))
+        agent.sendLinkProperties(lp)
+
+        requestCallback.assertNoCallback()
+        listenCallback.assertNoCallback()
+        if (!expectCreatedImmediately) {
+            agent.assertNoCallback()
+            agent.markConnected()
+            agent.expectCallback<OnNetworkCreated>()
+        } else {
+            agent.expectCallback<OnNetworkCreated>()
+            agent.markConnected()
+        }
+        agent.expectPostConnectionCallbacks()
+
+        // onAvailable must be called only when the network connects, and no other callbacks may be
+        // called before that happens. The callbacks report the state of the network as it was when
+        // it connected, so they reflect the NC and LP changes made after registration.
+        requestCallback.expect<Available>(network)
+        listenCallback.expect<Available>(network)
+
+        requestCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+            NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+        listenCallback.expect<CapabilitiesChanged>(network) { it.caps.hasCapability(
+            NET_CAPABILITY_TEMPORARILY_NOT_METERED) }
+
+        requestCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+        listenCallback.expect<LinkPropertiesChanged>(network) { it.lp.equals(lp) }
+
+        requestCallback.expect<BlockedStatus>()
+        listenCallback.expect<BlockedStatus>()
+
+        // Except for network validation, ensure no more callbacks are sent.
+        requestCallback.expectCaps(network) {
+            it.hasCapability(NET_CAPABILITY_VALIDATED)
+        }
+        listenCallback.expectCaps(network) {
+            it.hasCapability(NET_CAPABILITY_VALIDATED)
+        }
+        unregister(agent)
+        // Lost implicitly checks that no further callbacks happened after connect.
+        requestCallback.expect<Lost>(network)
+        listenCallback.expect<Lost>(network)
+        assertNull(mCM.getLinkProperties(network))
+    }
+
+    @Test
+    fun testNativeNetworkCreation_PhysicalNetwork() {
+        // On T and below, the native network is only created when the agent connects.
+        // Starting in U, the native network is created as soon as the agent is registered.
+        doTestNativeNetworkCreation(expectCreatedImmediately = SdkLevel.isAtLeastU(),
+            intArrayOf(TRANSPORT_CELLULAR))
+    }
+
+    @Test
+    fun testNativeNetworkCreation_Vpn() {
+        // VPN networks are always created as soon as the agent is registered.
+        doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
index fcfecad..2704dd3 100644
--- a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -30,6 +30,7 @@
 import android.os.Build
 import android.os.Handler
 import android.os.HandlerThread
+import android.util.Log
 import androidx.test.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
@@ -41,6 +42,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.Collections
 
 // This test doesn't really have a constraint on how fast the methods should return. If it's
 // going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
@@ -64,10 +66,11 @@
 @IgnoreUpTo(Build.VERSION_CODES.R)
 @RunWith(DevSdkIgnoreRunner::class)
 class NetworkScoreTest {
+    private val TAG = javaClass.simpleName
     private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
-    private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+    private val mHandlerThread = HandlerThread("$TAG handler thread")
     private val mHandler by lazy { Handler(mHandlerThread.looper) }
-    private val agentsToCleanUp = mutableListOf<NetworkAgent>()
+    private val agentsToCleanUp = Collections.synchronizedList(mutableListOf<NetworkAgent>())
     private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
 
     @Before
@@ -83,15 +86,18 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler
             )
         }
+        Log.i(TAG, "Teardown on thread ${System.identityHashCode(Thread.currentThread())} " +
+                "cleaning up ${agentsToCleanUp.size} agents")
         agentsToCleanUp.forEach {
+            Log.i(TAG, "Unregister agent for net ${it.network}")
             it.unregister()
             agentCleanUpCb.eventuallyExpect<CallbackEntry.Lost> { cb -> cb.network == it.network }
         }
         mCm.unregisterNetworkCallback(agentCleanUpCb)
 
+        callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
         mHandlerThread.quitSafely()
         mHandlerThread.join()
-        callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
     }
 
     // Returns a networkCallback that sends onAvailable on the best network with TRANSPORT_TEST.
@@ -145,6 +151,8 @@
         val agent = object : NetworkAgent(context, looper, "NetworkScore test agent", nc,
                 LinkProperties(), score, config, NetworkProvider(context, looper,
                 "NetworkScore test provider")) {}.also {
+            Log.i(TAG, "Add on thread ${System.identityHashCode(Thread.currentThread())} " +
+                    "agent to clean up $it")
             agentsToCleanUp.add(it)
         }
         runWithShellPermissionIdentity({ agent.register() }, MANAGE_TEST_NETWORKS)
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index f86c5cd..d8a0b07 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -210,7 +210,6 @@
     private long mStartTime;
     private long mEndTime;
 
-    private long mBytesRead;
     private String mWriteSettingsMode;
     private String mUsageStatsMode;
 
@@ -229,6 +228,7 @@
             TrafficStats.setThreadStatsTag(NETWORK_TAG);
             urlc = (HttpURLConnection) network.openConnection(url);
             urlc.setConnectTimeout(TIMEOUT_MILLIS);
+            urlc.setReadTimeout(TIMEOUT_MILLIS);
             urlc.setUseCaches(false);
             // Disable compression so we generate enough traffic that assertWithinPercentage will
             // not be affected by the small amount of traffic (5-10kB) sent by the test harness.
@@ -236,11 +236,10 @@
             urlc.connect();
             boolean ping = urlc.getResponseCode() == 200;
             if (ping) {
-                in = new InputStreamReader(
-                        (InputStream) urlc.getContent());
-
-                mBytesRead = 0;
-                while (in.read() != -1) ++mBytesRead;
+                in = new InputStreamReader((InputStream) urlc.getContent());
+                // Since the test doesn't really care about the precise amount of data, instead
+                // of reading all contents, just read few bytes at the beginning.
+                in.read();
             }
         } catch (Exception e) {
             Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
@@ -379,7 +378,7 @@
                 .build(), callback);
         synchronized (this) {
             try {
-                wait((int) (TIMEOUT_MILLIS * 1.2));
+                wait((int) (TIMEOUT_MILLIS * 2.4));
             } catch (InterruptedException e) {
             }
         }
@@ -394,7 +393,7 @@
         assertFalse(mNetworkInterfacesToTest[networkTypeIndex].getSystemFeature()
                 + " is a reported system feature, "
                 + "however no corresponding connected network interface was found or the attempt "
-                + "to connect has timed out (timeout = " + TIMEOUT_MILLIS + "ms)."
+                + "to connect and read has timed out (timeout = " + (TIMEOUT_MILLIS * 2) + "ms)."
                 + mNetworkInterfacesToTest[networkTypeIndex].getErrorMessage(), hasFeature);
         return false;
     }
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index 9d73946..ce789fc 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -57,6 +57,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
@@ -68,6 +70,8 @@
 import java.io.OutputStream;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -508,17 +512,18 @@
      * @throws InterruptedException If the thread is interrupted.
      */
     public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
-            @NonNull String server, boolean requiresValidatedServer) throws InterruptedException {
+            @Nullable String server, boolean requiresValidatedServer) throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
-        NetworkCallback callback = new NetworkCallback() {
+        final NetworkCallback callback = new NetworkCallback() {
             @Override
             public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
                 Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
                 if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
                     return;
                 }
-                if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
+                Log.i(TAG, "Set private DNS server to " + server);
+                if (network.equals(n) && Objects.equals(server, lp.getPrivateDnsServerName())) {
                     latch.countDown();
                 }
             }
@@ -541,6 +546,27 @@
     }
 
     /**
+     * Get all testable Networks with internet capability.
+     */
+    public Network[] getTestableNetworks() {
+        final ArrayList<Network> testableNetworks = new ArrayList<Network>();
+        for (Network network : mCm.getAllNetworks()) {
+            final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+            if (nc != null
+                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                testableNetworks.add(network);
+            }
+        }
+
+        assertTrue("This test requires that at least one public Internet-providing"
+                        + " network be connected. Please ensure that the device is connected to"
+                        + " a network.",
+                testableNetworks.size() >= 1);
+        return testableNetworks.toArray(new Network[0]);
+    }
+
+    /**
      * Receiver that captures the last connectivity change's network type and state. Recognizes
      * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
      */
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 8396252..31f3124 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -3815,6 +3815,12 @@
 
         mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, callbacks);
 
+        if (mService.shouldCreateNetworksImmediately()) {
+            assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } else {
+            assertNull(eventOrder.poll());
+        }
+
         // Connect a network, and file a request for it after it has come up, to ensure the nascent
         // timer is cleared and the test does not have to wait for it. Filing the request after the
         // network has come up is necessary because ConnectivityService does not appear to clear the
@@ -3822,7 +3828,12 @@
         // connected.
         // TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
         mWiFiAgent.connectWithoutInternet();
-        waitForIdle();
+        if (!mService.shouldCreateNetworksImmediately()) {
+            assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } else {
+            waitForIdle();
+            assertNull(eventOrder.poll());
+        }
         mCm.requestNetwork(request, callback);
         callback.expectAvailableCallbacksUnvalidated(mWiFiAgent);
 
@@ -3839,7 +3850,6 @@
 
         // Disconnect the network and check that events happened in the right order.
         mCm.unregisterNetworkCallback(callback);
-        assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -7625,7 +7635,9 @@
         // Simple connection with initial LP should have updated ifaces.
         mCellAgent.connect(false);
         waitForIdle();
-        expectNotifyNetworkStatus(onlyCell(), onlyCell(), MOBILE_IFNAME);
+        List<Network> allNetworks = mService.shouldCreateNetworksImmediately()
+                ? cellAndWifi() : onlyCell();
+        expectNotifyNetworkStatus(allNetworks, onlyCell(), MOBILE_IFNAME);
         reset(mStatsManager);
 
         // Verify change fields other than interfaces does not trigger a notification to NSS.
@@ -7934,9 +7946,13 @@
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
 
         mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        final int netId = mCellAgent.getNetwork().netId;
         waitForIdle();
-        verify(mMockDnsResolver, never()).setResolverConfiguration(any());
-        verifyNoMoreInteractions(mMockDnsResolver);
+        if (mService.shouldCreateNetworksImmediately()) {
+            verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+        } else {
+            verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+        }
 
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -7952,10 +7968,13 @@
         mCellAgent.sendLinkProperties(cellLp);
         mCellAgent.connect(false);
         waitForIdle();
-
-        verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
-        // CS tells dnsresolver about the empty DNS config for this network.
+        if (!mService.shouldCreateNetworksImmediately()) {
+            // CS tells dnsresolver about the empty DNS config for this network.
+            verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+        }
         verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
+
+        verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockDnsResolver);
 
         cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
@@ -8070,10 +8089,13 @@
         mCm.requestNetwork(cellRequest, cellNetworkCallback);
 
         mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        final int netId = mCellAgent.getNetwork().netId;
         waitForIdle();
-        // CS tells netd about the empty DNS config for this network.
-        verify(mMockDnsResolver, never()).setResolverConfiguration(any());
-        verifyNoMoreInteractions(mMockDnsResolver);
+        if (mService.shouldCreateNetworksImmediately()) {
+            verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+        } else {
+            verify(mMockDnsResolver, never()).setResolverConfiguration(any());
+        }
 
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -8092,7 +8114,9 @@
         mCellAgent.sendLinkProperties(cellLp);
         mCellAgent.connect(false);
         waitForIdle();
-        verify(mMockDnsResolver, times(1)).createNetworkCache(eq(mCellAgent.getNetwork().netId));
+        if (!mService.shouldCreateNetworksImmediately()) {
+            verify(mMockDnsResolver, times(1)).createNetworkCache(netId);
+        }
         verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
                 mResolverParamsParcelCaptor.capture());
         ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue();
@@ -8103,6 +8127,7 @@
         assertEquals(2, resolvrParams.tlsServers.length);
         assertTrue(new ArraySet<>(resolvrParams.tlsServers).containsAll(
                 asList("2001:db8::1", "192.0.2.1")));
+        verifyNoMoreInteractions(mMockDnsResolver);
         reset(mMockDnsResolver);
         cellNetworkCallback.expect(AVAILABLE, mCellAgent);
         cellNetworkCallback.expect(NETWORK_CAPS_UPDATED, mCellAgent);
@@ -10474,7 +10499,8 @@
         if (inOrder != null) {
             return inOrder.verify(t);
         } else {
-            return verify(t);
+            // times(1) for consistency with the above. InOrder#verify always implies times(1).
+            return verify(t, times(1));
         }
     }
 
@@ -10523,6 +10549,21 @@
         }
     }
 
+    private void expectNativeNetworkCreated(int netId, int permission, String iface,
+            InOrder inOrder) throws Exception {
+        verifyWithOrder(inOrder, mMockNetd).networkCreate(nativeNetworkConfigPhysical(netId,
+                permission));
+        verifyWithOrder(inOrder, mMockDnsResolver).createNetworkCache(eq(netId));
+        if (iface != null) {
+            verifyWithOrder(inOrder, mMockNetd).networkAddInterface(netId, iface);
+        }
+    }
+
+    private void expectNativeNetworkCreated(int netId, int permission, String iface)
+            throws Exception {
+        expectNativeNetworkCreated(netId, permission, iface, null /* inOrder */);
+    }
+
     @Test
     public void testStackedLinkProperties() throws Exception {
         final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -10560,11 +10601,8 @@
         int cellNetId = mCellAgent.getNetwork().netId;
         waitForIdle();
 
-        verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId,
-                INetd.PERMISSION_NONE));
+        expectNativeNetworkCreated(cellNetId, INetd.PERMISSION_NONE, MOBILE_IFNAME);
         assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
-        verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
-        verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
         final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
                 mDeps.mReportedInterfaceHistory.newReadHead();
         assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
@@ -15111,7 +15149,7 @@
             UserHandle testHandle,
             TestNetworkCallback profileDefaultNetworkCallback,
             TestNetworkCallback disAllowProfileDefaultNetworkCallback) throws Exception {
-        final InOrder inOrder = inOrder(mMockNetd);
+        final InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
 
         mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellAgent.connect(true);
@@ -15127,8 +15165,16 @@
 
         final TestNetworkAgentWrapper workAgent =
                 makeEnterpriseNetworkAgent(profileNetworkPreference.getPreferenceEnterpriseId());
+        if (mService.shouldCreateNetworksImmediately()) {
+            expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+                    null /* iface */, inOrder);
+        }
         if (connectWorkProfileAgentAhead) {
             workAgent.connect(false);
+            if (!mService.shouldCreateNetworksImmediately()) {
+                expectNativeNetworkCreated(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM,
+                        null /* iface */, inOrder);
+            }
         }
 
         final TestOnCompleteListener listener = new TestOnCompleteListener();
@@ -15168,6 +15214,11 @@
 
         if (!connectWorkProfileAgentAhead) {
             workAgent.connect(false);
+            if (!mService.shouldCreateNetworksImmediately()) {
+                inOrder.verify(mMockNetd).networkCreate(
+                        nativeNetworkConfigPhysical(workAgent.getNetwork().netId,
+                                INetd.PERMISSION_SYSTEM));
+            }
         }
 
         profileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
@@ -15176,8 +15227,6 @@
         }
         mSystemDefaultNetworkCallback.assertNoCallback();
         mDefaultNetworkCallback.assertNoCallback();
-        inOrder.verify(mMockNetd).networkCreate(
-                nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM));
         inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
                 workAgent.getNetwork().netId,
                 uidRangeFor(testHandle, profileNetworkPreference),
@@ -17696,6 +17745,22 @@
         verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
     }
 
+    private void verifyMtuSetOnWifiInterfaceOnlyUpToT(int mtu) throws Exception {
+        if (!mService.shouldCreateNetworksImmediately()) {
+            verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+        } else {
+            verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+        }
+    }
+
+    private void verifyMtuSetOnWifiInterfaceOnlyStartingFromU(int mtu) throws Exception {
+        if (mService.shouldCreateNetworksImmediately()) {
+            verify(mMockNetd, times(1)).interfaceSetMtu(WIFI_IFNAME, mtu);
+        } else {
+            verify(mMockNetd, never()).interfaceSetMtu(eq(WIFI_IFNAME), anyInt());
+        }
+    }
+
     @Test
     public void testSendLinkPropertiesSetInterfaceMtuBeforeConnect() throws Exception {
         final int mtu = 1281;
@@ -17710,8 +17775,8 @@
         reset(mMockNetd);
 
         mWiFiAgent.connect(false /* validated */);
-        // The MTU is always (re-)applied when the network connects.
-        verifyMtuSetOnWifiInterface(mtu);
+        // Before U, the MTU is always (re-)applied when the network connects.
+        verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu);
     }
 
     @Test
@@ -17721,13 +17786,13 @@
         lp.setInterfaceName(WIFI_IFNAME);
         lp.setMtu(mtu);
 
-        // Registering an agent with an MTU doesn't set the MTU...
+        // Registering an agent with an MTU only sets the MTU on U+.
         mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
         waitForIdle();
-        verifyMtuNeverSetOnWifiInterface();
+        verifyMtuSetOnWifiInterfaceOnlyStartingFromU(mtu);
         reset(mMockNetd);
 
-        // ... but prevents future updates with the same MTU from setting the MTU.
+        // Future updates with the same MTU don't set the MTU even on T when it's not set initially.
         mWiFiAgent.sendLinkProperties(lp);
         waitForIdle();
         verifyMtuNeverSetOnWifiInterface();
@@ -17740,8 +17805,8 @@
         reset(mMockNetd);
 
         mWiFiAgent.connect(false /* validated */);
-        // The MTU is always (re-)applied when the network connects.
-        verifyMtuSetOnWifiInterface(mtu + 1);
+        // Before U, the MTU is always (re-)applied when the network connects.
+        verifyMtuSetOnWifiInterfaceOnlyUpToT(mtu + 1);
     }
 
     @Test
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index b651c33..4158663 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -313,8 +313,7 @@
          * Stop clatd.
          */
         @Override
-        public void stopClatd(@NonNull String iface, @NonNull String pfx96, @NonNull String v4,
-                @NonNull String v6, int pid) throws IOException {
+        public void stopClatd(int pid) throws IOException {
             if (pid == -1) {
                 fail("unsupported arg: " + pid);
             }
@@ -479,8 +478,7 @@
                 eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
         inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
         inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
-        inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
-                eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
+        inOrder.verify(mDeps).stopClatd(eq(CLATD_PID));
         inOrder.verify(mCookieTagMap).deleteEntry(eq(COOKIE_TAG_KEY));
         assertNull(coordinator.getClatdTrackerForTesting());
         inOrder.verifyNoMoreInteractions();
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 99f6d63..b8b0289 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -1926,12 +1926,17 @@
         // Templates w/o wifi network keys can query stats as usual.
         assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+        // Templates for test network does not need to enforce location permission.
+        final NetworkTemplate templateTestIface1 = new NetworkTemplate.Builder(MATCH_TEST)
+                .setWifiNetworkKeys(Set.of(TEST_IFACE)).build();
+        assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
 
         doReturn(true).when(mLocationPermissionChecker)
                 .checkCallersLocationPermission(any(), any(), anyInt(), anyBoolean(), any());
         assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateImsi1, 0L, 0L, 0L, 0L, 0);
+        assertNetworkTotal(templateTestIface1, 0L, 0L, 0L, 0L, 0);
     }
 
     /**