Merge 6c195cd0a4142fa24f949c5f720c47763040db12 on remote branch

Change-Id: I6192e02e3f9a10a36c8be9ad09f0b91118e385ce
diff --git a/Android.bp b/Android.bp
index ef7af7c..f9a785b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 android_app {
     name: "Iwlan",
     manifest: "AndroidManifest.xml",
@@ -16,6 +20,7 @@
         "android.net.ipsec.ike.stubs.system",
         "auto_value_annotations",
 	"framework-annotations-lib",
+	"framework-connectivity",
 	"framework-wifi"
     ],
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f0cdd15..6a7a2d4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -12,6 +12,7 @@
   <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
   <uses-permission android:name="android.permission.WAKE_LOCK" />
+  <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
 
   <protected-broadcast android:name="IkeAlarmReceiver.ACTION_DELETE_CHILD" />
   <protected-broadcast android:name="IkeAlarmReceiver.ACTION_REKEY_CHILD" />
@@ -20,21 +21,21 @@
   <protected-broadcast android:name="IkeAlarmReceiver.ACTION_DPD" />
   <protected-broadcast android:name="IkeAlarmReceiver.ACTION_KEEPALIVE" />
 
-  <application>
+  <application
+      android:directBootAware="true"
+      android:defaultToDeviceProtectedStorage="true">
     <service android:name=".IwlanDataService"
+         android:exported="true"
          android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE"
-         android:enabled="true"
-         android:directBootAware="true"
-         android:exported="true" >
+         android:enabled="true" >
          <intent-filter>
                <action android:name="android.telephony.data.DataService" />
          </intent-filter>
       </service>
       <service android:name=".IwlanNetworkService"
+               android:exported="true"
                android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"
-               android:enabled="true"
-               android:directBootAware="true"
-               android:exported="true" >
+               android:enabled="true" >
           <intent-filter>
               <action android:name="android.telephony.NetworkService" />
           </intent-filter>
@@ -44,7 +45,6 @@
          <intent-filter>
             <action android:name="android.telephony.action.CARRIER_CONFIG_CHANGED"/>
             <action android:name="android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"/>
-            <action android:name="android.intent.action.AIRPLANE_MODE" />
          </intent-filter>
     </receiver>
       <uses-library android:name="android.net.ipsec.ike" />
