Snap for 12304452 from 88ed8985ef52e9dfef0e4767902089b84e255168 to 24Q4-release

Change-Id: I0db8f12a33cba6db9aed0740008776c804896100
diff --git a/bpf/headers/include/bpf_helpers.h b/bpf/headers/include/bpf_helpers.h
index c94f1d8..ca0ca76 100644
--- a/bpf/headers/include/bpf_helpers.h
+++ b/bpf/headers/include/bpf_helpers.h
@@ -383,13 +383,17 @@
 static int (*bpf_probe_read_user_str)(void* dst, int size, const void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_user_str;
 static unsigned long long (*bpf_ktime_get_ns)(void) = (void*) BPF_FUNC_ktime_get_ns;
 static unsigned long long (*bpf_ktime_get_boot_ns)(void) = (void*)BPF_FUNC_ktime_get_boot_ns;
-static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
 static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void*) BPF_FUNC_get_current_pid_tgid;
 static unsigned long long (*bpf_get_current_uid_gid)(void) = (void*) BPF_FUNC_get_current_uid_gid;
 static unsigned long long (*bpf_get_smp_processor_id)(void) = (void*) BPF_FUNC_get_smp_processor_id;
 static long (*bpf_get_stackid)(void* ctx, void* map, uint64_t flags) = (void*) BPF_FUNC_get_stackid;
 static long (*bpf_get_current_comm)(void* buf, uint32_t buf_size) = (void*) BPF_FUNC_get_current_comm;
 
+static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
+#define bpf_printf(s, n...) bpf_trace_printk(s, sizeof(s), ## n)
+// Note: bpf only supports up to 3 arguments, log via: bpf_printf("msg %d %d %d", 1, 2, 3);
+// and read via the blocking: sudo cat /sys/kernel/debug/tracing/trace_pipe
+
 #define DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv,  \
                             min_loader, max_loader, opt, selinux, pindir, ignore_eng,    \
                             ignore_user, ignore_userdebug)                               \
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index 59712b1..c058433 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -604,6 +604,9 @@
     if (type == BPF_MAP_TYPE_DEVMAP || type == BPF_MAP_TYPE_DEVMAP_HASH)
         desired_map_flags |= BPF_F_RDONLY_PROG;
 
+    if (type == BPF_MAP_TYPE_LPM_TRIE)
+        desired_map_flags |= BPF_F_NO_PREALLOC;
+
     // The .h file enforces that this is a power of two, and page size will
     // also always be a power of two, so this logic is actually enough to
     // force it to be a multiple of the page size, as required by the kernel.
@@ -779,13 +782,17 @@
               .key_size = md[i].key_size,
               .value_size = md[i].value_size,
               .max_entries = max_entries,
-              .map_flags = md[i].map_flags,
+              .map_flags = md[i].map_flags | (type == BPF_MAP_TYPE_LPM_TRIE ? BPF_F_NO_PREALLOC : 0),
             };
             if (isAtLeastKernelVersion(4, 15, 0))
                 strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
             fd.reset(bpf(BPF_MAP_CREATE, req));
             saved_errno = errno;
-            ALOGD("bpf_create_map name %s, ret: %d", mapNames[i].c_str(), fd.get());
+            if (fd.ok()) {
+              ALOGD("bpf_create_map[%s] -> %d", mapNames[i].c_str(), fd.get());
+            } else {
+              ALOGE("bpf_create_map[%s] -> %d errno:%d", mapNames[i].c_str(), fd.get(), saved_errno);
+            }
         }
 
         if (!fd.ok()) return -saved_errno;
@@ -839,7 +846,8 @@
 
         int mapId = bpfGetFdMapId(fd);
         if (mapId == -1) {
-            ALOGE("bpfGetFdMapId failed, ret: %d [%d]", mapId, errno);
+            if (isAtLeastKernelVersion(4, 14, 0))
+                ALOGE("bpfGetFdMapId failed, ret: %d [%d]", mapId, errno);
         } else {
             ALOGI("map %s id %d", mapPinLoc.c_str(), mapId);
         }
