Merge changes I7ba60db8,Ib0afb414

* changes:
  Remove Python enables identical to global defaults.
  Update config to use self-contained python binaries
diff --git a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
index 6699c0d..a878fa5 100644
--- a/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
@@ -67,17 +67,17 @@
     }
 }
 
-static void com_android_networkstack_tethering_util_setupNaSocket(JNIEnv *env, jobject clazz,
+static void com_android_networkstack_tethering_util_setupNaSocket(JNIEnv *env, jclass clazz,
         jobject javaFd) {
     com_android_networkstack_tethering_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
 }
 
-static void com_android_networkstack_tethering_util_setupNsSocket(JNIEnv *env, jobject clazz,
+static void com_android_networkstack_tethering_util_setupNsSocket(JNIEnv *env, jclass clazz,
         jobject javaFd) {
     com_android_networkstack_tethering_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
 }
 
-static void com_android_networkstack_tethering_util_setupRaSocket(JNIEnv *env, jobject clazz,
+static void com_android_networkstack_tethering_util_setupRaSocket(JNIEnv *env, jclass clazz,
         jobject javaFd, jint ifIndex) {
     static const int kLinkLocalHopLimit = 255;
 
diff --git a/framework-t/Android.bp b/framework-t/Android.bp
index 1a8d46b..f8d7e4c 100644
--- a/framework-t/Android.bp
+++ b/framework-t/Android.bp
@@ -46,6 +46,7 @@
     libs: [
         "unsupportedappusage",
         "app-compat-annotations",
+        "androidx.annotation_annotation",
     ],
     impl_only_libs: [
         // The build system will use framework-bluetooth module_current stubs, because
diff --git a/nearby/framework/Android.bp b/nearby/framework/Android.bp
index f6e0995..278f823 100644
--- a/nearby/framework/Android.bp
+++ b/nearby/framework/Android.bp
@@ -45,6 +45,7 @@
     srcs: [":framework-nearby-java-sources"],
     sdk_version: "module_current",
     libs: [
+        "androidx.annotation_annotation",
         "framework-annotations-lib",
         "framework-bluetooth",
     ],
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 062d272..ad4596d 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -52,7 +52,7 @@
 }
 
 jstring com_android_server_connectivity_ClatCoordinator_selectIpv4Address(JNIEnv* env,
-                                                                          jobject clazz,
+                                                                          jclass clazz,
                                                                           jstring v4addr,
                                                                           jint prefixlen) {
     ScopedUtfChars address(env, v4addr);
@@ -84,7 +84,7 @@
 
 // Picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix.
 jstring com_android_server_connectivity_ClatCoordinator_generateIpv6Address(
-        JNIEnv* env, jobject clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str,
+        JNIEnv* env, jclass clazz, jstring ifaceStr, jstring v4Str, jstring prefix64Str,
         jint mark) {
     ScopedUtfChars iface(env, ifaceStr);
     ScopedUtfChars addr4(env, v4Str);
@@ -125,7 +125,7 @@
 }
 
 static jint com_android_server_connectivity_ClatCoordinator_createTunInterface(JNIEnv* env,
-                                                                               jobject clazz,
+                                                                               jclass clazz,
                                                                                jstring tuniface) {
     ScopedUtfChars v4interface(env, tuniface);
 
@@ -152,7 +152,7 @@
     return fd;
 }
 
-static jint com_android_server_connectivity_ClatCoordinator_detectMtu(JNIEnv* env, jobject clazz,
+static jint com_android_server_connectivity_ClatCoordinator_detectMtu(JNIEnv* env, jclass clazz,
                                                                       jstring platSubnet,
                                                                       jint plat_suffix, jint mark) {
     ScopedUtfChars platSubnetStr(env, platSubnet);
@@ -174,7 +174,7 @@
 }
 
 static jint com_android_server_connectivity_ClatCoordinator_openPacketSocket(JNIEnv* env,
-                                                                              jobject clazz) {
+                                                                              jclass clazz) {
     // Will eventually be bound to htons(ETH_P_IPV6) protocol,
     // but only after appropriate bpf filter is attached.
     const int sock = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, 0);
@@ -199,7 +199,7 @@
 }
 
 static jint com_android_server_connectivity_ClatCoordinator_openRawSocket6(JNIEnv* env,
-                                                                           jobject clazz,
+                                                                           jclass clazz,
                                                                            jint mark) {
     int sock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW);
     if (sock < 0) {
@@ -218,7 +218,7 @@
 }
 
 static void com_android_server_connectivity_ClatCoordinator_addAnycastSetsockopt(
-        JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
+        JNIEnv* env, jclass clazz, jobject javaFd, jstring addr6, jint ifindex) {
     int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
     if (sock < 0) {
         jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
@@ -244,7 +244,7 @@
 }
 
 static void com_android_server_connectivity_ClatCoordinator_configurePacketSocket(
-        JNIEnv* env, jobject clazz, jobject javaFd, jstring addr6, jint ifindex) {
+        JNIEnv* env, jclass clazz, jobject javaFd, jstring addr6, jint ifindex) {
     ScopedUtfChars addrStr(env, addr6);
 
     int sock = netjniutils::GetNativeFileDescriptor(env, javaFd);
@@ -268,7 +268,7 @@
 }
 
 static jint com_android_server_connectivity_ClatCoordinator_startClatd(
-        JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
+        JNIEnv* env, jclass clazz, jobject tunJavaFd, jobject readSockJavaFd,
         jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
     ScopedUtfChars ifaceStr(env, iface);
     ScopedUtfChars pfx96Str(env, pfx96);
@@ -415,7 +415,7 @@
     }
 }
 
-static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jobject clazz,
+static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* env, jclass clazz,
                                                                       jstring iface, jstring pfx96,
                                                                       jstring v4, jstring v6,
                                                                       jint pid) {
@@ -433,7 +433,7 @@
 }
 
 static jlong com_android_server_connectivity_ClatCoordinator_getSocketCookie(
-        JNIEnv* env, jobject clazz, jobject sockJavaFd) {
+        JNIEnv* env, jclass clazz, jobject sockJavaFd) {
     int sockFd = netjniutils::GetNativeFileDescriptor(env, sockJavaFd);
     if (sockFd < 0) {
         jniThrowExceptionFmt(env, "java/io/IOException", "Invalid socket file descriptor");
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index 5549fbe..843b7b3 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -310,9 +310,11 @@
                     NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
         }
 
+        // For testing purpose, fill legacy type for NetworkStatsService since it does not
+        // support transport types.
         final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
-                new NetworkAgentConfig.Builder().build(), callingUid, binder,
-                mNetworkProvider);
+                new NetworkAgentConfig.Builder().setLegacyType(ConnectivityManager.TYPE_TEST)
+                        .build(), callingUid, binder, mNetworkProvider);
         agent.register();
         agent.markConnected();
         return agent;
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
index 5fc3068..da79158 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -76,6 +76,7 @@
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 
+// TODO: b/268552823 Improve the readability of IpSecManagerTunnelTest
 @RunWith(AndroidJUnit4.class)
 @AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
 public class IpSecManagerTunnelTest extends IpSecBaseTest {
@@ -83,11 +84,6 @@
 
     private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
 
-    // Redefine this flag here so that IPsec code shipped in a mainline module can build on old
-    // platforms before FEATURE_IPSEC_TUNNEL_MIGRATION API is released.
-    private static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
-            "android.software.ipsec_tunnel_migration";
-
     private static final InetAddress LOCAL_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.1");
     private static final InetAddress REMOTE_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.2");
     private static final InetAddress LOCAL_OUTER_6 =
@@ -263,14 +259,23 @@
          *
          * @param ipsecNetwork The IPsec Interface based Network for binding sockets on
          * @param tunnelIface The IPsec tunnel interface that will be tested
-         * @param underlyingTunUtils The utility of the IPsec tunnel interface's underlying TUN
-         *     network
-         * @return the integer port of the inner socket if outbound, or 0 if inbound
-         *     IpSecTunnelTestRunnable
+         * @param tunUtils The utility of the IPsec tunnel interface's underlying TUN network
+         * @param inTunnelTransform The inbound tunnel mode transform
+         * @param outTunnelTransform The outbound tunnel mode transform
+         * @param localOuter The local address of the outer IP packet
+         * @param remoteOuter The remote address of the outer IP packet
+         * @param seqNum The expected sequence number of the inbound packet
          * @throws Exception if any part of the test failed.
          */
         public abstract int run(
-                Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils underlyingTunUtils)
+                Network ipsecNetwork,
+                IpSecTunnelInterface tunnelIface,
+                TunUtils tunUtils,
+                IpSecTransform inTunnelTransform,
+                IpSecTransform outTunnelTransform,
+                InetAddress localOuter,
+                InetAddress remoteOuter,
+                int seqNum)
                 throws Exception;
     }
 
@@ -306,18 +311,28 @@
     }
 
     private interface IpSecTunnelTestRunnableFactory {
+        /**
+         * Build a IpSecTunnelTestRunnable.
+         *
+         * @param transportInTunnelMode indicate if there needs to be a transport mode transform
+         *     inside the tunnel mode transform
+         * @param spi The IPsec SPI
+         * @param localInner The local address of the inner IP packet
+         * @param remoteInner The remote address of the inner IP packet
+         * @param inTransportTransform The inbound transport mode transform
+         * @param outTransportTransform The outbound transport mode transform
+         * @param encapPort The port of the UDP encapsulation socket
+         * @param innerSocketPort The inner socket port
+         */
         IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
                 boolean transportInTunnelMode,
                 int spi,
                 InetAddress localInner,
                 InetAddress remoteInner,
-                InetAddress localOuter,
-                InetAddress remoteOuter,
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
-                int innerSocketPort,
-                int expectedPacketSize)
+                int innerSocketPort)
                 throws Exception;
     }
 