diff --git a/src/com/google/android/iwlan/ErrorPolicyManager.java b/src/com/google/android/iwlan/ErrorPolicyManager.java
index c7f9fd9..66f6459 100644
--- a/src/com/google/android/iwlan/ErrorPolicyManager.java
+++ b/src/com/google/android/iwlan/ErrorPolicyManager.java
@@ -295,8 +295,7 @@
                 int protocolErrorType = ((IkeProtocolException) exception).getErrorType();
                 switch (protocolErrorType) {
                     case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED:
-                        // TODO: Change this as per b/175719031
-                        ret = DataFailCause.USER_AUTHENTICATION;
+                        ret = DataFailCause.IWLAN_IKEV2_AUTH_FAILURE;
                         break;
                     case IKE_PROTOCOL_ERROR_PDN_CONNECTION_REJECTION:
                         ret = DataFailCause.IWLAN_PDN_CONNECTION_REJECTION;
@@ -486,13 +485,13 @@
         for (int i = 0; i < apnArray.length(); i++) {
             JSONObject apnDetails = apnArray.getJSONObject(i);
 
-            String apnName = (String) apnDetails.get("ApnName");
+            String apnName = ((String) apnDetails.get("ApnName")).trim();
             JSONArray errorTypeArray = (JSONArray) apnDetails.get("ErrorTypes");
 
             for (int j = 0; j < errorTypeArray.length(); j++) {
                 JSONObject errorTypeObject = errorTypeArray.getJSONObject(j);
 
-                String errorTypeStr = (String) errorTypeObject.get("ErrorType");
+                String errorTypeStr = ((String) errorTypeObject.get("ErrorType")).trim();
                 JSONArray errorDetailArray = (JSONArray) errorTypeObject.get("ErrorDetails");
                 int errorType = UNKNOWN_ERROR_TYPE;
 
@@ -519,7 +518,7 @@
             throws JSONException, IllegalArgumentException {
         List<Integer> ret = new ArrayList<>();
         for (int i = 0; i < retryArray.length(); i++) {
-            String retryTime = retryArray.getString(i);
+            String retryTime = retryArray.getString(i).trim();
 
             // catch misplaced -1 retry times in the array.
             // 1. if it is not placed at the last position in the array
@@ -554,7 +553,8 @@
             throws JSONException, IllegalArgumentException {
         List<Integer> ret = new ArrayList<>();
         for (int i = 0; i < unthrottlingEvents.length(); i++) {
-            int event = IwlanEventListener.getUnthrottlingEvent(unthrottlingEvents.getString(i));
+            int event =
+              IwlanEventListener.getUnthrottlingEvent(unthrottlingEvents.getString(i).trim());
             if (event == IwlanEventListener.UNKNOWN_EVENT) {
                 throw new IllegalArgumentException(
                         "Unexpected unthrottlingEvent " + unthrottlingEvents.getString(i));
@@ -570,7 +570,7 @@
         boolean isValidErrorDetail = true;
 
         for (int i = 0; i < errorDetailArray.length(); i++) {
-            String errorDetail = errorDetailArray.getString(i);
+            String errorDetail = errorDetailArray.getString(i).trim();
             switch (errorType) {
                 case IKE_PROTOCOL_ERROR_TYPE:
                     isValidErrorDetail = verifyIkeProtocolErrorDetail(errorDetail);
diff --git a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
index 971f0d5..10b10e4 100644
--- a/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
+++ b/src/com/google/android/iwlan/IwlanBroadcastReceiver.java
@@ -42,6 +42,7 @@
         }
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         context.registerReceiver(getInstance(), intentFilter);
         mIsReceiverRegistered = true;
     }
diff --git a/src/com/google/android/iwlan/IwlanDataService.java b/src/com/google/android/iwlan/IwlanDataService.java
index 2aa6654..0394a34 100644
--- a/src/com/google/android/iwlan/IwlanDataService.java
+++ b/src/com/google/android/iwlan/IwlanDataService.java
@@ -43,7 +43,8 @@
 import android.telephony.data.DataProfile;
 import android.telephony.data.DataService;
 import android.telephony.data.DataServiceCallback;
-import android.telephony.data.SliceInfo;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -133,11 +134,13 @@
                 Network network, NetworkCapabilities networkCapabilities) {
             // onCapabilitiesChanged is guaranteed to be called immediately after onAvailable per
             // API
-            Log.d(TAG, "onCapabilitiesChanged: " + network);
+            Log.d(TAG, "onCapabilitiesChanged: " + network + " " + networkCapabilities);
             if (networkCapabilities != null) {
                 if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+                    Log.d(TAG, "Network " + network + " connected using transport MOBILE");
                     IwlanDataService.setNetworkConnected(true, network, Transport.MOBILE);
                 } else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+                    Log.d(TAG, "Network " + network + " connected using transport WIFI");
                     IwlanDataService.setNetworkConnected(true, network, Transport.WIFI);
                 } else {
                     Log.w(TAG, "Network does not have cellular or wifi capability");
@@ -455,6 +458,11 @@
                 }
             }
 
+            if (tunnelState.getTunnelLinkProperties().sliceInfo().isPresent()) {
+                responseBuilder.setSliceInfo(
+                        tunnelState.getTunnelLinkProperties().sliceInfo().get());
+            }
+
             return responseBuilder
                     .setAddresses(linkAddrList)
                     .setDnsAddresses(tunnelState.getTunnelLinkProperties().dnsAddresses())
@@ -532,6 +540,17 @@
          *     of values are 1-15 while 0 means no pdu session id was attached to this call.
          *     Reference: 3GPP TS 24.007 section 11.2.3.1b.
          * @param sliceInfo The slice info related to this data call.
+         * @param trafficDescriptor TrafficDescriptor for which data connection needs to be
+         *     established. It is used for URSP traffic matching as described in 3GPP TS 24.526
+         *     Section 4.2.2. It includes an optional DNN which, if present, must be used for
+         *     traffic matching; it does not specify the end point to be used for the data call.
+         * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this
+         *     request is allowed. If false, this request must not use the match-all URSP rule and
+         *     if a non-match-all rule is not found (or if URSP rules are not available) then {@link
+         *     DataCallResponse#getCause()} is {@link
+         *     android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed as some
+         *     requests need to have a hard failure if the intention cannot be met, for example, a
+         *     zero-rating slice.
          * @param callback The result callback for this request.
          */
         @Override
@@ -543,21 +562,27 @@
                 int reason,
                 @Nullable LinkProperties linkProperties,
                 @IntRange(from = 0, to = 15) int pduSessionId,
-                @Nullable SliceInfo sliceInfo,
+                @Nullable NetworkSliceInfo sliceInfo,
+                @Nullable TrafficDescriptor trafficDescriptor,
+                boolean matchAllRuleAllowed,
                 @NonNull DataServiceCallback callback) {
 
             Log.d(
                     SUB_TAG,
                     "Setup data call with network: "
                             + accessNetworkType
-                            + " reason: "
+                            + ", DataProfile: "
+                            + dataProfile
+                            + ", isRoaming:"
+                            + isRoaming
+                            + ", allowRoaming: "
+                            + allowRoaming
+                            + ", reason: "
                             + reason
-                            + " pduSessionId: "
-                            + pduSessionId
-                            + " linkProperties: "
+                            + ", linkProperties: "
                             + linkProperties
-                            + "DataProfile: "
-                            + dataProfile);
+                            + ", pduSessionId: "
+                            + pduSessionId);
 
             // Framework will never call bringup on the same APN back 2 back.
             // but add a safety check
@@ -574,11 +599,20 @@
             }
 
             synchronized (mTunnelStateForApn) {
-                if (isNetworkConnected(
-                                        IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
-                                        IwlanHelper.isCrossSimCallingEnabled(
-                                                mContext, getSlotIndex()))
-                                == false
+                boolean isDDS = IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex());
+                boolean isCSTEnabled =
+                        IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex());
+                boolean networkConnected = isNetworkConnected(isDDS, isCSTEnabled);
+                Log.d(
+                        SUB_TAG,
+                        "isDds: "
+                                + isDDS
+                                + ", isCstEnabled: "
+                                + isCSTEnabled
+                                + ", transport: "
+                                + sDefaultDataTransport);
+
+                if (networkConnected == false
                         || mTunnelStateForApn.get(dataProfile.getApn()) != null) {
                     deliverCallback(
                             CALLBACK_TYPE_SETUP_DATACALL_COMPLETE,
@@ -674,14 +708,12 @@
                         */
                         mTunnelStateForApn.get(apnName).setState(TunnelState.TUNNEL_IN_BRINGDOWN);
                         mTunnelStateForApn.get(apnName).setDataServiceCallback(callback);
-                        getTunnelManager()
-                                .closeTunnel(
-                                        apnName,
-                                        !isNetworkConnected(
-                                                IwlanHelper.isDefaultDataSlot(
-                                                        mContext, getSlotIndex()),
-                                                IwlanHelper.isCrossSimCallingEnabled(
-                                                        mContext, getSlotIndex())));
+                        boolean isConnected =
+                                isNetworkConnected(
+                                        IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()),
+                                        IwlanHelper.isCrossSimCallingEnabled(
+                                                mContext, getSlotIndex()));
+                        getTunnelManager().closeTunnel(apnName, !isConnected);
                         return;
                     }
                 }
@@ -705,7 +737,7 @@
             }
         }
 
-        private void forceCloseTunnels() {
+        void forceCloseTunnels() {
             synchronized (mTunnelStateForApn) {
                 for (Map.Entry<String, TunnelState> entry : mTunnelStateForApn.entrySet()) {
                     getTunnelManager().closeTunnel(entry.getKey(), true);
@@ -758,7 +790,6 @@
                         && mCarrierConfigReady == true
                         && mWfcEnabled == true
                         && mTunnelStateForApn.isEmpty()) {
-                    Log.d(TAG, "Trigger DNS prefetching");
 
                     // Get roaming status
                     TelephonyManager telephonyManager =
@@ -767,18 +798,9 @@
                             telephonyManager.createForSubscriptionId(
                                     IwlanHelper.getSubId(mContext, getSlotIndex()));
                     boolean isRoaming = telephonyManager.isNetworkRoaming();
-                    Log.d(TAG, "is Roaming " + isRoaming);
+                    Log.d(TAG, "Trigger EPDG prefetch. Roaming=" + isRoaming);
 
                     prefetchEpdgServerList(mIwlanDataService.sNetwork, isRoaming);
-                } else {
-                    Log.d(
-                            TAG,
-                            "Network connected:"
-                                    + networkConnected
-                                    + " CarrierConfigReady:"
-                                    + mCarrierConfigReady
-                                    + " WfcEnabled:"
-                                    + mWfcEnabled);
                 }
             }
         }
@@ -805,14 +827,6 @@
 
     @VisibleForTesting
     static boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) {
-        Log.d(
-                TAG,
-                "isDds: "
-                        + isDds
-                        + ", isCstEnabled: "
-                        + isCstEnabled
-                        + ", transport: "
-                        + sDefaultDataTransport);
         if (!isDds && isCstEnabled) {
             // Only Non-DDS sub with CST enabled, can use any transport.
             return sNetworkConnected;
@@ -823,18 +837,17 @@
     }
 
     @VisibleForTesting
+    /* Note: this api should have valid transport if networkConnected==true */
     static void setNetworkConnected(
             boolean networkConnected, Network network, Transport transport) {
         sNetworkConnected = networkConnected;
         sNetwork = network;
-
         if (networkConnected) {
             if (transport == Transport.UNSPECIFIED_NETWORK) {
-                // just return since we do not know the transport yet
+                Log.e(TAG, "setNetworkConnected: Network connected but transport unspecified");
                 return;
             }
-            if (sDefaultDataTransport != Transport.UNSPECIFIED_NETWORK
-                    && transport != sDefaultDataTransport) {
+            if (transport != sDefaultDataTransport) {
                 Log.d(
                         TAG,
                         "Transport was changed from "
@@ -851,7 +864,9 @@
 
         if (!networkConnected) {
             for (IwlanDataServiceProvider dp : sIwlanDataServiceProviderList) {
-                dp.forceCloseTunnelsInDeactivatingState();
+                //once network is disconnect, even NAT KA offload fails
+                //so we should force close all tunnels
+                dp.forceCloseTunnels();
             }
         } else {
             if (transport == Transport.WIFI) {
diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java
index 73bde13..7988aca 100644
--- a/src/com/google/android/iwlan/IwlanHelper.java
+++ b/src/com/google/android/iwlan/IwlanHelper.java
@@ -21,8 +21,6 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -224,7 +222,7 @@
             ImsMmTelManager imsMmTelManager = imsManager.getImsMmTelManager(subid);
             if (imsMmTelManager != null) {
                 try {
-                    isCstEnabled = imsMmTelManager.isCrossSimCallingEnabledByUser();
+                    isCstEnabled = imsMmTelManager.isCrossSimCallingEnabled();
                 } catch (Exception e) {
                     // Fail to query Cross-SIM calling setting, just return false to avoid an
                     // exception.
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index ede3ec8..f36f348 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -17,11 +17,9 @@
 package com.google.android.iwlan.epdg;
 
 import android.content.Context;
-import android.net.DnsResolver;
 import android.net.Network;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
@@ -46,7 +44,6 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 
 public class EpdgSelector {
@@ -144,50 +141,36 @@
         // Get All IP for each domain name
         Log.d(TAG, "Input domainName : " + domainName);
         try {
-            CompletableFuture<List<InetAddress>> result = new CompletableFuture();
-            final DnsResolver.Callback<List<InetAddress>> cb =
-                    new DnsResolver.Callback<List<InetAddress>>() {
-                        @Override
-                        public void onAnswer(
-                                @NonNull final List<InetAddress> answer, final int rcode) {
-                            if (rcode == 0) {
-                                result.complete(answer);
-                            } else {
-                                Log.e(TAG, "DnsResover Response Code = " + rcode);
-                            }
-                        }
-
-                        @Override
-                        public void onError(@Nullable final DnsResolver.DnsException error) {
-                            Log.e(TAG, "Resolve DNS with error : " + error);
-                            result.completeExceptionally(error);
-                        }
-                    };
-            DnsResolver.getInstance()
-                    .query(network, domainName, DnsResolver.FLAG_EMPTY, r -> r.run(), null, cb);
-
-            // Filter the IP list by input ProtoFilter
-            for (InetAddress ipAddress : result.get()) {
-                switch (filter) {
-                    case PROTO_FILTER_IPV4:
-                        if (ipAddress instanceof Inet4Address) {
-                            validIpList.add(ipAddress);
-                        }
-                        break;
-                    case PROTO_FILTER_IPV6:
-                        if (ipAddress instanceof Inet6Address) {
-                            validIpList.add(ipAddress);
-                        }
-                        break;
-                    case PROTO_FILTER_IPV4V6:
-                        validIpList.add(ipAddress);
-                        break;
-                    default:
-                        Log.d(TAG, "Invalid ProtoFilter : " + filter);
-                }
-            }
+            ipList = network.getAllByName(domainName);
         } catch (Exception e) {
-            Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e);
+            Log.e(TAG, "Exception when querying IP address : " + e);
+            return;
+        }
+
+        if (ipList == null) {
+            Log.e(TAG, "Get empty IP address list");
+            return;
+        }
+
+        // Filter the IP list by input ProtoFilter
+        for (InetAddress ipAddress : ipList) {
+            switch (filter) {
+                case PROTO_FILTER_IPV4:
+                    if (ipAddress instanceof Inet4Address) {
+                        validIpList.add(ipAddress);
+                    }
+                    break;
+                case PROTO_FILTER_IPV6:
+                    if (ipAddress instanceof Inet6Address) {
+                        validIpList.add(ipAddress);
+                    }
+                    break;
+                case PROTO_FILTER_IPV4V6:
+                    validIpList.add(ipAddress);
+                    break;
+                default:
+                    Log.d(TAG, "Invalid ProtoFilter : " + filter);
+            }
         }
     }
 
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index 3041faa..43c8bcf 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.net.InetAddresses;
+import android.net.IpPrefix;
 import android.net.IpSecManager;
 import android.net.IpSecTransform;
 import android.net.LinkAddress;
@@ -42,6 +43,7 @@
 import android.net.ipsec.ike.IkeSessionConfiguration;
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.SaProposal;
 import android.net.ipsec.ike.TunnelModeChildSessionParams;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -58,6 +60,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.telephony.data.NetworkSliceInfo;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -162,48 +165,43 @@
                 Collections.unmodifiableSet(
                         new HashSet<>(
                                 Arrays.asList(
-                                        CarrierConfigManager.Iwlan.DH_GROUP_1024_BIT_MODP,
-                                        CarrierConfigManager.Iwlan.DH_GROUP_1536_BIT_MODP,
-                                        CarrierConfigManager.Iwlan.DH_GROUP_2048_BIT_MODP)));
+                                        SaProposal.DH_GROUP_1024_BIT_MODP,
+                                        SaProposal.DH_GROUP_1536_BIT_MODP,
+                                        SaProposal.DH_GROUP_2048_BIT_MODP)));
         VALID_KEY_LENGTHS =
                 Collections.unmodifiableSet(
                         new HashSet<>(
                                 Arrays.asList(
-                                        CarrierConfigManager.Iwlan.KEY_LEN_AES_128,
-                                        CarrierConfigManager.Iwlan.KEY_LEN_AES_192,
-                                        CarrierConfigManager.Iwlan.KEY_LEN_AES_256)));
+                                        SaProposal.KEY_LEN_AES_128,
+                                        SaProposal.KEY_LEN_AES_192,
+                                        SaProposal.KEY_LEN_AES_256)));
 
         VALID_ENCRYPTION_ALGOS =
                 Collections.unmodifiableSet(
                         new HashSet<>(
                                 Arrays.asList(
-                                        CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CBC,
-                                        CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CTR)));
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_CBC,
+                                        SaProposal.ENCRYPTION_ALGORITHM_AES_CTR)));
 
         VALID_INTEGRITY_ALGOS =
                 Collections.unmodifiableSet(
                         new HashSet<>(
                                 Arrays.asList(
-                                        CarrierConfigManager.Iwlan.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
-                                        CarrierConfigManager.Iwlan.INTEGRITY_ALGORITHM_AES_XCBC_96,
-                                        CarrierConfigManager.Iwlan
-                                                .INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
-                                        CarrierConfigManager.Iwlan
-                                                .INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
-                                        CarrierConfigManager.Iwlan
-                                                .INTEGRITY_ALGORITHM_HMAC_SHA2_512_256)));
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+                                        SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+                                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256)));
 
         VALID_PRF_ALGOS =
                 Collections.unmodifiableSet(
                         new HashSet<>(
                                 Arrays.asList(
-                                        CarrierConfigManager.Iwlan.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
-                                        CarrierConfigManager.Iwlan
-                                                .PSEUDORANDOM_FUNCTION_AES128_XCBC,
-                                        CarrierConfigManager.Iwlan.PSEUDORANDOM_FUNCTION_SHA2_256,
-                                        CarrierConfigManager.Iwlan.PSEUDORANDOM_FUNCTION_SHA2_384,
-                                        CarrierConfigManager.Iwlan
-                                                .PSEUDORANDOM_FUNCTION_SHA2_512)));
+                                        SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+                                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512)));
     }
 
     private final EpdgSelector.EpdgSelectorCallback mSelectorCallback =