@@ -1007,17 +1015,20 @@
                   cs[i].name.c_str(), fd.get(), (!fd.ok() ? std::strerror(errno) : "no error"));
 
             if (!fd.ok()) {
-                vector<string> lines = android::base::Split(log_buf.data(), "\n");
+                if (log_buf.size()) {
+                    vector<string> lines = android::base::Split(log_buf.data(), "\n");
 
-                ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
-                for (const auto& line : lines) ALOGW("%s", line.c_str());
-                ALOGW("BPF_PROG_LOAD - END log_buf contents.");
+                    ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
+                    for (const auto& line : lines) ALOGW("%s", line.c_str());
+                    ALOGW("BPF_PROG_LOAD - END log_buf contents.");
+                }
 
                 if (cs[i].prog_def->optional) {
-                    ALOGW("failed program is marked optional - continuing...");
+                    ALOGW("failed program %s is marked optional - continuing...",
+                          cs[i].name.c_str());
                     continue;
                 }
-                ALOGE("non-optional program failed to load.");
+                ALOGE("non-optional program %s failed to load.", cs[i].name.c_str());
             }
         }
 
diff --git a/bpf/loader/initrc-doc/bpfloader-sdk34-14-U-QPR3.rc b/bpf/loader/initrc-doc/bpfloader-sdk34-14-U-QPR3.rc
new file mode 100644
index 0000000..8f3f462
--- /dev/null
+++ b/bpf/loader/initrc-doc/bpfloader-sdk34-14-U-QPR3.rc
@@ -0,0 +1,11 @@
+on load_bpf_programs
+    exec_start bpfloader
+
+service bpfloader /system/bin/netbpfload
+    capabilities CHOWN SYS_ADMIN NET_ADMIN
+    group root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw system
+    user root
+    rlimit memlock 1073741824 1073741824
+    oneshot
+    reboot_on_failure reboot,bpfloader-failed
+    updatable
diff --git a/bpf/loader/initrc-doc/bpfloader-sdk35-15-V.rc b/bpf/loader/initrc-doc/bpfloader-sdk35-15-V.rc
new file mode 100644
index 0000000..066cfc8
--- /dev/null
+++ b/bpf/loader/initrc-doc/bpfloader-sdk35-15-V.rc
@@ -0,0 +1,8 @@
+on load_bpf_programs
+    exec_start bpfloader
+
+service bpfloader /system/bin/false
+    user root
+    oneshot
+    reboot_on_failure reboot,netbpfload-missing
+    updatable
diff --git a/netd/Android.bp b/bpf/netd/Android.bp
similarity index 100%
rename from netd/Android.bp
rename to bpf/netd/Android.bp
diff --git a/netd/BpfBaseTest.cpp b/bpf/netd/BpfBaseTest.cpp
similarity index 100%
rename from netd/BpfBaseTest.cpp
rename to bpf/netd/BpfBaseTest.cpp
diff --git a/netd/BpfHandler.cpp b/bpf/netd/BpfHandler.cpp
similarity index 100%
rename from netd/BpfHandler.cpp
rename to bpf/netd/BpfHandler.cpp
diff --git a/netd/BpfHandler.h b/bpf/netd/BpfHandler.h
similarity index 100%
rename from netd/BpfHandler.h
rename to bpf/netd/BpfHandler.h
diff --git a/netd/BpfHandlerTest.cpp b/bpf/netd/BpfHandlerTest.cpp
similarity index 100%
rename from netd/BpfHandlerTest.cpp
rename to bpf/netd/BpfHandlerTest.cpp
diff --git a/netd/NetdUpdatable.cpp b/bpf/netd/NetdUpdatable.cpp
similarity index 100%
rename from netd/NetdUpdatable.cpp
rename to bpf/netd/NetdUpdatable.cpp
diff --git a/netd/include/NetdUpdatablePublic.h b/bpf/netd/include/NetdUpdatablePublic.h
similarity index 100%
rename from netd/include/NetdUpdatablePublic.h
rename to bpf/netd/include/NetdUpdatablePublic.h
diff --git a/netd/libnetd_updatable.map.txt b/bpf/netd/libnetd_updatable.map.txt
similarity index 100%
rename from netd/libnetd_updatable.map.txt
rename to bpf/netd/libnetd_updatable.map.txt
diff --git a/bpf/progs/Android.bp b/bpf/progs/Android.bp
index f6717c5..dc1f56d 100644
--- a/bpf/progs/Android.bp
+++ b/bpf/progs/Android.bp
@@ -47,8 +47,8 @@
         "com.android.tethering",
     ],
     visibility: [
+        "//packages/modules/Connectivity/bpf/netd",
         "//packages/modules/Connectivity/DnsResolver",
-        "//packages/modules/Connectivity/netd",
         "//packages/modules/Connectivity/service",
         "//packages/modules/Connectivity/service/native/libs/libclat",
         "//packages/modules/Connectivity/Tethering",
diff --git a/bpf/progs/dscpPolicy.c b/bpf/progs/dscpPolicy.c
index baabb02..39f2961 100644
--- a/bpf/progs/dscpPolicy.c
+++ b/bpf/progs/dscpPolicy.c
@@ -23,7 +23,10 @@
 #define ECN_MASK 3
 #define UPDATE_TOS(dscp, tos) ((dscp) << 2) | ((tos) & ECN_MASK)
 
-DEFINE_BPF_MAP_GRW(socket_policy_cache_map, HASH, uint64_t, RuleEntry, CACHE_MAP_SIZE, AID_SYSTEM)
+// The cache is never read nor written by userspace and is indexed by socket cookie % CACHE_MAP_SIZE
+#define CACHE_MAP_SIZE 32  // should be a power of two so we can % cheaply
+DEFINE_BPF_MAP_GRO(socket_policy_cache_map, PERCPU_ARRAY, uint32_t, RuleEntry, CACHE_MAP_SIZE,
+                   AID_SYSTEM)
 
 DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
 DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
@@ -43,6 +46,8 @@
     uint64_t cookie = bpf_get_socket_cookie(skb);
     if (!cookie) return;
 
+    uint32_t cacheid = cookie % CACHE_MAP_SIZE;
+
     __be16 sport = 0;
     uint16_t dport = 0;
     uint8_t protocol = 0;  // TODO: Use are reserved value? Or int (-1) and cast to uint below?
@@ -105,7 +110,8 @@
             return;
     }
 