@@ -327,17 +342,21 @@
                 int spi,
                 InetAddress localInner,
                 InetAddress remoteInner,
-                InetAddress localOuter,
-                InetAddress remoteOuter,
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
-                int unusedInnerSocketPort,
-                int expectedPacketSize) {
+                int unusedInnerSocketPort) {
             return new IpSecTunnelTestRunnable() {
                 @Override
                 public int run(
-                        Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+                        Network ipsecNetwork,
+                        IpSecTunnelInterface tunnelIface,
+                        TunUtils tunUtils,
+                        IpSecTransform inTunnelTransform,
+                        IpSecTransform outTunnelTransform,
+                        InetAddress localOuter,
+                        InetAddress remoteOuter,
+                        int seqNum)
                         throws Exception {
                     // Build a socket and send traffic
                     JavaUdpSocket socket = new JavaUdpSocket(localInner);
@@ -357,9 +376,14 @@
                     // Verify that an encrypted packet is sent. As of right now, checking encrypted
                     // body is not possible, due to the test not knowing some of the fields of the
                     // inner IP header (flow label, flags, etc)
+                    int innerFamily = localInner instanceof Inet4Address ? AF_INET : AF_INET6;
+                    int outerFamily = localOuter instanceof Inet4Address ? AF_INET : AF_INET6;
+                    boolean useEncap = encapPort != 0;
+                    int expectedPacketSize =
+                            getPacketSize(
+                                    innerFamily, outerFamily, useEncap, transportInTunnelMode);
                     tunUtils.awaitEspPacketNoPlaintext(
-                            spi, TEST_DATA, encapPort != 0, expectedPacketSize);
-
+                            spi, TEST_DATA, useEncap, expectedPacketSize);
                     socket.close();
 
                     return innerSocketPort;
@@ -375,18 +399,22 @@
                 int spi,
                 InetAddress localInner,
                 InetAddress remoteInner,
-                InetAddress localOuter,
-                InetAddress remoteOuter,
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
-                int innerSocketPort,
-                int expectedPacketSize)
+                int innerSocketPort)
                 throws Exception {
             return new IpSecTunnelTestRunnable() {
                 @Override
                 public int run(
-                        Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+                        Network ipsecNetwork,
+                        IpSecTunnelInterface tunnelIface,
+                        TunUtils tunUtils,
+                        IpSecTransform inTunnelTransform,
+                        IpSecTransform outTunnelTransform,
+                        InetAddress localOuter,
+                        InetAddress remoteOuter,
+                        int seqNum)
                         throws Exception {
                     // Build a socket and receive traffic
                     JavaUdpSocket socket = new JavaUdpSocket(localInner, innerSocketPort);
@@ -420,18 +448,22 @@
                 int spi,
                 InetAddress localInner,
                 InetAddress remoteInner,
-                InetAddress localOuter,
-                InetAddress remoteOuter,
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
-                int innerSocketPort,
-                int expectedPacketSize)
+                int innerSocketPort)
                 throws Exception {
             return new IpSecTunnelTestRunnable() {
                 @Override
                 public int run(
-                        Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+                        Network ipsecNetwork,
+                        IpSecTunnelInterface tunnelIface,
+                        TunUtils tunUtils,
+                        IpSecTransform inTunnelTransform,
+                        IpSecTransform outTunnelTransform,
+                        InetAddress localOuter,
+                        InetAddress remoteOuter,
+                        int seqNum)
                         throws Exception {
                     // Build a socket and receive traffic
                     JavaUdpSocket socket = new JavaUdpSocket(localInner);
@@ -456,7 +488,8 @@
                                         remoteOuter,
                                         localOuter,
                                         socket.getPort(),
-                                        encapPort);
+                                        encapPort,
+                                        seqNum);
                     } else {
                         pkt =
                                 getTunnelModePacket(
@@ -466,7 +499,8 @@
                                         remoteOuter,
                                         localOuter,
                                         socket.getPort(),
-                                        encapPort);
+                                        encapPort,
+                                        seqNum);
                     }
                     tunUtils.injectPacket(pkt);
 