@@ -219,22 +217,26 @@
                 }
             };
 
-    private static class TunnelConfig {
+    @VisibleForTesting
+    class TunnelConfig {
         @NonNull final TunnelCallback mTunnelCallback;
         // TODO: Change this to TunnelLinkProperties after removing autovalue
         private List<InetAddress> mPcscfAddrList;
         private List<InetAddress> mDnsAddrList;
         private List<LinkAddress> mInternalAddrList;
-        private byte[] mSnssai;
+
+        private InetAddress mSrcIpv6Address;
+        private int mSrcIpv6AddressPrefixLen;
+        private NetworkSliceInfo mSliceInfo;
         private boolean mIsBackoffTimeValid = false;
         private long mBackoffTime;
 
-        public byte[] getSnssai() {
-            return mSnssai;
+        public NetworkSliceInfo getSliceInfo() {
+            return mSliceInfo;
         }
 
-        public void setSnssai(byte[] snssai) {
-            mSnssai = snssai;
+        public void setSliceInfo(NetworkSliceInfo si) {
+            mSliceInfo = si;
         }
 
         public boolean isBackoffTimeValid() {
@@ -254,10 +256,16 @@
         IwlanError mError;
         private IpSecManager.IpSecTunnelInterface mIface;
 
-        public TunnelConfig(IkeSession ikeSession, TunnelCallback tunnelCallback) {
+        public TunnelConfig(
+                IkeSession ikeSession,
+                TunnelCallback tunnelCallback,
+                InetAddress srcIpv6Addr,
+                int srcIpv6PrefixLength) {
             mTunnelCallback = tunnelCallback;
             mIkeSession = ikeSession;
             mError = new IwlanError(IwlanError.NO_ERROR);
+            mSrcIpv6Address = srcIpv6Addr;
+            mSrcIpv6AddressPrefixLen = srcIpv6PrefixLength;
         }
 
         @NonNull
@@ -285,8 +293,42 @@
             return mInternalAddrList;
         }
 
+        boolean isPrefixSameAsSrcIP(LinkAddress laddr) {
+            if (laddr.isIpv6() && (laddr.getPrefixLength() == mSrcIpv6AddressPrefixLen)) {
+                IpPrefix assignedPrefix = new IpPrefix(laddr.getAddress(), laddr.getPrefixLength());
+                IpPrefix srcPrefix = new IpPrefix(mSrcIpv6Address, mSrcIpv6AddressPrefixLen);
+                if (assignedPrefix.equals(srcPrefix)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         public void setInternalAddrList(List<LinkAddress> internalAddrList) {
-            mInternalAddrList = internalAddrList;
+            mInternalAddrList = new ArrayList<LinkAddress>(internalAddrList);
+            if (getSrcIpv6Address() != null) {
+                // check if we can reuse src ipv6 address (i.e if prefix is same)
+                for (LinkAddress assignedAddr : internalAddrList) {
+                    if (isPrefixSameAsSrcIP(assignedAddr)) {
+                        // the assigned IPv6 address is same as pre-Handover IPv6
+                        // addr. Just reuse the pre-Handover Address so the IID is
+                        // preserved
+                        mInternalAddrList.remove(assignedAddr);
+
+                        // add original address
+                        mInternalAddrList.add(
+                                new LinkAddress(mSrcIpv6Address, mSrcIpv6AddressPrefixLen));
+
+                        Log.d(
+                                TAG,
+                                "Network assigned IP replaced OLD:"
+                                        + internalAddrList
+                                        + " NEW:"
+                                        + mInternalAddrList);
+                        break;
+                    }
+                }
+            }
         }
 
         @NonNull
@@ -309,6 +351,14 @@
         public void setIface(IpSecManager.IpSecTunnelInterface iface) {
             mIface = iface;
         }
+
+        public InetAddress getSrcIpv6Address() {
+            return mSrcIpv6Address;
+        }
+
+        public int getSrcIpv6AddressPrefixLen() {
+            return mSrcIpv6AddressPrefixLen;
+        }
     }
 
     @VisibleForTesting
@@ -365,13 +415,22 @@
                 TunnelConfig tunnelConfig = mApnNameToTunnelConfig.get(mApnName);
                 for (Ike3gppData payload : payloads) {
                     if (payload.getDataType() == DATA_TYPE_NOTIFY_N1_MODE_INFORMATION) {
-                        tunnelConfig.setSnssai(((Ike3gppN1ModeInformation) payload).getSnssai());
+                        Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_N1_MODE_INFORMATION");
+                        NetworkSliceInfo si =
+                                NetworkSliceSelectionAssistanceInformation.getSliceInfo(
+                                        ((Ike3gppN1ModeInformation) payload).getSnssai());
+                        if (si != null) {
+                            tunnelConfig.setSliceInfo(si);
+                            Log.d(TAG, "SliceInfo: " + si);
+                        }
                     } else if (payload.getDataType() == DATA_TYPE_NOTIFY_BACKOFF_TIMER) {
+                        Log.d(TAG, "Got payload DATA_TYPE_NOTIFY_BACKOFF_TIMER");
                         long backoffTime =
                                 decodeBackoffTime(
                                         ((Ike3gppBackoffTimer) payload).getBackoffTimer());
                         if (backoffTime > 0) {
                             tunnelConfig.setBackoffTime(backoffTime);
+                            Log.d(TAG, "Backoff Timer: " + backoffTime);
                         }
                     }
                 }
@@ -398,7 +457,7 @@
 
             IpSecManager.IpSecTunnelInterface tunnelInterface = tunnelConfig.getIface();
 
-            for (LinkAddress address : sessionConfiguration.getInternalAddresses()) {
+            for (LinkAddress address : tunnelConfig.getInternalAddrList()) {
                 try {
                     tunnelInterface.addAddress(address.getAddress(), address.getPrefixLength());
                 } catch (IOException e) {
@@ -412,7 +471,7 @@
                             .setDnsAddresses(tunnelConfig.getDnsAddrList())
                             .setPcscfAddresses(tunnelConfig.getPcscfAddrList())
                             .setIfaceName(tunnelConfig.getIface().getInterfaceName())
-                            .setSNssai(tunnelConfig.getSnssai())
+                            .setSliceInfo(tunnelConfig.getSliceInfo())
                             .build();
             mHandler.dispatchMessage(
                     mHandler.obtainMessage(
@@ -586,7 +645,13 @@
                                 getTmIkeSessionCallback(apnName),
                                 new TmChildSessionCallBack(apnName));
 
-        putApnNameToTunnelConfig(apnName, ikeSession, tunnelCallback);
+        boolean isSrcIpv6Present = setupRequest.srcIpv6Address().isPresent();
+        putApnNameToTunnelConfig(
+                apnName,
+                ikeSession,
+                tunnelCallback,
+                isSrcIpv6Present ? setupRequest.srcIpv6Address().get() : null,
+                setupRequest.srcIpv6AddressPrefixLength());
     }
 
     /**
@@ -733,6 +798,10 @@
 
         IkeSessionParams.Builder builder =
                 new IkeSessionParams.Builder(mContext)
+                        //permanently hardcode DSCP to 46 (Expedited Forwarding class)
+                        //See https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml
+                        //This will make WiFi prioritize IKE signallig under WMM AC_VO
+                        .setDscp(46)
                         .setServerHostname(mEpdgAddress.getHostName())
                         .setLocalIdentification(getLocalIdentification())
                         .setRemoteIdentification(getId(setupRequest.apnName(), false))
@@ -825,7 +894,7 @@
         for (int encryptionAlgo : encryptionAlgos) {
             validateConfig(encryptionAlgo, VALID_ENCRYPTION_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO);
 
-            if (encryptionAlgo == CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CBC) {
+            if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CBC) {
                 int[] aesCbcKeyLens =
                         getConfig(
                                 CarrierConfigManager.Iwlan
@@ -837,7 +906,7 @@
                 }
             }
 
-            if (encryptionAlgo == CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CTR) {
+            if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CTR) {
                 int[] aesCtrKeyLens =
                         getConfig(
                                 CarrierConfigManager.Iwlan
@@ -900,7 +969,7 @@
         for (int encryptionAlgo : encryptionAlgos) {
             if (validateConfig(encryptionAlgo, VALID_ENCRYPTION_ALGOS, CONFIG_TYPE_ENCRYPT_ALGO)) {
                 if (ChildSaProposal.getSupportedEncryptionAlgorithms().contains(encryptionAlgo)) {
-                    if (encryptionAlgo == CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CBC) {
+                    if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CBC) {
                         int[] aesCbcKeyLens =
                                 getConfig(
                                         CarrierConfigManager.Iwlan
@@ -914,7 +983,7 @@
                         }
                     }
 
-                    if (encryptionAlgo == CarrierConfigManager.Iwlan.ENCRYPTION_ALGORITHM_AES_CTR) {
+                    if (encryptionAlgo == SaProposal.ENCRYPTION_ALGORITHM_AES_CTR) {
                         int[] aesCtrKeyLens =
                                 getConfig(
                                         CarrierConfigManager.Iwlan
@@ -1026,7 +1095,7 @@
                 String message = error.getException().getCause().getMessage();
                 if (message != null
                         && (message.equals("Retransmitting IKE INIT request failure")
-                        || message.equals("Retransmitting failure"))) {
+                                || message.equals("Retransmitting failure"))) {
                     return true;
                 }
             }
@@ -1543,8 +1612,14 @@
 
     @VisibleForTesting
     void putApnNameToTunnelConfig(
-            String apnName, IkeSession ikeSession, TunnelCallback tunnelCallback) {
-        mApnNameToTunnelConfig.put(apnName, new TunnelConfig(ikeSession, tunnelCallback));
+            String apnName,
+            IkeSession ikeSession,
+            TunnelCallback tunnelCallback,
+            InetAddress srcIpv6Addr,
+            int srcIPv6AddrPrefixLen) {
+        mApnNameToTunnelConfig.put(
+                apnName,
+                new TunnelConfig(ikeSession, tunnelCallback, srcIpv6Addr, srcIPv6AddrPrefixLen));
     }
 
     @VisibleForTesting
diff --git a/src/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformation.java b/src/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformation.java
new file mode 100644
index 0000000..669aa5d
--- /dev/null
+++ b/src/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformation.java
@@ -0,0 +1,104 @@
+package com.google.android.iwlan.epdg;
+
+import android.support.annotation.NonNull;
+import android.telephony.data.NetworkSliceInfo;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class NetworkSliceSelectionAssistanceInformation {
+    private static final String TAG =
+            NetworkSliceSelectionAssistanceInformation.class.getSimpleName();
+    static final List<Integer> VALID_LENGTHS = Arrays.asList(1, 2, 4, 5, 8);
+
+    public static NetworkSliceInfo getSliceInfo(byte[] snssai) {
+        if (snssai == null) {
+            return null;
+        }
+        /**
+         * From 3GPP TS 24.501 Section 9.11.2.8, Content structure of the Value of S-NSSAI
+         *
+         * <p>Slice Service Type - 1 byte
+         * Slice Differentiator - 3 bytes
+         * Mapped HPLMN Slice Service Type - 1 byte
+         * Mapped HPLMN Slice Differentiator - 3 bytes
+         *
+         * <p>Depending on the value of the length field the following S-NSSAI contents are
+         * included:
+         * Bits 8 7 6 5 4 3 2 1
+         * 0 0 0 0 0 0 0 1 SST
+         * 0 0 0 0 0 0 1 0 SST and mapped HPLMN SST
+         * 0 0 0 0 0 1 0 0 SST and SD
+         * 0 0 0 0 0 1 0 1 SST, SD and mapped HPLMN SST
+         * 0 0 0 0 1 0 0 0 SST, SD, mapped HPLMN SST and mapped HPLMN SD
+         * All other values are reserved.
+         */
+        NetworkSliceInfo.Builder siBuilder = new NetworkSliceInfo.Builder();
+        int len = snssai.length;
+        // validity checks. See TS 24.501 Section 9.11.2.8
+        // length should be valid
+        if (!VALID_LENGTHS.contains(len)) {
+            Log.d(TAG, "Invalid S-NSSAI length : " + snssai.length);
+            return null;
+        }
+
+        switch (len) {
+            case 1: // get SST
+                siBuilder.setSliceServiceType(getSST(snssai, 0));
+                break;
+            case 2: // get SST and mapped SST
+                siBuilder.setSliceServiceType(getSST(snssai, 0));
+                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 1));
+                break;
+            case 4: // get SST and SD
+                siBuilder.setSliceServiceType(getSST(snssai, 0));
+                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
+                break;
+            case 5: // get SST, SD and mapped SST
+                siBuilder.setSliceServiceType(getSST(snssai, 0));
+                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
+                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 4));
+                break;
+            case 8: // get SST, SD, mapped SST, mapped SD
+                siBuilder.setSliceServiceType(getSST(snssai, 0));
+                siBuilder.setSliceDifferentiator(getSD(snssai, 1));
+                siBuilder.setMappedHplmnSliceServiceType(getSST(snssai, 4));
+                siBuilder.setMappedHplmnSliceDifferentiator(getSD(snssai, 5));
+                break;
+        }
+
+        return siBuilder.build();
+    }
+
+    private static int getSST(@NonNull byte[] snssai, int offset) {
+        if (offset < 0 || snssai.length < offset + 1) {
+            return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+        }
+        /**
+         * From 3GPP TS 23.003: Values 0 to 127 belong to the standardized SST range and they are
+         * defined in 3GPP TS 23.501. Values 128 to 255 belong to the Operator-specific range
+         */
+        return Byte.toUnsignedInt(snssai[offset]);
+    }
+
+    private static int getSD(byte[] snssai, int offset) {
+        int sliceDescriptor = NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+        /*
+         * Slice Descriptor is 3 bytes long
+         * The SD field has a reserved value "no SD value associated with the SST"
+         * defined as hexadecimal FFFFFF
+         */
+        if (offset >= 0 && snssai.length >= offset + 3) {
+            int sd = 0;
+            sd =
+                    (sd | snssai[offset + 2])
+                            | ((sd | snssai[offset + 1]) << 8)
+                            | ((sd | snssai[offset]) << 16);
+            if (sd != 0xFFFFFF) {
+                sliceDescriptor = sd;
+            }
+        }
+        return sliceDescriptor;
+    }
+}
diff --git a/src/com/google/android/iwlan/epdg/TunnelLinkProperties.java b/src/com/google/android/iwlan/epdg/TunnelLinkProperties.java
index 77f7295..864c282 100644
--- a/src/com/google/android/iwlan/epdg/TunnelLinkProperties.java
+++ b/src/com/google/android/iwlan/epdg/TunnelLinkProperties.java
@@ -17,6 +17,7 @@
 package com.google.android.iwlan.epdg;
 
 import android.net.LinkAddress;
+import android.telephony.data.NetworkSliceInfo;
 
 import com.google.auto.value.AutoValue;
 
@@ -34,10 +35,10 @@
 
     public abstract String ifaceName();
 
-    public abstract Optional<byte[]> sNssai();
+    public abstract Optional<NetworkSliceInfo> sliceInfo();
 
     static Builder builder() {
-        return new AutoValue_TunnelLinkProperties.Builder().setSNssai(Optional.empty());
+        return new AutoValue_TunnelLinkProperties.Builder().setSliceInfo(Optional.empty());
     }
 
     @AutoValue.Builder
@@ -50,11 +51,11 @@
 
         abstract Builder setIfaceName(String ifaceName);
 
-        public Builder setSNssai(byte[] snssai) {
-            return setSNssai(Optional.ofNullable(snssai));
+        public Builder setSliceInfo(NetworkSliceInfo si) {
+            return setSliceInfo(Optional.ofNullable(si));
         }
 
-        abstract Builder setSNssai(Optional<byte[]> snssai);
+        abstract Builder setSliceInfo(Optional<NetworkSliceInfo> si);
 
         abstract TunnelLinkProperties build();
     }
diff --git a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
index 44e50b0..320fbd0 100644
--- a/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
+++ b/test/com/google/android/iwlan/ErrorPolicyManagerTest.java
@@ -562,7 +562,7 @@
         assertEquals(5, time);
 
         int failCause = mErrorPolicyManager.getDataFailCause(apn1);
-        assertEquals(DataFailCause.USER_AUTHENTICATION, failCause);
+        assertEquals(DataFailCause.IWLAN_IKEV2_AUTH_FAILURE, failCause);
 
         failCause = mErrorPolicyManager.getDataFailCause(apn2);
         assertEquals(DataFailCause.IWLAN_PDN_CONNECTION_REJECTION, failCause);
diff --git a/test/com/google/android/iwlan/IwlanDataServiceTest.java b/test/com/google/android/iwlan/IwlanDataServiceTest.java
index 1320095..2c5de04 100644
--- a/test/com/google/android/iwlan/IwlanDataServiceTest.java
+++ b/test/com/google/android/iwlan/IwlanDataServiceTest.java
@@ -235,7 +235,7 @@
         boolean ret = mIwlanDataService.isNetworkConnected(true, false);
 
         assertFalse(ret);
-        verify(mMockIwlanDataServiceProvider).forceCloseTunnelsInDeactivatingState();
+        verify(mMockIwlanDataServiceProvider).forceCloseTunnels();
         mIwlanDataService.removeDataServiceProvider(mMockIwlanDataServiceProvider);
     }
 
@@ -319,6 +319,8 @@
                 null, /* LinkProperties */
                 2, /* pdu session id */
                 null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
                 mMockDataServiceCallback);
 
         verify(mMockDataServiceCallback, timeout(1000).times(1))
@@ -343,6 +345,8 @@
                 null, /* LinkProperties */
                 1, /* pdu session id */
                 null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
                 mMockDataServiceCallback);
 
         verify(mMockDataServiceCallback, timeout(1000).times(1))
@@ -379,6 +383,8 @@
                 null, /* LinkProperties */
                 1, /* pduSessionId */
                 null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
                 mMockDataServiceCallback);
 
         /* Check bringUpTunnel() is called. */