-    RuleEntry* existing_rule = bpf_socket_policy_cache_map_lookup_elem(&cookie);
+    // this array lookup cannot actually fail
+    RuleEntry* existing_rule = bpf_socket_policy_cache_map_lookup_elem(&cacheid);
 
     if (existing_rule &&
         v6_equal(src_ip, existing_rule->src_ip) &&
@@ -192,7 +198,7 @@
     };
 
     // Update cache with found policy.
-    bpf_socket_policy_cache_map_update_elem(&cookie, &value, BPF_ANY);
+    bpf_socket_policy_cache_map_update_elem(&cacheid, &value, BPF_ANY);
 
     if (new_dscp < 0) return;
 
diff --git a/bpf/progs/dscpPolicy.h b/bpf/progs/dscpPolicy.h
index dc431a7..6a6b711 100644
--- a/bpf/progs/dscpPolicy.h
+++ b/bpf/progs/dscpPolicy.h
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#define CACHE_MAP_SIZE 1024
 #define MAX_POLICIES 16
 
 #define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
diff --git a/common/flags.aconfig b/common/flags.aconfig
index 45cbb78..4c6d8ba 100644
--- a/common/flags.aconfig
+++ b/common/flags.aconfig
@@ -147,3 +147,12 @@
   bug: "335680025"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "tethering_active_sessions_metrics"
+  is_exported: true
+  namespace: "android_core_networking"
+  description: "Flag for collecting tethering active sessions metrics"
+  bug: "354619988"
+  is_fixed_read_only: true
+}
diff --git a/staticlibs/device/com/android/net/module/util/DeviceConfigUtils.java b/staticlibs/device/com/android/net/module/util/DeviceConfigUtils.java
index 0426ace..04ce2fa 100644
--- a/staticlibs/device/com/android/net/module/util/DeviceConfigUtils.java
+++ b/staticlibs/device/com/android/net/module/util/DeviceConfigUtils.java
@@ -22,6 +22,7 @@
 import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
 
 import static com.android.net.module.util.FeatureVersions.CONNECTIVITY_MODULE_ID;
+import static com.android.net.module.util.FeatureVersions.DNS_RESOLVER_MODULE_ID;
 import static com.android.net.module.util.FeatureVersions.MODULE_MASK;
 import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID;
 import static com.android.net.module.util.FeatureVersions.VERSION_MASK;
@@ -68,7 +69,8 @@
     @VisibleForTesting
     public static void resetPackageVersionCacheForTest() {
         sPackageVersion = -1;
-        sModuleVersion = -1;
+        sTetheringModuleVersion = -1;
+        sResolvModuleVersion = -1;
         sNetworkStackModuleVersion = -1;
     }
 
@@ -243,23 +245,23 @@
         }
     }
 
-    // Guess the tethering module name based on the package prefix of the connectivity resources
-    // Take the resource package name, cut it before "connectivity" and append "tethering".
+    // Guess an APEX module name based on the package prefix of the connectivity resources
+    // Take the resource package name, cut it before "connectivity" and append the module name.
     // Then resolve that package version number with packageManager.
