Merge "Migrate Test Targets to New Android Ownership Model" into main
diff --git a/Android.bp b/Android.bp
index 35a609a..3ac8129 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,6 +30,7 @@
 
     libs: [
         "android.net.ipsec.ike.stubs.system",
+        "androidx.annotation_annotation",
         "auto_value_annotations",
         "framework-annotations-lib",
         "framework-connectivity",
diff --git a/OWNERS b/OWNERS
index 54d6c0b..0d92786 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,5 @@
-edensu@google.com
 amreddy@google.com
 radhikaagrawal@google.com
 pochunlee@google.com
 apsankar@google.com
+tairuw@google.com
diff --git a/flags/Android.bp b/flags/Android.bp
index 28aa750..f3e0c76 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -21,12 +21,13 @@
 aconfig_declarations {
     name: "iwlan_telephony_flags",
     package: "com.google.android.iwlan.flags",
+    container: "system",
     srcs: [
-      "main.aconfig",
+        "main.aconfig",
     ],
 }
 
 java_aconfig_library {
     name: "iwlan_telephony_flags_lib",
-    aconfig_declarations: "iwlan_telephony_flags"
+    aconfig_declarations: "iwlan_telephony_flags",
 }
diff --git a/flags/main.aconfig b/flags/main.aconfig
index 9bc80a1..57b0527 100644
--- a/flags/main.aconfig
+++ b/flags/main.aconfig
@@ -1,4 +1,5 @@
 package: "com.google.android.iwlan.flags"
+container: "system"
 
 flag {
     name: "prevent_epdg_selection_threads_exhausted"
@@ -7,6 +8,24 @@
     bug: "278788983"
 }
 flag {
+    name: "aead_algos_enabled"
+    namespace: "iwlan_telephony"
+    description: "Add AES_GCM algorithm support in IWLAN."
+    bug: "306119890"
+}
+flag {
+    name: "multiple_sa_proposals"
+    namespace: "iwlan_telephony"
+    description: "Add multiple proposals of IKE SA and Child SA"
+    bug: "287296642"
+}
+flag {
+    name: "high_secure_transforms_prioritized"
+    namespace: "iwlan_telephony"
+    description: "Reorder IKE and Child SA security transforms with high secured as prioritized"
+    bug: "306323917"
+}
+flag {
     name: "epdg_selection_exclude_failed_ip_address"
     namespace: "iwlan_telephony"
     description: "Exclude the failed ip address in epdg selection until tunnel establish successfully or all ip address candidates are failed"
diff --git a/src/com/google/android/iwlan/IwlanCarrierConfig.java b/src/com/google/android/iwlan/IwlanCarrierConfig.java
index bb3a20f..e68341a 100644
--- a/src/com/google/android/iwlan/IwlanCarrierConfig.java
+++ b/src/com/google/android/iwlan/IwlanCarrierConfig.java
@@ -21,6 +21,8 @@
 import android.support.annotation.NonNull;
 import android.telephony.CarrierConfigManager;
 
+import androidx.annotation.VisibleForTesting;
+
 /** Class for handling IWLAN carrier configuration. */
 public class IwlanCarrierConfig {
     static final String PREFIX = "iwlan.";
@@ -42,6 +44,13 @@
             PREFIX + "n1_mode_exclusion_for_emergency_session";
 
     /**
+     * Key to decide whether N1 mode shall be enabled or disabled depending on 5G enabling status
+     * via the UI/UX. See {@link #DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL} for the default value.
+     */
+    public static final String KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL =
+            PREFIX + "update_n1_mode_on_ui_change_bool";
+
+    /**
      * Default delay in seconds for releasing the IWLAN connection after a WWAN handover. This is
      * the default value for {@link #KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT}.
      */
@@ -53,10 +62,18 @@
      */
     public static final boolean DEFAULT_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL = false;
 
-    private static PersistableBundle mHiddenBundle = new PersistableBundle();
+    /**
+     * The default value for determining whether N1 mode shall be enabled or disabled depending on
+     * 5G enabling status via the UI/UX.
+     */
+    public static final boolean DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL = true;
+
+    private static PersistableBundle sTestBundle = new PersistableBundle();
+
+    private static PersistableBundle sHiddenBundle = new PersistableBundle();
 
     static {
-        mHiddenBundle = createHiddenDefaultConfig();
+        sHiddenBundle = createHiddenDefaultConfig();
     }
 
     /**
@@ -72,6 +89,8 @@
         bundle.putBoolean(
                 KEY_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL,
                 DEFAULT_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL);
+        bundle.putBoolean(
+                KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL);
         return bundle;
     }
 
@@ -88,13 +107,17 @@
     }
 
     private static PersistableBundle getDefaultConfig(String key) {
+        if (sTestBundle.containsKey(key)) {
+            return sTestBundle;
+        }
+
         PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
         if (bundle.containsKey(key)) {
             return bundle;
         }
 
-        if (mHiddenBundle.containsKey(key)) {
-            return mHiddenBundle;
+        if (sHiddenBundle.containsKey(key)) {
+            return sHiddenBundle;
         }
 
         throw new IllegalArgumentException("Default config not found for key: " + key);
@@ -274,6 +297,7 @@
     public static boolean getDefaultConfigBoolean(String key) {
         return getDefaultConfig(key).getBoolean(key);
     }
+
     /**
      * Gets the default configuration int[] value for a given key.
      *
@@ -295,6 +319,7 @@
     public static long[] getDefaultConfigLongArray(String key) {
         return getDefaultConfig(key).getLongArray(key);
     }
+
     /**
      * Gets the default configuration double[] value for a given key.
      *
@@ -327,4 +352,64 @@
     public static boolean[] getDefaultConfigBooleanArray(String key) {
         return getDefaultConfig(key).getBooleanArray(key);
     }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigBundle(PersistableBundle bundle) {
+        sTestBundle.putAll(bundle);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigInt(@NonNull String key, int value) {
+        sTestBundle.putInt(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigLong(@NonNull String key, long value) {
+        sTestBundle.putLong(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigDouble(@NonNull String key, double value) {
+        sTestBundle.putDouble(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigBoolean(@NonNull String key, boolean value) {
+        sTestBundle.putBoolean(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigString(@NonNull String key, String value) {
+        sTestBundle.putString(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigIntArray(@NonNull String key, @NonNull int[] value) {
+        sTestBundle.putIntArray(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigLongArray(@NonNull String key, @NonNull long[] value) {
+        sTestBundle.putLongArray(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigDoubleArray(@NonNull String key, @NonNull double[] value) {
+        sTestBundle.putDoubleArray(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigBooleanArray(@NonNull String key, @NonNull boolean[] value) {
+        sTestBundle.putBooleanArray(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void putTestConfigStringArray(@NonNull String key, @NonNull String[] value) {
+        sTestBundle.putStringArray(key, value);
+    }
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static void resetTestConfig() {
+        sTestBundle.clear();
+    }
 }
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 2bf967a..7c680ea 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -20,6 +20,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.ipsec.ike.ike3gpp.Ike3gppParams.PDU_SESSION_ID_UNSET;
 
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_DEACTIVATE_DATA_CALL;
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_IN_DEACTIVATING_STATE;
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP;
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC;
+
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -923,7 +928,8 @@
                                     entry.getKey(),
                                     true /* forceClose */,
                                     getIwlanTunnelCallback(),
-                                    getIwlanTunnelMetrics());
+                                    getIwlanTunnelMetrics(),
+                                    BRINGDOWN_REASON_IN_DEACTIVATING_STATE);
                 }
             }
         }
@@ -1047,7 +1053,8 @@
                                     entry.getKey(),
                                     true /* forceClose */,
                                     getIwlanTunnelCallback(),
-                                    getIwlanTunnelMetrics());
+                                    getIwlanTunnelMetrics(),
+                                    BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP);
                 }
             }
         }