@@ -395,6 +401,49 @@
     }
 
     @Test
+    public void testSliceInfoInclusionInDataCallResponse() throws Exception {
+        DataProfile dp = buildDataProfile();
+
+        /* Wifi is connected */
+        mIwlanDataService.setNetworkConnected(true, mMockNetwork, IwlanDataService.Transport.WIFI);
+
+        doReturn(mMockEpdgTunnelManager).when(mSpyIwlanDataServiceProvider).getTunnelManager();
+
+        mSpyIwlanDataServiceProvider.setupDataCall(
+                AccessNetworkType.IWLAN, /* AccessNetworkType */
+                dp, /* dataProfile */
+                false, /* isRoaming */
+                true, /* allowRoaming */
+                DataService.REQUEST_REASON_NORMAL, /* DataService.REQUEST_REASON_NORMAL */
+                null, /* LinkProperties */
+                1, /* pduSessionId */
+                null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
+                mMockDataServiceCallback);
+
+        /* Check bringUpTunnel() is called. */
+        verify(mMockEpdgTunnelManager, times(1))
+                .bringUpTunnel(any(TunnelSetupRequest.class), any(IwlanTunnelCallback.class));
+
+        /* Check callback result is RESULT_SUCCESS when onOpened() is called. */
+        TunnelLinkProperties tp = TunnelLinkPropertiesTest.createTestTunnelLinkProperties();
+
+        ArgumentCaptor<DataCallResponse> dataCallResponseCaptor =
+                ArgumentCaptor.forClass(DataCallResponse.class);
+
+        mSpyIwlanDataServiceProvider.getIwlanTunnelCallback().onOpened(TEST_APN_NAME, tp);
+        verify(mMockDataServiceCallback, times(1))
+                .onSetupDataCallComplete(
+                        eq(DataServiceCallback.RESULT_SUCCESS), dataCallResponseCaptor.capture());
+
+        /* check that sliceinfo is filled up and matches */
+        DataCallResponse dataCallResponse = dataCallResponseCaptor.getValue();
+        assertNotNull(dataCallResponse.getSliceInfo());
+        assertEquals(dataCallResponse.getSliceInfo(), tp.sliceInfo().get());
+    }
+
+    @Test
     public void testIwlanDeactivateDataCallWithCloseTunnel() {
         DataProfile dp = buildDataProfile();
 
@@ -583,6 +632,8 @@
                 null, /* LinkProperties */
                 1, /* pdu session id */
                 null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
                 mMockDataServiceCallback);
 
         verify(mMockDataServiceCallback, timeout(1000).times(1))