-    // If that fails retry by appending "go.tethering" instead
-    private static long resolveTetheringModuleVersion(@NonNull Context context)
+    // If that fails retry by appending "go.<moduleName>" instead.
+    private static long resolveApexModuleVersion(@NonNull Context context, String moduleName)
             throws PackageManager.NameNotFoundException {
         final String pkgPrefix = resolvePkgPrefix(context);
         final PackageManager packageManager = context.getPackageManager();
         try {
-            return packageManager.getPackageInfo(pkgPrefix + "tethering",
+            return packageManager.getPackageInfo(pkgPrefix + moduleName,
                     PackageManager.MATCH_APEX).getLongVersionCode();
         } catch (PackageManager.NameNotFoundException e) {
             Log.d(TAG, "Device is using go modules");
             // fall through
         }
 
-        return packageManager.getPackageInfo(pkgPrefix + "go.tethering",
+        return packageManager.getPackageInfo(pkgPrefix + "go." + moduleName,
                 PackageManager.MATCH_APEX).getLongVersionCode();
     }
 
@@ -274,19 +276,35 @@
         return connResourcesPackage.substring(0, pkgPrefixLen);
     }
 
-    private static volatile long sModuleVersion = -1;
+    private static volatile long sTetheringModuleVersion = -1;
+
     private static long getTetheringModuleVersion(@NonNull Context context) {
-        if (sModuleVersion >= 0) return sModuleVersion;
+        if (sTetheringModuleVersion >= 0) return sTetheringModuleVersion;
 
         try {
-            sModuleVersion = resolveTetheringModuleVersion(context);
+            sTetheringModuleVersion = resolveApexModuleVersion(context, "tethering");
         } catch (PackageManager.NameNotFoundException e) {
             // It's expected to fail tethering module version resolution on the devices with
             // flattened apex
             Log.e(TAG, "Failed to resolve tethering module version: " + e);
             return DEFAULT_PACKAGE_VERSION;
         }
-        return sModuleVersion;
+        return sTetheringModuleVersion;
+    }
+
+    private static volatile long sResolvModuleVersion = -1;
+    private static long getResolvModuleVersion(@NonNull Context context) {
+        if (sResolvModuleVersion >= 0) return sResolvModuleVersion;
+
+        try {
+            sResolvModuleVersion = resolveApexModuleVersion(context, "resolv");
+        } catch (PackageManager.NameNotFoundException e) {
+            // It's expected to fail resolv module version resolution on the devices with
+            // flattened apex
+            Log.e(TAG, "Failed to resolve resolv module version: " + e);
+            return DEFAULT_PACKAGE_VERSION;
+        }
+        return sResolvModuleVersion;
     }
 
     private static volatile long sNetworkStackModuleVersion = -1;
@@ -342,6 +360,8 @@
             moduleVersion = getTetheringModuleVersion(context);
         } else if (moduleId == NETWORK_STACK_MODULE_ID) {
             moduleVersion = getNetworkStackModuleVersion(context);
+        } else if (moduleId == DNS_RESOLVER_MODULE_ID) {
+            moduleVersion = getResolvModuleVersion(context);
         } else {
             throw new IllegalArgumentException("Unknown module " + moduleId);
         }
diff --git a/staticlibs/device/com/android/net/module/util/FeatureVersions.java b/staticlibs/device/com/android/net/module/util/FeatureVersions.java
index d5f8124..d0cf3fd 100644
--- a/staticlibs/device/com/android/net/module/util/FeatureVersions.java
+++ b/staticlibs/device/com/android/net/module/util/FeatureVersions.java
@@ -37,6 +37,7 @@
     public static final long VERSION_MASK = 0x00F_FFFF_FFFFL;
     public static final long CONNECTIVITY_MODULE_ID = 0x01L << MODULE_SHIFT;
     public static final long NETWORK_STACK_MODULE_ID = 0x02L << MODULE_SHIFT;
+    public static final long DNS_RESOLVER_MODULE_ID = 0x03L << MODULE_SHIFT;
     // CLAT_ADDRESS_TRANSLATE is a feature of the network stack, which doesn't throw when system
     // try to add a NAT-T keepalive packet filter with v6 address, introduced in version
     // M-2023-Sept on July 3rd, 2023.
@@ -48,4 +49,11 @@
     // by BPF for the given uid and conditions, introduced in version M-2024-Feb on Nov 6, 2023.
     public static final long FEATURE_IS_UID_NETWORKING_BLOCKED =
             CONNECTIVITY_MODULE_ID + 34_14_00_000L;