@@ -498,17 +532,21 @@
                 int spi,
                 InetAddress localInner,
                 InetAddress remoteInner,
-                InetAddress localOuter,
-                InetAddress remoteOuter,
                 IpSecTransform inTransportTransform,
                 IpSecTransform outTransportTransform,
                 int encapPort,
-                int unusedInnerSocketPort,
-                int expectedPacketSize) {
+                int unusedInnerSocketPort) {
             return new IpSecTunnelTestRunnable() {
                 @Override
                 public int run(
-                        Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+                        Network ipsecNetwork,
+                        IpSecTunnelInterface tunnelIface,
+                        TunUtils tunUtils,
+                        IpSecTransform inTunnelTransform,
+                        IpSecTransform outTunnelTransform,
+                        InetAddress localOuter,
+                        InetAddress remoteOuter,
+                        int seqNum)
                         throws Exception {
                     mTestRunnableFactory
                             .getIpSecTunnelTestRunnable(
@@ -516,15 +554,19 @@
                                     spi,
                                     localInner,
                                     remoteInner,
-                                    localOuter,
-                                    remoteOuter,
                                     inTransportTransform,
                                     outTransportTransform,
                                     encapPort,
-                                    unusedInnerSocketPort,
-                                    expectedPacketSize)
-                            .run(ipsecNetwork, tunnelIface, sTunWrapper.utils);
-
+                                    unusedInnerSocketPort)
+                            .run(
+                                    ipsecNetwork,
+                                    tunnelIface,
+                                    tunUtils,
+                                    inTunnelTransform,
+                                    outTunnelTransform,
+                                    localOuter,
+                                    remoteOuter,
+                                    seqNum);
                     tunnelIface.setUnderlyingNetwork(sTunWrapperNew.network);
 
                     // Verify migrating to IPv4 and IPv6 addresses. It ensures that not only
@@ -623,19 +665,143 @@
                                     spi,
                                     localInner,
                                     remoteInner,
-                                    localOuter,
-                                    remoteOuter,
                                     inTransportTransform,
                                     outTransportTransform,
                                     useEncap ? encapSocket.getPort() : 0,
-                                    0,
-                                    expectedPacketSize)
-                            .run(ipsecNetwork, tunnelIface, tunUtils);
+                                    0)
+                            .run(
+                                    ipsecNetwork,
+                                    tunnelIface,
+                                    tunUtils,
+                                    inTransform,
+                                    outTransform,
+                                    localOuter,
+                                    remoteOuter,
+                                    1 /* seqNum */);
                 }
             }
         }
     }
 