@@ -1229,7 +1236,10 @@
 
         // TODO(b/309867756): Include N1_MODE_CAPABILITY inclusion status in metrics.
         private boolean needIncludeN1ModeCapability() {
-            if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+            if (!IwlanCarrierConfig.getConfigBoolean(
+                    mContext,
+                    getSlotIndex(),
+                    IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL)) {
                 return isN1ModeSupported();
             }
             if (!isN1ModeSupported()) {
@@ -1247,10 +1257,10 @@
 
         protected boolean isN1ModeSupported() {
             int[] nrAvailabilities =
-                    IwlanHelper.getConfig(
-                            CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+                    IwlanCarrierConfig.getConfigIntArray(
                             mContext,
-                            getSlotIndex());
+                            getSlotIndex(),
+                            CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
             Log.d(
                     TAG,
                     "KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY : "
@@ -1508,11 +1518,11 @@
                     if (cellInfolist != null
                             && iwlanDataServiceProvider.isRegisteredCellInfoChanged(cellInfolist)) {
                         int[] addrResolutionMethods =
-                                IwlanHelper.getConfig(
-                                        CarrierConfigManager.Iwlan
-                                                .KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+                                IwlanCarrierConfig.getConfigIntArray(
                                         mContext,
-                                        iwlanDataServiceProvider.getSlotIndex());
+                                        iwlanDataServiceProvider.getSlotIndex(),
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY);
                         for (int addrResolutionMethod : addrResolutionMethods) {
                             if (addrResolutionMethod
                                     == CarrierConfigManager.Iwlan.EPDG_ADDRESS_CELLULAR_LOC) {
@@ -1529,7 +1539,10 @@
                     int previousCallState = iwlanDataServiceProvider.mCallState;
                     int currentCallState = iwlanDataServiceProvider.mCallState = msg.arg2;
 
-                    if (!mFeatureFlags.updateN1ModeOnUiChange()) {
+                    if (!IwlanCarrierConfig.getConfigBoolean(
+                            mContext,
+                            iwlanDataServiceProvider.getSlotIndex(),
+                            IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL)) {
                         break;
                     }
 
@@ -1542,11 +1555,14 @@
                     break;
 
                 case IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT:
-                    if (!mFeatureFlags.updateN1ModeOnUiChange()) {
-                        break;
-                    }
                     iwlanDataServiceProvider =
                             (IwlanDataServiceProvider) getDataServiceProvider(msg.arg1);
+                    if (!IwlanCarrierConfig.getConfigBoolean(
+                            mContext,
+                            iwlanDataServiceProvider.getSlotIndex(),
+                            IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL)) {
+                        break;
+                    }
                     long allowedNetworkType = (long) msg.obj;
                     onPreferredNetworkTypeChanged(iwlanDataServiceProvider, allowedNetworkType);
                     break;
@@ -1642,7 +1658,8 @@
                                             dataProfile.getApnSetting().getApnName(),
                                             true /* forceClose */,
                                             iwlanDataServiceProvider.getIwlanTunnelCallback(),
-                                            iwlanDataServiceProvider.getIwlanTunnelMetrics());
+                                            iwlanDataServiceProvider.getIwlanTunnelMetrics(),
+                                            BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC);
                             iwlanDataServiceProvider.deliverCallback(
                                     IwlanDataServiceProvider.CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
                                     5 /* DataServiceCallback
@@ -1913,7 +1930,8 @@
                             matchingApn,
                             isNetworkLost || isHandoverSuccessful, /* forceClose */
                             serviceProvider.getIwlanTunnelCallback(),
-                            serviceProvider.getIwlanTunnelMetrics());
+                            serviceProvider.getIwlanTunnelMetrics(),
+                            BRINGDOWN_REASON_DEACTIVATE_DATA_CALL);
         }
 
         private void resumePendingDeactivationIfExists(
diff --git a/src/com/google/android/iwlan/IwlanEventListener.java b/src/com/google/android/iwlan/IwlanEventListener.java
index 890afb4..427f4a7 100644
--- a/src/com/google/android/iwlan/IwlanEventListener.java
+++ b/src/com/google/android/iwlan/IwlanEventListener.java
@@ -187,10 +187,6 @@
         public void onAllowedNetworkTypesChanged(
                 @TelephonyManager.AllowedNetworkTypesReason int reason,
                 @TelephonyManager.NetworkTypeBitMask long allowedNetworkType) {
-            if (!mFeatureFlags.updateN1ModeOnUiChange()) {
-                return;
-            }
-
             if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) {
                 return;
             }
diff --git a/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java
new file mode 100644
index 0000000..d85e322
--- /dev/null
+++ b/src/com/google/android/iwlan/epdg/EpdgChildSaProposal.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 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.google.android.iwlan.epdg;
+
+import android.net.ipsec.ike.ChildSaProposal;
+import android.util.Pair;
+
+public class EpdgChildSaProposal extends EpdgSaProposal {
+    /**
+     * Builds {@link ChildSaProposal} of carrier proposed encryption algorithms (non-AEAD) cipher
+     * suit.
+     */
+    public ChildSaProposal buildProposedChildSaProposal() {
+        return buildProposal(false, true);
+    }
+
+    /** Builds {@link ChildSaProposal} of carrier proposed AEAD algorithms cipher suit. */
+    public ChildSaProposal buildProposedChildSaAeadProposal() {
+        return buildProposal(true, true);
+    }
+
+    /**
+     * Builds {@link ChildSaProposal} of Iwlan supported encryption algorithms (non-AEAD) cipher
+     * suit.
+     */
+    public ChildSaProposal buildSupportedChildSaProposal() {
+        return buildProposal(false, false);
+    }
+
+    /** Builds {@link ChildSaProposal} of Iwlan supported AEAD algorithms cipher suit. */
+    public ChildSaProposal buildSupportedChildSaAeadProposal() {
+        return buildProposal(true, false);
+    }
+
+    private ChildSaProposal buildProposal(boolean isAead, boolean isProposed) {
+        ChildSaProposal.Builder saProposalBuilder = new ChildSaProposal.Builder();
+
+        int[] dhGroups = getDhGroups();
+        for (int dhGroup : dhGroups) {
+            saProposalBuilder.addDhGroup(dhGroup);
+        }
+
+        Pair<Integer, Integer>[] encrAlgos;
+
+        if (isAead) {
+            encrAlgos = (isProposed) ? getAeadAlgos() : getSupportedAeadAlgos();
+        } else {
+            encrAlgos = (isProposed) ? getEncryptionAlgos() : getSupportedEncryptionAlgos();
+        }
+
+        for (Pair<Integer, Integer> encrAlgo : encrAlgos) {
+            saProposalBuilder.addEncryptionAlgorithm(encrAlgo.first, encrAlgo.second);
+        }
+
+        if (!isAead) {
+            int[] integrityAlgos =
+                    (isProposed) ? getIntegrityAlgos() : getSupportedIntegrityAlgos();
+            for (int integrityAlgo : integrityAlgos) {
+                saProposalBuilder.addIntegrityAlgorithm(integrityAlgo);
+            }
+        }
+
+        return saProposalBuilder.build();
+    }
+}
diff --git a/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java
new file mode 100644
index 0000000..f4d6d56
--- /dev/null
+++ b/src/com/google/android/iwlan/epdg/EpdgIkeSaProposal.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020 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.google.android.iwlan.epdg;
+
+import android.net.ipsec.ike.IkeSaProposal;
+import android.util.Pair;
+
+import java.util.LinkedHashSet;
+
+public class EpdgIkeSaProposal extends EpdgSaProposal {
+    protected final LinkedHashSet<Integer> mProposedPrfAlgos = new LinkedHashSet<>();
+
+    /**
+     * Add proposed PRF algorithms by the carrier.
+     *
+     * @param prfAlgos proposed PRF algorithms
+     */
+    public void addProposedPrfAlgorithm(int[] prfAlgos) {
+        for (int prfAlgo : prfAlgos) {
+            if (validateConfig(prfAlgo, VALID_PRF_ALGOS, CONFIG_TYPE_PRF_ALGO)) {
+                mProposedPrfAlgos.add(prfAlgo);
+            }
+        }
+    }
+
+    private int[] getPrfAlgos() {
+        if (isSaferProposalsPrioritized()) {
+            return mProposedPrfAlgos.stream()
+                    .sorted(
+                            (item1, item2) ->
+                                    compareTransformPriority(VALID_PRF_ALGOS, item1, item2))
+                    .mapToInt(Integer::intValue)
+                    .toArray();
+        }
+
+        return mProposedPrfAlgos.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    private int[] getSupportedPrfAlgos() {
+        return VALID_PRF_ALGOS.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    /**
+     * Builds {@link IkeSaProposal} of carrier proposed encryption algorithms (non-AEAD) cipher
+     * suit.
+     */
+    public IkeSaProposal buildProposedIkeSaProposal() {
+        return buildProposal(false, true);
+    }
+
+    /** Builds {@link IkeSaProposal} of carrier proposed AEAD algorithms cipher suit. */
+    public IkeSaProposal buildProposedIkeSaAeadProposal() {
+        return buildProposal(true, true);
+    }
+
+    /**
+     * Builds {@link IkeSaProposal} of Iwlan supported encryption algorithms (non-AEAD) cipher suit.
+     */
+    public IkeSaProposal buildSupportedIkeSaProposal() {
+        return buildProposal(false, false);
+    }
+
+    /** Builds {@link IkeSaProposal} of Iwlan supported AEAD algorithms cipher suit. */
+    public IkeSaProposal buildSupportedIkeSaAeadProposal() {
+        return buildProposal(true, false);
+    }
+
+    private IkeSaProposal buildProposal(boolean isAead, boolean isProposed) {
+        IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder();
+
+        int[] dhGroups = isProposed ? getDhGroups() : getSupportedDhGroups();
+        for (int dhGroup : dhGroups) {
+            saProposalBuilder.addDhGroup(dhGroup);
+        }
+
+        Pair<Integer, Integer>[] encrAlgos;
+
+        if (isAead) {
+            encrAlgos = (isProposed) ? getAeadAlgos() : getSupportedAeadAlgos();
+        } else {
+            encrAlgos = (isProposed) ? getEncryptionAlgos() : getSupportedEncryptionAlgos();
+        }
+
+        for (Pair<Integer, Integer> encrAlgo : encrAlgos) {
+            saProposalBuilder.addEncryptionAlgorithm(encrAlgo.first, encrAlgo.second);
+        }
+
+        if (!isAead) {
+            int[] integrityAlgos =
+                    (isProposed) ? getIntegrityAlgos() : getSupportedIntegrityAlgos();
+            for (int integrityAlgo : integrityAlgos) {
+                saProposalBuilder.addIntegrityAlgorithm(integrityAlgo);
+            }
+        }
+
+        int[] prfAlgos = (isProposed) ? getPrfAlgos() : getSupportedPrfAlgos();
+        for (int prfAlgo : prfAlgos) {
+            saProposalBuilder.addPseudorandomFunction(prfAlgo);
+        }
+
+        return saProposalBuilder.build();
+    }
+}
diff --git a/src/com/google/android/iwlan/epdg/EpdgSaProposal.java b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java
new file mode 100644
index 0000000..871cc5c
--- /dev/null
+++ b/src/com/google/android/iwlan/epdg/EpdgSaProposal.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2020 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.google.android.iwlan.epdg;
+
+import android.net.ipsec.ike.SaProposal;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+abstract class EpdgSaProposal {
+    private static final String TAG = EpdgSaProposal.class.getSimpleName();
+    private static final Set<Integer> VALID_DH_GROUPS;
+    private static final Set<Integer> VALID_KEY_LENGTHS;
+    protected static final Set<Integer> VALID_PRF_ALGOS;
+    private static final Set<Integer> VALID_INTEGRITY_ALGOS;
+    private static final Set<Integer> VALID_ENCRYPTION_ALGOS;
+    private static final Set<Integer> VALID_AEAD_ALGOS;
+
+    private static final String CONFIG_TYPE_DH_GROUP = "dh group";
+    private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length";
+    protected static final String CONFIG_TYPE_PRF_ALGO = "prf algorithm";
+    private static final String CONFIG_TYPE_INTEGRITY_ALGO = "integrity algorithm";
+    private static final String CONFIG_TYPE_ENCRYPT_ALGO = "encryption algorithm";
+    private static final String CONFIG_TYPE_AEAD_ALGO = "AEAD algorithm";
+
+    private boolean mSaferAlgosPrioritized = false;
+
+    /*
+     * Each transform below filled with high secured order and index of each value
+     * represents the priority.
+     * Safer transform has high priority and proposals will be orders based on
+     * the priority order.
+     * For example, DH Group 4096 has more priority compare to 3072, and 3072
+     * has more priority than 2048.
+     * With reference to 3GPP TS 33.210 and RFC 8221, high secured transforms
+     * are prioritized in IKE and CHILD SA proposals.
+     */
+    static {
+        VALID_DH_GROUPS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.DH_GROUP_4096_BIT_MODP,
+                                        SaProposal.DH_GROUP_3072_BIT_MODP,
+                                        SaProposal.DH_GROUP_2048_BIT_MODP,
+                                        SaProposal.DH_GROUP_1536_BIT_MODP,
+                                        SaProposal.DH_GROUP_1024_BIT_MODP)));
+
+        VALID_KEY_LENGTHS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.KEY_LEN_AES_256,
+                                        SaProposal.KEY_LEN_AES_192,
+                                        SaProposal.KEY_LEN_AES_128)));
+
+        VALID_ENCRYPTION_ALGOS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_CTR)));
+
+        VALID_INTEGRITY_ALGOS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+                                        SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96)));
+
+        VALID_AEAD_ALGOS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16,
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8)));
+
+        VALID_PRF_ALGOS =
+                Collections.unmodifiableSet(
+                        new LinkedHashSet<Integer>(
+                                List.of(
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)));
+    }
+
+    protected final LinkedHashSet<Integer> mProposedDhGroups = new LinkedHashSet<>();
+    protected final LinkedHashSet<Integer> mProposedIntegrityAlgos = new LinkedHashSet<>();
+    protected final LinkedHashSet<Pair<Integer, Integer>> mProposedEncryptAlgos =
+            new LinkedHashSet<>();
+    protected final LinkedHashSet<Pair<Integer, Integer>> mProposedAeadAlgos =
+            new LinkedHashSet<>();
+
+    /**
+     * Add proposed DH groups by the carrier.
+     *
+     * @param dhGroups proposed DH groups
+     */
+    public void addProposedDhGroups(int[] dhGroups) {
+        for (int dhGroup : dhGroups) {
+            if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) {
+                mProposedDhGroups.add(dhGroup);
+            }
+        }
+    }
+
+    /**
+     * Add proposed integrity algorithms by the carrier.
+     *
+     * @param integrityAlgos proposed integrity algorithms
+     */
+    public void addProposedIntegrityAlgorithm(int[] integrityAlgos) {
+        for (int integrityAlgo : integrityAlgos) {
+            if (validateConfig(integrityAlgo, VALID_INTEGRITY_ALGOS, CONFIG_TYPE_INTEGRITY_ALGO)) {
+                mProposedIntegrityAlgos.add(integrityAlgo);
+            }
+        }
+    }
+
+    /**
+     * Add proposed encryption algorithms and respective key lengths by the carrier.
+     *
+     * @param encryptionAlgo proposed encryption algorithm
+     * @param keyLens proposed key lengths for the encryption algorithm
+     */
+    public void addProposedEncryptionAlgorithm(int encryptionAlgo, int[] keyLens) {
+        if (validateConfig(encryptionAlgo, VALID_ENCRYPTION_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+            for (int keyLen : keyLens) {
+                if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) {
+                    mProposedEncryptAlgos.add(new Pair<Integer, Integer>(encryptionAlgo, keyLen));
+                }
+            }
+        }
+    }
+
+    /**
+     * Add proposed AEAD algorithms and respective key lengths by the carrier.
+     *
+     * @param aeadAlgo proposed AEAD algorithm
+     * @param keyLens proposed key lengths for the encryption algorithm
+     */
+    public void addProposedAeadAlgorithm(int aeadAlgo, int[] keyLens) {
+        if (validateConfig(aeadAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_AEAD_ALGO)) {
+            for (int keyLen : keyLens) {
+                if (validateConfig(keyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) {
+                    mProposedAeadAlgos.add(new Pair<Integer, Integer>(aeadAlgo, keyLen));
+                }
+            }
+        }
+    }
+
+    /** Enable to reorder proposals with safer ciphers prioritized. */
+    public void enableReorderingSaferProposals() {
+        mSaferAlgosPrioritized = true;
+    }
+
+    /**
+     * Disable to reorder proposals with safer ciphers prioritized.Follows default configured order.
+     */
+    public void disableReorderingSaferProposals() {
+        mSaferAlgosPrioritized = false;
+    }
+
+    protected boolean isSaferProposalsPrioritized() {
+        return mSaferAlgosPrioritized;
+    }
+
+    protected int getIndexOf(Set<Integer> set, int value) {
+        Iterator<Integer> itr = set.iterator();
+        int index = 0;
+
+        while (itr.hasNext()) {
+            if (itr.next().equals(value)) {
+                return index;
+            }
+            index++;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Compares the priority of the transforms.
+     */
+    protected int compareTransformPriority(Set<Integer> transformGroup, int item1, int item2) {
+        return getIndexOf(transformGroup, item1) - getIndexOf(transformGroup, item2);
+    }
+
+    /**
+     * Compares the priority of the encryption/AEAD transforms.
+     * First value in pair is encryption/AEAD algorithm and
+     * second value in pair is key length of that algorithm.
+     * If Algorithms are same then compare the priotity of the key lengths else compare
+     * the priority of the algorithms.
+     */
+    protected int compareEncryptionTransformPriority(
+            Set<Integer> algos,
+            Set<Integer> keyLens,
+            Pair<Integer, Integer> item1,
+            Pair<Integer, Integer> item2) {
+        return item1.first.equals(item2.first)
+                ? getIndexOf(keyLens, item1.second) - getIndexOf(keyLens, item2.second)
+                : getIndexOf(algos, item1.first) - getIndexOf(algos, item2.first);
+    }
+
+    protected int[] getDhGroups() {
+        if (isSaferProposalsPrioritized()) {
+            return mProposedDhGroups.stream()
+                    .sorted(
+                            (item1, item2) ->
+                                    compareTransformPriority(VALID_DH_GROUPS, item1, item2))
+                    .mapToInt(Integer::intValue)
+                    .toArray();
+        }
+
+        return mProposedDhGroups.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    protected int[] getSupportedDhGroups() {
+        return VALID_DH_GROUPS.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    protected int[] getIntegrityAlgos() {
+        if (isSaferProposalsPrioritized()) {
+            return mProposedIntegrityAlgos.stream()
+                    .sorted(
+                            (item1, item2) ->
+                                    compareTransformPriority(VALID_INTEGRITY_ALGOS, item1, item2))
+                    .mapToInt(Integer::intValue)
+                    .toArray();
+        }
+
+        return mProposedIntegrityAlgos.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    protected int[] getSupportedIntegrityAlgos() {
+        return VALID_INTEGRITY_ALGOS.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    protected Pair<Integer, Integer>[] getEncryptionAlgos() {
+        if (isSaferProposalsPrioritized()) {
+            return mProposedEncryptAlgos.stream()
+                    .sorted(
+                            (item1, item2) ->
+                                    compareEncryptionTransformPriority(
+                                            VALID_ENCRYPTION_ALGOS,
+                                            VALID_KEY_LENGTHS,
+                                            item1,
+                                            item2))
+                    .toArray(Pair[]::new);
+        }
+
+        return mProposedEncryptAlgos.toArray(new Pair[mProposedEncryptAlgos.size()]);
+    }
+
+    protected Pair<Integer, Integer>[] getSupportedEncryptionAlgos() {
+        Pair<Integer, Integer>[] encrAlgos =
+                new Pair[VALID_ENCRYPTION_ALGOS.size() * VALID_KEY_LENGTHS.size()];
+        int index = 0;
+        for (int algo : VALID_ENCRYPTION_ALGOS) {
+            for (int len : VALID_KEY_LENGTHS) {
+                encrAlgos[index++] = new Pair(algo, len);
+            }
+        }
+
+        return encrAlgos;
+    }
+
+    protected Pair<Integer, Integer>[] getAeadAlgos() {
+        if (isSaferProposalsPrioritized()) {
+            return mProposedAeadAlgos.stream()
+                    .sorted(
+                            (item1, item2) ->
+                                    compareEncryptionTransformPriority(
+                                            VALID_AEAD_ALGOS, VALID_KEY_LENGTHS, item1, item2))
+                    .toArray(Pair[]::new);
+        }
+
+        return mProposedAeadAlgos.toArray(new Pair[mProposedAeadAlgos.size()]);
+    }
+
+    protected Pair<Integer, Integer>[] getSupportedAeadAlgos() {
+        Pair<Integer, Integer>[] aeadAlgos =
+                new Pair[VALID_AEAD_ALGOS.size() * VALID_KEY_LENGTHS.size()];
+        int index = 0;
+        for (int algo : VALID_AEAD_ALGOS) {
+            for (int len : VALID_KEY_LENGTHS) {
+                aeadAlgos[index++] = new Pair(algo, len);
+            }
+        }
+
+        return aeadAlgos;
+    }
+
+    protected static boolean validateConfig(
+            int config, Set<Integer> validConfigValues, String configType) {
+        if (validConfigValues.contains(config)) {
+            return true;
+        }
+
+        Log.e(TAG, "Invalid config value for " + configType + ":" + config);
+        return false;
+    }
+}
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index 3db6ac2..dc2c364 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -68,6 +68,7 @@
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 public class EpdgSelector {
@@ -101,6 +102,7 @@
     private static final int PCO_IPV6_LEN = 16; // 16 bytes for IPv6 address in PCO data.
 
     private static final String NO_DOMAIN = "NO_DOMAIN";
+    private static final Pattern PLMN_PATTERN = Pattern.compile("\\d{5,6}");
 
     BlockingQueue<Runnable> dnsResolutionQueue;
 
@@ -153,6 +155,9 @@
         mV4PcoData = new ArrayList<>();
         mV6PcoData = new ArrayList<>();
 
+        mV4PcoData = new ArrayList<>();
+        mV6PcoData = new ArrayList<>();
+
         mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId);
 
         mTemporaryExcludedAddresses = new HashSet<>();
@@ -1388,6 +1393,6 @@
      * @return True if the PLMN identifier is valid, false otherwise.
      */
     private static boolean isValidPlmn(String plmn) {
-        return plmn != null && plmn.matches("\\d{5,6}");
+        return plmn != null && PLMN_PATTERN.matcher(plmn).matches();
     }
 }
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index 47f3fa3..8b53ee1 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -76,6 +76,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import com.google.android.iwlan.ErrorPolicyManager;
+import com.google.android.iwlan.IwlanCarrierConfig;
 import com.google.android.iwlan.IwlanError;
 import com.google.android.iwlan.IwlanHelper;
 import com.google.android.iwlan.IwlanTunnelMetricsImpl;
@@ -187,6 +188,7 @@
     private static final Set<Integer> VALID_PRF_ALGOS;
     private static final Set<Integer> VALID_INTEGRITY_ALGOS;
     private static final Set<Integer> VALID_ENCRYPTION_ALGOS;
+    private static final Set<Integer> VALID_AEAD_ALGOS;
 
     private static final String CONFIG_TYPE_DH_GROUP = "dh group";
     private static final String CONFIG_TYPE_KEY_LEN = "algorithm key length";
@@ -221,6 +223,12 @@
                         SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
                         SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
 
+        VALID_AEAD_ALGOS =
+                Set.of(
+                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8,
+                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+                        SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16);
+
         VALID_PRF_ALGOS =
                 Set.of(
                         SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
@@ -237,11 +245,19 @@
     public static final int BRINGDOWN_REASON_UNKNOWN = 0;
     public static final int BRINGDOWN_REASON_DISABLE_N1_MODE = 1;
     public static final int BRINGDOWN_REASON_ENABLE_N1_MODE = 2;
+    public static final int BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC = 3;
+    public static final int BRINGDOWN_REASON_IN_DEACTIVATING_STATE = 4;
+    public static final int BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP = 5;
+    public static final int BRINGDOWN_REASON_DEACTIVATE_DATA_CALL = 6;
 
     @IntDef({
         BRINGDOWN_REASON_UNKNOWN,
         BRINGDOWN_REASON_DISABLE_N1_MODE,
         BRINGDOWN_REASON_ENABLE_N1_MODE,
+        BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC,
+        BRINGDOWN_REASON_IN_DEACTIVATING_STATE,
+        BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP,
+        BRINGDOWN_REASON_DEACTIVATE_DATA_CALL,
     })
     public @interface TunnelBringDownReason {}
 
@@ -253,6 +269,14 @@
                 return "BRINGDOWN_REASON_DISABLE_N1_MODE";
             case BRINGDOWN_REASON_ENABLE_N1_MODE:
                 return "BRINGDOWN_REASON_ENABLE_N1_MODE";
+            case BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC:
+                return "BRINGDOWN_REASON_SERVICE_OUT_OF_SYNC";
+            case BRINGDOWN_REASON_IN_DEACTIVATING_STATE:
+                return "BRINGDOWN_REASON_IN_DEACTIVATING_STATE";
+            case BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP:
+                return "BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP";
+            case BRINGDOWN_REASON_DEACTIVATE_DATA_CALL:
+                return "BRINGDOWN_REASON_DEACTIVATE_DATA_CALL";
             default:
                 return "Unknown(" + reason + ")";
         }
@@ -693,24 +717,6 @@
      * provided in {@link #bringUpTunnel}. If no tunnel was available, callback will be delivered
      * using client-provided provided tunnelCallback and iwlanTunnelMetrics
      *
-     * @param apnName apn name
-     * @param forceClose if true, results in local cleanup of tunnel
-     * @param tunnelCallback Used if no current or pending IWLAN tunnel exists
-     * @param iwlanTunnelMetrics Used to report metrics if no current or pending IWLAN tunnel exists
-     */
-    // TODO(b/309866889): Clarify tunnel bring down reason for tunnel closure.
-    public void closeTunnel(
-            @NonNull String apnName,
-            boolean forceClose,
-            @NonNull TunnelCallback tunnelCallback,
-            @NonNull IwlanTunnelMetricsImpl iwlanTunnelMetrics) {
-        closeTunnel(
-                apnName, forceClose, tunnelCallback, iwlanTunnelMetrics, BRINGDOWN_REASON_UNKNOWN);
-    }
-
-    /**
-     * Closes tunnel for an apn with reason.
-     *
      * @param apnName APN name
      * @param forceClose if {@code true}, triggers a local cleanup of the tunnel; if {@code false},
      *     performs a normal closure procedure
@@ -920,7 +926,35 @@
                 new TunnelModeChildSessionParams.Builder()
                         .setLifetimeSeconds(hardTimeSeconds, softTimeSeconds);
 
-        childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal());
+        if (mFeatureFlags.multipleSaProposals()
+                && IwlanCarrierConfig.getConfigBoolean(
+                        mContext,
+                        mSlotId,
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) {
+            EpdgChildSaProposal epdgChildSaProposal = createEpdgChildSaProposal();
+
+            if (mFeatureFlags.highSecureTransformsPrioritized()) {
+                epdgChildSaProposal.enableReorderingSaferProposals();
+            }
+
+            if (isChildSessionAeadAlgosAvailable()) {
+                childSessionParamsBuilder.addChildSaProposal(
+                        epdgChildSaProposal.buildProposedChildSaAeadProposal());
+            }
+            childSessionParamsBuilder.addChildSaProposal(
+                    epdgChildSaProposal.buildProposedChildSaProposal());
+            childSessionParamsBuilder.addChildSaProposal(
+                    epdgChildSaProposal.buildSupportedChildSaAeadProposal());
+            childSessionParamsBuilder.addChildSaProposal(
+                    epdgChildSaProposal.buildSupportedChildSaProposal());
+        } else {
+            if (mFeatureFlags.aeadAlgosEnabled() && isChildSessionAeadAlgosAvailable()) {
+                childSessionParamsBuilder.addChildSaProposal(buildAeadChildSaProposal());
+            } else {
+                childSessionParamsBuilder.addChildSaProposal(buildChildSaProposal());
+            }
+        }
 
         boolean handoverIPv4Present = setupRequest.srcIpv4Address().isPresent();
         boolean handoverIPv6Present = setupRequest.srcIpv6Address().isPresent();
@@ -1049,7 +1083,6 @@
                         .setLocalIdentification(getLocalIdentification())
                         .setRemoteIdentification(getId(setupRequest.apnName(), false))
                         .setAuthEap(null, getEapConfig())
-                        .addIkeSaProposal(buildIkeSaProposal())
                         .setNetwork(mDefaultNetwork)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
@@ -1058,6 +1091,32 @@
                         .setRetransmissionTimeoutsMillis(getRetransmissionTimeoutsFromConfig())
                         .setDpdDelaySeconds(getDpdDelayFromConfig());
 
+        if (mFeatureFlags.multipleSaProposals()
+                && IwlanCarrierConfig.getConfigBoolean(
+                        mContext,
+                        mSlotId,
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL)) {
+            EpdgIkeSaProposal epdgIkeSaProposal = createEpdgIkeSaProposal();
+
+            if (mFeatureFlags.highSecureTransformsPrioritized()) {
+                epdgIkeSaProposal.enableReorderingSaferProposals();
+            }
+
+            if (isIkeSessionAeadAlgosAvailable()) {
+                builder.addIkeSaProposal(epdgIkeSaProposal.buildProposedIkeSaAeadProposal());
+            }
+            builder.addIkeSaProposal(epdgIkeSaProposal.buildProposedIkeSaProposal());
+            builder.addIkeSaProposal(epdgIkeSaProposal.buildSupportedIkeSaAeadProposal());
+            builder.addIkeSaProposal(epdgIkeSaProposal.buildSupportedIkeSaProposal());
+        } else {
+            if (mFeatureFlags.aeadAlgosEnabled() && isIkeSessionAeadAlgosAvailable()) {
+                builder.addIkeSaProposal(buildIkeSaAeadProposal());
+            } else {
+                builder.addIkeSaProposal(buildIkeSaProposal());
+            }
+        }
+
         if (numPdnTunnels() == 0) {
             builder.addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT);
             Log.d(TAG, "IKE_OPTION_INITIAL_CONTACT");
@@ -1122,6 +1181,32 @@
                 builder3gppParams.build(), new TmIke3gppCallback(apnName, token));
     }
 
+    private boolean isChildSessionAeadAlgosAvailable() {
+        int[] encryptionAlgos =
+                getConfig(
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY);
+        for (int encryptionAlgo : encryptionAlgos) {
+            if (validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isIkeSessionAeadAlgosAvailable() {
+        int[] encryptionAlgos =
+                getConfig(
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY);
+        for (int encryptionAlgo : encryptionAlgos) {
+            if (validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean isValidChildSessionLifetime(int hardLifetimeSeconds, int softLifetimeSeconds) {
         return hardLifetimeSeconds >= CHILD_HARD_LIFETIME_SEC_MINIMUM
                 && hardLifetimeSeconds <= CHILD_HARD_LIFETIME_SEC_MAXIMUM
@@ -1140,6 +1225,124 @@
         return IwlanHelper.getConfig(configKey, mContext, mSlotId);
     }
 
+    private void createEpdgSaProposal(EpdgSaProposal epdgSaProposal, boolean isChildProposal) {
+        epdgSaProposal.addProposedDhGroups(
+                IwlanCarrierConfig.getConfigIntArray(
+                        mContext,
+                        mSlotId,
+                        CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY));
+
+        int[] encryptionAlgos =
+                isChildProposal
+                        ? IwlanCarrierConfig.getConfigIntArray(
+                                mContext,
+                                mSlotId,
+                                CarrierConfigManager.Iwlan
+                                    .KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY)
+                        : IwlanCarrierConfig.getConfigIntArray(
+                                mContext,
+                                mSlotId,
+                                CarrierConfigManager.Iwlan
+                                    .KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY);
+
+        for (int encryptionAlgo : encryptionAlgos) {
+            if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CBC) {
+                int[] aesCbcKeyLens =
+                        isChildProposal
+                                ? IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY)
+                                : IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY);
+                epdgSaProposal.addProposedEncryptionAlgorithm(encryptionAlgo, aesCbcKeyLens);
+            }
+
+            if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CTR) {
+                int[] aesCtrKeyLens =
+                        isChildProposal
+                                ? IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY)
+                                : IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY);
+                epdgSaProposal.addProposedEncryptionAlgorithm(encryptionAlgo, aesCtrKeyLens);
+            }
+        }
+
+        if (encryptionAlgos.length > 0) {
+            epdgSaProposal.addProposedIntegrityAlgorithm(
+                    IwlanCarrierConfig.getConfigIntArray(
+                            mContext,
+                            mSlotId,
+                            CarrierConfigManager.Iwlan
+                                    .KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY));
+        }
+
+        int[] aeadAlgos =
+                isChildProposal
+                        ? IwlanCarrierConfig.getConfigIntArray(
+                                mContext,
+                                mSlotId,
+                                CarrierConfigManager.Iwlan
+                                        .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY)
+                        : IwlanCarrierConfig.getConfigIntArray(
+                                mContext,
+                                mSlotId,
+                                CarrierConfigManager.Iwlan
+                                        .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY);
+        for (int aeadAlgo : aeadAlgos) {
+            if (!validateConfig(aeadAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+                continue;
+            }
+            if ((aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8)
+                    || (aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12)
+                    || (aeadAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) {
+                int[] aesGcmKeyLens =
+                        isChildProposal
+                                ? IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY)
+                                : IwlanCarrierConfig.getConfigIntArray(
+                                        mContext,
+                                        mSlotId,
+                                        CarrierConfigManager.Iwlan
+                                                .KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY);
+                epdgSaProposal.addProposedAeadAlgorithm(aeadAlgo, aesGcmKeyLens);
+            }
+        }
+    }
+
+    private EpdgChildSaProposal createEpdgChildSaProposal() {
+        EpdgChildSaProposal epdgChildSaProposal = new EpdgChildSaProposal();
+        createEpdgSaProposal(epdgChildSaProposal, true);
+        return epdgChildSaProposal;
+    }
+
+    private EpdgIkeSaProposal createEpdgIkeSaProposal() {
+        EpdgIkeSaProposal epdgIkeSaProposal = new EpdgIkeSaProposal();
+
+        createEpdgSaProposal(epdgIkeSaProposal, false);
+
+        epdgIkeSaProposal.addProposedPrfAlgorithm(
+                IwlanCarrierConfig.getConfigIntArray(
+                        mContext,
+                        mSlotId,
+                        CarrierConfigManager.Iwlan.KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY));
+        return epdgIkeSaProposal;
+    }
+
     private IkeSaProposal buildIkeSaProposal() {
         IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder();
 
@@ -1201,6 +1404,50 @@
         return saProposalBuilder.build();
     }
 
+    private IkeSaProposal buildIkeSaAeadProposal() {
+        IkeSaProposal.Builder saProposalBuilder = new IkeSaProposal.Builder();
+
+        int[] dhGroups = getConfig(CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY);
+        for (int dhGroup : dhGroups) {
+            if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) {
+                saProposalBuilder.addDhGroup(dhGroup);
+            }
+        }
+
+        int[] encryptionAlgos =
+                getConfig(
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY);
+        for (int encryptionAlgo : encryptionAlgos) {
+            if (!validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+                continue;
+            }
+            if ((encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8)
+                    || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12)
+                    || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) {
+                int[] aesGcmKeyLens =
+                        getConfig(
+                                CarrierConfigManager.Iwlan
+                                        .KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY);
+                for (int aesGcmKeyLen : aesGcmKeyLens) {
+                    if (validateConfig(aesGcmKeyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) {
+                        saProposalBuilder.addEncryptionAlgorithm(encryptionAlgo, aesGcmKeyLen);
+                    }
+                }
+            }
+        }
+
+        int[] prfAlgos =
+                getConfig(CarrierConfigManager.Iwlan.KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY);
+        for (int prfAlgo : prfAlgos) {
+            if (validateConfig(prfAlgo, VALID_PRF_ALGOS, CONFIG_TYPE_PRF_ALGO)) {
+                saProposalBuilder.addPseudorandomFunction(prfAlgo);
+            }
+        }
+
+        return saProposalBuilder.build();
+    }
+
     private boolean validateConfig(int config, Set<Integer> validConfigValues, String configType) {
         if (validConfigValues.contains(config)) {
             return true;
@@ -1280,6 +1527,42 @@
         return saProposalBuilder.build();
     }
 
+    private ChildSaProposal buildAeadChildSaProposal() {
+        ChildSaProposal.Builder saProposalBuilder = new ChildSaProposal.Builder();
+
+        int[] dhGroups = getConfig(CarrierConfigManager.Iwlan.KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY);
+        for (int dhGroup : dhGroups) {
+            if (validateConfig(dhGroup, VALID_DH_GROUPS, CONFIG_TYPE_DH_GROUP)) {
+                saProposalBuilder.addDhGroup(dhGroup);
+            }
+        }
+
+        int[] encryptionAlgos =
+                getConfig(
+                        CarrierConfigManager.Iwlan
+                                .KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY);
+        for (int encryptionAlgo : encryptionAlgos) {
+            if (!validateConfig(encryptionAlgo, VALID_AEAD_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
+                continue;
+            }
+            if ((encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8)
+                    || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12)
+                    || (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16)) {
+                int[] aesGcmKeyLens =
+                        getConfig(
+                                CarrierConfigManager.Iwlan
+                                        .KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY);
+                for (int aesGcmKeyLen : aesGcmKeyLens) {
+                    if (validateConfig(aesGcmKeyLen, VALID_KEY_LENGTHS, CONFIG_TYPE_KEY_LEN)) {
+                        saProposalBuilder.addEncryptionAlgorithm(encryptionAlgo, aesGcmKeyLen);
+                    }
+                }
+            }
+        }
+
+        return saProposalBuilder.build();
+    }
+
     private IkeIdentification getLocalIdentification() throws IwlanSimNotReadyException {
         String nai;
 
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index f5914d9..6168b82 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -27,6 +27,9 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_DEACTIVATE_DATA_CALL;
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -142,7 +145,6 @@
     @Mock private LinkAddress mMockIPv6LinkAddress;
     @Mock private Inet4Address mMockInet4Address;
     @Mock private Inet6Address mMockInet6Address;
-    @Mock private CarrierConfigManager mMockCarrierConfigManager;
     @Mock private FeatureFlags mFakeFeatureFlags;
 
     MockitoSession mStaticMockSession;
@@ -213,7 +215,6 @@
                         .mockStatic(ErrorPolicyManager.class)
                         .mockStatic(IwlanBroadcastReceiver.class)
                         .mockStatic(SubscriptionManager.class)
-                        .mockStatic(IwlanCarrierConfig.class)
                         .strictness(Strictness.LENIENT)
                         .startMocking();
 
@@ -262,8 +263,6 @@
 
         mIwlanDataService = spy(new IwlanDataService(mFakeFeatureFlags));
 
-        when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
-                .thenReturn(mMockCarrierConfigManager);
         // Injects the test looper into the IwlanDataServiceHandler
         doReturn(mTestLooper.getLooper()).when(mIwlanDataService).getLooper();
         mIwlanDataService.setAppContext(mMockContext);
@@ -281,8 +280,9 @@
 
         when(mMockConnectivityManager.getLinkProperties(eq(mMockNetwork)))
                 .thenReturn(mLinkProperties);
-
         when(mMockTunnelLinkProperties.ifaceName()).thenReturn("mockipsec0");
+
+        mockCarrierConfigForN1Mode(true);
     }
 
     private void moveTimeForwardAndDispatch(long milliSeconds) {
@@ -293,6 +293,7 @@
     @After
     public void cleanUp() throws Exception {
         mStaticMockSession.finishMocking();
+        IwlanCarrierConfig.resetTestConfig();
         mSpyIwlanDataServiceProvider.close();
         mTestLooper.dispatchAll();
         if (mIwlanDataService != null) {
@@ -458,7 +459,8 @@
         networkCallback.onLinkPropertiesChanged(mMockNetwork, newLinkProperties);
         verify(mMockEpdgTunnelManager, times(1))
                 .updateNetwork(eq(mMockNetwork), eq(newLinkProperties));
-        verify(mMockEpdgTunnelManager, never()).closeTunnel(any(), anyBoolean(), any(), any());
+        verify(mMockEpdgTunnelManager, never())
+                .closeTunnel(any(), anyBoolean(), any(), any(), anyInt());
     }
 
     @Test
@@ -867,7 +869,8 @@
                         eq(TEST_APN_NAME),
                         eq(false),
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        eq(BRINGDOWN_REASON_DEACTIVATE_DATA_CALL));
 
         /* Check callback result is RESULT_SUCCESS when onClosed() is called. */
         mSpyIwlanDataServiceProvider
@@ -907,7 +910,8 @@
                         eq(TEST_APN_NAME),
                         eq(true) /* forceClose */,
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        eq(BRINGDOWN_REASON_DEACTIVATE_DATA_CALL));
 
         /* Check callback result is RESULT_SUCCESS when onClosed() is called. */
         mSpyIwlanDataServiceProvider
@@ -922,11 +926,8 @@
     public void testDeactivateDataCall_DelayedReleaseAfterHandover() {
         DataProfile dp = buildImsDataProfile();
 
-        when(IwlanCarrierConfig.getConfigInt(
-                        mMockContext,
-                        DEFAULT_SLOT_INDEX,
-                        IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT))
-                .thenReturn(3);
+        IwlanCarrierConfig.putTestConfigInt(
+                IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT, 3);
         onSystemDefaultNetworkConnected(TRANSPORT_WIFI);
 
         mSpyIwlanDataServiceProvider.setTunnelState(
@@ -952,7 +953,8 @@
                         eq(TEST_APN_NAME),
                         anyBoolean(),
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        eq(BRINGDOWN_REASON_DEACTIVATE_DATA_CALL));
 
         moveTimeForwardAndDispatch(50);
         /* Check closeTunnel() is called. */
@@ -961,7 +963,8 @@
                         eq(TEST_APN_NAME),
                         eq(true) /* forceClose */,
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        eq(BRINGDOWN_REASON_DEACTIVATE_DATA_CALL));
 
         /* Check callback result is RESULT_SUCCESS when onClosed() is called. */
         mSpyIwlanDataServiceProvider
@@ -976,11 +979,8 @@
     public void testDeactivateDataCall_DelayedReleaseAfterHandover_NetworkReleaseBeforeDelay() {
         DataProfile dp = buildImsDataProfile();
 
-        when(IwlanCarrierConfig.getConfigInt(
-                        mMockContext,
-                        DEFAULT_SLOT_INDEX,
-                        IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT))
-                .thenReturn(3);
+        IwlanCarrierConfig.putTestConfigInt(
+                IwlanCarrierConfig.KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT, 3);
         when(ErrorPolicyManager.getInstance(eq(mMockContext), eq(DEFAULT_SLOT_INDEX)))
                 .thenReturn(mMockErrorPolicyManager);
         when(mMockErrorPolicyManager.getDataFailCause(eq(TEST_APN_NAME)))
@@ -1020,7 +1020,8 @@
                         eq(TEST_APN_NAME),
                         anyBoolean(),
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        anyInt());
 
         /* Check callback result is RESULT_SUCCESS when onClosed() is called. */
         mSpyIwlanDataServiceProvider
@@ -1037,7 +1038,8 @@
                         eq(TEST_APN_NAME),
                         anyBoolean(),
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        anyInt());
 
         // No additional callbacks are involved.
         verify(mMockDataServiceCallback, times(1)).onDeactivateDataCallComplete(anyInt());
@@ -1993,7 +1995,8 @@
                         eq(TEST_APN_NAME),
                         anyBoolean(),
                         any(IwlanTunnelCallback.class),
-                        any(IwlanTunnelMetricsImpl.class));
+                        any(IwlanTunnelMetricsImpl.class),
+                        eq(BRINGDOWN_REASON_DEACTIVATE_DATA_CALL));
 
         advanceCalendarByTimeMs(deactivationTime, calendar);
 
@@ -2044,7 +2047,13 @@
         Network newNetwork2 = createMockNetwork(mLinkProperties);
         onSystemDefaultNetworkConnected(
                 newNetwork2, mLinkProperties, TRANSPORT_WIFI, DEFAULT_SUB_INDEX);
-        verify(mMockEpdgTunnelManager, times(1)).closeTunnel(any(), anyBoolean(), any(), any());
+        verify(mMockEpdgTunnelManager, times(1))
+                .closeTunnel(
+                        any(),
+                        anyBoolean(),
+                        any(),
+                        any(),
+                        eq(BRINGDOWN_REASON_NETWORK_UPDATE_WHEN_TUNNEL_IN_BRINGUP));
     }
 
     public static TunnelLinkProperties createTunnelLinkProperties() throws Exception {
@@ -2072,15 +2081,6 @@
                 .build();
     }
 
-    private void setupMockForGetConfig(PersistableBundle bundle) {
-        if (bundle == null) {
-            bundle = new PersistableBundle();
-        }
-        when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
-                .thenReturn(mMockCarrierConfigManager);
-        when(mMockCarrierConfigManager.getConfigForSubId(DEFAULT_SLOT_INDEX)).thenReturn(bundle);
-    }
-
     private void mockCarrierConfigForN1Mode(boolean supportN1Mode) {
         PersistableBundle bundle = new PersistableBundle();
         if (supportN1Mode) {
@@ -2095,7 +2095,7 @@
                     CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
                     new int[] {CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA});
         }
-        setupMockForGetConfig(bundle);
+        IwlanCarrierConfig.putTestConfigBundle(bundle);
     }
 
     private void mockCallState(int callState) {
@@ -2132,7 +2132,7 @@
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA
                 });
