Merge changes Iaafb2f9a,I90777d4c

* changes:
  Retry IKE INIT if receiving Notify-Cookie
  Support Cookie notification type
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 71d96ff..e35976b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,11 +2,17 @@
   "presubmit": [
     {
       "name": "FrameworksIkeTests"
+    },
+    {
+      "name": "CtsIkeTestCases"
     }
   ],
   "postsubmit": [
     {
       "name": "FrameworksIkeTests"
+    },
+    {
+      "name": "CtsIkeTestCases"
     }
   ]
 }
\ No newline at end of file
diff --git a/api/current.txt b/api/current.txt
index 6a1d8a1..b150294 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -63,6 +63,7 @@
     method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
     method public void onIpSecTransformCreated(@NonNull android.net.IpSecTransform, int);
     method public void onIpSecTransformDeleted(@NonNull android.net.IpSecTransform, int);
+    method public default void onIpSecTransformsMigrated(@NonNull android.net.IpSecTransform, @NonNull android.net.IpSecTransform);
     method public void onOpened(@NonNull android.net.ipsec.ike.ChildSessionConfiguration);
   }
 
@@ -136,11 +137,14 @@
     method public void finalize();
     method public void kill();
     method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
+    method public void setNetwork(@NonNull android.net.Network);
   }
 
   public interface IkeSessionCallback {
     method public void onClosed();
     method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
+    method public default void onError(@NonNull android.net.ipsec.ike.exceptions.IkeException);
+    method public default void onIkeSessionConnectionInfoChanged(@NonNull android.net.ipsec.ike.IkeSessionConnectionInfo);
     method public void onOpened(@NonNull android.net.ipsec.ike.IkeSessionConfiguration);
   }
 
@@ -176,6 +180,7 @@
     method public boolean hasIkeOption(int);
     field public static final int IKE_OPTION_ACCEPT_ANY_REMOTE_ID = 0; // 0x0
     field public static final int IKE_OPTION_EAP_ONLY_AUTH = 1; // 0x1