+    private class MigrateTunnelModeIpSecTransformTestRunnableFactory
+            implements IpSecTunnelTestRunnableFactory {
+        private final IpSecTunnelTestRunnableFactory mTestRunnableFactory;
+
+        MigrateTunnelModeIpSecTransformTestRunnableFactory(boolean isOutputTest) {
+            if (isOutputTest) {
+                mTestRunnableFactory = new OutputIpSecTunnelTestRunnableFactory();
+            } else {
+                mTestRunnableFactory = new InputPacketGeneratorIpSecTunnelTestRunnableFactory();
+            }
+        }
+
+        @Override
+        public IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
+                boolean transportInTunnelMode,
+                int spi,
+                InetAddress localInner,
+                InetAddress remoteInner,
+                IpSecTransform inTransportTransform,
+                IpSecTransform outTransportTransform,
+                int encapPort,
+                int unusedInnerSocketPort) {
+            return new IpSecTunnelTestRunnable() {
+                @Override
+                public int run(
+                        Network ipsecNetwork,
+                        IpSecTunnelInterface tunnelIface,
+                        TunUtils tunUtils,
+                        IpSecTransform inTunnelTransform,
+                        IpSecTransform outTunnelTransform,
+                        InetAddress localOuter,
+                        InetAddress remoteOuter,
+                        int seqNum)
+                        throws Exception {
+                    final IpSecTunnelTestRunnable testRunnable =
+                            mTestRunnableFactory.getIpSecTunnelTestRunnable(
+                                    transportInTunnelMode,
+                                    spi,
+                                    localInner,
+                                    remoteInner,
+                                    inTransportTransform,
+                                    outTransportTransform,
+                                    encapPort,
+                                    unusedInnerSocketPort);
+                    testRunnable.run(
+                            ipsecNetwork,
+                            tunnelIface,
+                            tunUtils,
+                            inTunnelTransform,
+                            outTunnelTransform,
+                            localOuter,
+                            remoteOuter,
+                            seqNum++);
+
+                    tunnelIface.setUnderlyingNetwork(sTunWrapperNew.network);
+                    checkMigrateTunnelModeTransform(
+                            testRunnable,
+                            inTunnelTransform,
+                            outTunnelTransform,
+                            tunnelIface,
+                            ipsecNetwork,
+                            sTunWrapperNew.utils,
+                            LOCAL_OUTER_4_NEW,
+                            REMOTE_OUTER_4_NEW,
+                            seqNum++);
+
+                    // Only test migration to IPv6 in non-UDP Encapsulation case
+                    if (encapPort == 0) {
+                        checkMigrateTunnelModeTransform(
+                                testRunnable,
+                                inTunnelTransform,
+                                outTunnelTransform,
+                                tunnelIface,
+                                ipsecNetwork,
+                                sTunWrapperNew.utils,
+                                LOCAL_OUTER_6_NEW,
+                                REMOTE_OUTER_6_NEW,
+                                seqNum++);
+                    }
+
+                    // Unused return value for MigrateTunnelModeIpSecTransformTest
+                    return 0;
+                }
+            };
+        }
+
+        private void checkMigrateTunnelModeTransform(
+                IpSecTunnelTestRunnable testRunnable,
+                IpSecTransform inTunnelTransform,
+                IpSecTransform outTunnelTransform,
+                IpSecTunnelInterface tunnelIface,
+                Network ipsecNetwork,
+                TunUtils tunUtils,
+                InetAddress newLocalOuter,
+                InetAddress newRemoteOuter,
+                int seqNum)
+                throws Exception {
+            mISM.startTunnelModeTransformMigration(
+                    inTunnelTransform, newRemoteOuter, newLocalOuter);
+            mISM.startTunnelModeTransformMigration(
+                    outTunnelTransform, newLocalOuter, newRemoteOuter);
+
+            mISM.applyTunnelModeTransform(
+                    tunnelIface, IpSecManager.DIRECTION_IN, inTunnelTransform);
+            mISM.applyTunnelModeTransform(
+                    tunnelIface, IpSecManager.DIRECTION_OUT, outTunnelTransform);
+
+            testRunnable.run(
+                    ipsecNetwork,
+                    tunnelIface,
+                    tunUtils,
+                    inTunnelTransform,
+                    outTunnelTransform,
+                    newLocalOuter,
+                    newRemoteOuter,
+                    seqNum);
+        }
+    }
+
     private void checkTunnelOutput(
             int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
             throws Exception {
@@ -680,6 +846,28 @@
                 new MigrateIpSecTunnelTestRunnableFactory(false));
     }
 
