Merge "Update registerOffloadEngine() permission check" into main
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 6c0ca82..6f3e865 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -79,6 +79,7 @@
     private final TetheringConfiguration mConfig;
     // keyed by downstream type(TetheringManager.TETHERING_*).
     private final ArrayMap<AddressKey, LinkAddress> mCachedAddresses;
+    private final Random mRandom;
 
     public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
         mDownstreams = new ArraySet<>();
@@ -95,6 +96,7 @@
 
         mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"),
             new IpPrefix("172.16.0.0/12"), new IpPrefix("10.0.0.0/8")));
+        mRandom = new Random();
     }
 
     /**
@@ -263,12 +265,13 @@
         // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
         //
         // Additionally, it makes debug output easier to read by making the numbers smaller.
-        final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
+        final int randomInt = getRandomInt();
+        final int randomPrefixStart = randomInt & ~prefixRangeMask & prefixMask;
 
         // A random offset within the prefix. Used to determine the local address once the prefix
         // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
-        // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
-        final int subAddress = getSanitizedSubAddr(~prefixMask);
+        // For a PREFIX_LENGTH of 24, this is a number between 2 and 254.
+        final int subAddress = getSanitizedSubAddr(randomInt, ~prefixMask);
 
         // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
         // such that the prefix does not conflict with any upstream.
@@ -310,12 +313,12 @@
     /** Get random int which could be used to generate random address. */
     @VisibleForTesting
     public int getRandomInt() {
-        return (new Random()).nextInt();
+        return mRandom.nextInt();
     }
 
     /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
