Snap for 8618812 from 23ff4d4549779628a4c1eb8a7f5002a18cadde8d to mainline-tzdata4-release

Change-Id: I3a8bc409b9b5ff6d96ae3e56358a363dbc16c6a7
diff --git a/OWNERS b/OWNERS
index b6feac0..54d6c0b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,5 @@
 edensu@google.com
 amreddy@google.com
 radhikaagrawal@google.com
+pochunlee@google.com
+apsankar@google.com
diff --git a/assets/defaultiwlanerrorconfig.json b/assets/defaultiwlanerrorconfig.json
index 07b980d..eae1ae2 100644
--- a/assets/defaultiwlanerrorconfig.json
+++ b/assets/defaultiwlanerrorconfig.json
@@ -72,6 +72,12 @@
         "ErrorDetails": ["IO_EXCEPTION"],
         "RetryArray": ["0", "0", "0", "60+r15", "120", "-1"],
         "UnthrottlingEvents": ["APM_ENABLE_EVENT", "APM_DISABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_AP_CHANGED_EVENT"]
+      },
+      {
+        "ErrorType": "IKE_PROTOCOL_ERROR_TYPE",
+        "ErrorDetails": ["24"],
+        "RetryArray": ["10", "20", "40", "80", "160"],
+        "UnthrottlingEvents": ["APM_ENABLE_EVENT", "WIFI_DISABLE_EVENT", "WIFI_CALLING_DISABLE_EVENT"]
       }
     ]
   }
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index 15d8116..59fad64 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -302,6 +302,10 @@
             ret = DataFailCause.IWLAN_DNS_RESOLUTION_NAME_FAILURE;
         } else if (error.getErrorType() == IwlanError.IKE_INTERNAL_IO_EXCEPTION) {
             ret = DataFailCause.IWLAN_IKEV2_MSG_TIMEOUT;
+        } else if (error.getErrorType() == IwlanError.SIM_NOT_READY_EXCEPTION) {
+            ret = DataFailCause.IWLAN_PDN_CONNECTION_REJECTION;
+        } else if (error.getErrorType() == IwlanError.NETWORK_FAILURE) {
+            ret = DataFailCause.NETWORK_FAILURE;
         } else if (error.getErrorType() == IwlanError.IKE_PROTOCOL_EXCEPTION) {
             Exception exception = error.getException();
             if (exception != null && exception instanceof IkeProtocolException) {
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 923a393..a254535 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -872,8 +872,7 @@
                                 + ", transport: "
                                 + sDefaultDataTransport);
 
-                if (networkConnected == false
-                        || mTunnelStateForApn.get(dataProfile.getApn()) != null) {
+                if (networkConnected == false) {
                     deliverCallback(
                             CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
                             5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
@@ -882,6 +881,39 @@
                     return;
                 }
 
+                TunnelState tunnelState = mTunnelStateForApn.get(dataProfile.getApn());
+
+                // Return the existing PDN if the pduSessionId is the same and the tunnel state is
+                // TUNNEL_UP.
+                if (tunnelState != null) {
+                    if (tunnelState.getPduSessionId() == pduSessionId
+                            && tunnelState.getState() == TunnelState.TUNNEL_UP) {
+                        Log.w(
+                                SUB_TAG,
+                                "The tunnel for " + dataProfile.getApn() + " already exists.");
+                        deliverCallback(
+                                CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+                                DataServiceCallback.RESULT_SUCCESS,
+                                callback,
+                                apnTunnelStateToDataCallResponse(dataProfile.getApn()));
+                        return;
+                    } else {
+                        Log.e(
+                                SUB_TAG,
+                                "Force close the existing PDN. pduSessionId = "
+                                        + tunnelState.getPduSessionId()
+                                        + " Tunnel State = "
+                                        + tunnelState.getState());
+                        getTunnelManager().closeTunnel(dataProfile.getApn(), true /* forceClose */);
+                        deliverCallback(
+                                CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
+                                5 /* DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE */,
+                                callback,
+                                null);
+                        return;
+                    }
+                }
+
                 TunnelSetupRequest.Builder tunnelReqBuilder =
                         TunnelSetupRequest.builder()
                                 .setApnName(dataProfile.getApn())
diff --git a/src/com/google/android/iwlan/IwlanError.java b/src/com/google/android/iwlan/IwlanError.java
index 6d5e249..f621a7a 100644
--- a/src/com/google/android/iwlan/IwlanError.java
+++ b/src/com/google/android/iwlan/IwlanError.java
@@ -40,9 +40,11 @@
     // Known internal types
     public static final int EPDG_SELECTOR_SERVER_SELECTION_FAILED = 4;
     public static final int TUNNEL_TRANSFORM_FAILED = 5;
+    public static final int SIM_NOT_READY_EXCEPTION = 6;
+    public static final int NETWORK_FAILURE = 7;
 
     // Catch all exception
-    public static final int UNKNOWN_EXCEPTION = 6; // catch all
+    public static final int UNKNOWN_EXCEPTION = 8; // catch all
 
     @IntDef({
         NO_ERROR,
@@ -51,6 +53,8 @@
         IKE_GENERIC_EXCEPTION,
         EPDG_SELECTOR_SERVER_SELECTION_FAILED,
         TUNNEL_TRANSFORM_FAILED,
+        SIM_NOT_READY_EXCEPTION,
+        NETWORK_FAILURE,
         UNKNOWN_EXCEPTION
     })
     @interface IwlanErrorType {};
@@ -66,6 +70,8 @@
                             EPDG_SELECTOR_SERVER_SELECTION_FAILED,
                             "IWLAN_EPDG_SELECTOR_SERVER_SELECTION_FAILED");
                     put(TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+                    put(SIM_NOT_READY_EXCEPTION, "IWLAN_SIM_NOT_READY_EXCEPTION");
+                    put(NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE");
                     put(UNKNOWN_EXCEPTION, "IWLAN_UNKNOWN_EXCEPTION");
                 }
             };
@@ -192,6 +198,12 @@
             case "TUNNEL_TRANSFORM_FAILED":
                 ret = IwlanError.TUNNEL_TRANSFORM_FAILED;
                 break;
+            case "SIM_NOT_READY_EXCEPTION":
+                ret = IwlanError.SIM_NOT_READY_EXCEPTION;
+                break;
+            case "NETWORK_FAILURE":
+                ret = IwlanError.NETWORK_FAILURE;
+                break;
         }
         return ret;
     }
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index eecede0..1c824b7 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -62,6 +62,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.NetworkSliceInfo;
@@ -72,6 +73,7 @@
 import com.google.android.iwlan.ErrorPolicyManager;
 import com.google.android.iwlan.IwlanError;
 import com.google.android.iwlan.IwlanHelper;