+    private void checkMigrateTunnelModeTransformOutput(
+            int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+            throws Exception {
+        checkTunnel(
+                innerFamily,
+                outerFamily,
+                useEncap,
+                transportInTunnelMode,
+                new MigrateTunnelModeIpSecTransformTestRunnableFactory(true /* isOutputTest */));
+    }
+
+    private void checkMigrateTunnelModeTransformInput(
+            int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+            throws Exception {
+        checkTunnel(
+                innerFamily,
+                outerFamily,
+                useEncap,
+                transportInTunnelMode,
+                new MigrateTunnelModeIpSecTransformTestRunnableFactory(false /* isOutputTest */));
+    }
+
     /**
      * Validates that the kernel can talk to itself.
      *
@@ -719,22 +907,19 @@
                                     spi,
                                     localInner,
                                     remoteInner,
-                                    localOuter,
-                                    remoteOuter,
                                     inTransportTransform,
                                     outTransportTransform,
                                     useEncap ? encapSocket.getPort() : 0,
-                                    0,
-                                    expectedPacketSize);
+                                    0);
             int innerSocketPort =
                     buildTunnelNetworkAndRunTests(
-                    localInner,
-                    remoteInner,
-                    localOuter,
-                    remoteOuter,
-                    spi,
-                    useEncap ? encapSocket : null,
-                    outputIpSecTunnelTestRunnable);
+                            localInner,
+                            remoteInner,
+                            localOuter,
+                            remoteOuter,
+                            spi,
+                            useEncap ? encapSocket : null,
+                            outputIpSecTunnelTestRunnable);
 
             // Input direction tests, with matching inner socket ports.
             IpSecTunnelTestRunnable inputIpSecTunnelTestRunnable =
@@ -744,13 +929,10 @@
                                     spi,
                                     remoteInner,
                                     localInner,
-                                    localOuter,
-                                    remoteOuter,
                                     inTransportTransform,
                                     outTransportTransform,
                                     useEncap ? encapSocket.getPort() : 0,
-                                    innerSocketPort,
-                                    expectedPacketSize);
+                                    innerSocketPort);
             buildTunnelNetworkAndRunTests(
                     remoteInner,
                     localInner,
@@ -805,13 +987,10 @@
                             spi,
                             localInner,
                             remoteInner,
-                            localOuter,
-                            remoteOuter,
                             inTransportTransform,
                             outTransportTransform,
                             useEncap ? encapSocket.getPort() : 0,
-                            0,
-                            expectedPacketSize));
+                            0));
         }
     }
 
@@ -870,7 +1049,16 @@
                 mISM.applyTunnelModeTransform(
                         tunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
 
-                innerSocketPort = test.run(testNetwork, tunnelIface, sTunWrapper.utils);
+                innerSocketPort =
+                        test.run(
+                                testNetwork,
+                                tunnelIface,
+                                sTunWrapper.utils,
+                                inTransform,
+                                outTransform,
+                                localOuter,
+                                remoteOuter,
+                                1 /* seqNum */);
             }
 
             // Teardown the test network