-        setupMockForGetConfig(bundle);
+        IwlanCarrierConfig.putTestConfigBundle(bundle);
         assertTrue(mSpyIwlanDataServiceProvider.isN1ModeSupported());
 
         bundle.putIntArray(
@@ -2140,14 +2140,15 @@
                 new int[] {
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
                 });
-        setupMockForGetConfig(bundle);
+        IwlanCarrierConfig.putTestConfigBundle(bundle);
         assertFalse(mSpyIwlanDataServiceProvider.isN1ModeSupported());
     }
 
     @Test
     public void testMultipleAllowedNetworkTypeChangeInIdle_updateN1Mode() throws Exception {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
         mockCallState(CALL_STATE_IDLE);
         mockSetupDataCallWithPduSessionId(0);
         updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
@@ -2175,8 +2176,10 @@
     @Test
     public void testMultipleAllowedNetworkTypeChangeInCall_preferenceChanged_updateAfterCallEnds()
             throws Exception {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
+
         mockCallState(CALL_STATE_RINGING);
         mockSetupDataCallWithPduSessionId(0);
         updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
@@ -2208,8 +2211,10 @@
     @Test
     public void testMultipleAllowedNetworkTypeChangeInCall_preferenceNotChanged_noUpdate()
             throws Exception {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
+
         mockCallState(CALL_STATE_RINGING);
         mockSetupDataCallWithPduSessionId(0);
         updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
@@ -2234,8 +2239,10 @@
 
     @Test
     public void testOnAllowedNetworkTypeChange_flagDisabled_noTunnelClose() {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(false);
         mockCarrierConfigForN1Mode(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, false);
+
         mockCallState(CALL_STATE_IDLE);
         mockSetupDataCallWithPduSessionId(0);
         updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
@@ -2246,8 +2253,10 @@
 
     @Test
     public void testOnAllowedNetworkTypeChange_n1ModeNotSupported_noTunnelClose() {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(false);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
+
         mockCallState(CALL_STATE_IDLE);
         mockSetupDataCallWithPduSessionId(0);
         updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
@@ -2258,8 +2267,10 @@
 
     @Test
     public void testN1ModeNotSupported_tunnelBringupWithNoN1ModeCapability() {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(false);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
+
         mockSetupDataCallWithPduSessionId(1);
 
         ArgumentCaptor<TunnelSetupRequest> tunnelSetupRequestCaptor =
@@ -2272,8 +2283,10 @@
 
     @Test
     public void testNoN1ModeCapabilityInOngoingDataCall_newTunnelBringup_doNotIncludeN1() {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
         mockCarrierConfigForN1Mode(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, true);
+
         mockSetupDataCallWithPduSessionId(0);
 
         ArgumentCaptor<TunnelSetupRequest> tunnelSetupRequestCaptor =
@@ -2325,6 +2338,7 @@
     @Test
     public void testN1ModeForEmergencySession() {
         int pduSessionId = 5;
+        updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
         DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
         verifySetupDataCallRequestHandled(pduSessionId, dp);
 
@@ -2338,11 +2352,9 @@
 
     @Test
     public void testN1ModeExclusionForEmergencySession() {
-        when(IwlanCarrierConfig.getConfigBoolean(
-                        mMockContext,
-                        DEFAULT_SLOT_INDEX,
-                        IwlanCarrierConfig.KEY_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL))
-                .thenReturn(true);
+        IwlanCarrierConfig.putTestConfigBoolean(
+                IwlanCarrierConfig.KEY_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL, true);
+        updatePreferredNetworkType(NETWORK_TYPE_BITMASK_NR);
         DataProfile dp = buildDataProfile(ApnSetting.TYPE_EMERGENCY);
         verifySetupDataCallRequestHandled(5 /* pduSessionId */, dp);
 
diff --git a/test/com/google/android/iwlan/IwlanEventListenerTest.java b/test/com/google/android/iwlan/IwlanEventListenerTest.java
index a922a10..5999872 100644
--- a/test/com/google/android/iwlan/IwlanEventListenerTest.java
+++ b/test/com/google/android/iwlan/IwlanEventListenerTest.java
@@ -368,8 +368,6 @@
     @SuppressLint("MissingPermission")
     @Test
     public void testDisable5gViaUi() throws Exception {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
-
         when(mMockHandler.obtainMessage(
                         eq(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT),
                         eq(DEFAULT_SLOT_INDEX),
@@ -393,8 +391,6 @@
     @SuppressLint("MissingPermission")
     @Test
     public void testEnable5gViaUi() throws Exception {
-        when(mFakeFeatureFlags.updateN1ModeOnUiChange()).thenReturn(true);
-
         when(mMockHandler.obtainMessage(
                         eq(IwlanEventListener.PREFERRED_NETWORK_TYPE_CHANGED_EVENT),
                         eq(DEFAULT_SLOT_INDEX),
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index b7004e7..05ab0bc 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.google.android.iwlan.epdg;
 
+import static com.google.android.iwlan.epdg.EpdgTunnelManager.BRINGDOWN_REASON_UNKNOWN;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -26,6 +28,7 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
@@ -72,6 +75,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.util.Pair;
 
 import com.google.android.iwlan.IwlanError;
 import com.google.android.iwlan.IwlanTunnelMetricsImpl;
@@ -543,6 +547,207 @@
     }
 
     @Test
+    public void testAeadSaProposals() throws Exception {
+        when(mFakeFeatureFlags.aeadAlgosEnabled()).thenReturn(true);
+        final String apnName = "ims";
+        int[] aeadAlgos = {
+            SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8,
+            SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+            SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16,
+        };
+        int[] aeadAlgosKeyLens = {
+            SaProposal.KEY_LEN_AES_128, SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256,
+        };
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+
+        setupMockForGetConfig(bundle);
+
+        IkeSessionArgumentCaptors tunnelArgumentCaptors =
+                verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork);
+
+        IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue();
+
+        List<Pair<Integer, Integer>> ikeEncrAlgos =
+                ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms();
+
+        assertTrue(ikeEncrAlgos.contains(new Pair(aeadAlgos[0], aeadAlgosKeyLens[0])));
+        assertEquals(
+                "IKE AEAD algorithms mismatch",
+                (long) aeadAlgos.length * aeadAlgosKeyLens.length,
+                (long) ikeEncrAlgos.size());
+
+        ChildSessionParams childTunnelParams =
+                tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue();
+
+        List<Pair<Integer, Integer>> childEncrAlgos =
+                childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms();
+
+        assertTrue(childEncrAlgos.contains(new Pair(aeadAlgos[0], aeadAlgosKeyLens[0])));
+        assertEquals(
+                "Child AEAD algorithms mismatch",
+                (long) aeadAlgos.length * aeadAlgosKeyLens.length,
+                (long) childEncrAlgos.size());
+    }
+
+    @Test
+    public void testMultipleSaProposals() throws Exception {
+        when(mFakeFeatureFlags.multipleSaProposals()).thenReturn(true);
+        final String apnName = "ims";
+        PersistableBundle bundle = new PersistableBundle();
+
+        int[] aeadAlgos = {
+            SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+        };
+        int[] aeadAlgosKeyLens = {
+            SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256,
+        };
+
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+
+        bundle.putBoolean(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL,
+                true);
+        bundle.putBoolean(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL,
+                true);
+
+        setupMockForGetConfig(bundle);
+
+        IkeSessionArgumentCaptors tunnelArgumentCaptors =
+                verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork);
+
+        IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue();
+
+        assertTrue(ikeTunnelParams.getIkeSaProposals().size() > 1);
+
+        List<Pair<Integer, Integer>> ikeAeadAlgos =
+                ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms();
+        assertEquals(
+                "Reorder higher AEAD in  IKE SA mismatch",
+                (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+                (long) ikeAeadAlgos.get(0).first);
+
+        ChildSessionParams childTunnelParams =
+                tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue();
+
+        assertTrue(childTunnelParams.getChildSaProposals().size() > 1);
+
+        List<Pair<Integer, Integer>> childAeadAlgos =
+                childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms();
+        assertEquals(
+                "Reorder higher AEAD in  Child SA mismatch",
+                (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12,
+                (long) childAeadAlgos.get(0).first);
+    }
+
+    @Test
+    public void testSaProposalsReorder() throws Exception {
+        when(mFakeFeatureFlags.aeadAlgosEnabled()).thenReturn(true);
+        when(mFakeFeatureFlags.multipleSaProposals()).thenReturn(true);
+        when(mFakeFeatureFlags.highSecureTransformsPrioritized()).thenReturn(true);
+
+        final String apnName = "ims";
+        int[] aeadAlgos = {
+            SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
+        };
+        int[] aeadAlgosKeyLens = {
+            SaProposal.KEY_LEN_AES_128, SaProposal.KEY_LEN_AES_192, SaProposal.KEY_LEN_AES_256,
+        };
+
+        PersistableBundle bundle = new PersistableBundle();
+
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan
+                        .KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan
+                        .KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                aeadAlgos);
+        bundle.putIntArray(
+                CarrierConfigManager.Iwlan.KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
+                aeadAlgosKeyLens);
+
+        bundle.putBoolean(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL,
+                true);
+        bundle.putBoolean(
+                CarrierConfigManager.Iwlan.KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL,
+                true);
+
+        setupMockForGetConfig(bundle);
+
+        IkeSessionArgumentCaptors tunnelArgumentCaptors =
+                verifyBringUpTunnelWithDnsQuery(apnName, mMockDefaultNetwork);
+
+        IkeSessionParams ikeTunnelParams = tunnelArgumentCaptors.mIkeSessionParamsCaptor.getValue();
+
+        assertTrue(ikeTunnelParams.getIkeSaProposals().size() > 1);
+
+        List<Pair<Integer, Integer>> ikeEncrAlgos =
+                ikeTunnelParams.getIkeSaProposals().get(0).getEncryptionAlgorithms();
+
+        assertEquals(
+                "Reorder bigger key length in IKE SA mismatch",
+                (long) SaProposal.KEY_LEN_AES_256,
+                (long) ikeEncrAlgos.get(0).second);
+
+        List<Pair<Integer, Integer>> ikeAeadAlgos =
+                ikeTunnelParams.getIkeSaProposals().get(1).getEncryptionAlgorithms();
+        assertEquals(
+                "Reorder higher AEAD in  IKE SA mismatch",
+                (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16,
+                (long) ikeAeadAlgos.get(0).first);
+
+        ChildSessionParams childTunnelParams =
+                tunnelArgumentCaptors.mChildSessionParamsCaptor.getValue();
+
+        assertTrue(childTunnelParams.getChildSaProposals().size() > 1);
+
+        List<Pair<Integer, Integer>> childEncrAlgos =
+                childTunnelParams.getChildSaProposals().get(0).getEncryptionAlgorithms();
+
+        assertEquals(
+                "Reorder bigger key length in Child SA mismatch",
+                (long) SaProposal.KEY_LEN_AES_256,
+                (long) childEncrAlgos.get(0).second);
+
+        List<Pair<Integer, Integer>> childAeadAlgos =
+                childTunnelParams.getChildSaProposals().get(1).getEncryptionAlgorithms();
+        assertEquals(
+                "Reorder higher AEAD in  Child SA mismatch",
+                (long) SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16,
+                (long) childAeadAlgos.get(0).first);
+    }
+
+    @Test
     public void testCloseTunnelWithNoTunnelForApn() throws Exception {
         String testApnName = "www.xyz.com";
         doReturn(0L)
@@ -553,7 +758,8 @@
                 testApnName,
                 false /*forceClose*/,
                 mMockIwlanTunnelCallback,
-                mMockIwlanTunnelMetrics);
+                mMockIwlanTunnelMetrics,
+                BRINGDOWN_REASON_UNKNOWN);
         mTestLooper.dispatchAll();
 
         verify(mEpdgTunnelManager).closePendingRequestsForApn(eq(testApnName));
@@ -582,7 +788,8 @@
                 testApnName,
                 true /*forceClose*/,
                 mMockIwlanTunnelCallback,
-                mMockIwlanTunnelMetrics);
+                mMockIwlanTunnelMetrics,
+                BRINGDOWN_REASON_UNKNOWN);
         mTestLooper.dispatchAll();
 
         verify(mMockIkeSession).kill();
@@ -606,7 +813,8 @@
                 testApnName,
                 false /*forceClose*/,
                 mMockIwlanTunnelCallback,
-                mMockIwlanTunnelMetrics);
+                mMockIwlanTunnelMetrics,
+                BRINGDOWN_REASON_UNKNOWN);
         mTestLooper.dispatchAll();
 
         verify(mMockIkeSession).close();
@@ -1153,6 +1361,7 @@
         when(mMockTelephonyManager.createForSubscriptionId(DEFAULT_SUBID))
                 .thenReturn(mMockTelephonyManager);
         when(mMockCarrierConfigManager.getConfigForSubId(DEFAULT_SLOT_INDEX)).thenReturn(bundle);
+        when(mMockCarrierConfigManager.getConfigForSubId(anyInt(), anyString())).thenReturn(bundle);
     }
 
     private void setVariable(Object target, String variableName, Object value) throws Exception {
@@ -2091,7 +2300,8 @@
                 TEST_APN_NAME,
                 false /*forceClose*/,
                 mMockIwlanTunnelCallback,
-                mMockIwlanTunnelMetrics);
+                mMockIwlanTunnelMetrics,
+                BRINGDOWN_REASON_UNKNOWN);
         mTestLooper.dispatchAll();
 
         verify(mMockIwlanTunnelCallback, times(1))