+    field public static final int IKE_OPTION_MOBIKE = 2; // 0x2
   }
 
   public static final class IkeSessionParams.Builder {
@@ -348,11 +353,19 @@
   public abstract class IkeException extends java.lang.Exception {
   }
 
-  public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeException {
+  public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeNonProtocolException {
     ctor public IkeInternalException(@NonNull Throwable);
     ctor public IkeInternalException(@NonNull String, @NonNull Throwable);
   }
 
+  public final class IkeNetworkLostException extends android.net.ipsec.ike.exceptions.IkeNonProtocolException {
+    ctor public IkeNetworkLostException(@NonNull android.net.Network);
+    method @NonNull public android.net.Network getNetwork();
+  }
+
+  public abstract class IkeNonProtocolException extends android.net.ipsec.ike.exceptions.IkeException {
+  }
+
   public abstract class IkeProtocolException extends android.net.ipsec.ike.exceptions.IkeException {
     method public int getErrorType();
     field public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; // 0x18
diff --git a/api/system-current.txt b/api/system-current.txt
index 684c496..fd1f09e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15,7 +15,7 @@
 package android.net.ipsec.ike {
 
   public interface IkeSessionCallback {
-    method public void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException);
+    method @Deprecated public default void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException);
   }
 
   public final class IkeSessionParams {
diff --git a/src/java/android/net/ipsec/ike/ChildSessionCallback.java b/src/java/android/net/ipsec/ike/ChildSessionCallback.java
index 9b67a00..1dd4530 100644
--- a/src/java/android/net/ipsec/ike/ChildSessionCallback.java
+++ b/src/java/android/net/ipsec/ike/ChildSessionCallback.java
@@ -108,7 +108,6 @@
      *     {@link IpSecManager#DIRECTION_IN}
      * @param outIpSecTransform IpSecTransform to be used for traffic with {@link PolicyDirection}
      *     {@link IpSecManager#DIRECTION_OUT}
-     * @hide
      */
     default void onIpSecTransformsMigrated(
             @NonNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform) {}
diff --git a/src/java/android/net/ipsec/ike/IkeSession.java b/src/java/android/net/ipsec/ike/IkeSession.java
index cab99cc..5dab372 100644
--- a/src/java/android/net/ipsec/ike/IkeSession.java
+++ b/src/java/android/net/ipsec/ike/IkeSession.java
@@ -296,7 +296,6 @@
      * @param network the Network to use for this IkeSession
      * @throws IllegalStateException if MOBIKE is not configured in IkeSessionParams, MOBIKE is not
      *     active for this IkeSession, or if the Network was not specified in IkeSessionParams.
-     * @hide
      */
     public void setNetwork(@NonNull Network network) {
         mIkeSessionStateMachine.setNetwork(network);
diff --git a/src/java/android/net/ipsec/ike/IkeSessionCallback.java b/src/java/android/net/ipsec/ike/IkeSessionCallback.java
index 21fac18..9468011 100644
--- a/src/java/android/net/ipsec/ike/IkeSessionCallback.java
+++ b/src/java/android/net/ipsec/ike/IkeSessionCallback.java
@@ -67,12 +67,13 @@
      * INVALID_MESSAGE_ID.
      *
      * @param exception the detailed error information.
+     * @deprecated Implementers should override {@link #onError(IkeException)} to handle {@link
+     *     IkeProtocolException}s instead of using this method.
      * @hide
      */
-    // TODO: b/158033037 Deprecate this method and add a public API that takes an IkeException when
-    // exposing MOBIKE APIs
     @SystemApi
-    void onError(@NonNull IkeProtocolException exception);
+    @Deprecated
+    default void onError(@NonNull IkeProtocolException exception) {}
 
     /**
      * Called if a recoverable error is encountered in an established {@link IkeSession}.
@@ -81,7 +82,6 @@
      * non-protocol errors such as the underlying {@link android.net.Network} dying.
      *
      * @param exception the detailed error information.
-     * @hide
      */
     default void onError(@NonNull IkeException exception) {
         if (exception instanceof IkeProtocolException) {
@@ -108,7 +108,6 @@
      * </ul>
      *
      * @param connectionInfo the updated IkeSessionConnectionInfo for the Session.
-     * @hide
      */
     default void onIkeSessionConnectionInfoChanged(
             @NonNull IkeSessionConnectionInfo connectionInfo) {}
diff --git a/src/java/android/net/ipsec/ike/IkeSessionParams.java b/src/java/android/net/ipsec/ike/IkeSessionParams.java
index b093219..d2482e3 100644
--- a/src/java/android/net/ipsec/ike/IkeSessionParams.java
+++ b/src/java/android/net/ipsec/ike/IkeSessionParams.java
@@ -117,27 +117,39 @@
     /**
      * If set, the IKE library will attempt to enable MOBIKE for the resulting IKE Session.
      *
+     * <p>To support MOBIKE, callers must implement:
+     *
+     * <ul>
+     *   <li>{@link IkeSessionCallback#onIkeSessionConnectionInfoChanged(IkeSessionConnectionInfo)}:
+     *       this MUST migrate all IpSecTunnelInterface instances associated with this IkeSession.
+     *   <li>{@link ChildSessionCallback#onIpSecTransformsMigrated(android.net.IpSecTransform,
+     *       android.net.IpSecTransform)}: this MUST re-apply the migrated transforms to the
+     *       IpSecTunnelInterface associated with this ChildSessionCallback, via {@link
+     *       android.net.IpSecManager#applyTunnelModeTransform(
+     *       android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform)}.
+     * </ul>
+     *
      * <p>MOBIKE support is compatible with two Network modes:
      *
      * <ul>
      *   <li><b>Caller managed:</b> The caller controls the underlying Network for the IKE Session
      *       at all times. The IKE Session will only change underlying Networks if the caller
      *       initiates it through {@link IkeSession#setNetwork(Network)}. If the caller-specified
-     *       Network dies, they will be notified via {@link
+     *       Network is lost, they will be notified via {@link
      *       IkeSessionCallback#onError(android.net.ipsec.ike.exceptions.IkeException)} with an
-     *       {@link android.net.ipsec.ike.exceptions.IkeNetworkDiedException} specifying the Network
-     *       that died.
-     *   <li><b>Platform Default:</b> The IKE Session will always track the Platform default
-     *       Network. The IKE Session will start on the Platform default Network, and any subsequent
-     *       changes to the default Network (after the IKE_AUTH exchange completes) will cause the
-     *       IKE Session's underlying Network to change. If the default Network dies with no
-     *       replacements, the caller will be notified via {@link
+     *       {@link android.net.ipsec.ike.exceptions.IkeNetworkLostException} specifying the Network
+     *       that was lost.
+     *   <li><b>Platform Default:</b> The IKE Session will always track the application default
+     *       Network. The IKE Session will start on the application default Network, and any
+     *       subsequent changes to the default Network (after the IKE_AUTH exchange completes) will
+     *       cause the IKE Session's underlying Network to change. If the default Network is lost
+     *       with no replacements, the caller will be notified via {@link
      *       IkeSessionCallback#onError(android.net.ipsec.ike.exceptions.IkeException)} with an
-     *       {@link android.net.ipsec.ike.exceptions.IkeNetworkDiedException}. The caller can either
+     *       {@link android.net.ipsec.ike.exceptions.IkeNetworkLostException}. The caller can either
      *       wait until for a new default Network to become available or they may close the Session
      *       manually via {@link IkeSession#close()}. Note that the IKE Session's maximum
      *       retransmissions may expire while waiting for a new default Network, in which case the
-     *       Session will automatically close
+     *       Session will automatically close.
      * </ul>
      *
      * <p>Use of MOBIKE in the IKE Session requires the peer to also support MOBIKE.
@@ -147,9 +159,8 @@
      *
      * <p>Checking for MOBIKE use in an IKE Session is done via {@link
      * IkeSessionConfiguration#isIkeExtensionEnabled(int)}.
-     *
-     * @hide
      */
+    // TODO(b/175416035): update docs to @link to API for migrating IpSecTunnelInterfaces
     public static final int IKE_OPTION_MOBIKE = 2;
 
     private static final int MIN_IKE_OPTION = IKE_OPTION_ACCEPT_ANY_REMOTE_ID;
diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java b/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java
index aaa69e4..37f7293 100644
--- a/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java
+++ b/src/java/android/net/ipsec/ike/exceptions/IkeInternalException.java
@@ -23,7 +23,7 @@
  * <p>Causes may include exceptions such as {@link IpSecManager.SpiUnavailableException} when the
  * requested SPI resources failed to be allocated.
  */
-public final class IkeInternalException extends IkeException {
+public final class IkeInternalException extends IkeNonProtocolException {
     /**
      * Constructs a new exception with the specified cause.
      *
diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeNetworkDiedException.java b/src/java/android/net/ipsec/ike/exceptions/IkeNetworkLostException.java
similarity index 75%
rename from src/java/android/net/ipsec/ike/exceptions/IkeNetworkDiedException.java
rename to src/java/android/net/ipsec/ike/exceptions/IkeNetworkLostException.java
index 5fdae70..d638502 100644
--- a/src/java/android/net/ipsec/ike/exceptions/IkeNetworkDiedException.java
+++ b/src/java/android/net/ipsec/ike/exceptions/IkeNetworkLostException.java
@@ -16,15 +16,20 @@
 
 package android.net.ipsec.ike.exceptions;
 
+import android.annotation.NonNull;
 import android.net.Network;
 import android.net.ipsec.ike.IkeSessionCallback;
 
 import java.util.Objects;
 
 /**
- * IkeNetworkDiedException is returned to the caller via {@link
+ * IkeNetworkLostException is returned to the caller via {@link
  * IkeSessionCallback#onError(IkeException)} if the underlying Network for the {@link IkeSession}
- * dies with no alternatives.
+ * was lost with no alternatives.
+ *
+ * <p>This Exception corresponds to {@link
+ * android.net.ConnectivityManager.NetworkCallback#onLost(android.net.Network)} being invoked for
+ * the specified underlying Network.
  *
  * <p>When the caller receives this Exception, they must either:
  *
@@ -40,21 +45,20 @@
  *       </ul>
  *   <li>close the corresponding IkeSession.
  * </ul>
- *
- * @hide
  */
-public final class IkeNetworkDiedException extends IkeNonProtocolException {
+public final class IkeNetworkLostException extends IkeNonProtocolException {
     private final Network mNetwork;
 
-    /** Constructs an IkeNetworkDiedException to indicate the specified Network died. */
-    public IkeNetworkDiedException(Network network) {
+    /** Constructs an IkeNetworkLostException to indicate the specified Network was lost. */
+    public IkeNetworkLostException(@NonNull Network network) {
         super();
         Objects.requireNonNull(network, "network is null");
 
         mNetwork = network;
     }
 
-    /** Returns the IkeSession's underlying Network that died. */
+    /** Returns the IkeSession's underlying Network that was lost. */
+    @NonNull
     public Network getNetwork() {
         return mNetwork;
     }
diff --git a/src/java/android/net/ipsec/ike/exceptions/IkeNonProtocolException.java b/src/java/android/net/ipsec/ike/exceptions/IkeNonProtocolException.java
index d8a3162..f005504 100644
--- a/src/java/android/net/ipsec/ike/exceptions/IkeNonProtocolException.java
+++ b/src/java/android/net/ipsec/ike/exceptions/IkeNonProtocolException.java
@@ -18,8 +18,6 @@
 
 /**
  * IkeNonProtocolException encapsulates all implementation-specific non-protocol IKE errors.
- *
- * @hide
  */
 public abstract class IkeNonProtocolException extends IkeException {
     /** @hide */
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 6deac8d..c3c648d 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -69,6 +69,7 @@
 import android.net.IpSecManager.ResourceUnavailableException;
 import android.net.IpSecManager.SpiUnavailableException;
 import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
@@ -87,7 +88,7 @@
 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
-import android.net.ipsec.ike.exceptions.IkeNetworkDiedException;
+import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.net.ipsec.ike.exceptions.InvalidKeException;
 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
@@ -162,6 +163,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
@@ -435,6 +437,11 @@
     /** Local port assigned on device. Initialized in Initial State. */
     @VisibleForTesting int mLocalPort;
 
+    /** Available remote addresses that are v4. Resolved in Initial State. */
+    @VisibleForTesting final List<Inet4Address> mRemoteAddressesV4 = new ArrayList<>();
+    /** Available remote addresses that are v6. Resolved in Initial State. */
+    @VisibleForTesting final List<Inet6Address> mRemoteAddressesV6 = new ArrayList<>();
+
     /** Indicates if both sides support NAT traversal. Set in IKE INIT. */
     @VisibleForTesting boolean mSupportNatTraversal;
     /** Indicates if local node is behind a NAT. */
@@ -1204,9 +1211,21 @@
         @Override
         public void enterState() {
             try {
-                // TODO(b/149954916): Do DNS resolution asynchronously and support resolving
-                // multiple addresses.
-                mRemoteAddress = mNetwork.getByName(mIkeSessionParams.getServerHostname());
+                // TODO(b/149954916): Do DNS resolution asynchronously
+                InetAddress[] allRemoteAddresses =
+                        mNetwork.getAllByName(mIkeSessionParams.getServerHostname());
+
+                logd("Resolved addresses for peer: " + Arrays.toString(allRemoteAddresses));
+
+                for (InetAddress remoteAddress : allRemoteAddresses) {
+                    if (remoteAddress instanceof Inet4Address) {
+                        mRemoteAddressesV4.add((Inet4Address) remoteAddress);
+                    } else {
+                        mRemoteAddressesV6.add((Inet6Address) remoteAddress);
+                    }
+                }
+
+                setRemoteAddress();
 
                 boolean isIpv4 = mRemoteAddress instanceof Inet4Address;
                 if (isIpv4) {
@@ -1244,6 +1263,33 @@
     }
 
     /**
+     * Set the remote address for the peer.
+     *
+     * <p>Prefers IPv6 addresses if:
+     *
+     * <ul>
+     *   <li>an IPv6 address is known for the peer, and
+     *   <li>the current underlying Network has a global (non-link local) IPv6 address available
+     * </ul>
+     *
+     * Otherwise, an IPv4 address will be used.
+     */
+    private void setRemoteAddress() {
+        LinkProperties linkProperties = mConnectivityManager.getLinkProperties(mNetwork);
+        if (!mRemoteAddressesV6.isEmpty() && linkProperties.hasGlobalIpv6Address()) {
+            // TODO(b/175348096): randomly choose from available addresses
+            mRemoteAddress = mRemoteAddressesV6.get(0);
+        } else {
+            if (mRemoteAddressesV4.isEmpty()) {
+                throw new IllegalArgumentException("No valid IPv4 or IPv6 addresses for peer");
+            }
+
+            // TODO(b/175348096): randomly choose from available addresses
+            mRemoteAddress = mRemoteAddressesV4.get(0);
+        }
+    }
+
+    /**
      * Idle represents a state when there is no ongoing IKE exchange affecting established IKE SA.
      */
     class Idle extends LocalRequestQueuer {
@@ -5469,11 +5515,13 @@
             throw new IllegalStateException("MOBIKE must be enabled to update the Network");
         }
 
-        // TODO(b/172060298): prefer IPv6 once Responder addresses are cached
-        boolean isIpv4 = mRemoteAddress instanceof Inet4Address;
         Network oldNetwork = mNetwork;
         mNetwork = network;
 
+        setRemoteAddress();
+
+        boolean isIpv4 = mRemoteAddress instanceof Inet4Address;
+
         try {
             // Only switch the IkeSocket if the underlying Network actually changes. This may not
             // always happen (ex: the underlying Network loses the current local address)
@@ -5529,6 +5577,6 @@
     @Override
     public void onUnderlyingNetworkDied() {
         executeUserCallback(
-                () -> mIkeSessionCallback.onError(new IkeNetworkDiedException(mNetwork)));
+                () -> mIkeSessionCallback.onError(new IkeNetworkLostException(mNetwork)));
     }
 }
diff --git a/src/java/com/android/internal/net/ipsec/ike/net/IkeDefaultNetworkCallback.java b/src/java/com/android/internal/net/ipsec/ike/net/IkeDefaultNetworkCallback.java
index f3c7fa0..fa4c6bf 100644
--- a/src/java/com/android/internal/net/ipsec/ike/net/IkeDefaultNetworkCallback.java
+++ b/src/java/com/android/internal/net/ipsec/ike/net/IkeDefaultNetworkCallback.java
@@ -21,7 +21,7 @@
 import java.net.InetAddress;
 
 /**
- * IkeDefaultNetworkCallback is a network callback used to track the platform's default network.
+ * IkeDefaultNetworkCallback is a network callback used to track the application default Network.
  *
  * <p>This NetworkCallback will notify IkeSessionStateMachine if:
  *
@@ -47,7 +47,7 @@
             return;
         }
 
-        logd("Platform default Network changed to " + network);
+        logd("Application default Network changed to " + network);
         mIkeNetworkUpdater.onUnderlyingNetworkUpdated(network);
     }
 }
diff --git a/tests/cts/src/android/ipsec/ike/cts/SaProposalTest.java b/tests/cts/src/android/ipsec/ike/cts/SaProposalTest.java
index e58a3fe..0b8bfca 100644
--- a/tests/cts/src/android/ipsec/ike/cts/SaProposalTest.java
+++ b/tests/cts/src/android/ipsec/ike/cts/SaProposalTest.java
@@ -59,28 +59,54 @@
 
 @RunWith(AndroidJUnit4.class)
 public class SaProposalTest {
-    private static final List<Pair<Integer, Integer>> NORMAL_MODE_CIPHERS = new ArrayList<>();
-    private static final List<Pair<Integer, Integer>> COMBINED_MODE_CIPHERS = new ArrayList<>();
-    private static final List<Integer> INTEGRITY_ALGOS = new ArrayList<>();
+    private static final List<Pair<Integer, Integer>> IKE_NORMAL_MODE_CIPHERS = new ArrayList<>();
+    private static final List<Pair<Integer, Integer>> IKE_COMBINED_MODE_CIPHERS = new ArrayList<>();
+    private static final List<Integer> IKE_INTEGRITY_ALGOS = new ArrayList<>();
+    private static final List<Pair<Integer, Integer>> CHILD_NORMAL_MODE_CIPHERS = new ArrayList<>();
+    private static final List<Pair<Integer, Integer>> CHILD_COMBINED_MODE_CIPHERS =
+            new ArrayList<>();
+    private static final List<Integer> CHILD_INTEGRITY_ALGOS = new ArrayList<>();
     private static final List<Integer> DH_GROUPS = new ArrayList<>();
     private static final List<Integer> DH_GROUPS_WITH_NONE = new ArrayList<>();
     private static final List<Integer> PRFS = new ArrayList<>();
 
     static {
-        NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED));
-        NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128));
-        NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192));
-        NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256));
+        IKE_NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED));
+        IKE_NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128));
+        IKE_NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192));
+        IKE_NORMAL_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256));
 
-        COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128));
-        COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192));
-        COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256));
+        for (Pair<Integer, Integer> pair : IKE_NORMAL_MODE_CIPHERS) {
+            // TODO: b/1522448 Check against ChildSaProposal#getSupportedEncryptionAlgorithms
+            // when it is exposed
+            if (pair.first != ENCRYPTION_ALGORITHM_3DES) {
+                CHILD_NORMAL_MODE_CIPHERS.add(pair);
+            }
+        }
 
-        INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
-        INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_AES_XCBC_96);
-        INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
-        INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
-        INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+        IKE_COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128));
+        IKE_COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192));
+        IKE_COMBINED_MODE_CIPHERS.add(new Pair<>(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256));
+
+        for (Pair<Integer, Integer> pair : IKE_COMBINED_MODE_CIPHERS) {
+            // TODO: b/1522448 Add ChaChaPoly in IKE_COMBINED_MODE_CIPHERS and check against
+            // ChildSaProposal#getSupportedEncryptionAlgorithms when it is exposed
+            CHILD_COMBINED_MODE_CIPHERS.add(pair);
+        }
+
+        IKE_INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+        IKE_INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_AES_XCBC_96);
+        IKE_INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+        IKE_INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+        IKE_INTEGRITY_ALGOS.add(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+
+        for (Integer algo : IKE_INTEGRITY_ALGOS) {
+            // TODO: b/1522448 Check against ChildSaProposal#getSupportedIntegrityAlgorithms
+            // when it is exposed
+            if (algo != INTEGRITY_ALGORITHM_AES_XCBC_96) {
+                CHILD_INTEGRITY_ALGOS.add(algo);
+            }
+        }
 
         DH_GROUPS.add(DH_GROUP_1024_BIT_MODP);
         DH_GROUPS.add(DH_GROUP_2048_BIT_MODP);
@@ -97,7 +123,7 @@
 
     // Package private
     static IkeSaProposal buildIkeSaProposalWithNormalModeCipher() {
-        return buildIkeSaProposal(NORMAL_MODE_CIPHERS, INTEGRITY_ALGOS, PRFS, DH_GROUPS);
+        return buildIkeSaProposal(IKE_NORMAL_MODE_CIPHERS, IKE_INTEGRITY_ALGOS, PRFS, DH_GROUPS);
     }
 
     // Package private
@@ -111,7 +137,7 @@
         if (hasIntegrityNone) {
             integerAlgos.add(INTEGRITY_ALGORITHM_NONE);
         }
-        return buildIkeSaProposal(COMBINED_MODE_CIPHERS, integerAlgos, PRFS, DH_GROUPS);
+        return buildIkeSaProposal(IKE_COMBINED_MODE_CIPHERS, integerAlgos, PRFS, DH_GROUPS);
     }
 
     private static IkeSaProposal buildIkeSaProposal(
@@ -139,7 +165,8 @@
 
     // Package private
     static ChildSaProposal buildChildSaProposalWithNormalModeCipher() {
-        return buildChildSaProposal(NORMAL_MODE_CIPHERS, INTEGRITY_ALGOS, DH_GROUPS_WITH_NONE);
+        return buildChildSaProposal(
+                CHILD_NORMAL_MODE_CIPHERS, CHILD_INTEGRITY_ALGOS, DH_GROUPS_WITH_NONE);
     }
 
     // Package private
@@ -154,7 +181,7 @@
             integerAlgos.add(INTEGRITY_ALGORITHM_NONE);
         }
 
-        return buildChildSaProposal(COMBINED_MODE_CIPHERS, integerAlgos, DH_GROUPS_WITH_NONE);
+        return buildChildSaProposal(CHILD_COMBINED_MODE_CIPHERS, integerAlgos, DH_GROUPS_WITH_NONE);
     }
 
     private static ChildSaProposal buildChildSaProposal(
@@ -179,15 +206,15 @@
     // Package private
     static ChildSaProposal buildChildSaProposalWithOnlyCiphers() {
         return buildChildSaProposal(
-                COMBINED_MODE_CIPHERS, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
+                CHILD_COMBINED_MODE_CIPHERS, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
     }
 
     @Test
     public void testBuildIkeSaProposalWithNormalModeCipher() {
         IkeSaProposal saProposal = buildIkeSaProposalWithNormalModeCipher();
 
-        assertEquals(NORMAL_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
-        assertEquals(INTEGRITY_ALGOS, saProposal.getIntegrityAlgorithms());
+        assertEquals(IKE_NORMAL_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(IKE_INTEGRITY_ALGOS, saProposal.getIntegrityAlgorithms());
         assertEquals(PRFS, saProposal.getPseudorandomFunctions());
         assertEquals(DH_GROUPS, saProposal.getDhGroups());
     }
@@ -197,7 +224,7 @@
         IkeSaProposal saProposal =
                 buildIkeSaProposalWithCombinedModeCipher(false /* hasIntegrityNone */);
 
-        assertEquals(COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(IKE_COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
         assertEquals(PRFS, saProposal.getPseudorandomFunctions());
         assertEquals(DH_GROUPS, saProposal.getDhGroups());
         assertTrue(saProposal.getIntegrityAlgorithms().isEmpty());
@@ -208,7 +235,7 @@
         IkeSaProposal saProposal =
                 buildIkeSaProposalWithCombinedModeCipher(true /* hasIntegrityNone */);
 
-        assertEquals(COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(IKE_COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
         assertEquals(PRFS, saProposal.getPseudorandomFunctions());
         assertEquals(DH_GROUPS, saProposal.getDhGroups());
         assertEquals(Arrays.asList(INTEGRITY_ALGORITHM_NONE), saProposal.getIntegrityAlgorithms());
@@ -218,8 +245,8 @@
     public void testBuildChildSaProposalWithNormalModeCipher() {
         ChildSaProposal saProposal = buildChildSaProposalWithNormalModeCipher();
 
-        assertEquals(NORMAL_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
-        assertEquals(INTEGRITY_ALGOS, saProposal.getIntegrityAlgorithms());
+        assertEquals(CHILD_NORMAL_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(CHILD_INTEGRITY_ALGOS, saProposal.getIntegrityAlgorithms());
         assertEquals(DH_GROUPS_WITH_NONE, saProposal.getDhGroups());
     }
 
@@ -228,7 +255,7 @@
         ChildSaProposal saProposal =
                 buildChildSaProposalWithCombinedModeCipher(false /* hasIntegrityNone */);
 
-        assertEquals(COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(CHILD_COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
         assertTrue(saProposal.getIntegrityAlgorithms().isEmpty());
         assertEquals(DH_GROUPS_WITH_NONE, saProposal.getDhGroups());
     }
@@ -238,7 +265,7 @@
         ChildSaProposal saProposal =
                 buildChildSaProposalWithCombinedModeCipher(true /* hasIntegrityNone */);
 
-        assertEquals(COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(CHILD_COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
         assertEquals(Arrays.asList(INTEGRITY_ALGORITHM_NONE), saProposal.getIntegrityAlgorithms());
         assertEquals(DH_GROUPS_WITH_NONE, saProposal.getDhGroups());
     }
@@ -247,7 +274,7 @@
     public void testBuildChildSaProposalWithOnlyCiphers() {
         ChildSaProposal saProposal = buildChildSaProposalWithOnlyCiphers();
 
-        assertEquals(COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
+        assertEquals(CHILD_COMBINED_MODE_CIPHERS, saProposal.getEncryptionAlgorithms());
         assertTrue(saProposal.getIntegrityAlgorithms().isEmpty());
         assertTrue(saProposal.getDhGroups().isEmpty());
     }
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 90986a6..528bbe2 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -97,6 +97,8 @@
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.content.Context;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.eap.EapSessionConfig;
 import android.net.ipsec.ike.ChildSaProposal;
@@ -117,7 +119,7 @@
 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
-import android.net.ipsec.ike.exceptions.IkeNetworkDiedException;
+import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.net.ipsec.ike.exceptions.InvalidSyntaxException;
 import android.net.ipsec.ike.exceptions.NoValidProposalChosenException;
@@ -204,6 +206,7 @@
 
 import java.io.IOException;
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
@@ -362,6 +365,8 @@
     private IkeUdp6Socket mSpyIkeUdp6Socket;
     private IkeSocket mSpyCurrentIkeSocket;
 
+    private LinkAddress mMockLinkAddressGlobalV6;
+
     private IkeNattKeepalive mMockIkeNattKeepalive;
 
     private TestLooper mLooper;
@@ -766,6 +771,10 @@
                         eq(mMockDefaultNetwork), eq(false /* isIpv4 */), any(), anyInt()))
                 .thenReturn(LOCAL_ADDRESS_V6);
 
+        mMockLinkAddressGlobalV6 = mock(LinkAddress.class);
+        when(mMockLinkAddressGlobalV6.getAddress()).thenReturn(UPDATED_LOCAL_ADDRESS_V6);
+        when(mMockLinkAddressGlobalV6.isGlobalPreferred()).thenReturn(true);
+
         mMockEapAuthenticatorFactory = mock(IkeEapAuthenticatorFactory.class);
         mMockEapAuthenticator = mock(EapAuthenticator.class);
         doReturn(mMockEapAuthenticator)
@@ -1419,7 +1428,7 @@
                         .build();
         mIkeSessionStateMachine = makeAndStartIkeSession(ikeParams);
 
-        verify(mMockDefaultNetwork).getByName(REMOTE_HOSTNAME);
+        verify(mMockDefaultNetwork).getAllByName(REMOTE_HOSTNAME);
     }
 
     @Test
@@ -4839,7 +4848,6 @@
 
         IkeSessionParams mockSessionParams = mock(IkeSessionParams.class);
         when(mockSessionParams.getServerHostname()).thenReturn(REMOTE_HOSTNAME);
-        when(mMockDefaultNetwork.getByName(REMOTE_HOSTNAME)).thenReturn(REMOTE_ADDRESS);
 
         RuntimeException cause = new RuntimeException();
         when(mockSessionParams.getSaProposalsInternal()).thenThrow(cause);
@@ -5462,8 +5470,15 @@
 
         // IKE client always supports NAT-T. So the peer decides if both sides support NAT-T.
         mIkeSessionStateMachine.mSupportNatTraversal = doesPeerSupportNatt;
-        mIkeSessionStateMachine.mLocalAddress = isIpv4 ? LOCAL_ADDRESS : LOCAL_ADDRESS_V6;
-        mIkeSessionStateMachine.mRemoteAddress = isIpv4 ? REMOTE_ADDRESS : REMOTE_ADDRESS_V6;
+        if (isIpv4) {
+            mIkeSessionStateMachine.mLocalAddress = LOCAL_ADDRESS;
+            mIkeSessionStateMachine.mRemoteAddress = REMOTE_ADDRESS;
+            mIkeSessionStateMachine.mRemoteAddressesV4.add(REMOTE_ADDRESS);
+        } else {
+            mIkeSessionStateMachine.mLocalAddress = LOCAL_ADDRESS_V6;
+            mIkeSessionStateMachine.mRemoteAddress = REMOTE_ADDRESS_V6;
+            mIkeSessionStateMachine.mRemoteAddressesV6.add(REMOTE_ADDRESS_V6);
+        }
 
         if (doesPeerSupportNatt && isIpv4) {
             // Assume NATs are detected on both sides
@@ -5613,7 +5628,7 @@
 
         ArgumentCaptor<IkeException> exceptionCaptor = ArgumentCaptor.forClass(IkeException.class);
         verify(mMockIkeSessionCallback).onError(exceptionCaptor.capture());
-        IkeNetworkDiedException cause = (IkeNetworkDiedException) exceptionCaptor.getValue();
+        IkeNetworkLostException cause = (IkeNetworkLostException) exceptionCaptor.getValue();
         assertEquals(mMockDefaultNetwork, cause.getNetwork());
     }
 
@@ -5635,9 +5650,23 @@
     private Network mockNewNetworkAndAddress(boolean isIpv4) throws Exception {
         Network newNetwork = mock(Network.class);
 
-        InetAddress expectedRemoteAddress = isIpv4 ? REMOTE_ADDRESS : REMOTE_ADDRESS_V6;
-        InetAddress injectedLocalAddress =
-                isIpv4 ? UPDATED_LOCAL_ADDRESS : UPDATED_LOCAL_ADDRESS_V6;
+        InetAddress expectedRemoteAddress;
+        InetAddress injectedLocalAddress;
+        if (isIpv4) {
+            expectedRemoteAddress = REMOTE_ADDRESS;
+            injectedLocalAddress = UPDATED_LOCAL_ADDRESS;
+
+            mIkeSessionStateMachine.mRemoteAddressesV4.add((Inet4Address) expectedRemoteAddress);
+        } else {
+            expectedRemoteAddress = REMOTE_ADDRESS_V6;
+            injectedLocalAddress = UPDATED_LOCAL_ADDRESS_V6;
+            mIkeSessionStateMachine.mRemoteAddressesV6.add((Inet6Address) expectedRemoteAddress);
+
+            LinkProperties linkProperties = new LinkProperties();
+            linkProperties.addLinkAddress(mMockLinkAddressGlobalV6);
+            when(mMockConnectManager.getLinkProperties(eq(newNetwork))).thenReturn(linkProperties);
+        }
+
         when(mMockIkeLocalAddressGenerator.generateLocalAddress(
                         eq(newNetwork), eq(isIpv4), eq(expectedRemoteAddress), anyInt()))
                 .thenReturn(injectedLocalAddress);
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
index 5d14a04..7528c4c 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionTestBase.java
@@ -50,6 +50,7 @@
 
 import java.net.Inet4Address;
 import java.net.Inet6Address;
+import java.net.InetAddress;
 import java.util.concurrent.Executor;
 
 public abstract class IkeSessionTestBase {
@@ -109,10 +110,12 @@
                 .newWakeLock(anyInt(), argThat(tag -> tag.contains(LOCAL_REQUEST_WAKE_LOCK_TAG)));
 
         mMockDefaultNetwork = mock(Network.class);
-        doReturn(REMOTE_ADDRESS).when(mMockDefaultNetwork).getByName(REMOTE_HOSTNAME);
-        doReturn(REMOTE_ADDRESS)
+        doReturn(new InetAddress[] {REMOTE_ADDRESS})
                 .when(mMockDefaultNetwork)
-                .getByName(REMOTE_ADDRESS.getHostAddress());
+                .getAllByName(REMOTE_HOSTNAME);
+        doReturn(new InetAddress[] {REMOTE_ADDRESS})
+                .when(mMockDefaultNetwork)
+                .getAllByName(REMOTE_ADDRESS.getHostAddress());
 
         mMockSocketKeepalive = mock(SocketKeepalive.class);