@@ -622,6 +673,8 @@
                 null, /* LinkProperties */
                 1, /* pduSessionId */
                 null, /* sliceInfo */
+                null, /* trafficDescriptor */
+                true, /* matchAllRuleAllowed */
                 mMockDataServiceCallback);
 
         /* Check bringUpTunnel() is called. */
diff --git a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
index d7ffb0e..54c2fac 100644
--- a/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgSelectorTest.java
@@ -16,22 +16,11 @@
 
 package com.google.android.iwlan.epdg;
 
-import static android.net.DnsResolver.TYPE_A;
-import static android.net.DnsResolver.TYPE_AAAA;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
-import static java.util.stream.Collectors.toList;
-
 import android.content.Context;
-import android.net.DnsResolver;
-import android.net.InetAddresses;
 import android.net.Network;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityGsm;
@@ -50,22 +39,21 @@
 
 import com.google.android.iwlan.IwlanError;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.*;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
+@RunWith(JUnit4.class)
 public class EpdgSelectorTest {
     private static final String TAG = "EpdgSelectorTest";
     private EpdgSelector mEpdgSelector;
@@ -83,6 +71,8 @@
     private byte[] pcoData = testPcoString.getBytes();
     private List<String> ehplmnList = new ArrayList<String>();
 
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
     @Mock private Context mMockContext;
     @Mock private Network mMockNetwork;
     @Mock private SubscriptionManager mMockSubscriptionManager;
@@ -98,17 +88,11 @@
     @Mock private CellIdentityLte mMockCellIdentityLte;
     @Mock private CellInfoNr mMockCellInfoNr;
     @Mock private CellIdentityNr mMockCellIdentityNr;
-    @Mock private DnsResolver mMockDnsResolver;
 
     private PersistableBundle mTestBundle;
-    private FakeDns mFakeDns;
-    MockitoSession mStaticMockSession;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mStaticMockSession = mockitoSession().mockStatic(DnsResolver.class).startMocking();
-
         mEpdgSelector = new EpdgSelector(mMockContext, DEFAULT_SLOT_INDEX);
 
         when(mMockContext.getSystemService(eq(SubscriptionManager.class)))
@@ -135,24 +119,14 @@
         when(mMockContext.getSystemService(eq(CarrierConfigManager.class)))
                 .thenReturn(mMockCarrierConfigManager);
         when(mMockCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mTestBundle);
-
-        lenient().when(DnsResolver.getInstance()).thenReturn(mMockDnsResolver);
-
-        mFakeDns = new FakeDns();
-        mFakeDns.startMocking();
-    }
-
-    @After
-    public void cleanUp() throws Exception {
-        mStaticMockSession.finishMocking();
-        mFakeDns.clearAll();
     }
 
     @Test
     public void testStaticMethodPass() throws Exception {
-        // Set DnsResolver query mock
+        // Set Network.getAllByName mock
         final String testStaticAddress = "epdg.epc.mnc088.mcc888.pub.3gppnetwork.org";
-        mFakeDns.setAnswer(testStaticAddress, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+        when(mMockNetwork.getAllByName(eq(testStaticAddress)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS)});
 
         // Set carrier config mock
         mTestBundle.putIntArray(
@@ -184,17 +158,14 @@
     public void testPlmnResolutionMethodWithNoPlmnInCarrierConfig() throws Exception {
         // setUp() fills default values for mcc-mnc
         String expectedFqdn1 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
-        String expectedFqdn2 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org";
-
-        mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
-        mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
+        when(mMockNetwork.getAllByName(eq(expectedFqdn1)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_1)});
 
         ArrayList<InetAddress> testInetAddresses =
                 getValidatedServerListWithDefaultParams(false /*isEmergency*/);
 
-        assertEquals(testInetAddresses.size(), 2);
+        assertEquals(testInetAddresses.size(), 1);
         assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IP_ADDRESS_1)));