+import com.google.android.iwlan.exceptions.IwlanSimNotReadyException;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -110,6 +112,7 @@
     private static final int EVENT_IPSEC_TRANSFORM_CREATED = 7;
     private static final int EVENT_IPSEC_TRANSFORM_DELETED = 8;
     private static final int EVENT_UPDATE_NETWORK = 9;
+    private static final int EVENT_IKE_SESSION_OPENED = 10;
     private static final int IKE_HARD_LIFETIME_SEC_MINIMUM = 300;
     private static final int IKE_HARD_LIFETIME_SEC_MAXIMUM = 86400;
     private static final int IKE_SOFT_LIFETIME_SEC_MINIMUM = 120;
@@ -384,6 +387,12 @@
             return sb.toString();
         }
 
+        public boolean hasTunnelOpened() {
+            return mInternalAddrList != null
+                    && !mInternalAddrList.isEmpty() /* The child session is opened */
+                    && mIface != null; /* The tunnel interface is bring up */
+        }
+
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
@@ -434,27 +443,10 @@
         @Override
         public void onOpened(IkeSessionConfiguration sessionConfiguration) {
             Log.d(TAG, "Ike session opened for apn: " + mApnName);
-            TunnelConfig tunnelConfig = mApnNameToTunnelConfig.get(mApnName);
-            tunnelConfig.setPcscfAddrList(sessionConfiguration.getPcscfServers());
-
-            boolean enabledFastReauth =
-                    (boolean)
-                            getConfig(
-                                    CarrierConfigManager.Iwlan
-                                            .KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL);
-            Log.d(
-                    TAG,
-                    "CarrierConfigManager.Iwlan.KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL "
-                            + enabledFastReauth);
-            if (enabledFastReauth) {
-                EapInfo eapInfo = sessionConfiguration.getEapInfo();
-                if (eapInfo != null && eapInfo instanceof EapAkaInfo) {
-                    mNextReauthId = ((EapAkaInfo) eapInfo).getReauthId();
-                    Log.d(TAG, "Update ReauthId: " + Arrays.toString(mNextReauthId));
-                } else {
-                    mNextReauthId = null;
-                }
-            }
+            mHandler.sendMessage(
+                    mHandler.obtainMessage(
+                            EVENT_IKE_SESSION_OPENED,
+                            new IkeSessionOpenedData(mApnName, sessionConfiguration)));
         }
 
         @Override