@@ -909,13 +1097,14 @@
     }
 
     private EspHeader buildTransportModeEspPacket(
-            int spi, InetAddress src, InetAddress dst, int port, Payload payload) throws Exception {
+            int spi, int seqNum, InetAddress src, InetAddress dst, Payload payload)
+            throws Exception {
         IpHeader preEspIpHeader = getIpHeader(payload.getProtocolId(), src, dst, payload);
 
         return new EspHeader(
                 payload.getProtocolId(),
                 spi,
-                1, // sequence number
+                seqNum,
                 CRYPT_KEY, // Same key for auth and crypt
                 payload.getPacketBytes(preEspIpHeader));
     }
@@ -928,13 +1117,14 @@
             InetAddress dstOuter,
             int port,
             int encapPort,
+            int seqNum,
             Payload payload)
             throws Exception {
         IpHeader innerIp = getIpHeader(payload.getProtocolId(), srcInner, dstInner, payload);
         return new EspHeader(
                 innerIp.getProtocolId(),
                 spi,
-                1, // sequence number
+                seqNum, // sequence number
                 CRYPT_KEY, // Same key for auth and crypt
                 innerIp.getPacketBytes());
     }
@@ -958,13 +1148,14 @@
             InetAddress srcOuter,
             InetAddress dstOuter,
             int port,
-            int encapPort)
+            int encapPort,
+            int seqNum)
             throws Exception {
         UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
 
         EspHeader espPayload =
                 buildTunnelModeEspPacket(
-                        spi, srcInner, dstInner, srcOuter, dstOuter, port, encapPort, udp);
+                        spi, srcInner, dstInner, srcOuter, dstOuter, port, encapPort, seqNum, udp);
         return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
     }
 
@@ -976,11 +1167,13 @@
             InetAddress srcOuter,
             InetAddress dstOuter,
             int port,