-    private int getSanitizedSubAddr(final int subAddrMask) {
-        final int randomSubAddr = getRandomInt() & subAddrMask;
+    private int getSanitizedSubAddr(final int randomInt, final int subAddrMask) {
+        final int randomSubAddr = randomInt & subAddrMask;
         // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
         // avoid 3 consecutive address.
         if (PREFIX_LENGTH > 30) return randomSubAddr;
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index a7a4059..2a9892f 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -76,23 +76,43 @@
 }
 
 static Status initPrograms(const char* cg2_path) {
+    if (!cg2_path) {
+        ALOGE("cg2_path is NULL");
+        return statusFromErrno(EINVAL, "cg2_path is NULL");
+    }
+
     // This code was mainlined in T, so this should be trivially satisfied.
-    if (!modules::sdklevel::IsAtLeastT()) abort();
+    if (!modules::sdklevel::IsAtLeastT()) {
+        ALOGE("S- platform is unsupported");
+        abort();
+    }
 
     // S requires eBPF support which was only added in 4.9, so this should be satisfied.
-    if (!bpf::isAtLeastKernelVersion(4, 9, 0)) abort();
+    if (!bpf::isAtLeastKernelVersion(4, 9, 0)) {
+        ALOGE("kernel version < 4.9.0 is unsupported");
+        abort();
+    }
 
     // U bumps the kernel requirement up to 4.14
-    if (modules::sdklevel::IsAtLeastU() && !bpf::isAtLeastKernelVersion(4, 14, 0)) abort();
+    if (modules::sdklevel::IsAtLeastU() && !bpf::isAtLeastKernelVersion(4, 14, 0)) {
+        ALOGE("U+ platform with kernel version < 4.14.0 is unsupported");
+        abort();
+    }
 
     if (modules::sdklevel::IsAtLeastV()) {
         // V bumps the kernel requirement up to 4.19
         // see also: //system/netd/tests/kernel_test.cpp TestKernel419
-        if (!bpf::isAtLeastKernelVersion(4, 19, 0)) abort();
+        if (!bpf::isAtLeastKernelVersion(4, 19, 0)) {
+            ALOGE("V+ platform with kernel version < 4.19.0 is unsupported");
+            abort();
+        }
 
         // Technically already required by U, but only enforce on V+
         // see also: //system/netd/tests/kernel_test.cpp TestKernel64Bit
-        if (bpf::isKernel32Bit() && bpf::isAtLeastKernelVersion(5, 16, 0)) abort();
+        if (bpf::isKernel32Bit() && bpf::isAtLeastKernelVersion(5, 16, 0)) {
+            ALOGE("V+ platform with 32 bit kernel, version >= 5.16.0 is unsupported");
+            abort();
+        }
     }
 
     // Linux 6.1 is highest version supported by U, starting with V new kernels,
@@ -102,10 +122,16 @@
     // it does not affect unprivileged apps, the 32-on-64 compatibility
     // problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
     // see also: //system/bpf/bpfloader/BpfLoader.cpp main()
-    if (bpf::isUserspace32bit() && bpf::isAtLeastKernelVersion(6, 2, 0)) abort();
+    if (bpf::isUserspace32bit() && bpf::isAtLeastKernelVersion(6, 2, 0)) {
+        ALOGE("32 bit userspace with Kernel version >= 6.2.0 is unsupported");
+        abort();
+    }
 
     // U mandates this mount point (though it should also be the case on T)
-    if (modules::sdklevel::IsAtLeastU() && !!strcmp(cg2_path, "/sys/fs/cgroup")) abort();
+    if (modules::sdklevel::IsAtLeastU() && !!strcmp(cg2_path, "/sys/fs/cgroup")) {
+        ALOGE("U+ platform with cg2_path != /sys/fs/cgroup is unsupported");
+        abort();
+    }
 
     unique_fd cg_fd(open(cg2_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
     if (!cg_fd.ok()) {
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 01b8de7..48e86d8 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -48,7 +48,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.PermissionUtils;
 import com.android.net.module.util.SharedLog;
@@ -238,18 +237,7 @@
         mDeps = deps;
 
         // Interface match regex.
-        String ifaceMatchRegex = mDeps.getInterfaceRegexFromResource(mContext);
-        // "*" is a magic string to indicate "pick the default".
-        if (ifaceMatchRegex.equals("*")) {
-            if (SdkLevel.isAtLeastU()) {
-                // On U+, include both usb%d and eth%d interfaces.
-                ifaceMatchRegex = "(usb|eth)\\d+";
-            } else {
-                // On T, include only eth%d interfaces.
-                ifaceMatchRegex = "eth\\d+";
-            }
-        }
-        mIfaceMatch = ifaceMatchRegex;
+        mIfaceMatch = mDeps.getInterfaceRegexFromResource(mContext);
 
         // Read default Ethernet interface configuration from resources
         final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context);
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 045d707f..f30abc6 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -194,11 +194,8 @@
         -->
     </string-array>
 
-    <!-- Regex of wired ethernet ifaces. Network interfaces that match this regex will be tracked
-         by ethernet service.
-         If set to "*", ethernet service uses "(eth|usb)\\d+" on Android U+ and eth\\d+ on
-         Android T. -->
-    <string translatable="false" name="config_ethernet_iface_regex">*</string>
+    <!-- Regex of wired ethernet ifaces -->
+    <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
 
     <!-- Ignores Wi-Fi validation failures after roam.
     If validation fails on a Wi-Fi network after a roam to a new BSSID,
diff --git a/service/src/com/android/server/BpfLoaderRcUtils.java b/service/src/com/android/server/BpfLoaderRcUtils.java
new file mode 100644
index 0000000..1b6ee55
--- /dev/null
+++ b/service/src/com/android/server/BpfLoaderRcUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BpfRcUtils is responsible for comparing the bpf loader rc file.
+ *
+ * {@hide}
+ */
+public class BpfLoaderRcUtils {
+    public static final String TAG = BpfLoaderRcUtils.class.getSimpleName();
+
+    private static final List<String> BPF_LOADER_RC_S_T = List.of(
+            "service bpfloader /system/bin/bpfloader",
+            "capabilities CHOWN SYS_ADMIN NET_ADMIN",
+            "rlimit memlock 1073741824 1073741824",
+            "oneshot",
+            "reboot_on_failure reboot,bpfloader-failed",
+            "updatable"
+    );
+
+    private static final List<String> BPF_LOADER_RC_U = List.of(
+            "service bpfloader /system/bin/bpfloader",
+            "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"
+    );
+
+    private static final List<String> BPF_LOADER_RC_UQPR2 = List.of(
+            "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"
+    );
+
+
+    private static final String BPF_LOADER_RC_FILE_PATH = "/etc/init/bpfloader.rc";
+    private static final String NET_BPF_LOAD_RC_FILE_PATH = "/etc/init/netbpfload.rc";
+
+    private BpfLoaderRcUtils() {
+    }
+
+    /**
+     * Load the bpf rc file content from the input stream.
+     */
+    @VisibleForTesting
+    public static List<String> loadExistingBpfRcFile(@NonNull InputStream inputStream) {
+        List<String> contents = new ArrayList<>();
+        boolean bpfSectionFound = false;
+        try (BufferedReader br = new BufferedReader(
+                new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                line = line.trim();
+                if (line.isEmpty()) {
+                    continue;
+                }
+                if (line.startsWith("#")) {
+                    continue;
+                }
+                // If bpf service section was found and new service or action section start. The
+                // read should stop.
+                if (bpfSectionFound && (line.startsWith("service ") || (line.startsWith("on ")))) {
+                    break;
+                }
+                if (line.startsWith("service bpfloader ")) {
+                    bpfSectionFound = true;
+                }
+                if (bpfSectionFound) {
+                    contents.add(line);
+                }
+            }
+        } catch (IOException e) {
+            Log.wtf("read input stream failed.", e);
+            contents.clear();
+            return contents;
+        }
+        return contents;
+    }
+
+    /**
+     * Check the bpfLoader rc file on the system image matches any of the template files.
+     */
+    public static boolean checkBpfLoaderRc() {
+        File bpfRcFile = new File(BPF_LOADER_RC_FILE_PATH);
+        if (!bpfRcFile.exists()) {
+            if (SdkLevel.isAtLeastU()) {
+                bpfRcFile = new File(NET_BPF_LOAD_RC_FILE_PATH);
+            }
+            if (!bpfRcFile.exists()) {
+                Log.wtf(TAG,
+                        "neither " + BPF_LOADER_RC_FILE_PATH + " nor " + NET_BPF_LOAD_RC_FILE_PATH
+                                + " exist.");
+                return false;
+            }
+            // Check bpf rc file in U QPR2
+            return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_UQPR2);
+        }
+
+        if (SdkLevel.isAtLeastU()) {
+            // Check bpf rc file in U
+            return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_U);
+        }
+        // Check bpf rc file in S/T
+        return compareBpfLoaderRc(bpfRcFile, BPF_LOADER_RC_S_T);
+    }
+
+    private static boolean compareBpfLoaderRc(@NonNull File bpfRcFile,
+            @NonNull List<String> template) {
+        try {
+            List<String> actualContent = loadExistingBpfRcFile(new FileInputStream(bpfRcFile));
+            if (!actualContent.equals(template)) {
+                Log.wtf(TAG, "BPF rc file is not same as the template files " + actualContent);
+                return false;
+            }
+        } catch (FileNotFoundException e) {
+            Log.wtf(bpfRcFile.getPath() + " doesn't exist.", e);
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 8f29078..9268da5 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -1876,6 +1876,10 @@
             activityManager.registerUidFrozenStateChangedCallback(
                     (Runnable r) -> r.run(), frozenStateChangedCallback);
         }
+
+        if (mDeps.isFeatureNotChickenedOut(mContext, LOG_BPF_RC)) {
+            mHandler.post(BpfLoaderRcUtils::checkBpfLoaderRc);
+        }
     }
 
     /**
@@ -3335,6 +3339,8 @@
     public static final String ALLOW_SYSUI_CONNECTIVITY_REPORTS =
             "allow_sysui_connectivity_reports";
 
+    public static final String LOG_BPF_RC = "log_bpf_rc_force_disable";
+
     private void enforceInternetPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INTERNET,
@@ -4784,7 +4790,7 @@
         // If the Private DNS mode is opportunistic, reprogram the DNS servers
         // in order to restart a validation pass from within netd.
         final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
-        if (cfg.useTls && TextUtils.isEmpty(cfg.hostname)) {
+        if (cfg.inOpportunisticMode()) {
             updateDnses(nai.linkProperties, null, nai.network.getNetId());
         }
     }
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index 894bcc4..8e6854a 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -302,7 +302,7 @@
         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                 PRIVATE_DNS_OFF);
 
-        final boolean useTls = privateDnsCfg.useTls;
+        final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF;
         final PrivateDnsValidationStatuses statuses =
                 useTls ? mPrivateDnsValidationMap.get(netId) : null;
         final boolean validated = (null != statuses) && statuses.hasValidatedServer();
@@ -370,7 +370,7 @@
         // networks like IMS.
         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                 PRIVATE_DNS_OFF);
-        final boolean useTls = privateDnsCfg.useTls;
+        final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF;
         final boolean strictMode = privateDnsCfg.inStrictMode();
 
         paramsParcel.netId = netId;
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
index 9e1e26e..111e0ba 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
@@ -16,12 +16,17 @@
 
 package com.android.net.module.util.netlink;
 
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.NETLINK_XFRM;
+
 import android.system.OsConstants;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage;
+
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 
 /**
  * NetlinkMessage base class for other, more specific netlink message types.
@@ -75,6 +80,8 @@
             parsed = parseInetDiagMessage(nlmsghdr, byteBuffer);
         } else if (nlFamily == OsConstants.NETLINK_NETFILTER) {
             parsed = parseNfMessage(nlmsghdr, byteBuffer);
+        } else if (nlFamily == NETLINK_XFRM) {
+            parsed = parseXfrmMessage(nlmsghdr, byteBuffer);
         } else {
             parsed = null;
         }
@@ -168,4 +175,19 @@
             default: return null;
         }
     }
+
+    @Nullable
+    private static NetlinkMessage parseXfrmMessage(
+            @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) {
+        return (NetlinkMessage) XfrmNetlinkMessage.parseXfrmInternal(nlmsghdr, byteBuffer);
+    }
+
+    /** A convenient method to create a ByteBuffer for encoding a new message */
+    protected static ByteBuffer newNlMsgByteBuffer(int payloadLen) {
+        final int length = StructNlMsgHdr.STRUCT_SIZE + payloadLen;
+        final ByteBuffer byteBuffer = ByteBuffer.allocate(length);
+        byteBuffer.order(ByteOrder.nativeOrder());
+
+        return byteBuffer;
+    }
 }
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
index 5052cb8..ff37639 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
@@ -128,6 +128,14 @@
         nlmsg_pid = 0;
     }
 
+    public StructNlMsgHdr(int payloadLen, short type, short flags, int seq) {
+        nlmsg_len = StructNlMsgHdr.STRUCT_SIZE + payloadLen;
+        nlmsg_type = type;
+        nlmsg_flags = flags;
+        nlmsg_seq = seq;
+        nlmsg_pid = 0;
+    }
+
     /**
      * Write netlink message header to ByteBuffer.
      */
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/OWNERS b/staticlibs/device/com/android/net/module/util/netlink/xfrm/OWNERS
index 97b4da0..fca70aa 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/xfrm/OWNERS
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/OWNERS
@@ -1,3 +1,2 @@
-# Bug component: 1364804
-
-per-file **Xfrm* = file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
+# Bug component: 685852
+file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java
new file mode 100644
index 0000000..bdcdcd8
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmId.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.net.InetAddress;
+
+/**
+ * Struct xfrm_id
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_id {
+ *      xfrm_address_t daddr;
+ *      __be32 spi;
+ *      __u8 proto;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmId extends Struct {
+    public static final int STRUCT_SIZE = 24;
+
+    @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+    public final byte[] nestedStructDAddr;
+
+    @Field(order = 1, type = Type.UBE32)
+    public final long spi;
+
+    @Field(order = 2, type = Type.U8, padding = 3)
+    public final short proto;
+
+    @Computed private final StructXfrmAddressT mDestXfrmAddressT;
+
+    // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+    public StructXfrmId(@NonNull final byte[] nestedStructDAddr, long spi, short proto) {
+        this.nestedStructDAddr = nestedStructDAddr.clone();
+        this.spi = spi;
+        this.proto = proto;
+
+        mDestXfrmAddressT = new StructXfrmAddressT(this.nestedStructDAddr);
+    }
+
+    // Constructor to build a new message
+    public StructXfrmId(@NonNull final InetAddress destAddress, long spi, short proto) {
+        this(new StructXfrmAddressT(destAddress).writeToBytes(), spi, proto);
+    }
+
+    /** Return the destination address */
+    public InetAddress getDestAddress(int family) {
+        return mDestXfrmAddressT.getAddress(family);
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java
new file mode 100644
index 0000000..12f68c8
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfg.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_INF;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.math.BigInteger;
+
+/**
+ * Struct xfrm_lifetime_cfg
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_lifetime_cfg {
+ *      __u64 soft_byte_limit;
+ *      __u64 hard_byte_limit;
+ *      __u64 soft_packet_limit;
+ *      __u64 hard_packet_limit;
+ *      __u64 soft_add_expires_seconds;
+ *      __u64 hard_add_expires_seconds;
+ *      __u64 soft_use_expires_seconds;
+ *      __u64 hard_use_expires_seconds;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmLifetimeCfg extends Struct {
+    public static final int STRUCT_SIZE = 64;
+
+    @Field(order = 0, type = Type.U64)
+    public final BigInteger softByteLimit;
+
+    @Field(order = 1, type = Type.U64)
+    public final BigInteger hardByteLimit;
+
+    @Field(order = 2, type = Type.U64)
+    public final BigInteger softPacketLimit;
+
+    @Field(order = 3, type = Type.U64)
+    public final BigInteger hardPacketLimit;
+
+    @Field(order = 4, type = Type.U64)
+    public final BigInteger softAddExpiresSeconds;
+
+    @Field(order = 5, type = Type.U64)
+    public final BigInteger hardAddExpiresSeconds;
+
+    @Field(order = 6, type = Type.U64)
+    public final BigInteger softUseExpiresSeconds;
+
+    @Field(order = 7, type = Type.U64)
+    public final BigInteger hardUseExpiresSeconds;
+
+    // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+    public StructXfrmLifetimeCfg(
+            @NonNull final BigInteger softByteLimit,
+            @NonNull final BigInteger hardByteLimit,
+            @NonNull final BigInteger softPacketLimit,
+            @NonNull final BigInteger hardPacketLimit,
+            @NonNull final BigInteger softAddExpiresSeconds,
+            @NonNull final BigInteger hardAddExpiresSeconds,
+            @NonNull final BigInteger softUseExpiresSeconds,
+            @NonNull final BigInteger hardUseExpiresSeconds) {
+        this.softByteLimit = softByteLimit;
+        this.hardByteLimit = hardByteLimit;
+        this.softPacketLimit = softPacketLimit;
+        this.hardPacketLimit = hardPacketLimit;
+        this.softAddExpiresSeconds = softAddExpiresSeconds;
+        this.hardAddExpiresSeconds = hardAddExpiresSeconds;
+        this.softUseExpiresSeconds = softUseExpiresSeconds;
+        this.hardUseExpiresSeconds = hardUseExpiresSeconds;
+    }
+
+    // Constructor to build a new message
+    public StructXfrmLifetimeCfg() {
+        this(
+                XFRM_INF,
+                XFRM_INF,
+                XFRM_INF,
+                XFRM_INF,
+                BigInteger.ZERO,
+                BigInteger.ZERO,
+                BigInteger.ZERO,
+                BigInteger.ZERO);
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java
new file mode 100644
index 0000000..6a539c7
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCur.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+
+import java.math.BigInteger;
+
+/**
+ * Struct xfrm_lifetime_cur
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_lifetime_cur {
+ *      __u64 bytes;
+ *      __u64 packets;
+ *      __u64 add_time;
+ *      __u64 use_time;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmLifetimeCur extends Struct {
+    public static final int STRUCT_SIZE = 32;
+
+    @Field(order = 0, type = Type.U64)
+    public final BigInteger bytes;
+
+    @Field(order = 1, type = Type.U64)
+    public final BigInteger packets;
+
+    @Field(order = 2, type = Type.U64)
+    public final BigInteger addTime;
+
+    @Field(order = 3, type = Type.U64)
+    public final BigInteger useTime;
+
+    public StructXfrmLifetimeCur(
+            @NonNull final BigInteger bytes,
+            @NonNull final BigInteger packets,
+            @NonNull final BigInteger addTime,
+            @NonNull final BigInteger useTime) {
+        this.bytes = bytes;
+        this.packets = packets;
+        this.addTime = addTime;
+        this.useTime = useTime;
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java
new file mode 100644
index 0000000..7bd2ca1
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmSelector.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import androidx.annotation.NonNull;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Struct xfrm_selector
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <pre>
+ * struct xfrm_selector {
+ *      xfrm_address_t daddr;
+ *      xfrm_address_t saddr;
+ *      __be16 dport;
+ *      __be16 dport_mask;
+ *      __be16 sport;
+ *      __be16 sport_mask;
+ *      __u16 family;
+ *      __u8 prefixlen_d;
+ *      __u8 prefixlen_s;
+ *      __u8 proto;
+ *      int ifindex;
+ *      __kernel_uid32_t user;
+ * };
+ * </pre>
+ *
+ * @hide
+ */
+public class StructXfrmSelector extends Struct {
+    public static final int STRUCT_SIZE = 56;
+
+    @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+    public final byte[] nestedStructDAddr;
+
+    @Field(order = 1, type = Type.ByteArray, arraysize = 16)
+    public final byte[] nestedStructSAddr;
+
+    @Field(order = 2, type = Type.UBE16)
+    public final int dPort;
+
+    @Field(order = 3, type = Type.UBE16)
+    public final int dPortMask;
+
+    @Field(order = 4, type = Type.UBE16)
+    public final int sPort;
+
+    @Field(order = 5, type = Type.UBE16)
+    public final int sPortMask;
+
+    @Field(order = 6, type = Type.U16)
+    public final int selectorFamily;
+
+    @Field(order = 7, type = Type.U8, padding = 1)
+    public final short prefixlenD;
+
+    @Field(order = 8, type = Type.U8, padding = 1)
+    public final short prefixlenS;
+
+    @Field(order = 9, type = Type.U8, padding = 1)
+    public final short proto;
+
+    @Field(order = 10, type = Type.S32)
+    public final int ifIndex;
+
+    @Field(order = 11, type = Type.S32)
+    public final int user;
+
+    // Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
+    public StructXfrmSelector(
+            @NonNull final byte[] nestedStructDAddr,
+            @NonNull final byte[] nestedStructSAddr,
+            int dPort,
+            int dPortMask,
+            int sPort,
+            int sPortMask,
+            int selectorFamily,
+            short prefixlenD,
+            short prefixlenS,
+            short proto,
+            int ifIndex,
+            int user) {
+        this.nestedStructDAddr = nestedStructDAddr.clone();
+        this.nestedStructSAddr = nestedStructSAddr.clone();
+        this.dPort = dPort;
+        this.dPortMask = dPortMask;
+        this.sPort = sPort;
+        this.sPortMask = sPortMask;
+        this.selectorFamily = selectorFamily;
+        this.prefixlenD = prefixlenD;
+        this.prefixlenS = prefixlenS;
+        this.proto = proto;
+        this.ifIndex = ifIndex;
+        this.user = user;
+    }
+
+    // Constructor to build a new message
+    public StructXfrmSelector(int selectorFamily) {
+        this(
+                new byte[StructXfrmAddressT.STRUCT_SIZE],
+                new byte[StructXfrmAddressT.STRUCT_SIZE],
+                0 /* dPort */,
+                0 /* dPortMask */,
+                0 /* sPort */,
+                0 /* sPortMask */,
+                selectorFamily,
+                (short) 0 /* prefixlenD */,
+                (short) 0 /* prefixlenS */,
+                (short) 0 /* proto */,
+                0 /* ifIndex */,
+                0 /* user */);
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java
new file mode 100644
index 0000000..680a7ca
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_MSG_GETSA;
+
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * An XfrmNetlinkMessage subclass for XFRM_MSG_GETSA messages.
+ *
+ * <p>see include/uapi/linux/xfrm.h
+ *
+ * <p>XFRM_MSG_GETSA syntax
+ *
+ * <ul>
+ *   <li>TLV: xfrm_usersa_id
+ *   <li>Optional Attributes: XFRMA_MARK, XFRMA_SRCADDR
+ * </ul>
+ *
+ * @hide
+ */
+public class XfrmNetlinkGetSaMessage extends XfrmNetlinkMessage {
+    @NonNull private final StructXfrmUsersaId mXfrmUsersaId;
+
+    private XfrmNetlinkGetSaMessage(
+            @NonNull final StructNlMsgHdr header, @NonNull final StructXfrmUsersaId xfrmUsersaId) {
+        super(header);
+        mXfrmUsersaId = xfrmUsersaId;
+    }
+
+    private XfrmNetlinkGetSaMessage(
+            @NonNull final StructNlMsgHdr header,
+            @NonNull final InetAddress destAddress,
+            long spi,
+            short proto) {
+        super(header);
+
+        final int family =
+                destAddress instanceof Inet4Address ? OsConstants.AF_INET : OsConstants.AF_INET6;
+        mXfrmUsersaId = new StructXfrmUsersaId(destAddress, spi, family, proto);
+    }
+
+    @Override
+    protected void packPayload(@NonNull final ByteBuffer byteBuffer) {
+        mXfrmUsersaId.writeToByteBuffer(byteBuffer);
+    }
+
+    /**
+     * Parse XFRM_MSG_GETSA message from ByteBuffer.
+     *
+     * <p>This method should be called from NetlinkMessage#parse(ByteBuffer, int) for generic
+     * message validation and processing
+     *
+     * @param nlmsghdr netlink message header.
+     * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. MUST be
+     *                   host order
+     */
+    @Nullable
+    static XfrmNetlinkGetSaMessage parseInternal(
+            @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) {
+        final StructXfrmUsersaId xfrmUsersaId = Struct.parse(StructXfrmUsersaId.class, byteBuffer);
+        if (xfrmUsersaId == null) {
+            return null;
+        }
+
+        // Attributes not supported. Don't bother handling them.
+
+        return new XfrmNetlinkGetSaMessage(nlmsghdr, xfrmUsersaId);
+    }
+
+    /** A convenient method to create a XFRM_MSG_GETSA message. */
+    public static byte[] newXfrmNetlinkGetSaMessage(
+            @NonNull final InetAddress destAddress, long spi, short proto) {
+        final int payloadLen = StructXfrmUsersaId.STRUCT_SIZE;
+
+        final StructNlMsgHdr nlmsghdr =
+                new StructNlMsgHdr(payloadLen, XFRM_MSG_GETSA, NLM_F_REQUEST, 0);
+        final XfrmNetlinkGetSaMessage message =
+                new XfrmNetlinkGetSaMessage(nlmsghdr, destAddress, spi, proto);
+
+        final ByteBuffer byteBuffer = newNlMsgByteBuffer(payloadLen);
+        message.pack(byteBuffer);
+
+        return byteBuffer.array();
+    }
+
+    public StructXfrmUsersaId getStructXfrmUsersaId() {
+        return mXfrmUsersaId;
+    }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
index ee34e57..9773cd6 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java
@@ -17,10 +17,14 @@
 package com.android.net.module.util.netlink.xfrm;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.net.module.util.netlink.NetlinkMessage;
 import com.android.net.module.util.netlink.StructNlMsgHdr;
 
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
 /** Base calss for XFRM netlink messages */
 // Developer notes: The Linux kernel includes a number of XFRM structs that are not standard netlink
 // attributes (e.g., xfrm_usersa_id). These structs are unlikely to change size, so this XFRM
@@ -28,12 +32,47 @@
 // struct size changes, it should be caught by CTS and then developers should add
 // kernel-version-based behvaiours.
 public abstract class XfrmNetlinkMessage extends NetlinkMessage {
-    // TODO: STOPSHIP: b/308011229 Remove it when OsConstants.IPPROTO_ESP is exposed
+    // TODO: b/312498032 Remove it when OsConstants.IPPROTO_ESP is stable
     public static final int IPPROTO_ESP = 50;
+    // TODO: b/312498032 Remove it when OsConstants.NETLINK_XFRM is stable
+    public static final int NETLINK_XFRM = 6;
+
+    /* see include/uapi/linux/xfrm.h */
+    public static final short XFRM_MSG_NEWSA = 16;
+    public static final short XFRM_MSG_GETSA = 18;
+
+    public static final BigInteger XFRM_INF = new BigInteger("FFFFFFFFFFFFFFFF", 16);
 
     public XfrmNetlinkMessage(@NonNull final StructNlMsgHdr header) {
         super(header);
     }
 
-    // TODO: Add the support for parsing messages
+    /**
+     * Parse XFRM message from ByteBuffer.
+     *
+     * <p>This method should be called from NetlinkMessage#parse(ByteBuffer, int) for generic
+     * message validation and processing
+     *
+     * @param nlmsghdr netlink message header.
+     * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. MUST be
+     *                   host order
+     */
+    @Nullable
+    public static XfrmNetlinkMessage parseXfrmInternal(
+            @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) {
+        switch (nlmsghdr.nlmsg_type) {
+            case XFRM_MSG_GETSA:
+                return XfrmNetlinkGetSaMessage.parseInternal(nlmsghdr, byteBuffer);
+            default:
+                return null;
+        }
+    }
+
+    protected abstract void packPayload(@NonNull final ByteBuffer byteBuffer);
+
+    /** Write a XFRM message to {@link ByteBuffer}. */
+    public void pack(@NonNull final ByteBuffer byteBuffer) {
+        getHeader().pack(byteBuffer);
+        packPayload(byteBuffer);
+    }
 }
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/OWNERS b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/OWNERS
new file mode 100644
index 0000000..fca70aa
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 685852
+file:platform/frameworks/base:main:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java
new file mode 100644
index 0000000..c9741cf
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmIdTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.IPPROTO_ESP;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmIdTest {
+    private static final String EXPECTED_HEX_STRING =
+            "C0000201000000000000000000000000" + "53FA0FDD32000000";
+    private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+    private static final InetAddress DEST_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
+    private static final long SPI = 0x53fa0fdd;
+    private static final short PROTO = IPPROTO_ESP;
+
+    @Test
+    public void testEncode() throws Exception {
+        final StructXfrmId struct = new StructXfrmId(DEST_ADDRESS, SPI, PROTO);
+
+        final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+        buffer.order(ByteOrder.nativeOrder());
+        struct.writeToByteBuffer(buffer);
+
+        assertArrayEquals(EXPECTED_HEX, buffer.array());
+    }
+
+    @Test
+    public void testDecode() throws Exception {
+        final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+        buffer.order(ByteOrder.nativeOrder());
+        final StructXfrmId struct = StructXfrmId.parse(StructXfrmId.class, buffer);
+
+        assertEquals(DEST_ADDRESS, struct.getDestAddress(OsConstants.AF_INET));
+        assertEquals(SPI, struct.spi);
+        assertEquals(PROTO, struct.proto);
+    }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java
new file mode 100644
index 0000000..69360f6
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCfgTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_INF;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmLifetimeCfgTest {
+    private static final String EXPECTED_HEX_STRING =
+            "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "00000000000000000000000000000000"
+                    + "00000000000000000000000000000000";
+    private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+
+    @Test
+    public void testEncode() throws Exception {
+        final StructXfrmLifetimeCfg struct = new StructXfrmLifetimeCfg();
+
+        final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+        buffer.order(ByteOrder.nativeOrder());
+        struct.writeToByteBuffer(buffer);
+
+        assertArrayEquals(EXPECTED_HEX, buffer.array());
+    }
+
+    @Test
+    public void testDecode() throws Exception {
+        final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+        buffer.order(ByteOrder.nativeOrder());
+        final StructXfrmLifetimeCfg struct =
+                StructXfrmLifetimeCfg.parse(StructXfrmLifetimeCfg.class, buffer);
+
+        assertEquals(XFRM_INF, struct.softByteLimit);
+        assertEquals(XFRM_INF, struct.hardByteLimit);
+        assertEquals(XFRM_INF, struct.softPacketLimit);
+        assertEquals(XFRM_INF, struct.hardPacketLimit);
+        assertEquals(BigInteger.ZERO, struct.softAddExpiresSeconds);
+        assertEquals(BigInteger.ZERO, struct.hardAddExpiresSeconds);
+        assertEquals(BigInteger.ZERO, struct.softUseExpiresSeconds);
+        assertEquals(BigInteger.ZERO, struct.hardUseExpiresSeconds);
+    }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java
new file mode 100644
index 0000000..008c922
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmLifetimeCurTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmLifetimeCurTest {
+    private static final String EXPECTED_HEX_STRING =
+            "00000000000000000000000000000000" + "8CFE4265000000000000000000000000";
+    private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+    private static final BigInteger ADD_TIME;
+
+    static {
+        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        cal.set(2023, Calendar.NOVEMBER, 2, 1, 42, 36);
+        final long timestampSeconds = TimeUnit.MILLISECONDS.toSeconds(cal.getTimeInMillis());
+        ADD_TIME = BigInteger.valueOf(timestampSeconds);
+    }
+
+    @Test
+    public void testEncode() throws Exception {
+        final StructXfrmLifetimeCur struct =
+                new StructXfrmLifetimeCur(
+                        BigInteger.ZERO, BigInteger.ZERO, ADD_TIME, BigInteger.ZERO);
+
+        final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+        buffer.order(ByteOrder.nativeOrder());
+        struct.writeToByteBuffer(buffer);
+
+        assertArrayEquals(EXPECTED_HEX, buffer.array());
+    }
+
+    @Test
+    public void testDecode() throws Exception {
+        final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+        buffer.order(ByteOrder.nativeOrder());
+        final StructXfrmLifetimeCur struct =
+                StructXfrmLifetimeCur.parse(StructXfrmLifetimeCur.class, buffer);
+
+        assertEquals(BigInteger.ZERO, struct.bytes);
+        assertEquals(BigInteger.ZERO, struct.packets);
+        assertEquals(ADD_TIME, struct.addTime);
+        assertEquals(BigInteger.ZERO, struct.useTime);
+    }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java
new file mode 100644
index 0000000..99f3b2a
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmSelectorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StructXfrmSelectorTest {
+    private static final String EXPECTED_HEX_STRING =
+            "00000000000000000000000000000000"
+                    + "00000000000000000000000000000000"
+                    + "00000000000000000200000000000000"
+                    + "0000000000000000";
+    private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+
+    private static final byte[] XFRM_ADDRESS_T_ANY_BYTES = new byte[16];
+    private static final int FAMILY = OsConstants.AF_INET;
+
+    @Test
+    public void testEncode() throws Exception {
+        final StructXfrmSelector struct = new StructXfrmSelector(FAMILY);
+
+        final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+        buffer.order(ByteOrder.nativeOrder());
+        struct.writeToByteBuffer(buffer);
+
+        assertArrayEquals(EXPECTED_HEX, buffer.array());
+    }
+
+    @Test
+    public void testDecode() throws Exception {
+        final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+        buffer.order(ByteOrder.nativeOrder());
+        final StructXfrmSelector struct =
+                StructXfrmSelector.parse(StructXfrmSelector.class, buffer);
+
+        assertArrayEquals(XFRM_ADDRESS_T_ANY_BYTES, struct.nestedStructDAddr);
+        assertArrayEquals(XFRM_ADDRESS_T_ANY_BYTES, struct.nestedStructSAddr);
+        assertEquals(0, struct.dPort);
+        assertEquals(0, struct.dPortMask);
+        assertEquals(0, struct.sPort);
+        assertEquals(0, struct.sPortMask);
+        assertEquals(FAMILY, struct.selectorFamily);
+        assertEquals(0, struct.prefixlenD);
+        assertEquals(0, struct.prefixlenS);
+        assertEquals(0, struct.proto);
+        assertEquals(0, struct.ifIndex);
+        assertEquals(0, struct.user);
+    }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java
index 52fd591..b659f62 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java
@@ -52,7 +52,7 @@
     public void testEncode() throws Exception {
         final StructXfrmUsersaId struct = new StructXfrmUsersaId(DEST_ADDRESS, SPI, FAMILY, PROTO);
 
-        ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
+        final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
         buffer.order(ByteOrder.nativeOrder());
         struct.writeToByteBuffer(buffer);
 
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java
new file mode 100644
index 0000000..0ab36e7
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util.netlink.xfrm;
+
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.IPPROTO_ESP;
+import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.NETLINK_XFRM;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.net.InetAddresses;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+import com.android.net.module.util.netlink.NetlinkMessage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class XfrmNetlinkGetSaMessageTest {
+    private static final String EXPECTED_HEX_STRING =
+            "28000000120001000000000000000000"
+                    + "C0000201000000000000000000000000"
+                    + "7768440002003200";
+    private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
+    private static final InetAddress DEST_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1");
+    private static final long SPI = 0x77684400;
+    private static final int FAMILY = OsConstants.AF_INET;
+    private static final short PROTO = IPPROTO_ESP;
+
+    @Test
+    public void testEncode() throws Exception {
+        final byte[] result =
+                XfrmNetlinkGetSaMessage.newXfrmNetlinkGetSaMessage(DEST_ADDRESS, SPI, PROTO);
+        assertArrayEquals(EXPECTED_HEX, result);
+    }
+
+    @Test
+    public void testDecode() throws Exception {
+        final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
+        buffer.order(ByteOrder.nativeOrder());
+        final XfrmNetlinkGetSaMessage message =
+                (XfrmNetlinkGetSaMessage) NetlinkMessage.parse(buffer, NETLINK_XFRM);
+        final StructXfrmUsersaId struct = message.getStructXfrmUsersaId();
+
+        assertEquals(DEST_ADDRESS, struct.getDestAddress());
+        assertEquals(SPI, struct.spi);
+        assertEquals(FAMILY, struct.family);
+        assertEquals(PROTO, struct.proto);
+        assertEquals(0, buffer.remaining());
+    }
+}
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 90b7875..0ffe81e 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -36,6 +36,7 @@
     <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
         <option name="force-skip-system-props" value="true" />
         <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+        <option name="set-global-setting" key="low_power_standby_enabled" value="0" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 308aead..9ff0f2f 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -860,4 +860,9 @@
             assertEquals(DnsResolver.ERROR_SYSTEM, e.code);
         }
     }
+
+    @Test
+    public void testNoRawBinderAccess() {
+        assertNull(mContext.getSystemService("dnsresolver"));
+    }
 }
diff --git a/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt b/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt
new file mode 100644
index 0000000..2cf6b17
--- /dev/null
+++ b/tests/unit/java/com/android/server/BpfLoaderRcUtilsTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.S)
+class BpfLoaderRcUtilsTest {
+    @Test
+    fun testLoadExistingBpfRcFile() {
+
+        val inputString = """
+            service a
+            # test comment
+            service bpfloader /system/bin/bpfloader
+                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
+                # comment 漢字
+                reboot_on_failure reboot,bpfloader-failed
+                updatable
+            
+            #test comment
+            on b 
+              oneshot 
+              # test comment
+        """.trimIndent()
+        val expectedResult = listOf(
+            "service bpfloader /system/bin/bpfloader",
+            "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"
+        )
+
+        assertEquals(expectedResult,
+                BpfLoaderRcUtils.loadExistingBpfRcFile(inputString.byteInputStream()))
+    }
+
+    @Test
+    fun testCheckBpfRcFile() {
+        assertTrue(BpfLoaderRcUtils.checkBpfLoaderRc())
+    }
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 9fad766..8f5fd7c 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -161,6 +161,7 @@
 import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
 import static com.android.server.ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS;
 import static com.android.server.ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION;
+import static com.android.server.ConnectivityService.LOG_BPF_RC;
 import static com.android.server.ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID;
 import static com.android.server.ConnectivityService.PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED;
 import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM;
@@ -2157,6 +2158,8 @@
             switch (name) {
                 case ALLOW_SYSUI_CONNECTIVITY_REPORTS:
                     return true;
+                case LOG_BPF_RC:
+                    return true;
                 default:
                     return super.isFeatureNotChickenedOut(context, name);
             }
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index afb9abd..44512bb 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -19,6 +19,7 @@
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER;
 import static android.net.NetworkCapabilities.MAX_TRANSPORT;
@@ -329,14 +330,14 @@
     public void testOverrideDefaultMode() throws Exception {
         // Hard-coded default is opportunistic mode.
         final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx);
-        assertTrue(cfgAuto.useTls);
+        assertEquals(PRIVATE_DNS_MODE_OPPORTUNISTIC, cfgAuto.mode);
         assertEquals("", cfgAuto.hostname);
         assertEquals(new InetAddress[0], cfgAuto.ips);
 
         // Pretend a gservices push sets the default to "off".
         ConnectivitySettingsManager.setPrivateDnsDefaultMode(mCtx, PRIVATE_DNS_MODE_OFF);
         final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx);
-        assertFalse(cfgOff.useTls);
+        assertEquals(PRIVATE_DNS_MODE_OFF, cfgOff.mode);
         assertEquals("", cfgOff.hostname);
         assertEquals(new InetAddress[0], cfgOff.ips);
 
@@ -344,7 +345,7 @@
         ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
         ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com");
         final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx);
-        assertTrue(cfgStrict.useTls);
+        assertEquals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, cfgStrict.mode);
         assertEquals("strictmode.com", cfgStrict.hostname);
         assertEquals(new InetAddress[0], cfgStrict.ips);
     }
@@ -419,7 +420,7 @@
 
         // The PrivateDnsConfig map is empty, so the default PRIVATE_DNS_OFF is returned.
         PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
-        assertFalse(privateDnsCfg.useTls);
+        assertEquals(PRIVATE_DNS_MODE_OFF, privateDnsCfg.mode);
         assertEquals("", privateDnsCfg.hostname);
         assertEquals(new InetAddress[0], privateDnsCfg.ips);
 
@@ -431,7 +432,7 @@
                         VALIDATION_RESULT_SUCCESS));
         mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
         privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
-        assertTrue(privateDnsCfg.useTls);
+        assertEquals(PRIVATE_DNS_MODE_OPPORTUNISTIC, privateDnsCfg.mode);
         assertEquals("", privateDnsCfg.hostname);
         assertEquals(new InetAddress[0], privateDnsCfg.ips);
 
@@ -439,14 +440,14 @@
         mDnsManager.updatePrivateDns(network, new PrivateDnsConfig(tlsName, tlsAddrs));
         mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
         privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
-        assertTrue(privateDnsCfg.useTls);
+        assertEquals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, privateDnsCfg.mode);
         assertEquals(tlsName, privateDnsCfg.hostname);
         assertEquals(tlsAddrs, privateDnsCfg.ips);
 
         // The network is removed, so the PrivateDnsConfig map becomes empty again.
         mDnsManager.removeNetwork(network);
         privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
-        assertFalse(privateDnsCfg.useTls);
+        assertEquals(PRIVATE_DNS_MODE_OFF, privateDnsCfg.mode);
         assertEquals("", privateDnsCfg.hostname);
         assertEquals(new InetAddress[0], privateDnsCfg.ips);
     }
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index f4c62c5..958c4f2 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -132,6 +132,7 @@
         it[ConnectivityService.KEY_DESTROY_FROZEN_SOCKETS_VERSION] = true
         it[ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION] = true
         it[ConnectivityService.ALLOW_SYSUI_CONNECTIVITY_REPORTS] = true
+        it[ConnectivityService.LOG_BPF_RC] = true
     }
     fun enableFeature(f: String) = enabledFeatures.set(f, true)
     fun disableFeature(f: String) = enabledFeatures.set(f, false)