@@ -731,6 +723,7 @@
 
     private void onBringUpTunnel(TunnelSetupRequest setupRequest, TunnelCallback tunnelCallback) {
         String apnName = setupRequest.apnName();
+        IkeSessionParams ikeSessionParams = null;
 
         Log.d(
                 TAG,
@@ -739,11 +732,21 @@
                         + "ePDG : "
                         + mEpdgAddress.getHostAddress());
 
+        try {
+            ikeSessionParams = buildIkeSessionParams(setupRequest, apnName);
+        } catch (IwlanSimNotReadyException e) {
+            mRequestQueue.poll();
+            IwlanError iwlanError = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION);
+            reportIwlanError(apnName, iwlanError);
+            tunnelCallback.onClosed(apnName, iwlanError);
+            return;
+        }
+
         IkeSession ikeSession =
                 getIkeSessionCreator()
                         .createIkeSession(
                                 mContext,
-                                buildIkeSessionParams(setupRequest, apnName),
+                                ikeSessionParams,
                                 buildChildSessionParams(setupRequest),
                                 Executors.newSingleThreadExecutor(),
                                 getTmIkeSessionCallback(apnName),
@@ -888,7 +891,8 @@
     // Returns the IMEISV or device IMEI, in that order of priority.
     private @Nullable String getMobileDeviceIdentity() {
         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
-        telephonyManager = telephonyManager.createForSubscriptionId(mSlotId);
+        telephonyManager =
+                telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
         if (telephonyManager == null) {
             return null;
         }
@@ -907,8 +911,8 @@
         return imei.substring(0, imei.length() - 1) + imeisv_suffix;
     }
 
-    private IkeSessionParams buildIkeSessionParams(
-            TunnelSetupRequest setupRequest, String apnName) {
+    private IkeSessionParams buildIkeSessionParams(TunnelSetupRequest setupRequest, String apnName)
+            throws IwlanSimNotReadyException {
         int hardTimeSeconds =
                 (int) getConfig(CarrierConfigManager.Iwlan.KEY_IKE_REKEY_HARD_TIMER_SEC_INT);
         int softTimeSeconds =
@@ -952,6 +956,7 @@
                         .setNetwork(mNetwork)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+                        .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY)
                         .setLifetimeSeconds(hardTimeSeconds, softTimeSeconds)
                         .setRetransmissionTimeoutsMillis(getRetransmissionTimeoutsFromConfig())
                         .setDpdDelaySeconds(getDpdDelayFromConfig());
@@ -1179,13 +1184,13 @@
         return saProposalBuilder.build();
     }
 
-    private IkeIdentification getLocalIdentification() {
+    private IkeIdentification getLocalIdentification() throws IwlanSimNotReadyException {
         String nai;
 
         nai = IwlanHelper.getNai(mContext, mSlotId, mNextReauthId);
 
         if (nai == null) {
-            throw new IllegalArgumentException("Nai is null.");
+            throw new IwlanSimNotReadyException("Nai is null.");
         }
 
         Log.d(TAG, "getLocalIdentification: Nai: " + nai);
@@ -1210,12 +1215,12 @@
         }
     }
 
-    private EapSessionConfig getEapConfig() {
+    private EapSessionConfig getEapConfig() throws IwlanSimNotReadyException {
         int subId = IwlanHelper.getSubId(mContext, mSlotId);
         String nai = IwlanHelper.getNai(mContext, mSlotId, null);
 
         if (nai == null) {
-            throw new IllegalArgumentException("Nai is null.");
+            throw new IwlanSimNotReadyException("Nai is null.");
         }
 
         EapSessionConfig.EapAkaOption option = null;
@@ -1261,6 +1266,18 @@
                 case EVENT_TUNNEL_BRINGUP_REQUEST:
                     TunnelRequestWrapper tunnelRequestWrapper = (TunnelRequestWrapper) msg.obj;
                     TunnelSetupRequest setupRequest = tunnelRequestWrapper.getSetupRequest();
+
+                    if (IwlanHelper.getSubId(mContext, mSlotId)
+                            == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                        Log.e(TAG, "SIM isn't ready");
+                        IwlanError iwlanError = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION);
+                        reportIwlanError(setupRequest.apnName(), iwlanError);
+                        tunnelRequestWrapper
+                                .getTunnelCallback()
+                                .onClosed(setupRequest.apnName(), iwlanError);
+                        return;
+                    }
+
                     if (!canBringUpTunnel(setupRequest.apnName())) {
                         Log.d(TAG, "Cannot bring up tunnel as retry time has not passed");
                         tunnelRequestWrapper
@@ -1376,7 +1393,13 @@
                     if (sessionClosedData.mIwlanError.getErrorType() != IwlanError.NO_ERROR) {
                         iwlanError = sessionClosedData.mIwlanError;
                     } else {
-                        iwlanError = tunnelConfig.getError();
+                        // If IKE session setup failed without error cause, Iwlan reports
+                        // NETWORK_FAILURE instead of NO_ERROR
+                        if (!tunnelConfig.hasTunnelOpened()) {
+                            iwlanError = new IwlanError(IwlanError.NETWORK_FAILURE);
+                        } else {
+                            iwlanError = tunnelConfig.getError();
+                        }
                     }
 
                     IpSecManager.IpSecTunnelInterface iface = tunnelConfig.getIface();
@@ -1417,7 +1440,7 @@
                     tunnelConfig = mApnNameToTunnelConfig.get(apnName);
 
                     // Update the global cache if they aren't equal
-                    if (!mNetwork.equals(network)) {
+                    if (mNetwork == null || !mNetwork.equals(network)) {
                         Log.d(TAG, "Updating mNetwork to " + network);
                         mNetwork = network;
                     }
@@ -1523,6 +1546,34 @@
                     tunnelConfig.getIkeSession().close();
                     break;
 
+                case EVENT_IKE_SESSION_OPENED:
+                    IkeSessionOpenedData ikeSessionOpenedData = (IkeSessionOpenedData) msg.obj;
+                    IkeSessionConfiguration sessionConfiguration =
+                            ikeSessionOpenedData.mIkeSessionConfiguration;
+
+                    tunnelConfig = mApnNameToTunnelConfig.get(ikeSessionOpenedData.mApnName);
+                    tunnelConfig.setPcscfAddrList(sessionConfiguration.getPcscfServers());
+
+                    boolean enabledFastReauth =
+                            (boolean)
+                                    getConfig(
+                                            CarrierConfigManager.Iwlan
+                                                    .KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL);
+                    Log.d(
+                            TAG,
+                            "CarrierConfigManager.Iwlan.KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL "
+                                    + enabledFastReauth);
+
+                    if (enabledFastReauth) {
+                        EapInfo eapInfo = sessionConfiguration.getEapInfo();
+                        if (eapInfo != null && eapInfo instanceof EapAkaInfo) {
+                            mNextReauthId = ((EapAkaInfo) eapInfo).getReauthId();
+                            Log.d(TAG, "Update ReauthId: " + Arrays.toString(mNextReauthId));
+                        } else {
+                            mNextReauthId = null;
+                        }
+                    }
+                    break;
                 default:
                     throw new IllegalStateException("Unexpected value: " + msg.what);
             }
@@ -1722,6 +1773,7 @@
         }
     }
 
+    // Data received from IkeSessionStateMachine on successful EVENT_CHILD_SESSION_OPENED.
     private static final class TunnelOpenedData {
         final String mApnName;
         final List<InetAddress> mInternalDnsServers;
@@ -1737,6 +1789,20 @@
         }
     }
 