-        assertTrue(testInetAddresses.contains(InetAddress.getByName(TEST_IP_ADDRESS_2)));
     }
 
     private void testPlmnResolutionMethod(boolean isEmergency) throws Exception {
@@ -218,13 +189,20 @@
                 CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY,
                 new String[] {"310-480", "300-120", "311-120"});
 
-        mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
-        mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
-        mFakeDns.setAnswer(expectedFqdn3, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+        when(mMockNetwork.getAllByName(eq(expectedFqdn1)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_1)});
+        when(mMockNetwork.getAllByName(eq(expectedFqdn2)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_2)});
+        when(mMockNetwork.getAllByName(eq(expectedFqdn3)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS)});
 
         ArrayList<InetAddress> testInetAddresses =
                 getValidatedServerListWithDefaultParams(isEmergency);
 
+        verify(mMockNetwork).getAllByName(expectedFqdn1);
+        verify(mMockNetwork).getAllByName(expectedFqdn2);
+        verify(mMockNetwork).getAllByName(expectedFqdn3);
+
         assertEquals(testInetAddresses.size(), 3);
         assertEquals(testInetAddresses.get(0), InetAddress.getByName(TEST_IP_ADDRESS));
         assertEquals(testInetAddresses.get(1), InetAddress.getByName(TEST_IP_ADDRESS_2));
@@ -238,10 +216,12 @@
         final String addr2 = "epdg.epc.mnc120.mcc300.pub.3gppnetwork.org";
         final String addr3 = "epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
         final String testStaticAddress = addr1 + "," + addr2 + "," + addr3;
-
-        mFakeDns.setAnswer(addr1, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
-        mFakeDns.setAnswer(addr2, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
-        mFakeDns.setAnswer(addr3, new String[] {TEST_IP_ADDRESS}, TYPE_A);
+        when(mMockNetwork.getAllByName(eq(addr1)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_1)});
+        when(mMockNetwork.getAllByName(eq(addr2)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_2)});
+        when(mMockNetwork.getAllByName(eq(addr3)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS)});
 
         // Set carrier config mock
         mTestBundle.putIntArray(
@@ -350,7 +330,15 @@
         int testLac = 65484;
         int testTac = 65484;
         int testNrTac = 16764074;
-
+        String fqdn1_emergency = "lacffcc.sos.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
+        String fqdn1 = "lacffcc.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
+        String fqdn2_emergency =
+                "tac-lbcc.tac-hbff.tac.sos.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
+        String fqdn2 = "tac-lbcc.tac-hbff.tac.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
+        String fqdn3_emergency =
+                "tac-lbaa.tac-mbcc.tac-hbff.5gstac.sos.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
+        String fqdn3 =
+                "tac-lbaa.tac-mbcc.tac-hbff.5gstac.epdg.epc.mnc120.mcc311.pub.3gppnetwork.org";
         List<CellInfo> fakeCellInfoArray = new ArrayList<CellInfo>();
 
         mTestBundle.putIntArray(
@@ -388,164 +376,27 @@
 
         when(mMockTelephonyManager.getAllCellInfo()).thenReturn(fakeCellInfoArray);
 
-        setAnswerForCellularMethod(isEmergency, 311, 120);
-        setAnswerForCellularMethod(isEmergency, 300, 120);
+        String expectedFqdn1 = (isEmergency) ? fqdn1_emergency : fqdn1;
+        String expectedFqdn2 = (isEmergency) ? fqdn2_emergency : fqdn2;
+        String expectedFqdn3 = (isEmergency) ? fqdn3_emergency : fqdn3;
+
+        when(mMockNetwork.getAllByName(eq(expectedFqdn1)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS)});
+        when(mMockNetwork.getAllByName(eq(expectedFqdn2)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_1)});
+        when(mMockNetwork.getAllByName(eq(expectedFqdn3)))
+                .thenReturn(new InetAddress[] {InetAddress.getByName(TEST_IP_ADDRESS_2)});
 
         ArrayList<InetAddress> testInetAddresses =
                 getValidatedServerListWithDefaultParams(isEmergency);
 
+        verify(mMockNetwork, times(2)).getAllByName(expectedFqdn1);
+        verify(mMockNetwork).getAllByName(expectedFqdn2);
+        verify(mMockNetwork).getAllByName(expectedFqdn3);
+
         assertEquals(testInetAddresses.size(), 3);
         assertEquals(testInetAddresses.get(0), InetAddress.getByName(TEST_IP_ADDRESS));
         assertEquals(testInetAddresses.get(1), InetAddress.getByName(TEST_IP_ADDRESS_1));
         assertEquals(testInetAddresses.get(2), InetAddress.getByName(TEST_IP_ADDRESS_2));
     }
-
-    private void setAnswerForCellularMethod(boolean isEmergency, int mcc, int mnc)
-            throws Exception {
-        String expectedFqdn1 =
-                (isEmergency)
-                        ? "lacffcc.sos.epdg.epc.mnc" + mnc + ".mcc" + mcc + ".pub.3gppnetwork.org"
-                        : "lacffcc.epdg.epc.mnc" + mnc + ".mcc" + mcc + ".pub.3gppnetwork.org";
-        String expectedFqdn2 =
-                (isEmergency)
-                        ? "tac-lbcc.tac-hbff.tac.sos.epdg.epc.mnc"
-                                + mnc
-                                + ".mcc"
-                                + mcc
-                                + ".pub.3gppnetwork.org"
-                        : "tac-lbcc.tac-hbff.tac.epdg.epc.mnc"
-                                + mnc
-                                + ".mcc"
-                                + mcc
-                                + ".pub.3gppnetwork.org";
-        String expectedFqdn3 =
-                (isEmergency)
-                        ? "tac-lbaa.tac-mbcc.tac-hbff.5gstac.sos.epdg.epc.mnc"
-                                + mnc
-                                + ".mcc"
-                                + mcc
-                                + ".pub.3gppnetwork.org"
-                        : "tac-lbaa.tac-mbcc.tac-hbff.5gstac.epdg.epc.mnc"
-                                + mnc
-                                + ".mcc"
-                                + mcc
-                                + ".pub.3gppnetwork.org";
-
-        mFakeDns.setAnswer(expectedFqdn1, new String[] {TEST_IP_ADDRESS}, TYPE_A);
-        mFakeDns.setAnswer(expectedFqdn2, new String[] {TEST_IP_ADDRESS_1}, TYPE_A);
-        mFakeDns.setAnswer(expectedFqdn3, new String[] {TEST_IP_ADDRESS_2}, TYPE_A);
-    }
-
-    /**
-     * Fakes DNS responses.
-     *
-     * <p>Allows test methods to configure the IP addresses that will be resolved by
-     * Network#getAllByName and by DnsResolver#query.
-     */
-    class FakeDns {
-        /** Data class to record the Dns entry. */
-        class DnsEntry {
-            final String mHostname;
-            final int mType;
-            final List<InetAddress> mAddresses;
-
-            DnsEntry(String host, int type, List<InetAddress> addr) {
-                mHostname = host;
-                mType = type;
-                mAddresses = addr;
-            }
-            // Full match or partial match that target host contains the entry hostname to support
-            // random private dns probe hostname.
-            private boolean matches(String hostname, int type) {
-                return hostname.endsWith(mHostname) && type == mType;
-            }
-        }
-
-        private final ArrayList<DnsEntry> mAnswers = new ArrayList<DnsEntry>();
-
-        /** Clears all DNS entries. */
-        private synchronized void clearAll() {
-            mAnswers.clear();
-        }
-
-        /** Returns the answer for a given name and type on the given mock network. */
-        private synchronized List<InetAddress> getAnswer(Object mock, String hostname, int type) {
-            return mAnswers.stream()
-                    .filter(e -> e.matches(hostname, type))
-                    .map(answer -> answer.mAddresses)
-                    .findFirst()
-                    .orElse(null);
-        }
-
-        /** Sets the answer for a given name and type. */
-        private synchronized void setAnswer(String hostname, String[] answer, int type)
-                throws UnknownHostException {
-            DnsEntry record = new DnsEntry(hostname, type, generateAnswer(answer));
-            // Remove the existing one.
-            mAnswers.removeIf(entry -> entry.matches(hostname, type));
-            // Add or replace a new record.
-            mAnswers.add(record);
-        }
-
-        private List<InetAddress> generateAnswer(String[] answer) {
-            if (answer == null) return new ArrayList<>();
-            return Arrays.stream(answer)
-                    .map(addr -> InetAddresses.parseNumericAddress(addr))
-                    .collect(toList());
-        }
-
-        // Regardless of the type, depends on what the responses contained in the network.
-        private List<InetAddress> queryAllTypes(Object mock, String hostname) {
-            List<InetAddress> answer = new ArrayList<>();
-            addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_A));
-            addAllIfNotNull(answer, getAnswer(mock, hostname, TYPE_AAAA));
-            return answer;
-        }
-
-        private void addAllIfNotNull(List<InetAddress> list, List<InetAddress> c) {
-            if (c != null) {
-                list.addAll(c);
-            }
-        }
-
-        /** Starts mocking DNS queries. */
-        private void startMocking() throws UnknownHostException {
-            doAnswer(
-                            invocation -> {
-                                return mockQuery(
-                                        invocation,
-                                        1 /* posHostname */,
-                                        3 /* posExecutor */,
-                                        5 /* posCallback */,
-                                        -1 /* posType */);
-                            })
-                    .when(mMockDnsResolver)
-                    .query(any(), any(), anyInt(), any(), any(), any());
-        }
-
-        // Mocking queries on DnsResolver#query.
-        private Answer mockQuery(
-                InvocationOnMock invocation,
-                int posHostname,
-                int posExecutor,
-                int posCallback,
-                int posType) {
-            String hostname = (String) invocation.getArgument(posHostname);
-            Executor executor = (Executor) invocation.getArgument(posExecutor);
-            DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(posCallback);
-            List<InetAddress> answer;
-
-            answer = queryAllTypes(invocation.getMock(), hostname);
-
-            if (answer != null && answer.size() > 0) {
-                new Handler(Looper.getMainLooper())
-                        .post(
-                                () -> {
-                                    executor.execute(() -> callback.onAnswer(answer, 0));
-                                });
-            }
-            // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
-            return null;
-        }
-    }
 }