+
+    // DDR is a feature implemented across NetworkStack, ConnectivityService and DnsResolver.
+    // The flag that enables this feature is in NetworkStack.
+    public static final long FEATURE_DDR_IN_CONNECTIVITY =
+            CONNECTIVITY_MODULE_ID + 35_11_00_000L;
+    public static final long FEATURE_DDR_IN_DNSRESOLVER =
+            DNS_RESOLVER_MODULE_ID + 35_11_00_000L;
 }
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
index 9fb61d9..a5af09b 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/DeviceConfigUtilsTest.java
@@ -24,6 +24,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.net.module.util.FeatureVersions.CONNECTIVITY_MODULE_ID;
+import static com.android.net.module.util.FeatureVersions.DNS_RESOLVER_MODULE_ID;
 import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID;
 
 import static org.junit.Assert.assertEquals;
@@ -77,7 +78,9 @@
     private static final int TEST_DEFAULT_FLAG_VALUE = 0;
     private static final int TEST_MAX_FLAG_VALUE = 1000;
     private static final int TEST_MIN_FLAG_VALUE = 100;
-    private static final long TEST_PACKAGE_VERSION = 290000000;
+    private static final long TEST_PACKAGE_VERSION = 290500000;
+    private static final long TEST_GO_PACKAGE_VERSION = 290000000;  // Not updated
+    private static final long TEST_RESOLV_PACKAGE_VERSION = 290300000;  // Updated, but older.
     private static final String TEST_PACKAGE_NAME = "test.package.name";
     // The APEX name is the name of the APEX module, as in android.content.pm.ModuleInfo, and is
     // used for its mount point in /apex. APEX packages are actually APKs with a different
@@ -85,14 +88,18 @@
     // that manifest, and is reflected in android.content.pm.ApplicationInfo. Contrary to the APEX
     // (module) name, different package names are typically used to identify the organization that
     // built and signed the APEX modules.
-    private static final String TEST_APEX_PACKAGE_NAME = "com.prefix.android.tethering";
-    private static final String TEST_GO_APEX_PACKAGE_NAME = "com.prefix.android.go.tethering";
+    private static final String TEST_TETHERING_PACKAGE_NAME = "com.prefix.android.tethering";
+    private static final String TEST_GO_TETHERING_PACKAGE_NAME = "com.prefix.android.go.tethering";
+    private static final String TEST_RESOLV_PACKAGE_NAME = "com.prefix.android.resolv";
+    private static final String TEST_GO_RESOLV_PACKAGE_NAME = "com.prefix.android.go.resolv";
     private static final String TEST_CONNRES_PACKAGE_NAME =
             "com.prefix.android.connectivity.resources";
     private static final String TEST_NETWORKSTACK_NAME = "com.prefix.android.networkstack";
     private static final String TEST_GO_NETWORKSTACK_NAME = "com.prefix.android.go.networkstack";
     private final PackageInfo mPackageInfo = new PackageInfo();
-    private final PackageInfo mApexPackageInfo = new PackageInfo();
+    private final PackageInfo mGoApexPackageInfo = new PackageInfo();
+    private final PackageInfo mTetheringApexPackageInfo = new PackageInfo();
+    private final PackageInfo mResolvApexPackageInfo = new PackageInfo();
     private MockitoSession mSession;
 
     @Mock private Context mContext;
@@ -105,13 +112,22 @@
         mSession = mockitoSession().spyStatic(DeviceConfig.class).startMocking();
 
         mPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
-        mApexPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
+        mTetheringApexPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
+        mGoApexPackageInfo.setLongVersionCode(TEST_GO_PACKAGE_VERSION);
+        mResolvApexPackageInfo.setLongVersionCode(TEST_RESOLV_PACKAGE_VERSION);
 
         doReturn(mPm).when(mContext).getPackageManager();
         doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
         doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt());
         doReturn(mPackageInfo).when(mPm).getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt());
-        doReturn(mApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_APEX_PACKAGE_NAME), anyInt());
+        doReturn(mTetheringApexPackageInfo).when(mPm).getPackageInfo(
+                eq(TEST_TETHERING_PACKAGE_NAME), anyInt());
+        doReturn(mResolvApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_RESOLV_PACKAGE_NAME),
+                anyInt());
+        doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_TETHERING_PACKAGE_NAME),
+                anyInt());
+        doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_RESOLV_PACKAGE_NAME),
+                anyInt());
 
         doReturn(mResources).when(mContext).getResources();
 
