summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Qiu <zqiu@google.com>2016-12-15 15:22:31 -0800
committerPeter Qiu <zqiu@google.com>2017-01-07 19:06:28 -0800
commit87c6f1b149804685e46c18d2ad11262f611c9255 (patch)
tree2bd5c99f827275b949045228a3465a67fc04d04d
parentd28cfdde236d3d7c72f0c57ca7f18622b16d421a (diff)
downloadandroid_frameworks_opt_net_wifi-87c6f1b149804685e46c18d2ad11262f611c9255.tar.gz
android_frameworks_opt_net_wifi-87c6f1b149804685e46c18d2ad11262f611c9255.tar.bz2
android_frameworks_opt_net_wifi-87c6f1b149804685e46c18d2ad11262f611c9255.zip
hotspot2: add support for matching Passpoint provider
Added support for matching Passpoint provider based on the content of ANQP elements. While there: - updated IMSIParameter and added unit tests for it - added utility class ANQPMatcher for providing ANQP element matching functions Bug: 33246489 Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh Change-Id: Ibdd49aaf44a097c1ee523284888faace6b866485
-rw-r--r--service/java/com/android/server/wifi/IMSIParameter.java115
-rw-r--r--service/java/com/android/server/wifi/SIMAccessor.java2
-rw-r--r--service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java257
-rw-r--r--service/java/com/android/server/wifi/hotspot2/AuthMatch.java35
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointManager.java13
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java5
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointProvider.java92
-rw-r--r--service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java24
-rw-r--r--tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java161
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java338
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java33
-rw-r--r--tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java322
12 files changed, 1319 insertions, 78 deletions
diff --git a/service/java/com/android/server/wifi/IMSIParameter.java b/service/java/com/android/server/wifi/IMSIParameter.java
index deea870bc..ab9ec0a28 100644
--- a/service/java/com/android/server/wifi/IMSIParameter.java
+++ b/service/java/com/android/server/wifi/IMSIParameter.java
@@ -1,8 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server.wifi;
-import java.io.IOException;
+import android.text.TextUtils;
+/**
+ * Class for storing an IMSI (International Mobile Subscriber Identity) parameter. The IMSI
+ * contains number (up to 15) of numerical digits. When an IMSI ends with a '*', the specified
+ * IMSI is a prefix.
+ */
public class IMSIParameter {
+ private static final int MAX_IMSI_LENGTH = 15;
+
+ /**
+ * MCC (Mobile Country Code) is a 3 digit number and MNC (Mobile Network Code) is also a 3
+ * digit number.
+ */
+ private static final int MCC_MNC_LENGTH = 6;
+
private final String mImsi;
private final boolean mPrefix;
@@ -11,11 +40,22 @@ public class IMSIParameter {
mPrefix = prefix;
}
- public IMSIParameter(String imsi) throws IOException {
- if (imsi == null || imsi.length() == 0) {
- throw new IOException("Bad IMSI: '" + imsi + "'");
+ /**
+ * Build an IMSIParameter object from the given string. A null will be returned for a
+ * malformed string.
+ *
+ * @param imsi The IMSI string
+ * @return {@link IMSIParameter}
+ */
+ public static IMSIParameter build(String imsi) {
+ if (TextUtils.isEmpty(imsi)) {
+ return null;
+ }
+ if (imsi.length() > MAX_IMSI_LENGTH) {
+ return null;
}
+ // Detect the first non-digit character.
int nonDigit;
char stopChar = '\0';
for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
@@ -26,48 +66,55 @@ public class IMSIParameter {
}
if (nonDigit == imsi.length()) {
- mImsi = imsi;
- mPrefix = false;
+ // Full IMSI.
+ return new IMSIParameter(imsi, false);
}
else if (nonDigit == imsi.length()-1 && stopChar == '*') {
- mImsi = imsi.substring(0, nonDigit);
- mPrefix = true;
- }
- else {
- throw new IOException("Bad IMSI: '" + imsi + "'");
+ // IMSI prefix.
+ return new IMSIParameter(imsi.substring(0, nonDigit), true);
}
+ return null;
}
- public boolean matches(String fullIMSI) {
+ /**
+ * Perform matching against the given full IMSI.
+ *
+ * @param fullIMSI The full IMSI to match against
+ * @return true if matched
+ */
+ public boolean matchesImsi(String fullIMSI) {
+ if (fullIMSI == null) {
+ return false;
+ }
+
if (mPrefix) {
+ // Prefix matching.
return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length());
- }
- else {
+ } else {
+ // Exact matching.
return mImsi.equals(fullIMSI);
}
}
+ /**
+ * Perform matching against the given MCC-MNC (Mobile Country Code and Mobile Network
+ * Code) combination.
+ *
+ * @param mccMnc The MCC-MNC to match against
+ * @return true if matched
+ */
public boolean matchesMccMnc(String mccMnc) {
- if (mPrefix) {
- // For a prefix match, the entire prefix must match the mcc+mnc
- return mImsi.regionMatches(false, 0, mccMnc, 0, mImsi.length());
+ if (mccMnc == null) {
+ return false;
}
- else {
- // For regular match, the entire length of mcc+mnc must match this IMSI
- return mImsi.regionMatches(false, 0, mccMnc, 0, mccMnc.length());
+ if (mccMnc.length() != MCC_MNC_LENGTH) {
+ return false;
}
- }
-
- public boolean isPrefix() {
- return mPrefix;
- }
-
- public String getImsi() {
- return mImsi;
- }
-
- public int prefixLength() {
- return mImsi.length();
+ int checkLength = MCC_MNC_LENGTH;
+ if (mPrefix && mImsi.length() < MCC_MNC_LENGTH) {
+ checkLength = mImsi.length();
+ }
+ return mImsi.regionMatches(false, 0, mccMnc, 0, checkLength);
}
@Override
@@ -75,12 +122,12 @@ public class IMSIParameter {
if (this == thatObject) {
return true;
}
- else if (thatObject == null || getClass() != thatObject.getClass()) {
+ if (!(thatObject instanceof IMSIParameter)) {
return false;
}
IMSIParameter that = (IMSIParameter) thatObject;
- return mPrefix == that.mPrefix && mImsi.equals(that.mImsi);
+ return mPrefix == that.mPrefix && TextUtils.equals(mImsi, that.mImsi);
}
@Override
diff --git a/service/java/com/android/server/wifi/SIMAccessor.java b/service/java/com/android/server/wifi/SIMAccessor.java
index d4decc4e5..21bfb9c51 100644
--- a/service/java/com/android/server/wifi/SIMAccessor.java
+++ b/service/java/com/android/server/wifi/SIMAccessor.java
@@ -23,7 +23,7 @@ public class SIMAccessor {
List<String> imsis = new ArrayList<>();
for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) {
String imsi = mTelephonyManager.getSubscriberId(subId);
- if (imsi != null && mccMnc.matches(imsi)) {
+ if (imsi != null && mccMnc.matchesImsi(imsi)) {
imsis.add(imsi);
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
new file mode 100644
index 000000000..649534662
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class for providing matching functions against ANQP elements.
+ */
+public class ANQPMatcher {
+ /**
+ * Match the domain names in the ANQP element against the provider's FQDN and SIM credential.
+ * The Domain Name ANQP element might contain domains for 3GPP network (e.g.
+ * wlan.mnc*.mcc*.3gppnetwork.org), so we should match that against the provider's SIM
+ * credential if one is provided.
+ *
+ * @param element The Domain Name ANQP element
+ * @param fqdn The FQDN to compare against
+ * @param imsiParam The IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ public static boolean matchDomainName(DomainNameElement element, String fqdn,
+ IMSIParameter imsiParam, List<String> simImsiList) {
+ if (element == null) {
+ return false;
+ }
+
+ for (String domain : element.getDomains()) {
+ if (DomainMatcher.arg2SubdomainOfArg1(fqdn, domain)) {
+ return true;
+ }
+
+ // Try to retrieve the MCC-MNC string from the domain (for 3GPP network domain) and
+ // match against the provider's SIM credential.
+ if (matchMccMnc(Utils.getMccMnc(Utils.splitDomain(domain)), imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the roaming consortium OIs in the ANQP element against the roaming consortium OIs
+ * of a provider.
+ *
+ * @param element The Roaming Consortium ANQP element
+ * @param providerOIs The roaming consortium OIs of the provider
+ * @return true if a match is found
+ */
+ public static boolean matchRoamingConsortium(RoamingConsortiumElement element,
+ long[] providerOIs) {
+ if (element == null) {
+ return false;
+ }
+ if (providerOIs == null) {
+ return false;
+ }
+ List<Long> rcOIs = element.getOIs();
+ for (long oi : providerOIs) {
+ if (rcOIs.contains(oi)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the NAI realm in the ANQP element against the realm and authentication method of
+ * a provider.
+ *
+ * @param element The NAI Realm ANQP element
+ * @param realm The realm of the provider's credential
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID,
+ AuthParam authParam) {
+ if (element == null || element.getRealmDataList().isEmpty()) {
+ return AuthMatch.INDETERMINATE;
+ }
+
+ int bestMatch = AuthMatch.NONE;
+ for (NAIRealmData realmData : element.getRealmDataList()) {
+ int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam);
+ if (match > bestMatch) {
+ bestMatch = match;
+ if (bestMatch == AuthMatch.EXACT) {
+ break;
+ }
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
+ * Match the 3GPP Network in the ANQP element against the SIM credential of a provider.
+ *
+ * @param element 3GPP Network ANQP element
+ * @param imsiParam The IMSI parameter of the provider's SIM credential
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a matched is found
+ */
+ public static boolean matchThreeGPPNetwork(ThreeGPPNetworkElement element,
+ IMSIParameter imsiParam, List<String> simImsiList) {
+ if (element == null) {
+ return false;
+ }
+ for (CellularNetwork network : element.getNetworks()) {
+ if (matchCellularNetwork(network, imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the given NAI Realm data against the realm and authentication method of a provider.
+ *
+ * @param realmData The NAI Realm data
+ * @param realm The realm of the provider's credential
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID,
+ AuthParam authParam) {
+ // Check for realm domain name match.
+ int realmMatch = AuthMatch.NONE;
+ for (String realmStr : realmData.getRealms()) {
+ if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) {
+ realmMatch = AuthMatch.REALM;
+ break;
+ }
+ }
+
+ if (realmMatch == AuthMatch.NONE || realmData.getEAPMethods().isEmpty()) {
+ return realmMatch;
+ }
+
+ // Check for EAP method match.
+ int eapMethodMatch = AuthMatch.NONE;
+ for (EAPMethod eapMethod : realmData.getEAPMethods()) {
+ eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam);
+ if (eapMethodMatch != AuthMatch.NONE) {
+ break;
+ }
+ }
+
+ if (eapMethodMatch == AuthMatch.NONE) {
+ return AuthMatch.NONE;
+ }
+ return realmMatch | eapMethodMatch;
+ }
+
+ /**
+ * Match the given EAPMethod against the authentication method of a provider.
+ *
+ * @param method The EAP Method
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ private static int matchEAPMethod(EAPMethod method, int eapMethodID, AuthParam authParam) {
+ if (method.getEAPMethodID() != eapMethodID) {
+ return AuthMatch.NONE;
+ }
+ // Check for authentication parameter match.
+ if (authParam != null) {
+ Map<Integer, Set<AuthParam>> authParams = method.getAuthParams();
+ Set<AuthParam> paramSet = authParams.get(authParam.getAuthTypeID());
+ if (paramSet == null || !paramSet.contains(authParam)) {
+ return AuthMatch.NONE;
+ }
+ return AuthMatch.METHOD_PARAM;
+ }
+ return AuthMatch.METHOD;
+ }
+
+ /**
+ * Match a cellular network information in the 3GPP Network ANQP element against the SIM
+ * credential of a provider.
+ *
+ * @param network The cellular network that contained list of PLMNs
+ * @param imsiParam IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ private static boolean matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam,
+ List<String> simImsiList) {
+ for (String plmn : network.getPlmns()) {
+ if (matchMccMnc(plmn, imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match a MCC-MNC against the SIM credential of a provider.
+ *
+ * @param mccMnc The string containing MCC-MNC
+ * @param imsiParam The IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam,
+ List<String> simImsiList) {
+ if (imsiParam == null || simImsiList == null) {
+ return false;
+ }
+ // Match against the IMSI parameter in the provider.
+ if (!imsiParam.matchesMccMnc(mccMnc)) {
+ return false;
+ }
+ // Additional check for verifying the match with IMSIs from the SIM cards, since the IMSI
+ // parameter might not contain the full 6-digit MCC MNC (e.g. IMSI parameter is an IMSI
+ // prefix that contained less than 6-digit of numbers "12345*").
+ for (String imsi : simImsiList) {
+ if (imsi.startsWith(mccMnc)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AuthMatch.java b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
index cd988b548..3abf35fb6 100644
--- a/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
+++ b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.server.wifi.hotspot2;
/**
@@ -9,13 +24,13 @@ package com.android.server.wifi.hotspot2;
* must be maintained accordingly.
*/
public abstract class AuthMatch {
- public static final int None = -1;
- public static final int Indeterminate = 0;
- public static final int Realm = 0x04;
- public static final int Method = 0x02;
- public static final int Param = 0x01;
- public static final int MethodParam = Method | Param;
- public static final int Exact = Realm | Method | Param;
+ public static final int NONE = -1;
+ public static final int INDETERMINATE = 0;
+ public static final int REALM = 0x04;
+ public static final int METHOD = 0x02;
+ public static final int PARAM = 0x01;
+ public static final int METHOD_PARAM = METHOD | PARAM;
+ public static final int EXACT = REALM | METHOD | PARAM;
public static String toString(int match) {
if (match < 0) {
@@ -26,13 +41,13 @@ public abstract class AuthMatch {
}
StringBuilder sb = new StringBuilder();
- if ((match & Realm) != 0) {
+ if ((match & REALM) != 0) {
sb.append("Realm");
}
- if ((match & Method) != 0) {
+ if ((match & METHOD) != 0) {
sb.append("Method");
}
- if ((match & Param) != 0) {
+ if ((match & PARAM) != 0) {
sb.append("Param");
}
return sb.toString();
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 31a0b5ef6..88b04a379 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -43,7 +43,6 @@ import com.android.server.wifi.WifiNative;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -177,20 +176,16 @@ public class PasspointManager {
// Verify IMSI against the IMSI of the installed SIM cards for SIM credential.
if (config.credential.simCredential != null) {
- try {
- if (mSimAccessor.getMatchingImsis(
- new IMSIParameter(config.credential.simCredential.imsi)) == null) {
- Log.e(TAG, "IMSI does not match any SIM card");
- return false;
- }
- } catch (IOException e) {
+ if (mSimAccessor.getMatchingImsis(
+ IMSIParameter.build(config.credential.simCredential.imsi)) == null) {
+ Log.e(TAG, "IMSI does not match any SIM card");
return false;
}
}
// Create a provider and install the necessary certificates and keys.
PasspointProvider newProvider = mObjectFactory.makePasspointProvider(
- config, mKeyStore, mProviderID++);
+ config, mKeyStore, mSimAccessor, mProviderID++);
if (!newProvider.installCertsAndKeys()) {
Log.e(TAG, "Failed to install certificates and keys to keystore");
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
index b5793e7e0..69bf99a9e 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
@@ -19,6 +19,7 @@ package com.android.server.wifi.hotspot2;
import android.net.wifi.hotspot2.PasspointConfiguration;
import com.android.server.wifi.Clock;
+import com.android.server.wifi.SIMAccessor;
import com.android.server.wifi.WifiKeyStore;
import com.android.server.wifi.WifiNative;
@@ -48,8 +49,8 @@ public class PasspointObjectFactory{
* @return {@link PasspointProvider}
*/
public PasspointProvider makePasspointProvider(PasspointConfiguration config,
- WifiKeyStore keyStore, long providerId) {
- return new PasspointProvider(config, keyStore, providerId);
+ WifiKeyStore keyStore, SIMAccessor simAccessor, long providerId) {
+ return new PasspointProvider(config, keyStore, simAccessor, providerId);
}
/**
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
index a0a52f6ea..61fb0352e 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -16,19 +16,29 @@
package com.android.server.wifi.hotspot2;
+import android.net.wifi.EAPConstants;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.security.Credentials;
import android.util.Log;
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.SIMAccessor;
import com.android.server.wifi.WifiKeyStore;
import com.android.server.wifi.hotspot2.anqp.ANQPElement;
import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
/**
@@ -53,12 +63,37 @@ public class PasspointProvider {
private String mClientPrivateKeyAlias;
private String mClientCertificateAlias;
+ private final IMSIParameter mImsiParameter;
+ private final List<String> mMatchingSIMImsiList;
+
+ private final int mEAPMethodID;
+ private final AuthParam mAuthParam;
+
public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
- long providerId) {
+ SIMAccessor simAccessor, long providerId) {
// Maintain a copy of the configuration to avoid it being updated by others.
mConfig = new PasspointConfiguration(config);
mKeyStore = keyStore;
mProviderId = providerId;
+
+ // Setup EAP method and authentication parameter based on the credential.
+ if (mConfig.credential.userCredential != null) {
+ mEAPMethodID = EAPConstants.EAP_TTLS;
+ mAuthParam = new NonEAPInnerAuth(NonEAPInnerAuth.getAuthTypeID(
+ mConfig.credential.userCredential.nonEapInnerMethod));
+ mImsiParameter = null;
+ mMatchingSIMImsiList = null;
+ } else if (mConfig.credential.certCredential != null) {
+ mEAPMethodID = EAPConstants.EAP_TLS;
+ mAuthParam = null;
+ mImsiParameter = null;
+ mMatchingSIMImsiList = null;
+ } else {
+ mEAPMethodID = mConfig.credential.simCredential.eapType;
+ mAuthParam = null;
+ mImsiParameter = IMSIParameter.build(mConfig.credential.simCredential.imsi);
+ mMatchingSIMImsiList = simAccessor.getMatchingImsis(mImsiParameter);
+ }
}
public PasspointConfiguration getConfig() {
@@ -160,12 +195,31 @@ public class PasspointProvider {
/**
* Return the matching status with the given AP, based on the ANQP elements from the AP.
+ *
* @param anqpElements ANQP elements from the AP
- * @return {@link com.android.server.wifi.hotspot2.PasspointMatch}
+ * @return {@link PasspointMatch}
*/
public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements) {
- // TODO(b/33246489): To be implemented.
- return PasspointMatch.None;
+ PasspointMatch providerMatch = matchProvider(anqpElements);
+
+ // Perform authentication match against the NAI Realm.
+ int authMatch = ANQPMatcher.matchNAIRealm(
+ (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm),
+ mConfig.credential.realm, mEAPMethodID, mAuthParam);
+
+ // Auth mismatch, demote provider match.
+ if (authMatch == AuthMatch.NONE) {
+ return PasspointMatch.None;
+ }
+
+ // No realm match, return provider match as is.
+ if ((authMatch & AuthMatch.REALM) == 0) {
+ return providerMatch;
+ }
+
+ // Realm match, promote provider match to roaming if no other provider match is found.
+ return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider
+ : providerMatch;
}
/**
@@ -207,4 +261,34 @@ public class PasspointProvider {
return null;
}
+
+ /**
+ * Perform a provider match based on the given ANQP elements.
+ *
+ * @param anqpElements List of ANQP elements
+ * @return {@link PasspointMatch}
+ */
+ private PasspointMatch matchProvider(Map<ANQPElementType, ANQPElement> anqpElements) {
+ // Domain name matching.
+ if (ANQPMatcher.matchDomainName(
+ (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName),
+ mConfig.homeSp.fqdn, mImsiParameter, mMatchingSIMImsiList)) {
+ return PasspointMatch.HomeProvider;
+ }
+
+ // Roaming Consortium OI matching.
+ if (ANQPMatcher.matchRoamingConsortium(
+ (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
+ mConfig.homeSp.roamingConsortiumOIs)) {
+ return PasspointMatch.RoamingProvider;
+ }
+
+ // 3GPP Network matching.
+ if (ANQPMatcher.matchThreeGPPNetwork(
+ (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork),
+ mImsiParameter, mMatchingSIMImsiList)) {
+ return PasspointMatch.RoamingProvider;
+ }
+ return PasspointMatch.None;
+ }
}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java
index 1afe0d099..b69339315 100644
--- a/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java
@@ -21,6 +21,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.net.ProtocolException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
/**
* The Non-EAP Inner Authentication Type authentication parameter, IEEE802.11-2012, table 8-188.
@@ -30,11 +32,20 @@ import java.nio.ByteBuffer;
* 1
*/
public class NonEAPInnerAuth extends AuthParam {
+ public static final int AUTH_TYPE_UNKNOWN = 0;
public static final int AUTH_TYPE_PAP = 1;
public static final int AUTH_TYPE_CHAP = 2;
public static final int AUTH_TYPE_MSCHAP = 3;
public static final int AUTH_TYPE_MSCHAPV2 = 4;
+ private static final Map<String, Integer> AUTH_TYPE_MAP = new HashMap<>();
+ static {
+ AUTH_TYPE_MAP.put("PAP", AUTH_TYPE_PAP);
+ AUTH_TYPE_MAP.put("CHAP", AUTH_TYPE_CHAP);
+ AUTH_TYPE_MAP.put("MS-CHAP", AUTH_TYPE_MSCHAP);
+ AUTH_TYPE_MAP.put("MS-CHAP-V2", AUTH_TYPE_MSCHAPV2);
+ }
+
@VisibleForTesting
public static final int EXPECTED_LENGTH_VALUE = 1;
@@ -61,6 +72,19 @@ public class NonEAPInnerAuth extends AuthParam {
return new NonEAPInnerAuth(authType);
}
+ /**
+ * Convert an authentication type string to an integer representation.
+ *
+ * @param typeStr The string of authentication type
+ * @return int
+ */
+ public static int getAuthTypeID(String typeStr) {
+ if (AUTH_TYPE_MAP.containsKey(typeStr)) {
+ return AUTH_TYPE_MAP.get(typeStr).intValue();
+ }
+ return AUTH_TYPE_UNKNOWN;
+ }
+
@Override
public boolean equals(Object thatObject) {
if (thatObject == this) {
diff --git a/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java b/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java
new file mode 100644
index 000000000..d99d8a51b
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.IMSIParameter}.
+ */
+@SmallTest
+public class IMSIParameterTest {
+ /**
+ * Data points for testing function {@link IMSIParameter#build}.
+ */
+ private static final Map<String, IMSIParameter> BUILD_PARAM_TEST_MAP = new HashMap<>();
+ static {
+ BUILD_PARAM_TEST_MAP.put(null, null);
+ BUILD_PARAM_TEST_MAP.put("", null);
+ BUILD_PARAM_TEST_MAP.put("1234a123", null); // IMSI contained invalid character.
+ BUILD_PARAM_TEST_MAP.put("1234567890123451", null); // IMSI exceeding max length.
+ BUILD_PARAM_TEST_MAP.put("123456789012345", new IMSIParameter("123456789012345", false));
+ BUILD_PARAM_TEST_MAP.put("1234*", new IMSIParameter("1234", true));
+ }
+
+ /**
+ * Verify the expectations of {@link IMSIParameter#build} function using the predefined
+ * test data {@link #BUILD_PARAM_TEST_MAP}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyBuildIMSIParameter() throws Exception {
+ for (Map.Entry<String, IMSIParameter> entry : BUILD_PARAM_TEST_MAP.entrySet()) {
+ assertEquals(entry.getValue(), IMSIParameter.build(entry.getKey()));
+ }
+ }
+
+ /**
+ * Verify that attempt to match a null IMSI will not cause any crash and should return false.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesNullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+ assertFalse(param.matchesImsi(null));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a full IMSI will only match against an IMSI of the
+ * same value.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesImsiWithFullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+
+ // Full IMSI requires exact matching.
+ assertFalse(param.matchesImsi("123"));
+ assertFalse(param.matchesImsi("12345"));
+ assertTrue(param.matchesImsi("1234"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a prefix IMSI will match against any IMSI that
+ * starts with the same prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesImsiWithPrefixImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", true);
+
+ // Prefix IMSI will match any IMSI that starts with the same prefix.
+ assertFalse(param.matchesImsi("123"));
+ assertTrue(param.matchesImsi("12345"));
+ assertTrue(param.matchesImsi("1234"));
+ }
+
+ /**
+ * Verify that attempt to match a null MCC-MNC will not cause any crash and should return
+ * false.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesNullMccMnc() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+ assertFalse(param.matchesMccMnc(null));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a full IMSI will only match against a 6 digit
+ * MCC-MNC that is a prefix of the IMSI.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncFullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234567890", false);
+
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing an IMSI prefix that's less than 6 digits
+ * will match against any 6-digit MCC-MNC that starts with the same prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncWithPrefixImsiLessThanMccMncLength() throws Exception {
+ IMSIParameter param = new IMSIParameter("12345", true);
+
+ assertFalse(param.matchesMccMnc("123448")); // Prefix mismatch
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123457"));
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing an IMSI prefix that's more than 6 digits
+ * will only match against a 6-digit MCC-MNC that matches the first 6-digit of the prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncWithPrefixImsiMoreThanMccMncLength() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234567890", true);
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
new file mode 100644
index 000000000..5fbd4aaf7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.EAPConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.anqp.eap.InnerAuthEAP;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPMatcher}.
+ */
+@SmallTest
+public class ANQPMatcherTest {
+ /**
+ * Verify that domain name match will fail when a null Domain Name ANQP element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameWithNullElement() throws Exception {
+ assertFalse(ANQPMatcher.matchDomainName(null, "test.com", null, null));
+ }
+
+ /**
+ * Verify that domain name match will succeed when the specified FQDN matches a domain name
+ * in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameUsingFQDN() throws Exception {
+ String fqdn = "test.com";
+ String[] domains = new String[] {fqdn};
+ DomainNameElement element = new DomainNameElement(Arrays.asList(domains));
+ assertTrue(ANQPMatcher.matchDomainName(element, fqdn, null, null));
+ }
+
+ /**
+ * Verify that domain name match will succeed when the specified IMSI parameter and IMSI list
+ * matches a 3GPP network domain in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameUsingIMSI() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+ // 3GPP network domain with MCC=123 and MNC=456.
+ String[] domains = new String[] {"wlan.mnc457.mcc123.3gppnetwork.org"};
+ DomainNameElement element = new DomainNameElement(Arrays.asList(domains));
+ assertTrue(ANQPMatcher.matchDomainName(element, null, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that roaming consortium match will fail when a null Roaming Consortium ANQP
+ * element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortiumWithNullElement() throws Exception {
+ assertFalse(ANQPMatcher.matchRoamingConsortium(null, new long[0]));
+ }
+
+ /**
+ * Verify that a roaming consortium match will succeed when the specified OI matches
+ * an OI in the Roaming Consortium ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortium() throws Exception {
+ long oi = 0x1234L;
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(new Long[] {oi}));
+ assertTrue(ANQPMatcher.matchRoamingConsortium(element, new long[] {oi}));
+ }
+
+ /**
+ * Verify that an indeterminate match will be returned when matching a null NAI Realm
+ * ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithNullElement() throws Exception {
+ assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(null, "test.com",
+ EAPConstants.EAP_TLS, new InnerAuthEAP(EAPConstants.EAP_TTLS)));
+ }
+
+ /**
+ * Verify that an indeterminate match will be returned when matching a NAI Realm
+ * ANQP element contained no NAI realm data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithEmtpyRealmData() throws Exception {
+ NAIRealmElement element = new NAIRealmElement(new ArrayList<NAIRealmData>());
+ assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(element, "test.com",
+ EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a realm match will be returned when the specified realm matches a realm
+ * in the NAI Realm ANQP element with no EAP methods.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithRealmMatch() throws Exception {
+ String realm = "test.com";
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), new ArrayList<EAPMethod>());
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+ assertEquals(AuthMatch.REALM, ANQPMatcher.matchNAIRealm(element, realm,
+ EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a realm and method match will be returned when the specified realm and EAP
+ * method matches a realm in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithRealmMethodMatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TLS;
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, new HashMap<Integer, Set<AuthParam>>());
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.REALM | AuthMatch.METHOD,
+ ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, null));
+ }
+
+ /**
+ * Verify that an exact match will be returned when the specified realm, EAP
+ * method, and the authentication parameter matches a realm with the associated EAP method and
+ * authentication parameter in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithExactMatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.EXACT,
+ ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, authParam));
+ }
+
+ /**
+ * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified EAP method
+ * doesn't match with the corresponding EAP method in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithEAPMethodMismatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.NONE,
+ ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified authentication
+ * parameter doesn't match with the corresponding authentication parameter in the NAI Realm
+ * ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithAuthTypeMismatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ // Mismatch in authentication type.
+ assertEquals(AuthMatch.NONE,
+ ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_PAP)));
+ }
+
+ /**
+ * Verify that 3GPP Network match will fail when a null element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithNullElement() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123456789", "123498723"});
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(null, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will succeed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that matches the both IMSI parameter and an IMSI from the IMSI list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetwork() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123456789", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123456"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element matches both IMSI parameter
+ // and an IMSI from the installed SIM card.
+ assertTrue(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will failed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that match the IMSI parameter but not the IMSI list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithoutSimImsiMatch() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123456"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element doesn't match any of the IMSIs
+ // from the installed SIM card.
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will failed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that doesn't match with the IMSI parameter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithImsiParamMismatch() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123356"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element doesn't match the IMSI parameter.
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index af66dc0b7..59dfa3b58 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -70,6 +70,7 @@ public class PasspointManagerTest {
private static final String TEST_FRIENDLY_NAME = "friendly name";
private static final String TEST_REALM = "realm.test.com";
private static final String TEST_IMSI = "1234*";
+ private static final IMSIParameter TEST_IMSI_PARAM = IMSIParameter.build(TEST_IMSI);
private static final String TEST_SSID = "TestSSID";
private static final long TEST_BSSID = 0x1234L;
@@ -166,8 +167,8 @@ public class PasspointManagerTest {
config.credential.userCredential.eapType = EAPConstants.EAP_TTLS;
config.credential.userCredential.nonEapInnerMethod = "MS-CHAP";
PasspointProvider provider = createMockProvider(config);
- when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), anyLong()))
- .thenReturn(provider);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(provider);
assertTrue(mManager.addOrUpdateProvider(config));
return provider;
@@ -277,8 +278,8 @@ public class PasspointManagerTest {
config.credential.userCredential.eapType = EAPConstants.EAP_TTLS;
config.credential.userCredential.nonEapInnerMethod = "MS-CHAP";
PasspointProvider provider = createMockProvider(config);
- when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), anyLong()))
- .thenReturn(provider);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(provider);
assertTrue(mManager.addOrUpdateProvider(config));
verifyInstalledConfig(config);
@@ -304,11 +305,10 @@ public class PasspointManagerTest {
config.credential.simCredential = new Credential.SimCredential();
config.credential.simCredential.imsi = TEST_IMSI;
config.credential.simCredential.eapType = EAPConstants.EAP_SIM;
- when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI)))
- .thenReturn(new ArrayList<String>());
+ when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(new ArrayList<String>());
PasspointProvider provider = createMockProvider(config);
- when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), anyLong()))
- .thenReturn(provider);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(provider);
assertTrue(mManager.addOrUpdateProvider(config));
verifyInstalledConfig(config);
@@ -335,7 +335,7 @@ public class PasspointManagerTest {
config.credential.simCredential = new Credential.SimCredential();
config.credential.simCredential.imsi = TEST_IMSI;
config.credential.simCredential.eapType = EAPConstants.EAP_SIM;
- when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI))).thenReturn(null);
+ when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(null);
assertFalse(mManager.addOrUpdateProvider(config));
}
@@ -358,11 +358,10 @@ public class PasspointManagerTest {
origConfig.credential.simCredential = new Credential.SimCredential();
origConfig.credential.simCredential.imsi = TEST_IMSI;
origConfig.credential.simCredential.eapType = EAPConstants.EAP_SIM;
- when(mSimAccessor.getMatchingImsis(new IMSIParameter(TEST_IMSI)))
- .thenReturn(new ArrayList<String>());
+ when(mSimAccessor.getMatchingImsis(TEST_IMSI_PARAM)).thenReturn(new ArrayList<String>());
PasspointProvider origProvider = createMockProvider(origConfig);
- when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), anyLong()))
- .thenReturn(origProvider);
+ when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(origProvider);
assertTrue(mManager.addOrUpdateProvider(origConfig));
verifyInstalledConfig(origConfig);
@@ -381,8 +380,8 @@ public class PasspointManagerTest {
newConfig.credential.userCredential.eapType = EAPConstants.EAP_TTLS;
newConfig.credential.userCredential.nonEapInnerMethod = "MS-CHAP";
PasspointProvider newProvider = createMockProvider(newConfig);
- when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore), anyLong()))
- .thenReturn(newProvider);
+ when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(newProvider);
assertTrue(mManager.addOrUpdateProvider(newConfig));
verifyInstalledConfig(newConfig);
}
@@ -409,8 +408,8 @@ public class PasspointManagerTest {
config.credential.userCredential.nonEapInnerMethod = "MS-CHAP";
PasspointProvider provider = mock(PasspointProvider.class);
when(provider.installCertsAndKeys()).thenReturn(false);
- when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), anyLong()))
- .thenReturn(provider);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong())).thenReturn(provider);
assertFalse(mManager.addOrUpdateProvider(config));
}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
index db8a43a9d..0cef02f7b 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
@@ -16,19 +16,34 @@
package com.android.server.wifi.hotspot2;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.net.wifi.EAPConstants;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.wifi.FakeKeys;
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.SIMAccessor;
import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
import org.junit.Before;
import org.junit.Test;
@@ -36,6 +51,11 @@ import org.mockito.Mock;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
* Unit tests for {@link com.android.server.wifi.hotspot2.PasspointProvider}.
@@ -48,6 +68,7 @@ public class PasspointProviderTest {
private static final String CLIENT_PRIVATE_KEY_ALIAS = "USRPKEY_HS2_12";
@Mock WifiKeyStore mKeyStore;
+ @Mock SIMAccessor mSimAccessor;
PasspointProvider mProvider;
/** Sets up test. */
@@ -63,7 +84,7 @@ public class PasspointProviderTest {
* @return {@link com.android.server.wifi.hotspot2.PasspointProvider}
*/
private PasspointProvider createProvider(PasspointConfiguration config) {
- return new PasspointProvider(config, mKeyStore, PROVIDER_ID);
+ return new PasspointProvider(config, mKeyStore, mSimAccessor, PROVIDER_ID);
}
/**
@@ -83,6 +104,59 @@ public class PasspointProviderTest {
}
/**
+ * Helper function for creating a Domain Name ANQP element.
+ *
+ * @param domains List of domain names
+ * @return {@link DomainNameElement}
+ */
+ private DomainNameElement createDomainNameElement(String[] domains) {
+ return new DomainNameElement(Arrays.asList(domains));
+ }
+
+ /**
+ * Helper function for creating a NAI Realm ANQP element.
+ *
+ * @param realm The realm of the network
+ * @param eapMethodID EAP Method ID
+ * @param authParam Authentication parameter
+ * @return {@link NAIRealmElement}
+ */
+ private NAIRealmElement createNAIRealmElement(String realm, int eapMethodID,
+ AuthParam authParam) {
+ Map<Integer, Set<AuthParam>> authParamMap = new HashMap<>();
+ if (authParam != null) {
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ authParamMap.put(authParam.getAuthTypeID(), authSet);
+ }
+ EAPMethod eapMethod = new EAPMethod(eapMethodID, authParamMap);
+ NAIRealmData realmData = new NAIRealmData(Arrays.asList(new String[] {realm}),
+ Arrays.asList(new EAPMethod[] {eapMethod}));
+ return new NAIRealmElement(Arrays.asList(new NAIRealmData[] {realmData}));
+ }
+
+ /**
+ * Helper function for creating a Roaming Consortium ANQP element.
+ *
+ * @param rcOIs Roaming consortium OIs
+ * @return {@link RoamingConsortiumElement}
+ */
+ private RoamingConsortiumElement createRoamingConsortiumElement(Long[] rcOIs) {
+ return new RoamingConsortiumElement(Arrays.asList(rcOIs));
+ }
+
+ /**
+ * Helper function for creating a 3GPP Network ANQP element.
+ *
+ * @param imsiList List of IMSI to be included in a 3GPP Network
+ * @return {@link ThreeGPPNetworkElement}
+ */
+ private ThreeGPPNetworkElement createThreeGPPNetworkElement(String[] imsiList) {
+ CellularNetwork network = new CellularNetwork(Arrays.asList(imsiList));
+ return new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ }
+
+ /**
* Verify that modification to the configuration used for creating PasspointProvider
* will not change the configuration stored inside the PasspointProvider.
*
@@ -94,6 +168,8 @@ public class PasspointProviderTest {
PasspointConfiguration config = new PasspointConfiguration();
config.homeSp = new HomeSP();
config.homeSp.fqdn = "test1";
+ config.credential = new Credential();
+ config.credential.userCredential = new Credential.UserCredential();
mProvider = createProvider(config);
verifyInstalledConfig(config, true);
@@ -115,6 +191,8 @@ public class PasspointProviderTest {
PasspointConfiguration config = new PasspointConfiguration();
config.homeSp = new HomeSP();
config.homeSp.fqdn = "test1";
+ config.credential = new Credential();
+ config.credential.userCredential = new Credential.UserCredential();
mProvider = createProvider(config);
verifyInstalledConfig(config, true);
@@ -238,4 +316,246 @@ public class PasspointProviderTest {
assertTrue(mProvider.getClientPrivateKeyAlias() == null);
assertTrue(mProvider.getClientCertificateAlias() == null);
}
+
+ /**
+ * Verify that a provider is a home provider when its FQDN matches a domain name in the
+ * Domain Name ANQP element and no NAI realm is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithoutNAIRealm() throws Exception {
+ String testDomain = "test.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.homeSp.fqdn = testDomain;
+ config.credential = new Credential();
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ mProvider = createProvider(config);
+
+ // Setup ANQP elements.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its FQDN matches a domain name in the
+ * Domain Name ANQP element and the provider's credential matches the NAI realm provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithNAIRealmMatch() throws Exception {
+ String testDomain = "test.com";
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.homeSp.fqdn = testDomain;
+ config.credential = new Credential();
+ config.credential.realm = testRealm;
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2)));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that there is no match when the provider's FQDN matches a domain name in the
+ * Domain Name ANQP element but the provider's credential doesn't match the authentication
+ * method provided in the NAI realm.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithNAIRealmMismatch() throws Exception {
+ String testDomain = "test.com";
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.homeSp.fqdn = testDomain;
+ config.credential = new Credential();
+ config.credential.realm = testRealm;
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null));
+
+ assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its SIM credential matches an 3GPP network
+ * domain name in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void match3GPPNetworkDomainName() throws Exception {
+ String testImsi = "1234567890";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.credential = new Credential();
+ config.credential.simCredential = new Credential.SimCredential();
+ config.credential.simCredential.imsi = testImsi;
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"wlan.mnc456.mcc123.3gppnetwork.org"}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when a roaming consortium OI matches an OI
+ * in the roaming consortium ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortium() throws Exception {
+ long[] providerRCOIs = new long[] {0x1234L, 0x2345L};
+ Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L};
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.homeSp.roamingConsortiumOIs = providerRCOIs;
+ config.credential = new Credential();
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ mProvider = createProvider(config);
+
+ // Setup Roaming Consortium ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpRCOIs));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when the provider's IMSI parameter and an
+ * IMSI from the SIM card matches a MCC-MNC in the 3GPP Network ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetwork() throws Exception {
+ String testImsi = "1234567890";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.credential = new Credential();
+ config.credential.simCredential = new Credential.SimCredential();
+ config.credential.simCredential.imsi = testImsi;
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup 3GPP Network ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQP3GPPNetwork,
+ createThreeGPPNetworkElement(new String[] {"123456"}));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when its credential matches a NAI realm in
+ * the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealm() throws Exception {
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.credential = new Credential();
+ config.credential.realm = testRealm;
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ mProvider = createProvider(config);
+
+ // Setup NAI Realm ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2)));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its FQDN, roaming consortium OI, and
+ * IMSI all matched against the ANQP elements, since we prefer matching home provider over
+ * roaming provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchHomeOverRoamingProvider() throws Exception {
+ // Setup test data.
+ String testDomain = "test.com";
+ String testImsi = "1234567890";
+ long[] providerRCOIs = new long[] {0x1234L, 0x2345L};
+ Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L};
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = new HomeSP();
+ config.homeSp.fqdn = testDomain;
+ config.homeSp.roamingConsortiumOIs = providerRCOIs;
+ config.credential = new Credential();
+ config.credential.simCredential = new Credential.SimCredential();
+ config.credential.simCredential.imsi = testImsi;
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup ANQP elements.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpRCOIs));
+ anqpElementMap.put(ANQPElementType.ANQP3GPPNetwork,
+ createThreeGPPNetworkElement(new String[] {"123456"}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
}