-            int encapPort)
+            int encapPort,
+            int seqNum)
             throws Exception {
         UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
 
-        EspHeader espPayload = buildTransportModeEspPacket(spiInner, srcInner, dstInner, port, udp);
+        EspHeader espPayload =
+                buildTransportModeEspPacket(spiInner, seqNum, srcInner, dstInner, udp);
         espPayload =
                 buildTunnelModeEspPacket(
                         spiOuter,
@@ -990,6 +1183,7 @@
                         dstOuter,
                         port,
                         encapPort,
+                        seqNum,
                         espPayload);
         return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
     }
@@ -998,13 +1192,19 @@
             int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
             throws Exception {
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
-        checkTunnelOutput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
-        checkTunnelInput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+        checkMigrateTunnelOutput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+        checkMigrateTunnelInput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
     }
 
-    /** Checks if FEATURE_IPSEC_TUNNEL_MIGRATION is enabled on the device */
-    private static boolean hasIpsecTunnelMigrateFeature() {
-        return sContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION);
+    private void doTestMigrateTunnelModeTransform(
+            int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+            throws Exception {
+        assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+        assumeTrue(mCtsNetUtils.hasIpsecTunnelMigrateFeature());
+        checkMigrateTunnelModeTransformOutput(
+                innerFamily, outerFamily, useEncap, transportInTunnelMode);
+        checkMigrateTunnelModeTransformInput(
+                innerFamily, outerFamily, useEncap, transportInTunnelMode);
     }
 
     @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
@@ -1012,28 +1212,7 @@
     public void testHasIpSecTunnelMigrateFeature() throws Exception {
         // FEATURE_IPSEC_TUNNEL_MIGRATION is required when VSR API is U/U+
         if (getVsrApiLevel() > Build.VERSION_CODES.TIRAMISU) {
-            assertTrue(hasIpsecTunnelMigrateFeature());
-        }
-    }
-
-    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
-    @Test
-    public void testMigrateTunnelModeTransform() throws Exception {
-        assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
-        assumeTrue(hasIpsecTunnelMigrateFeature());
-
-        IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
-        transformBuilder.setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
-        transformBuilder.setAuthentication(
-                new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
-        int spi = getRandomSpi(LOCAL_OUTER_4, REMOTE_OUTER_4);
-
-        try (IpSecManager.SecurityParameterIndex outSpi =
-                        mISM.allocateSecurityParameterIndex(REMOTE_OUTER_4, spi);
-                IpSecTransform outTunnelTransform =
-                        transformBuilder.buildTunnelModeTransform(LOCAL_INNER_4, outSpi)) {
-            mISM.startTunnelModeTransformMigration(
-                    outTunnelTransform, LOCAL_OUTER_4_NEW, REMOTE_OUTER_4_NEW);
+            assertTrue(mCtsNetUtils.hasIpsecTunnelMigrateFeature());
         }
     }
 
@@ -1266,4 +1445,76 @@
         assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
         checkTunnelReflected(AF_INET6, AF_INET6, false, false);
     }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV4InV4() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET, false, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV6InV4() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET6, AF_INET, false, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV4InV6() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET6, false, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV6InV6() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET6, false, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV4InV4UdpEncap() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET, true, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTransportInTunnelModeV6InV4UdpEncap() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET6, AF_INET, true, true);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV4InV4() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET, false, false);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV6InV4() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET6, AF_INET, false, false);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV4InV6() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET6, false, false);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV6InV6() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET6, AF_INET6, false, false);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV4InV4UdpEncap() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET, AF_INET, true, false);
+    }
+
+    @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    public void testMigrateTransformTunnelV6InV4UdpEncap() throws Exception {
+        doTestMigrateTunnelModeTransform(AF_INET6, AF_INET, true, false);
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/TunUtils.java b/tests/cts/net/src/android/net/cts/TunUtils.java
index 0377160..268d8d2 100644
--- a/tests/cts/net/src/android/net/cts/TunUtils.java
+++ b/tests/cts/net/src/android/net/cts/TunUtils.java
@@ -22,7 +22,6 @@
 import static android.net.cts.PacketUtils.UDP_HDRLEN;
 import static android.system.OsConstants.IPPROTO_UDP;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.os.ParcelFileDescriptor;
@@ -32,6 +31,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -140,10 +140,8 @@
     public byte[] awaitEspPacketNoPlaintext(
             int spi, byte[] plaintext, boolean useEncap, int expectedPacketSize) throws Exception {
         final byte[] espPkt = awaitPacket(
-                (pkt) -> isEspFailIfSpecifiedPlaintextFound(pkt, spi, useEncap, plaintext));
-
-        // Validate packet size
-        assertEquals(expectedPacketSize, espPkt.length);
+            (pkt) -> expectedPacketSize == pkt.length
+                    && isEspFailIfSpecifiedPlaintextFound(pkt, spi, useEncap, plaintext));
 
         return espPkt; // We've found the packet we're looking for.
     }