@@ -342,9 +358,9 @@
     @Test
     public void testFeatureIsEnabledOnGo() throws Exception {
         doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(
-                eq(TEST_APEX_PACKAGE_NAME), anyInt());
-        doReturn(mApexPackageInfo).when(mPm).getPackageInfo(
-                eq(TEST_GO_APEX_PACKAGE_NAME), anyInt());
+                eq(TEST_TETHERING_PACKAGE_NAME), anyInt());
+        doReturn(mTetheringApexPackageInfo).when(mPm).getPackageInfo(
+                eq(TEST_GO_TETHERING_PACKAGE_NAME), anyInt());
         doReturn("0").when(() -> DeviceConfig.getProperty(
                 NAMESPACE_CONNECTIVITY, TEST_EXPERIMENT_FLAG));
         doReturn("0").when(() -> DeviceConfig.getProperty(
@@ -483,6 +499,31 @@
                 mContext, 889900000L + CONNECTIVITY_MODULE_ID));
     }
 
+
+    @Test
+    public void testIsFeatureSupported_resolvFeature() throws Exception {
+        assertTrue(DeviceConfigUtils.isFeatureSupported(
+                mContext, TEST_RESOLV_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
+        // Return false because feature requires a future version.
+        assertFalse(DeviceConfigUtils.isFeatureSupported(
+                mContext, 889900000L + DNS_RESOLVER_MODULE_ID));
+    }
+
+    @Test
+    public void testIsFeatureSupported_goResolvFeature() throws Exception {
+        doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(eq(TEST_RESOLV_PACKAGE_NAME),
+                anyInt());
+        doReturn(mGoApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_GO_RESOLV_PACKAGE_NAME),
+                anyInt());
+        assertFalse(DeviceConfigUtils.isFeatureSupported(
+                mContext, TEST_RESOLV_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
+        assertTrue(DeviceConfigUtils.isFeatureSupported(
+                mContext, TEST_GO_PACKAGE_VERSION + DNS_RESOLVER_MODULE_ID));
+        // Return false because feature requires a future version.
+        assertFalse(DeviceConfigUtils.isFeatureSupported(
+                mContext, 889900000L + DNS_RESOLVER_MODULE_ID));
+    }
+
     @Test
     public void testIsFeatureSupported_illegalModule() throws Exception {
         assertThrows(IllegalArgumentException.class,
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index ea6b078..03ea178 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -20,6 +20,9 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.tethering.apex" />
 
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
 
@@ -46,4 +49,21 @@
         <option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
         <option name="collect-on-run-ended-only" value="true" />
     </metrics_collector>
+
+<!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+    one of the Mainline modules below is present on the device used for testing. -->
+<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+    <!-- Tethering Module (internal version). -->
+    <option name="mainline-module-package-name" value="com.google.android.tethering" />
+    <!-- Tethering Module (AOSP version). -->
+    <option name="mainline-module-package-name" value="com.android.tethering" />
+    <!-- NetworkStack Module (internal version). Should always be installed with CaptivePortalLogin. -->
+    <option name="mainline-module-package-name" value="com.google.android.networkstack" />
+    <!-- NetworkStack Module (AOSP version). Should always be installed with CaptivePortalLogin. -->
+    <option name="mainline-module-package-name" value="com.android.networkstack" />
+    <!-- Resolver Module (internal version). -->
+    <option name="mainline-module-package-name" value="com.google.android.resolv" />
+    <!-- Resolver Module (AOSP version). -->
+    <option name="mainline-module-package-name" value="com.android.resolv" />
+</object>
 </configuration>
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index 2a6c638..c480135 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -23,10 +23,13 @@
 import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
 import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
 import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+import static android.net.cts.PacketUtils.ICMP_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
 import static android.system.OsConstants.FIONREAD;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -152,10 +155,17 @@
         final IpSecTransformState transformState =
                 futureIpSecTransform.get(SOCK_TIMEOUT, TimeUnit.MILLISECONDS);
 
-        assertEquals(txHighestSeqNum, transformState.getTxHighestSequenceNumber());
+        // There might be ICMPv6(Router Solicitation) packets. Thus we can only check the lower
+        // bound of the outgoing traffic.
+        final long icmpV6RsCnt = transformState.getTxHighestSequenceNumber() - txHighestSeqNum;
+        assertTrue(icmpV6RsCnt >= 0);
+
+        final long adjustedPacketCnt = packetCnt + icmpV6RsCnt;
+        final long adjustedByteCnt = byteCnt + icmpV6RsCnt * (IP6_HDRLEN + ICMP_HDRLEN);
+
+        assertEquals(adjustedPacketCnt, transformState.getPacketCount());
+        assertEquals(adjustedByteCnt, transformState.getByteCount());
         assertEquals(rxHighestSeqNum, transformState.getRxHighestSequenceNumber());
-        assertEquals(packetCnt, transformState.getPacketCount());
-        assertEquals(byteCnt, transformState.getByteCount());
         assertArrayEquals(replayBitmap, transformState.getReplayBitmap());
     }
 
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
index 22a51d6..890c071 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -65,6 +65,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.net.module.util.CollectionUtils;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 
@@ -119,7 +120,7 @@
 
     private static final int TIMEOUT_MS = 500;
 
-    private static final int PACKET_COUNT = 5000;
+    private static final int PACKET_COUNT = 100;
 
     // Static state to reduce setup/teardown
     private static ConnectivityManager sCM;
@@ -1088,6 +1089,27 @@
             UdpEncapsulationSocket encapSocket,
             IpSecTunnelTestRunnable test)
             throws Exception {
+        return buildTunnelNetworkAndRunTests(
+                localInner,
+                remoteInner,
+                localOuter,
+                remoteOuter,
+                spi,
+                encapSocket,
+                test,
+                true /* enableEncrypt */);
+    }
+
+    private int buildTunnelNetworkAndRunTests(
+            InetAddress localInner,
+            InetAddress remoteInner,
+            InetAddress localOuter,
+            InetAddress remoteOuter,
+            int spi,
+            UdpEncapsulationSocket encapSocket,
+            IpSecTunnelTestRunnable test,
+            boolean enableEncrypt)
+            throws Exception {
         int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
         TestNetworkCallback testNetworkCb = null;
         int innerSocketPort;
@@ -1115,8 +1137,12 @@
 
             // Configure Transform parameters
             IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
-            transformBuilder.setEncryption(
-                    new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+
+            if (enableEncrypt) {
+                transformBuilder.setEncryption(
+                        new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+            }
+
             transformBuilder.setAuthentication(
                     new IpSecAlgorithm(
                             IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
@@ -1167,8 +1193,8 @@
         return innerSocketPort;
     }
 
-    private int buildTunnelNetworkAndRunTestsSimple(int spi, IpSecTunnelTestRunnable test)
-            throws Exception {
+    private int buildTunnelNetworkAndRunTestsSimple(
+            int spi, IpSecTunnelTestRunnable test, boolean enableEncrypt) throws Exception {
         return buildTunnelNetworkAndRunTests(
                 LOCAL_INNER_6,
                 REMOTE_INNER_6,
@@ -1176,7 +1202,8 @@
                 REMOTE_OUTER_6,
                 spi,
                 null /* encapSocket */,
-                test);
+                test,
+                enableEncrypt);
     }
 
     private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
@@ -1787,10 +1814,11 @@
                             PACKET_COUNT,
                             PACKET_COUNT,
                             PACKET_COUNT * (long) innerPacketSize,
-                            newReplayBitmap(REPLAY_BITMAP_LEN_BYTE * 8));
+                            newReplayBitmap(PACKET_COUNT));
 
                     return innerSocketPort;
-                });
+                },
+                true /* enableEncrypt */);
     }
 
     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -1814,17 +1842,22 @@
                     ipsecNetwork.bindSocket(outSocket.mSocket);
                     int innerSocketPort = outSocket.getPort();
 
