Merge "Log bpf rc file if it differs from the template files." 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/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index f2279f8..9268da5 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -4790,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/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);
     }