+    // Data received from IkeSessionStateMachine on successful EVENT_IKE_SESSION_OPENED.
+    private static final class IkeSessionOpenedData {
+        final String mApnName;
+        final IkeSessionConfiguration mIkeSessionConfiguration;
+
+        private IkeSessionOpenedData(
+                String apnName, IkeSessionConfiguration ikeSessionConfiguration) {
+            mApnName = apnName;
+            mIkeSessionConfiguration = ikeSessionConfiguration;
+        }
+    }
+
+    // Data received from IkeSessionStateMachine if either IKE session or Child session have been
+    // closed, normally or exceptionally.
     private static final class SessionClosedData {
         final String mApnName;
         final IwlanError mIwlanError;
@@ -1951,6 +2017,11 @@
     }
 
     @VisibleForTesting
+    TunnelConfig getTunnelConfigForApn(String apnName) {
+        return mApnNameToTunnelConfig.get(apnName);
+    }
+
+    @VisibleForTesting
     long reportIwlanError(String apnName, IwlanError error) {
         return ErrorPolicyManager.getInstance(mContext, mSlotId).reportIwlanError(apnName, error);
     }
diff --git a/src/com/google/android/iwlan/exceptions/IwlanException.java b/src/com/google/android/iwlan/exceptions/IwlanException.java
new file mode 100644
index 0000000..9afad54
--- /dev/null
+++ b/src/com/google/android/iwlan/exceptions/IwlanException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.exceptions;
+
+/** Generic Exception for Iwlan error. */
+public abstract class IwlanException extends Exception {
+    protected IwlanException() {
+        super();
+    }
+
+    protected IwlanException(String message) {
+        super(message);
+    }
+
+    protected IwlanException(Throwable cause) {
+        super(cause);
+    }
+
+    protected IwlanException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/com/google/android/iwlan/exceptions/IwlanSimNotReadyException.java b/src/com/google/android/iwlan/exceptions/IwlanSimNotReadyException.java
new file mode 100644
index 0000000..424bd5a
--- /dev/null
+++ b/src/com/google/android/iwlan/exceptions/IwlanSimNotReadyException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.exceptions;
+
+/** Represent the error when getting the invalid Subscription info */
+public final class IwlanSimNotReadyException extends IwlanException {
+    public IwlanSimNotReadyException() {
+        super();
+    }
+
+    public IwlanSimNotReadyException(String message) {
+        super(message);
+    }
+
+    public IwlanSimNotReadyException(Throwable cause) {
+        super(cause);
+    }
+
+    public IwlanSimNotReadyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index 718a875..3bc624b 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -191,7 +191,7 @@
     }
 
     @Test
-    public void testInvalidCarrierConfig() throws Exception {
+    public void testDefaultPolicyFallback() throws Exception {
         String apn = "ims";
         String config =
                 "[{"
@@ -217,17 +217,21 @@
 
         sleep(1000);
 
-        // IKE_PROTOCOL_ERROR_TYPE(24) and retryArray = 5, 10, 15 as it will fallback due to failed
-        // parsing
+        // Fallback to default Iwlan error policy for IKE_PROTOCOL_ERROR_TYPE(24) because of failed
+        // parsing (or lack of explicit carrier-defined policy).
         IwlanError iwlanError = buildIwlanIkeAuthFailedError();
         long time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
-        assertEquals(5, time);
-        time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
         assertEquals(10, time);
         time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
-        assertEquals(10, time);
+        assertEquals(20, time);
         time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
-        assertEquals(10, time);
+        assertEquals(40, time);
+        time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+        assertEquals(80, time);
+        time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+        assertEquals(160, time);
+        time = mErrorPolicyManager.reportIwlanError(apn, iwlanError);
+        assertEquals(86400, time);
     }
 
     @Test
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index 97b409c..5960e00 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -36,6 +36,7 @@
 import android.net.ipsec.ike.IkeFqdnIdentification;
 import android.net.ipsec.ike.IkeSession;
 import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.SaProposal;
 import android.net.ipsec.ike.TunnelModeChildSessionParams;
@@ -71,6 +72,7 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -79,8 +81,16 @@
     public static final int DEFAULT_SLOT_INDEX = 0;
     public static final int DEFAULT_SUBID = 0;
 
-    private final String TEST_IP_ADDRESS = "127.0.0.1";
-    private final String TEST_APN_NAME = "www.xyz.com";
+    private static final String TEST_IP_ADDRESS = "127.0.0.1";
+    private static final String TEST_APN_NAME = "www.xyz.com";
+
+    private static final ArrayList<InetAddress> EXPECTED_EPDG_ADDRESSES =
+            new ArrayList<>(
+                    Arrays.asList(
+                            new InetAddress[] {
+                                InetAddresses.parseNumericAddress(TEST_IP_ADDRESS)
+                            }));
+
     private EpdgTunnelManager mEpdgTunnelManager;
 
     private class IwlanTunnelCallback implements EpdgTunnelManager.TunnelCallback {
@@ -103,6 +113,7 @@
     @Mock TelephonyManager mMockTelephonyManager;
     @Mock EpdgTunnelManager.IkeSessionCreator mMockIkeSessionCreator;
     @Mock IkeException mMockIkeException;
+    @Mock IkeSessionConfiguration mMockIkeSessionConfiguration;
 
     @Before
     public void setUp() throws Exception {
@@ -123,6 +134,10 @@
                         any(EpdgSelector.EpdgSelectorCallback.class)))
                 .thenReturn(new IwlanError(IwlanError.NO_ERROR));
 
+        // initialize carrier configuration
+        PersistableBundle bundle = new PersistableBundle();
+        setupMockForGetConfig(bundle);
+
         // Fake local address
         List<InetAddress> testLocalAddressList = new ArrayList<InetAddress>();
         InetAddress testInetaddr = InetAddress.getByName(TEST_IP_ADDRESS);
@@ -227,6 +242,9 @@
         TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
         doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
 
+        PersistableBundle bundle = new PersistableBundle();
+        setupMockForGetConfig(bundle);
+
         boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
         assertTrue(ret);
         mTestLooper.dispatchAll();
@@ -241,6 +259,160 @@
                         any());
     }
 