-                    int expectedPacketSize =
-                            getPacketSize(
-                                    AF_INET6,
-                                    AF_INET6,
-                                    false /* useEncap */,
-                                    false /* transportInTunnelMode */);
+                    int outSeqNum = 1;
+                    int receivedTestDataEspCnt = 0;
 
-                    for (int i = 0; i < PACKET_COUNT; i++) {
+                    while (receivedTestDataEspCnt < PACKET_COUNT) {
                         outSocket.sendTo(TEST_DATA, REMOTE_INNER_6, innerSocketPort);
-                        tunUtils.awaitEspPacketNoPlaintext(
-                                spi, TEST_DATA, false /* useEncap */, expectedPacketSize);
+
+                        byte[] pkt = null;
+
+                        // If it is an ESP that contains the TEST_DATA, move to the next
+                        // loop. Otherwise, the ESP may contain an ICMPv6(Router Solicitation).
+                        // In this case, just increase the expected sequence number and continue
+                        // waiting for the ESP with TEST_DATA
+                        do {
+                            pkt = tunUtils.awaitEspPacket(spi, false /* useEncap */, outSeqNum++);
+                        } while (CollectionUtils.indexOfSubArray(pkt, TEST_DATA) == -1);
+                        receivedTestDataEspCnt++;
                     }
 
                     final int innerPacketSize =
@@ -1838,6 +1871,7 @@
                             newReplayBitmap(0));
 
                     return innerSocketPort;