diff --git a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
index 79e3726..7c4d19a 100644
--- a/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
+++ b/test/com/google/android/iwlan/epdg/EpdgTunnelManagerTest.java
@@ -29,12 +29,15 @@
 
 import android.content.Context;
 import android.net.InetAddresses;
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeFqdnIdentification;
 import android.net.ipsec.ike.IkeSession;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.InvalidIkeSpiException;
@@ -64,6 +67,8 @@
 import org.mockito.junit.MockitoRule;
 
 import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
@@ -208,7 +213,7 @@
         TunnelSetupRequest TSR = getBasicTunnelSetupRequest(TEST_APN_NAME, ApnSetting.PROTOCOL_IP);
 
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName2, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName2, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
         doReturn(true).when(mEpdgTunnelManager).canBringUpTunnel(eq(TEST_APN_NAME));
 
         boolean ret = mEpdgTunnelManager.bringUpTunnel(TSR, mMockIwlanTunnelCallback);
@@ -246,7 +251,7 @@
         String testApnName = "www.xyz.com";
 
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
 
         boolean ret = mEpdgTunnelManager.closeTunnel(testApnName, true /*forceClose*/);
         assertTrue(ret);
@@ -259,7 +264,7 @@
         String testApnName = "www.xyz.com";
 
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
 
         boolean ret = mEpdgTunnelManager.closeTunnel(testApnName, false /*forceClose*/);
         assertTrue(ret);
@@ -590,6 +595,20 @@
     }
 
     @Test
+    public void testIpv6PrefixMatching() throws Exception {
+        InetAddress a1 = InetAddress.getByName("2600:381:4872:5d1e:ac45:69c7:bab2:639b");
+        LinkAddress l1 = new LinkAddress(a1, 64);
+        InetAddress src = InetAddress.getByName("2600:381:4872:5d1e:0:10:3582:a501");
+        EpdgTunnelManager.TunnelConfig tf =
+                mEpdgTunnelManager.new TunnelConfig(null, null, src, 64);
+        assertTrue(tf.isPrefixSameAsSrcIP(l1));
+
+        // different prefix length
+        LinkAddress l2 = new LinkAddress(a1, 63);
+        assertFalse(tf.isPrefixSameAsSrcIP(l2));
+    }
+
+    @Test
     public void testBackOffTimeCalculation() throws Exception {
         // unit: 10 mins value: 2 expectedTime: 1200 (10 * 60 * 2)
         verifyBackOffTimer("00000010", 1200);
@@ -768,7 +787,7 @@
 
         doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
 
         mEpdgTunnelManager.setIsEpdgAddressSelected(true);
 
@@ -818,7 +837,7 @@
         doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
 
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
 
         mEpdgTunnelManager.setIsEpdgAddressSelected(true);
 
@@ -1025,7 +1044,7 @@
         doReturn(0L).when(mEpdgTunnelManager).reportIwlanError(eq(testApnName), eq(error));
 
         mEpdgTunnelManager.putApnNameToTunnelConfig(
-                testApnName, mMockIkeSession, mMockIwlanTunnelCallback);
+                testApnName, mMockIkeSession, mMockIwlanTunnelCallback, null, 0);
 
         mEpdgTunnelManager.setIsEpdgAddressSelected(true);
 
@@ -1202,4 +1221,137 @@
         IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
         assertEquals(ikeSessionParams.getNattKeepAliveDelaySeconds(), ikeDefaultNattTimerValue);
     }
+
+    @Test
+    public void testTunnelSetupRequestParams() throws Exception {
+        String testApnName = "www.xyz.com";
+        Network testNetwork = new Network(123);
+        Inet6Address testAddressV6 = Inet6Address.getByAddress("25.25.25.25", new byte[16], 0);
+        Inet4Address testAddressV4 = (Inet4Address) Inet4Address.getByName("30.30.30.30");
+        int pduSessionId = 5;
+        boolean isRoaming = false;
+        boolean isEmergency = true;
+        boolean requestPcscf = true;
+        int ipv6AddressLen = 64;
+
+        TunnelSetupRequest tsr =
+                TunnelSetupRequest.builder()
+                        .setApnName(testApnName)
+                        .setNetwork(testNetwork)
+                        .setApnIpProtocol(ApnSetting.PROTOCOL_IPV4V6)
+                        .setSrcIpv6Address(testAddressV6)
+                        .setSrcIpv6AddressPrefixLength(ipv6AddressLen)
+                        .setSrcIpv4Address(testAddressV4)
+                        .setPduSessionId(pduSessionId)
+                        .setIsRoaming(isRoaming)
+                        .setIsEmergency(isEmergency)
+                        .setRequestPcscf(requestPcscf)
+                        .build();
+
+        setupMockForGetConfig(null);
+        when(mMockEpdgSelector.getValidatedServerList(
+                        anyInt(),
+                        eq(isRoaming),
+                        eq(isEmergency),
+                        eq(testNetwork),
+                        any(EpdgSelector.EpdgSelectorCallback.class)))
+                .thenReturn(new IwlanError(IwlanError.NO_ERROR));
+
+        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(testApnName));
+
+        boolean ret = mEpdgTunnelManager.bringUpTunnel(tsr, mMockIwlanTunnelCallback);
+        assertTrue(ret);
+
+        // verify isRoaming, isEmergency and Network variables.
+        verify(mMockEpdgSelector)
+                .getValidatedServerList(
+                        anyInt(), // only Ipv6 address is added
+                        eq(isRoaming),
+                        eq(isEmergency),
+                        eq(testNetwork),
+                        any(EpdgSelector.EpdgSelectorCallback.class));
+
+        ArrayList<InetAddress> ipList = new ArrayList<>();
+        ipList.add(InetAddress.getByName(TEST_IP_ADDRESS));
+        mEpdgTunnelManager.sendSelectionRequestComplete(
+                ipList, new IwlanError(IwlanError.NO_ERROR));
+
+        ArgumentCaptor<IkeSessionParams> ikeSessionParamsCaptor =
+                ArgumentCaptor.forClass(IkeSessionParams.class);
+        ArgumentCaptor<TunnelModeChildSessionParams> childSessionParamsCaptor =
+                ArgumentCaptor.forClass(TunnelModeChildSessionParams.class);
+
+        verify(mMockIkeSessionCreator)
+                .createIkeSession(
+                        eq(mMockContext),
+                        ikeSessionParamsCaptor.capture(),
+                        childSessionParamsCaptor.capture(),
+                        any(Executor.class),
+                        any(IkeSessionCallback.class),
+                        any(ChildSessionCallback.class));
+
+        IkeSessionParams ikeSessionParams = ikeSessionParamsCaptor.getValue();
+        TunnelModeChildSessionParams childSessionParams = childSessionParamsCaptor.getValue();
+
+        // apnName verification. By default remote identification is type fqdn
+        IkeFqdnIdentification ikeId =
+                (IkeFqdnIdentification) ikeSessionParams.getRemoteIdentification();
+        assertEquals(ikeId.fqdn, testApnName);
+
+        // verify Network
+        assertEquals(ikeSessionParams.getConfiguredNetwork(), testNetwork);
+
+        // verify requestPcscf (true) with Apn protocol IPV6
+        // it should add the pcscf config requests of type ConfigRequestIpv6PcscfServer and
+        // ConfigRequestIpv4PcscfServer
+        assertTrue(
+                ikeSessionParams.getConfigurationRequests().stream()
+                        .anyMatch(c -> c instanceof IkeSessionParams.ConfigRequestIpv6PcscfServer));
+        assertTrue(
+                ikeSessionParams.getConfigurationRequests().stream()
+                        .anyMatch(c -> c instanceof IkeSessionParams.ConfigRequestIpv4PcscfServer));
+
+        // verify pduSessionID
+        assertEquals(
+                ikeSessionParams.getIke3gppExtension().getIke3gppParams().getPduSessionId(),
+                pduSessionId);
+
+        // verify src ipv6  and src ipv4 address
+        List<TunnelModeChildSessionParams.TunnelModeChildConfigRequest> configRequests =
+                childSessionParams.getConfigurationRequests();
+        boolean ipv6ConfigRequestPresent = false;
+        boolean ipv4ConfigRequestPresent = true;
+        for (TunnelModeChildSessionParams.TunnelModeChildConfigRequest configRequest :
+                configRequests) {
+            if (configRequest instanceof TunnelModeChildSessionParams.ConfigRequestIpv6Address) {
+                ipv6ConfigRequestPresent = true;
+                assertEquals(
+                        ((TunnelModeChildSessionParams.ConfigRequestIpv6Address) configRequest)
+                                .getAddress(),
+                        testAddressV6);
+                assertEquals(
+                        ((TunnelModeChildSessionParams.ConfigRequestIpv6Address) configRequest)
+                                .getPrefixLength(),
+                        ipv6AddressLen);
+            }
+            if (configRequest instanceof TunnelModeChildSessionParams.ConfigRequestIpv4Address) {
+                ipv4ConfigRequestPresent = true;
+                assertEquals(
+                        ((TunnelModeChildSessionParams.ConfigRequestIpv4Address) configRequest)
+                                .getAddress(),
+                        testAddressV4);
+            }
+        }
+        assertTrue(ipv6ConfigRequestPresent);
+        assertTrue(ipv4ConfigRequestPresent);
+    }
 }