+    private void setupTunnelBringup() throws Exception {
+        setupMockForGetConfig(null);
+        doReturn(null)
+                .when(mMockIkeSessionCreator)
+                .createIkeSession(
+                        eq(mMockContext),
+                        any(IkeSessionParams.class),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+
+        doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+        boolean ret =
+                mEpdgTunnelManager.bringUpTunnel(
+                        getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+                        mMockIwlanTunnelCallback);
+        assertTrue(ret);
+        mTestLooper.dispatchAll();
+
+        ArrayList<InetAddress> ipList = new ArrayList<>();
+        ipList.add(InetAddress.getByName(TEST_IP_ADDRESS));
+        mEpdgTunnelManager.sendSelectionRequestComplete(
+                ipList, new IwlanError(IwlanError.NO_ERROR), 1);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testBringUpTunnelSetsDeviceIdentityImeiSv() throws Exception {
+        when(mMockContext.getSystemService(eq(TelephonyManager.class)))
+                .thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.createForSubscriptionId(DEFAULT_SUBID))
+                .thenReturn(mMockTelephonyManager);
+
+        String TEST_IMEI = "012345678901234";
+        String TEST_IMEI_SUFFIX = "56";
+        String EXPECTED_IMEISV = TEST_IMEI.substring(0, TEST_IMEI.length() - 1) + TEST_IMEI_SUFFIX;
+        when(mMockTelephonyManager.getImei()).thenReturn(TEST_IMEI);
+        when(mMockTelephonyManager.getDeviceSoftwareVersion()).thenReturn(TEST_IMEI_SUFFIX);
+
+        setupTunnelBringup();
+
+        ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        verify(mMockIkeSessionCreator, atLeastOnce())
+                .createIkeSession(
+                        eq(mMockContext),
+                        ikeSessionParamsCaptor.capture(),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+        IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
+        assertEquals(
+                ikeSessionParams.getIke3gppExtension().getIke3gppParams().getMobileDeviceIdentity(),
+                EXPECTED_IMEISV);
+    }
+
+    @Test
+    public void testBringUpTunnelSetsDeviceIdentityImei() throws Exception {
+        when(mMockContext.getSystemService(eq(TelephonyManager.class)))
+                .thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.createForSubscriptionId(DEFAULT_SUBID))
+                .thenReturn(mMockTelephonyManager);
+
+        String TEST_IMEI = "012345678901234";
+        when(mMockTelephonyManager.getImei()).thenReturn(TEST_IMEI);
+        when(mMockTelephonyManager.getDeviceSoftwareVersion()).thenReturn(null);
+
+        setupTunnelBringup();
+
+        ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        verify(mMockIkeSessionCreator, atLeastOnce())
+                .createIkeSession(
+                        eq(mMockContext),
+                        ikeSessionParamsCaptor.capture(),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+        IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
+        assertEquals(
+                ikeSessionParams.getIke3gppExtension().getIke3gppParams().getMobileDeviceIdentity(),
+                TEST_IMEI);
+    }
+
+    @Test
+    public void testBringUpTunnelNoDeviceIdentityWhenImeiUnavailable() throws Exception {
+        when(mMockContext.getSystemService(eq(TelephonyManager.class)))
+                .thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.createForSubscriptionId(DEFAULT_SUBID))
+                .thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.getImei()).thenReturn(null);
+
+        setupTunnelBringup();
+
+        ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        verify(mMockIkeSessionCreator, atLeastOnce())
+                .createIkeSession(
+                        eq(mMockContext),
+                        ikeSessionParamsCaptor.capture(),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+        IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
+        assertEquals(
+                ikeSessionParams.getIke3gppExtension().getIke3gppParams().getMobileDeviceIdentity(),
+                null);
+    }
+
+    @Test
+    public void testBringUpTunnelWithMobilityOptions() throws Exception {
+        doReturn(null)
+                .when(mMockIkeSessionCreator)
+                .createIkeSession(
+                        eq(mMockContext),
+                        any(IkeSessionParams.class),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+
+        doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
+
+        boolean ret =
+                mEpdgTunnelManager.bringUpTunnel(
+                        getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+                        mMockIwlanTunnelCallback);
+        assertTrue(ret);
+        mTestLooper.dispatchAll();
+
+        mEpdgTunnelManager.sendSelectionRequestComplete(
+                EXPECTED_EPDG_ADDRESSES, new IwlanError(IwlanError.NO_ERROR), 1);
+        mTestLooper.dispatchAll();
+
+        ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        verify(mMockIkeSessionCreator, atLeastOnce())
+                .createIkeSession(
+                        eq(mMockContext),
+                        ikeSessionParamsCaptor.capture(),
+                        any(ChildSessionParams.class),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+        IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
+        assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE));
+        assertTrue(ikeSessionParams.hasIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY));
+    }
+
     @Test
     public void testCloseTunnelWithNoTunnelForApn() throws Exception {
         String testApnName = "www.xyz.com";
@@ -819,7 +991,7 @@
     @Test
     public void testHandleOnClosedWithEpdgAddressSelected_True() throws Exception {
         String testApnName = "www.xyz.com";
-        IwlanError error = new IwlanError(IwlanError.NO_ERROR);
+        IwlanError error = new IwlanError(IwlanError.NETWORK_FAILURE);
 
         doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
         mEpdgTunnelManager.putApnNameToTunnelConfig(
@@ -838,7 +1010,7 @@
     @Test
     public void testHandleOnClosedWithEpdgAddressSelected_False() throws Exception {
         String testApnName = "www.xyz.com";
-        IwlanError error = new IwlanError(IwlanError.NO_ERROR);
+        IwlanError error = new IwlanError(IwlanError.NETWORK_FAILURE);
 
         doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
 
@@ -928,6 +1100,32 @@
     }
 
     @Test
+    public void testIkeSessionOnOpenedUpdatesPcscfAddrInTunnelConfig() throws Exception {
+        String testApnName = "ims";
+        IwlanError error = new IwlanError(IwlanError.NO_ERROR);
+
+        doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
+        mEpdgTunnelManager.putApnNameToTunnelConfig(
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
+
+        List<InetAddress> ipList = new ArrayList<>();
+        ipList.add(InetAddress.getByName(TEST_IP_ADDRESS));
+        when(mMockIkeSessionConfiguration.getPcscfServers()).thenReturn(ipList);
+
+        PersistableBundle bundle = new PersistableBundle();
+        setupMockForGetConfig(bundle);
+
+        mEpdgTunnelManager
+                .getTmIkeSessionCallback(testApnName)
+                .onOpened(mMockIkeSessionConfiguration);
+        mTestLooper.dispatchAll();
+
+        EpdgTunnelManager.TunnelConfig testApnTunnelConfig =
+                mEpdgTunnelManager.getTunnelConfigForApn(testApnName);
+        assertEquals(testApnTunnelConfig.getPcscfAddrList(), ipList);
+    }
+
+    @Test
     public void testSetIkeTrafficSelectorsIPv4() throws Exception {
         testSetIkeTrafficSelectors(ApnSetting.PROTOCOL_IP, false);
     }
@@ -1420,4 +1618,65 @@
         assertTrue(ipv6ConfigRequestPresent);
         assertTrue(ipv4ConfigRequestPresent);
     }
+
+    @Test
+    public void testBringupTunnelFailWithInvalidSimState() throws Exception {
+        String testApnName = "www.xyz.com";
+        IwlanError error = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION);
+
+        doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
+        doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName));
+
+        PersistableBundle bundle = new PersistableBundle();
+        setupMockForGetConfig(bundle);
+
+        when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(DEFAULT_SLOT_INDEX))
+                .thenReturn(null);
+
+        boolean ret =
+                mEpdgTunnelManager.bringUpTunnel(
+                        getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+                        mMockIwlanTunnelCallback);
+        assertTrue(ret);
+        mTestLooper.dispatchAll();
+
+        ArrayList<InetAddress> ipList = new ArrayList<>();
+        ipList.add(InetAddress.getByName(TEST_IP_ADDRESS));
+        mEpdgTunnelManager.sendSelectionRequestComplete(
+                ipList, new IwlanError(IwlanError.NO_ERROR), 1);
+        mTestLooper.dispatchAll();
+
+        verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
+    }
+
+    @Test
+    public void testBringupTunnelFailWithInvalidNai() throws Exception {
+        String testApnName = "www.xyz.com";
+        IwlanError error = new IwlanError(IwlanError.SIM_NOT_READY_EXCEPTION);
+
+        doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
+        doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(testApnName));
+
+        PersistableBundle bundle = new PersistableBundle();
+        setupMockForGetConfig(bundle);
+
+        when(mMockSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(DEFAULT_SLOT_INDEX))
+                .thenReturn(mMockSubscriptionInfo)
+                .thenReturn(null);
+
+        boolean ret =
+                mEpdgTunnelManager.bringUpTunnel(
+                        getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP),
+                        mMockIwlanTunnelCallback);
+        assertTrue(ret);
+        mTestLooper.dispatchAll();
+
+        ArrayList<InetAddress> ipList = new ArrayList<>();
+        ipList.add(InetAddress.getByName(TEST_IP_ADDRESS));
+        mEpdgTunnelManager.sendSelectionRequestComplete(
+                ipList, new IwlanError(IwlanError.NO_ERROR), 1);
+        mTestLooper.dispatchAll();
+
+        verify(mMockIwlanTunnelCallback, times(1)).onClosed(eq(testApnName), eq(error));
+    }
 }