-                });
+                },
+                false /* enableEncrypt */);
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
index 4d924d1..1588835 100644
--- a/tests/cts/net/src/android/net/cts/PacketUtils.java
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -49,6 +49,7 @@
     static final int TCP_HDRLEN = 20;
     static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
     static final int ESP_HDRLEN = 8;
+    static final int ICMP_HDRLEN = 8;
     static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
     static final int ESP_TRAILER_LEN = 2;
 
diff --git a/tests/cts/net/src/android/net/cts/TunUtils.java b/tests/cts/net/src/android/net/cts/TunUtils.java
index 268d8d2..8bf4998 100644
--- a/tests/cts/net/src/android/net/cts/TunUtils.java
+++ b/tests/cts/net/src/android/net/cts/TunUtils.java
@@ -47,6 +47,8 @@
     protected static final int IP4_PROTO_OFFSET = 9;
     protected static final int IP6_PROTO_OFFSET = 6;
 
+    private static final int SEQ_NUM_MATCH_NOT_REQUIRED = -1;
+
     private static final int DATA_BUFFER_LEN = 4096;
     private static final int TIMEOUT = 2000;
 
@@ -146,16 +148,30 @@
         return espPkt; // We've found the packet we're looking for.
     }
 
+    /** Await the expected ESP packet */
     public byte[] awaitEspPacket(int spi, boolean useEncap) throws Exception {
-        return awaitPacket((pkt) -> isEsp(pkt, spi, useEncap));
+        return awaitEspPacket(spi, useEncap, SEQ_NUM_MATCH_NOT_REQUIRED);
     }
 
-    private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
+    /** Await the expected ESP packet with a matching sequence number */
+    public byte[] awaitEspPacket(int spi, boolean useEncap, int seqNum) throws Exception {
+        return awaitPacket((pkt) -> isEsp(pkt, spi, seqNum, useEncap));
+    }
+
+    private static boolean isMatchingEspPacket(byte[] pkt, int espOffset, int spi, int seqNum) {
         ByteBuffer buffer = ByteBuffer.wrap(pkt);
         buffer.get(new byte[espOffset]); // Skip IP, UDP header
         int actualSpi = buffer.getInt();
+        int actualSeqNum = buffer.getInt();
 
-        return actualSpi == spi;
+        if (actualSeqNum < 0) {
+            throw new UnsupportedOperationException(
+                    "actualSeqNum overflowed and needs to be converted to an unsigned integer");
+        }
+
+        boolean isSeqNumMatched = (seqNum == SEQ_NUM_MATCH_NOT_REQUIRED || seqNum == actualSeqNum);
+
+        return actualSpi == spi && isSeqNumMatched;
     }
 
     /**
@@ -173,29 +189,32 @@
             fail("Banned plaintext packet found");
         }
 
-        return isEsp(pkt, spi, encap);
+        return isEsp(pkt, spi, SEQ_NUM_MATCH_NOT_REQUIRED, encap);
     }
 
-    private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
+    private static boolean isEsp(byte[] pkt, int spi, int seqNum, boolean encap) {
         if (isIpv6(pkt)) {
             if (encap) {
                 return pkt[IP6_PROTO_OFFSET] == IPPROTO_UDP
-                        && isSpiEqual(pkt, IP6_HDRLEN + UDP_HDRLEN, spi);
+                        && isMatchingEspPacket(pkt, IP6_HDRLEN + UDP_HDRLEN, spi, seqNum);
             } else {
-                return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+                return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP
+                        && isMatchingEspPacket(pkt, IP6_HDRLEN, spi, seqNum);
             }
 
         } else {
             // Use default IPv4 header length (assuming no options)
             if (encap) {
                 return pkt[IP4_PROTO_OFFSET] == IPPROTO_UDP
-                        && isSpiEqual(pkt, IP4_HDRLEN + UDP_HDRLEN, spi);
+                        && isMatchingEspPacket(pkt, IP4_HDRLEN + UDP_HDRLEN, spi, seqNum);
             } else {
-                return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP4_HDRLEN, spi);
+                return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP
+                        && isMatchingEspPacket(pkt, IP4_HDRLEN, spi, seqNum);
             }
         }
     }
 
+
     public static boolean isIpv6(byte[] pkt) {
         // First nibble shows IP version. 0x60 for IPv6
         return (pkt[0] & (byte) 0xF0) == (byte) 0x60;