diff --git a/test/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformationTest.java b/test/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformationTest.java
new file mode 100644
index 0000000..4ebae32
--- /dev/null
+++ b/test/com/google/android/iwlan/epdg/NetworkSliceSelectionAssistanceInformationTest.java
@@ -0,0 +1,135 @@
+package com.google.android.iwlan.epdg;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.telephony.data.NetworkSliceInfo;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.*;
+
+@RunWith(JUnit4.class)
+public class NetworkSliceSelectionAssistanceInformationTest {
+    private static final String TAG = "NssaiTest";
+
+    @Before
+    public void setUp() throws Exception {}
+
+    @After
+    public void cleanUp() throws Exception {}
+
+    @Test
+    public void testNullNssai() throws Exception {
+        NetworkSliceInfo si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(null);
+        assertNull(si);
+    }
+
+    @Test
+    public void testNssaiWithInvalidLength() throws Exception {
+        // only these lengths are valid: 1, 2, 4, 5, 8
+
+        byte[] nssai0 = {}; // length 0
+        byte[] nssai3 = {1, 1, 1}; // length 3
+        byte[] nssai6 = {1, 1, 1, 2, 1, 1}; // length 6
+        byte[] nssai10 = {1, 1, 1, 2, 1, 1, 2, 1, 1, 1}; // length 10
+
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai0);
+        assertNull(si);
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai3);
+        assertNull(si);
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai6);
+        assertNull(si);
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai10);
+        assertNull(si);
+    }
+
+    @Test
+    public void testNssaiWithSST() throws Exception {
+        byte[] nssai = {3}; // SST = 3
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai);
+
+        assertNotNull(si);
+        assertEquals(si.getSliceServiceType(), 3);
+        assertEquals(si.getMappedHplmnSliceServiceType(), NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE);
+        assertEquals(si.getSliceDifferentiator(), NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+        assertEquals(
+                si.getMappedHplmnSliceDifferentiator(),
+                NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+    }
+
+    @Test
+    public void testNssaiWithSSTandMappedSST() throws Exception {
+        byte[] nssai = {3, 2}; // SST = 3, mapped SST=2
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai);
+
+        assertNotNull(si);
+        assertEquals(si.getSliceServiceType(), 3);
+        assertEquals(si.getMappedHplmnSliceServiceType(), 2);
+        assertEquals(si.getSliceDifferentiator(), NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+        assertEquals(
+                si.getMappedHplmnSliceDifferentiator(),
+                NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+    }
+
+    @Test
+    public void testNssaiWithSSTandSD() throws Exception {
+        byte[] nssai = {3, 0x0a, 0x0b, 0x0c}; // SST = 3, SD=0x0a0b0c
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai);
+
+        assertNotNull(si);
+        assertEquals(si.getSliceServiceType(), 3);
+        assertEquals(si.getMappedHplmnSliceServiceType(), NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE);
+        assertEquals(si.getSliceDifferentiator(), 0x0a0b0c);
+        assertEquals(
+                si.getMappedHplmnSliceDifferentiator(),
+                NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+    }
+
+    @Test
+    public void testNssaiWithSSTandSDandMappedSST() throws Exception {
+        byte[] nssai = {3, 0x0a, 0x0b, 0x0c, 3}; // SST = 3, SD=0x0a0b0c, Mapped SST=3
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai);
+
+        assertNotNull(si);
+        assertEquals(si.getSliceServiceType(), 3);
+        assertEquals(si.getMappedHplmnSliceServiceType(), 3);
+        assertEquals(si.getSliceDifferentiator(), 0x0a0b0c);
+        assertEquals(
+                si.getMappedHplmnSliceDifferentiator(),
+                NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE);
+    }
+
+    @Test
+    public void testNssaiWithSSTandSDandMappedSSTandMappedSD() throws Exception {
+        byte[] nssai = {
+            3, 0x0a, 0x0b, 0x0c, 2, 0x0F, 0x0E, 0x0D
+        }; // SST = 3, SD=0x0a0b0c, Mapped SST=2, Mapped SD =0x0f0e0d
+        NetworkSliceInfo si;
+
+        si = NetworkSliceSelectionAssistanceInformation.getSliceInfo(nssai);
+
+        assertNotNull(si);
+        assertEquals(si.getSliceServiceType(), 3);
+        assertEquals(si.getMappedHplmnSliceServiceType(), 2);
+        assertEquals(si.getSliceDifferentiator(), 0x0a0b0c);
+        assertEquals(si.getMappedHplmnSliceDifferentiator(), 0x0f0e0d);
+    }
+}
diff --git a/test/com/google/android/iwlan/epdg/TunnelLinkPropertiesTest.java b/test/com/google/android/iwlan/epdg/TunnelLinkPropertiesTest.java
index b1e2505..4f8f8cc 100644
--- a/test/com/google/android/iwlan/epdg/TunnelLinkPropertiesTest.java
+++ b/test/com/google/android/iwlan/epdg/TunnelLinkPropertiesTest.java
@@ -17,6 +17,7 @@
 package com.google.android.iwlan.epdg;
 
 import android.net.LinkAddress;
+import android.telephony.data.NetworkSliceInfo;
 
 import java.net.InetAddress;
 import java.util.LinkedList;
@@ -28,7 +29,8 @@
     private static final String DNS_ADDRESS = "8.8.8.8";
     private static final String PSCF_ADDRESS = "10.159.204.230";
     private static final String INTERFACE_NAME = "ipsec6";
-    private static final byte[] SNSSAI = new byte[] {1, 2, 3};
+    private static final NetworkSliceInfo SLICE_INFO =
+            NetworkSliceSelectionAssistanceInformation.getSliceInfo(new byte[] {1});
 
     public static TunnelLinkProperties createTestTunnelLinkProperties() throws Exception {
         List<LinkAddress> mInternalAddressList = new LinkedList<>();
@@ -47,7 +49,7 @@
                 .setDnsAddresses(mDNSAddressList)
                 .setPcscfAddresses(mPCSFAddressList)
                 .setIfaceName(INTERFACE_NAME)
-                .setSNssai(SNSSAI)
+                .setSliceInfo(SLICE_INFO)
                 .build();
     }
 }