@@ -153,11 +151,11 @@
     }
 
     private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
-        // Check SPI byte by byte.
-        return pkt[espOffset] == (byte) ((spi >>> 24) & 0xff)
-                && pkt[espOffset + 1] == (byte) ((spi >>> 16) & 0xff)
-                && pkt[espOffset + 2] == (byte) ((spi >>> 8) & 0xff)
-                && pkt[espOffset + 3] == (byte) (spi & 0xff);
+        ByteBuffer buffer = ByteBuffer.wrap(pkt);
+        buffer.get(new byte[espOffset]); // Skip IP, UDP header
+        int actualSpi = buffer.getInt();
+
+        return actualSpi == spi;
     }
 
     /**
@@ -180,8 +178,13 @@
 
     private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
         if (isIpv6(pkt)) {
-            // IPv6 UDP encap not supported by kernels; assume non-encap.
-            return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+            if (encap) {
+                return pkt[IP6_PROTO_OFFSET] == IPPROTO_UDP
+                        && isSpiEqual(pkt, IP6_HDRLEN + UDP_HDRLEN, spi);
+            } else {
+                return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+            }
+
         } else {
             // Use default IPv4 header length (assuming no options)
             if (encap) {
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index df3a4aa..d817630 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -75,6 +75,13 @@
 
 public final class CtsNetUtils {
     private static final String TAG = CtsNetUtils.class.getSimpleName();
+
+    // Redefine this flag here so that IPsec code shipped in a mainline module can build on old
+    // platforms before FEATURE_IPSEC_TUNNEL_MIGRATION API is released.
+    // TODO: b/275378783 Remove this flag and use the platform API when it is available.
+    private static final String FEATURE_IPSEC_TUNNEL_MIGRATION =
+            "android.software.ipsec_tunnel_migration";
+
     private static final int SOCKET_TIMEOUT_MS = 2000;
     private static final int PRIVATE_DNS_PROBE_MS = 1_000;
 
@@ -115,6 +122,11 @@
                 || getFirstApiLevel() >= Build.VERSION_CODES.Q;
     }
 
+    /** Checks if FEATURE_IPSEC_TUNNEL_MIGRATION is enabled on the device */
+    public boolean hasIpsecTunnelMigrateFeature() {
+        return mContext.getPackageManager().hasSystemFeature(FEATURE_IPSEC_TUNNEL_MIGRATION);
+    }
+
     /**
      * Sets the given appop using shell commands
      *
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 6c89c38..1cc0c89 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -3817,13 +3817,13 @@
 
     @Test
     public void testExplicitlySelected() throws Exception {
-        NetworkRequest request = new NetworkRequest.Builder()
+        final NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
                 .build();
-        TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerNetworkCallback(request, callback);
 
-        // Bring up validated cell.
+        // Bring up validated cell
         mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellAgent.connect(true);
         callback.expectAvailableThenValidatedCallbacks(mCellAgent);
@@ -3881,6 +3881,12 @@
         assertEquals(mWiFiAgent.getNetwork(), mCm.getActiveNetwork());
         expectUnvalidationCheckWillNotNotify(mWiFiAgent);
 
+        // Now request cell so it doesn't disconnect during the test
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .clearCapabilities().addTransportType(TRANSPORT_CELLULAR).build();
+        final TestNetworkCallback cellCallback = new TestNetworkCallback();
+        mCm.requestNetwork(cellRequest, cellCallback);
+
         mEthernetAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
         mEthernetAgent.connect(true);
         callback.expectAvailableCallbacksUnvalidated(mEthernetAgent);
@@ -3922,6 +3928,7 @@
 
         callback.expect(LOST, mWiFiAgent);
         callback.expect(LOST, mCellAgent);
+        mCm.unregisterNetworkCallback(cellCallback);
     }
 
     private void doTestFirstEvaluation(