Snap for 8121614 from 8dbd67e9015fb4c966d206a5440f015dbfde1cb1 to sdk-release
Change-Id: I2a36d620fdcb5aa7ecb85d07a1efdad202d42326
diff --git a/src/com/google/android/iwlan/IwlanHelper.java b/src/com/google/android/iwlan/IwlanHelper.java
index b0ab666..7e39059 100644
--- a/src/com/google/android/iwlan/IwlanHelper.java
+++ b/src/com/google/android/iwlan/IwlanHelper.java
@@ -22,6 +22,7 @@
import android.location.Country;
import android.location.CountryDetector;
import android.net.ConnectivityManager;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
@@ -38,6 +39,7 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -46,8 +48,13 @@
private static final String TAG = IwlanHelper.class.getSimpleName();
private static CountryDetector mCountryDetector;
private static final String LAST_KNOWN_COUNTRY_CODE_KEY = "last_known_country_code";
+ private static IpPrefix mNat64Prefix = new IpPrefix("64:ff9b::/96");
- public static String getNai(Context context, int slotId) {
+ public static String getNai(Context context, int slotId, byte[] nextReauthId) {
+ if (nextReauthId != null) {
+ return new String(nextReauthId, StandardCharsets.UTF_8);
+ }
+
StringBuilder naiBuilder = new StringBuilder();
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
SubscriptionInfo subInfo = null;
@@ -105,15 +112,37 @@
public static List<InetAddress> getAddressesForNetwork(Network network, Context context) {
ConnectivityManager connectivityManager =
context.getSystemService(ConnectivityManager.class);
- List<InetAddress> gatewayList = new ArrayList<InetAddress>();
+ List<InetAddress> gatewayList = new ArrayList<>();
if (network != null) {
LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
if (linkProperties != null) {
- for (LinkAddress laddr : linkProperties.getLinkAddresses()) {
- InetAddress inetaddr = laddr.getAddress();
+ for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
+ InetAddress inetAddr = linkAddr.getAddress();
// skip linklocal and loopback addresses
- if (!inetaddr.isLoopbackAddress() && !inetaddr.isLinkLocalAddress()) {
- gatewayList.add(inetaddr);
+ if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) {
+ gatewayList.add(inetAddr);
+ }
+ }
+ if (linkProperties.getNat64Prefix() != null) {
+ mNat64Prefix = linkProperties.getNat64Prefix();
+ }
+ }
+ }
+ return gatewayList;
+ }
+
+ public static List<InetAddress> getStackedAddressesForNetwork(
+ Network network, Context context) {
+ ConnectivityManager connectivityManager =
+ context.getSystemService(ConnectivityManager.class);
+ List<InetAddress> gatewayList = new ArrayList<>();
+ if (network != null) {
+ LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
+ if (linkProperties != null) {
+ for (LinkAddress linkAddr : linkProperties.getAllLinkAddresses()) {
+ InetAddress inetAddr = linkAddr.getAddress();
+ if ((inetAddr instanceof Inet4Address)) {
+ gatewayList.add(inetAddr);
}
}
}
@@ -121,6 +150,16 @@
return gatewayList;
}
+ /**
+ * The method is to check if this IP address is an IPv4-embedded IPv6 address(Pref64::/n).
+ *
+ * @param ipAddress IP address
+ * @return True if it is an IPv4-embedded IPv6 addres, otherwise false.
+ */
+ public static boolean isIpv4EmbeddedIpv6Address(@NonNull InetAddress ipAddress) {
+ return (ipAddress instanceof Inet6Address) && mNat64Prefix.contains(ipAddress);
+ }
+
public static boolean hasIpv6Address(List<InetAddress> localAddresses) {
for (InetAddress address : localAddresses) {
if (address instanceof Inet6Address) {
diff --git a/src/com/google/android/iwlan/epdg/EpdgSelector.java b/src/com/google/android/iwlan/epdg/EpdgSelector.java
index db23223..bbc474f 100644
--- a/src/com/google/android/iwlan/epdg/EpdgSelector.java
+++ b/src/com/google/android/iwlan/epdg/EpdgSelector.java
@@ -17,9 +17,12 @@
package com.google.android.iwlan.epdg;
import android.content.Context;
+import android.net.DnsResolver;
+import android.net.DnsResolver.DnsException;
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;
@@ -29,6 +32,7 @@
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoNr;
+import android.telephony.CellInfoTdscdma;
import android.telephony.CellInfoWcdma;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -40,12 +44,16 @@
import com.google.android.iwlan.IwlanError;
import com.google.android.iwlan.IwlanHelper;
+import com.google.android.iwlan.epdg.NaptrDnsResolver.NaptrTarget;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.*;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
public class EpdgSelector {
private static final String TAG = "EpdgSelector";
@@ -162,7 +170,7 @@
}
break;
case PROTO_FILTER_IPV6:
- if (ipAddress instanceof Inet6Address) {
+ if (!IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) {
validIpList.add(ipAddress);
}
break;
@@ -579,6 +587,206 @@
}
}
+ private String composeFqdnWithMccMnc(String mcc, String mnc, boolean isEmergency) {
+ StringBuilder domainName = new StringBuilder();
+
+ /*
+ * Operator Identifier based ePDG FQDN format:
+ * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org
+ *
+ * Operator Identifier based Emergency ePDG FQDN format:
+ * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org
+ */
+ domainName.setLength(0);
+ if (isEmergency) {
+ domainName.append("sos.");
+ }
+ domainName
+ .append("epdg.epc.mnc")
+ .append(mnc)
+ .append(".mcc")
+ .append(mcc)
+ .append(".pub.3gppnetwork.org");
+
+ return domainName.toString();
+ }
+
+ private boolean isRegisteredWith3GPP(TelephonyManager telephonyManager) {
+ List<CellInfo> cellInfoList = telephonyManager.getAllCellInfo();
+ if (cellInfoList == null) {
+ Log.e(TAG, "cellInfoList is NULL");
+ } else {
+ for (CellInfo cellInfo : cellInfoList) {
+ if (!cellInfo.isRegistered()) {
+ continue;
+ }
+ if (cellInfo instanceof CellInfoGsm
+ || cellInfo instanceof CellInfoTdscdma
+ || cellInfo instanceof CellInfoWcdma
+ || cellInfo instanceof CellInfoLte
+ || cellInfo instanceof CellInfoNr) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void processNaptrResponse(
+ int filter,
+ ArrayList<InetAddress> validIpList,
+ boolean isEmergency,
+ Network network,
+ boolean isRegisteredWith3GPP,
+ List<NaptrTarget> naptrResponse,
+ Set<String> plmnsFromCarrierConfig,
+ String registeredhostName) {
+ Set<String> resultSet = new LinkedHashSet<>();
+
+ for (NaptrTarget target : naptrResponse) {
+ Log.d(TAG, "NaptrTarget - name: " + target.mName);
+ Log.d(TAG, "NaptrTarget - type: " + target.mType);
+ if (target.mType == NaptrDnsResolver.TYPE_A) {
+ resultSet.add(target.mName);
+ }
+ }
+
+ /*
+ * As 3GPP TS 23.402 4.5.4.5 bullet 2a,
+ * if the device registers via 3GPP and its PLMN info is in the NAPTR response,
+ * try to connect ePDG with this PLMN info.
+ */
+ if (isRegisteredWith3GPP) {
+ if (resultSet.contains(registeredhostName)) {
+ getIP(registeredhostName, filter, validIpList, network);
+ resultSet.remove(registeredhostName);
+ }
+ }
+
+ /*
+ * As 3GPP TS 23.402 4.5.4.5 bullet 2b
+ * Check if there is any PLMN in both ePDG selection information and the DNS response
+ */
+ for (String plmn : plmnsFromCarrierConfig) {
+ String[] mccmnc = splitMccMnc(plmn);
+ String carrierConfighostName = composeFqdnWithMccMnc(mccmnc[0], mccmnc[1], isEmergency);
+
+ if (resultSet.contains(carrierConfighostName)) {
+ getIP(carrierConfighostName, filter, validIpList, network);
+ resultSet.remove(carrierConfighostName);
+ }
+ }
+
+ /*
+ * Do FQDN with the remaining PLMNs in the ResultSet
+ */
+ for (String result : resultSet) {
+ getIP(result, filter, validIpList, network);
+ }
+ }
+
+ private void resolutionMethodVisitedCountry(
+ int filter, ArrayList<InetAddress> validIpList, boolean isEmergency, Network network) {
+ StringBuilder domainName = new StringBuilder();
+
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ telephonyManager =
+ telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId));
+
+ if (telephonyManager == null) {
+ Log.e(TAG, "TelephonyManager is NULL");
+ return;
+ }
+
+ final boolean isRegisteredWith3GPP = isRegisteredWith3GPP(telephonyManager);
+
+ // Get ePDG selection information from CarrierConfig
+ final Set<String> plmnsFromCarrierConfig =
+ new LinkedHashSet<>(
+ Arrays.asList(
+ IwlanHelper.getConfig(
+ CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY,
+ mContext,
+ mSlotId)));
+
+ final String cellMcc = telephonyManager.getNetworkOperator().substring(0, 3);
+ final String cellMnc = telephonyManager.getNetworkOperator().substring(3);
+ final String plmnFromNetwork =
+ new StringBuilder().append(cellMcc).append("-").append(cellMnc).toString();
+ final String registeredhostName = composeFqdnWithMccMnc(cellMcc, cellMnc, isEmergency);
+
+ /*
+ * As TS 23 402 4.5.4.4 bullet 3a
+ * If the UE determines to be located in a country other than its home country
+ * If the UE is registered via 3GPP access to a PLMN and this PLMN matches an entry
+ in the ePDG selection information, then the UE shall select an ePDG in this PLMN.
+ */
+ if (isRegisteredWith3GPP) {
+ if (plmnsFromCarrierConfig.contains(plmnFromNetwork)) {
+ getIP(registeredhostName, filter, validIpList, network);
+ }
+ }
+
+ /*
+ * Visited Country FQDN format:
+ * epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org
+ *
+ * Visited Country Emergency ePDG FQDN format:
+ * sos.epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org
+ */
+ if (isEmergency) {
+ domainName.append("sos.");
+ }
+ domainName
+ .append("epdg.epc.mcc")
+ .append(cellMcc)
+ .append(".visited-country.pub.3gppnetwork.org");
+
+ Log.d(TAG, "Visited Country FQDN with " + domainName.toString());
+
+ CompletableFuture<List<NaptrTarget>> naptrDnsResult = new CompletableFuture<>();
+ DnsResolver.Callback<List<NaptrTarget>> naptrDnsCb =
+ new DnsResolver.Callback<List<NaptrTarget>>() {
+ @Override
+ public void onAnswer(@NonNull final List<NaptrTarget> answer, final int rcode) {
+ if (rcode == 0 && answer.size() != 0) {
+ naptrDnsResult.complete(answer);
+ } else {
+ naptrDnsResult.completeExceptionally(new UnknownHostException());
+ }
+ }
+
+ @Override
+ public void onError(@Nullable final DnsException error) {
+ naptrDnsResult.completeExceptionally(error);
+ }
+ };
+ NaptrDnsResolver.query(network, domainName.toString(), r -> r.run(), null, naptrDnsCb);
+
+ try {
+ final List<NaptrTarget> naptrResponse = naptrDnsResult.get();
+ // Check if there is any record in the NAPTR response
+ if (naptrResponse != null && naptrResponse.size() > 0) {
+ processNaptrResponse(
+ filter,
+ validIpList,
+ isEmergency,
+ network,
+ isRegisteredWith3GPP,
+ naptrResponse,
+ plmnsFromCarrierConfig,
+ registeredhostName);
+ }
+ } catch (ExecutionException e) {
+ Log.e(TAG, "Cause of ExecutionException: ", e.getCause());
+ } catch (InterruptedException e) {
+ if (Thread.currentThread().interrupted()) {
+ Thread.currentThread().interrupt();
+ }
+ Log.e(TAG, "InterruptedException: ", e);
+ }
+ }
+
public IwlanError getValidatedServerList(
int transactionId,
@ProtoFilter int filter,
@@ -600,6 +808,19 @@
mContext,
mSlotId);
+ final boolean isVisitedCountryMethodRequired =
+ Arrays.stream(addrResolutionMethods)
+ .anyMatch(
+ i ->
+ i
+ == CarrierConfigManager.Iwlan
+ .EPDG_ADDRESS_VISITED_COUNTRY);
+
+ // In the visited country
+ if (isRoaming && !inSameCountry() && isVisitedCountryMethodRequired) {
+ resolutionMethodVisitedCountry(filter, validIpList, isEmergency, network);
+ }
+
for (int addrResolutionMethod : addrResolutionMethods) {
switch (addrResolutionMethod) {
case CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC:
diff --git a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
index 4a6c62c..9f3bb96 100644
--- a/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
+++ b/src/com/google/android/iwlan/epdg/EpdgTunnelManager.java
@@ -28,6 +28,8 @@
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.Network;
+import android.net.eap.EapAkaInfo;
+import android.net.eap.EapInfo;
import android.net.eap.EapSessionConfig;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -145,6 +147,7 @@
private InetAddress mEpdgAddress;
private Network mNetwork;
private int mTransactionId = 0;
+ private int mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
private boolean mIsEpdgAddressSelected;
private IkeSessionCreator mIkeSessionCreator;
@@ -154,6 +157,8 @@
private List<InetAddress> mLocalAddresses;
+ @Nullable private byte[] mNextReauthId = null;
+
private static final Set<Integer> VALID_DH_GROUPS;
private static final Set<Integer> VALID_KEY_LENGTHS;
private static final Set<Integer> VALID_PRF_ALGOS;
@@ -431,6 +436,25 @@
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;
+ }
+ }
}
@Override
@@ -442,12 +466,18 @@
@Override
public void onClosedExceptionally(IkeException exception) {
Log.d(TAG, "Ike session onClosedExceptionally for apn: " + mApnName);
+
+ mNextReauthId = null;
+
onSessionClosedWithException(exception, mApnName, EVENT_IKE_SESSION_CLOSED);
}
@Override
public void onError(IkeProtocolException exception) {
Log.d(TAG, "Ike session onError for apn: " + mApnName);
+
+ mNextReauthId = null;
+
Log.d(
TAG,
"ErrorType:"
@@ -1175,7 +1205,9 @@
}
private IkeIdentification getLocalIdentification() {
- String nai = IwlanHelper.getNai(mContext, mSlotId);
+ String nai;
+
+ nai = IwlanHelper.getNai(mContext, mSlotId, mNextReauthId);
if (nai == null) {
throw new IllegalArgumentException("Nai is null.");
@@ -1205,15 +1237,20 @@
private EapSessionConfig getEapConfig() {
int subId = IwlanHelper.getSubId(mContext, mSlotId);
- String nai = IwlanHelper.getNai(mContext, mSlotId);
+ String nai = IwlanHelper.getNai(mContext, mSlotId, null);
if (nai == null) {
throw new IllegalArgumentException("Nai is null.");
}
+ EapSessionConfig.EapAkaOption option = null;
+ if (mNextReauthId != null) {
+ option = new EapSessionConfig.EapAkaOption.Builder().setReauthId(mNextReauthId).build();
+ }
+
Log.d(TAG, "getEapConfig: Nai: " + nai);
return new EapSessionConfig.Builder()
- .setEapAkaConfig(subId, TelephonyManager.APPTYPE_USIM)
+ .setEapAkaConfig(subId, TelephonyManager.APPTYPE_USIM, option)
.setEapIdentity(nai.getBytes(StandardCharsets.US_ASCII))
.build();
}
@@ -1412,10 +1449,17 @@
}
try {
+ if (mEpdgAddress instanceof Inet4Address
+ && mProtoFilter == EpdgSelector.PROTO_FILTER_IPV6) {
+ mLocalAddresses =
+ IwlanHelper.getStackedAddressesForNetwork(
+ mNetwork, mContext);
+ }
InetAddress localAddress =
(mEpdgAddress instanceof Inet4Address)
? IwlanHelper.getIpv4Address(mLocalAddresses)
: IwlanHelper.getIpv6Address(mLocalAddresses);
+ Log.d(TAG, "Local address = " + localAddress);
tunnelConfig.setIface(
ipSecManager.createIpSecTunnelInterface(
localAddress, mEpdgAddress, mNetwork));
@@ -1473,19 +1517,19 @@
return;
}
- int protoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
+ mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4V6;
if (!IwlanHelper.hasIpv6Address(mLocalAddresses)) {
- protoFilter = EpdgSelector.PROTO_FILTER_IPV4;
+ mProtoFilter = EpdgSelector.PROTO_FILTER_IPV4;
}
if (!IwlanHelper.hasIpv4Address(mLocalAddresses)) {
- protoFilter = EpdgSelector.PROTO_FILTER_IPV6;
+ mProtoFilter = EpdgSelector.PROTO_FILTER_IPV6;
}
EpdgSelector epdgSelector = getEpdgSelector();
IwlanError epdgError =
epdgSelector.getValidatedServerList(
++mTransactionId,
- protoFilter,
+ mProtoFilter,
setupRequest.isRoaming(),
setupRequest.isEmergency(),
mNetwork,