diff options
author | Ecco Park <eccopark@google.com> | 2019-01-30 01:00:15 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-01-30 01:00:15 +0000 |
commit | f71fed24f316cdf228312c0320077cb509d60788 (patch) | |
tree | 73f7a2c2597bca06950009e1c6805bd20d87dfee | |
parent | f167c24a7c5634b42583551357fec7e2a3409dde (diff) | |
parent | 21c656b1685937afc33b5ea9f4a14fc5b0e14b79 (diff) | |
download | android_frameworks_opt_net_wifi-f71fed24f316cdf228312c0320077cb509d60788.tar.gz android_frameworks_opt_net_wifi-f71fed24f316cdf228312c0320077cb509d60788.tar.bz2 android_frameworks_opt_net_wifi-f71fed24f316cdf228312c0320077cb509d60788.zip |
Merge "Passpoint: auto-connection for SIM-based profiles."
13 files changed, 849 insertions, 19 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 4668cca66..b408b9469 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -4492,7 +4492,11 @@ public class ClientModeImpl extends StateMachine { break; case CMD_RESET_SIM_NETWORKS: log("resetting EAP-SIM/AKA/AKA' networks since SIM was changed"); - mWifiConfigManager.resetSimNetworks(message.arg1 == 1); + boolean simPresent = message.arg1 == 1; + if (!simPresent) { + mPasspointManager.removeEphemeralProviders(); + } + mWifiConfigManager.resetSimNetworks(simPresent); break; case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: mBluetoothConnectionActive = (message.arg1 diff --git a/service/java/com/android/server/wifi/IMSIParameter.java b/service/java/com/android/server/wifi/IMSIParameter.java index ab9ec0a28..5754fb132 100644 --- a/service/java/com/android/server/wifi/IMSIParameter.java +++ b/service/java/com/android/server/wifi/IMSIParameter.java @@ -24,13 +24,13 @@ import android.text.TextUtils; * 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; + public static final int MCC_MNC_LENGTH = 6; + + private static final int MAX_IMSI_LENGTH = 15; private final String mImsi; private final boolean mPrefix; diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 37544fbab..3525cf51b 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -277,7 +277,8 @@ public class WifiInjector { mSimAccessor, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore, mWifiMetrics); mPasspointNetworkEvaluator = new PasspointNetworkEvaluator( - mPasspointManager, mWifiConfigManager, mConnectivityLocalLog); + mPasspointManager, mWifiConfigManager, mConnectivityLocalLog, + mCarrierNetworkConfig, TelephonyManager.from(mContext)); mWifiMetrics.setPasspointManager(mPasspointManager); mScanRequestProxy = new ScanRequestProxy(mContext, (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE), diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java index a75a48896..d95ab3831 100644 --- a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java +++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java @@ -16,6 +16,8 @@ package com.android.server.wifi.hotspot2; +import static com.android.server.wifi.hotspot2.Utils.isCarrierEapMethod; + import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.hotspot2.anqp.CellularNetwork; import com.android.server.wifi.hotspot2.anqp.DomainNameElement; @@ -122,6 +124,28 @@ public class ANQPMatcher { } /** + * Get a EAP-Method from a corresponding NAI realm that has one of them (EAP-SIM/AKA/AKA)'. + * + * @param realm a realm of the provider's credential. + * @param element The NAI Realm ANQP element + * @return a EAP Method (EAP-SIM/AKA/AKA') from matching NAI realm, {@code -1} otherwise. + */ + public static int getCarrierEapMethodFromMatchingNAIRealm(String realm, + NAIRealmElement element) { + if (element == null || element.getRealmDataList().isEmpty()) { + return -1; + } + + for (NAIRealmData realmData : element.getRealmDataList()) { + int eapMethodID = getEapMethodForNAIRealmWithCarrier(realm, realmData); + if (eapMethodID != -1) { + return eapMethodID; + } + } + return -1; + } + + /** * Match the 3GPP Network in the ANQP element against the SIM credential of a provider. * * @param element 3GPP Network ANQP element @@ -186,6 +210,29 @@ public class ANQPMatcher { return realmMatch | eapMethodMatch; } + private static int getEapMethodForNAIRealmWithCarrier(String realm, + NAIRealmData realmData) { + int realmMatch = AuthMatch.NONE; + + for (String realmStr : realmData.getRealms()) { + if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) { + realmMatch = AuthMatch.REALM; + break; + } + } + + if (realmMatch == AuthMatch.NONE) { + return -1; + } + + for (EAPMethod eapMethod : realmData.getEAPMethods()) { + if (isCarrierEapMethod(eapMethod.getEAPMethodID())) { + return eapMethod.getEAPMethodID(); + } + } + return -1; + } + /** * Match the given EAPMethod against the authentication method of a provider. * diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java index 1ba4ff4ca..d0bd6ba5d 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java @@ -168,6 +168,9 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { } XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST); for (PasspointProvider provider : providerList) { + if (provider.isEphemeral()) { + continue; + } serializeProvider(out, provider); } XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST); diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index 5683b8306..d23905729 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -27,6 +27,9 @@ import static android.net.wifi.WifiManager.EXTRA_ICON; import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD; import static android.net.wifi.WifiManager.EXTRA_URL; +import static com.android.server.wifi.hotspot2.Utils.isCarrierEapMethod; + +import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; @@ -37,14 +40,20 @@ import android.net.wifi.WifiManager; import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Looper; +import android.os.Process; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import com.android.server.wifi.Clock; +import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.SIMAccessor; +import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiConfigManager; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiKeyStore; @@ -53,6 +62,7 @@ import com.android.server.wifi.WifiNative; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.Constants; import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; +import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; import com.android.server.wifi.util.InformationElementUtil; @@ -106,6 +116,7 @@ public class PasspointManager { private final CertificateVerifier mCertVerifier; private final WifiMetrics mWifiMetrics; private final PasspointProvisioner mPasspointProvisioner; + private final TelephonyManager mTelephonyManager; // Counter used for assigning unique identifier to each provider. private long mProviderIndex; @@ -225,6 +236,7 @@ public class PasspointManager { mWifiConfigManager = wifiConfigManager; mWifiMetrics = wifiMetrics; mProviderIndex = 0; + mTelephonyManager = TelephonyManager.from(context); wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigUserStoreData( mKeyStore, mSimAccessor, new UserDataSourceHandler())); wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigSharedStoreData( @@ -312,6 +324,216 @@ public class PasspointManager { } /** + * Finds a EAP method from a NAI realm element matched with MCC/MNC of current carrier. + * + * @param scanDetails a list of scanResults used to find a matching AP. + * @return a EAP method which should be one of EAP-Methods(EAP-SIM,AKA and AKA') if matching + * realm is found, {@code -1} otherwise. + */ + public int findEapMethodFromNAIRealmMatchedWithCarrier(List<ScanDetail> scanDetails) { + if (!mWifiConfigManager.isSimPresent()) { + return -1; + } + if (scanDetails == null || scanDetails.isEmpty()) { + return -1; + } + + String mccMnc = mTelephonyManager.getNetworkOperator(); + if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) { + return -1; + } + + String domain = Utils.getRealmForMccMnc(mccMnc); + if (domain == null) { + return -1; + } + for (ScanDetail scanDetail : scanDetails) { + if (!scanDetail.getNetworkDetail().isInterworking()) { + // Skip non-Passpoint APs. + continue; + } + + // Lookup ANQP data in the cache. + long bssid; + ScanResult scanResult = scanDetail.getScanResult(); + InformationElementUtil.RoamingConsortium roamingConsortium = + InformationElementUtil.getRoamingConsortiumIE(scanResult.informationElements); + InformationElementUtil.Vsa vsa = InformationElementUtil.getHS2VendorSpecificIE( + scanResult.informationElements); + try { + bssid = Utils.parseMac(scanResult.BSSID); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid BSSID provided in the scan result: " + scanResult.BSSID); + continue; + } + ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(scanResult.SSID, bssid, + scanResult.hessid, + vsa.anqpDomainID); + ANQPData anqpEntry = mAnqpCache.getEntry(anqpKey); + + if (anqpEntry == null) { + mAnqpRequestManager.requestANQPElements(bssid, anqpKey, + roamingConsortium.anqpOICount > 0, + vsa.hsRelease == NetworkDetail.HSRelease.R2); + Log.d(TAG, "ANQP entry not found for: " + anqpKey); + continue; + } + + // Find a matching domain that has following EAP methods(SIM/AKA/AKA') in NAI realms. + NAIRealmElement naiRealmElement = (NAIRealmElement) anqpEntry.getElements().get( + Constants.ANQPElementType.ANQPNAIRealm); + int eapMethod = ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(domain, + naiRealmElement); + if (eapMethod != -1) { + return eapMethod; + } + } + return -1; + } + + /** + * Creates an ephemeral {@link PasspointConfiguration} for current carrier(SIM) on the device. + * + * @param eapMethod eapMethod used to connect Passpoint Network. + * @return return the {@link PasspointConfiguration} if a configuration is created successfully, + * {@code null} otherwise. + */ + public PasspointConfiguration createEphemeralPasspointConfigForCarrier(int eapMethod) { + String mccMnc = mTelephonyManager.getNetworkOperator(); + if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) { + Log.e(TAG, "invalid length of mccmnc"); + return null; + } + + if (!isCarrierEapMethod(eapMethod)) { + Log.e(TAG, "invalid eapMethod type"); + return null; + } + + String domain = Utils.getRealmForMccMnc(mccMnc); + if (domain == null) { + Log.e(TAG, "can't make a home domain name using " + mccMnc); + return null; + } + PasspointConfiguration config = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(domain); + homeSp.setFriendlyName(mTelephonyManager.getNetworkOperatorName()); + config.setHomeSp(homeSp); + + Credential credential = new Credential(); + credential.setRealm(domain); + Credential.SimCredential simCredential = new Credential.SimCredential(); + + // prefix match + simCredential.setImsi(mccMnc + "*"); + simCredential.setEapType(eapMethod); + credential.setSimCredential(simCredential); + config.setCredential(credential); + if (!config.validate()) { + Log.e(TAG, "Transient PasspointConfiguration is not a valid format"); + return null; + } + return config; + } + + /** + * Check if the {@link PasspointProvider} for a carrier exists. + * @param mccmnc a MCC/MNC of the carrier to find + * @return {@code true} if the provider already exists, {@code false} otherwise. + */ + public boolean hasCarrierProvider(@Nullable String mccmnc) { + String domain = Utils.getRealmForMccMnc(mccmnc); + if (domain == null) { + Log.e(TAG, "can't make a home domain name using " + mccmnc); + return false; + } + + // Check if we already have this provider + for (Map.Entry<String, PasspointProvider> provider : mProviders.entrySet()) { + PasspointConfiguration installedConfig = provider.getValue().getConfig(); + if (installedConfig.getCredential().getSimCredential() == null) { + continue; + } + if (domain.equals(provider.getKey())) { + // We already have the provider that has same FQDN. + return true; + } + + IMSIParameter imsiParameter = provider.getValue().getImsiParameter(); + if (imsiParameter == null) { + continue; + } + + if (imsiParameter.matchesMccMnc(mccmnc)) { + // We already have the provider that has same IMSI. + return true; + } + } + return false; + } + + /** + * Installs a {@link PasspointConfiguration} created for auto connection with EAP-SIM/AKA/AKA'. + * + * It install the Passpoint configuration created on runtime when the (MCC/MNC) of carrier that + * supports encrypted IMSI is matched with one of ScanResults + * + * @param config the Passpoint Configuration to connect the AP with EAP-SIM/AKA/AKA' + * @return {@code true} if config is installed successfully, {@code false} otherwise. + */ + public boolean installEphemeralPasspointConfigForCarrier(PasspointConfiguration config) { + if (config == null) { + Log.e(TAG, "PasspointConfiguration for carrier is null"); + return false; + } + if (!mWifiConfigManager.isSimPresent()) { + Log.e(TAG, "Sim is not presented on the device"); + return false; + } + + Credential.SimCredential simCredential = config.getCredential().getSimCredential(); + if (simCredential == null || simCredential.getImsi() == null) { + Log.e(TAG, "This is not for a carrier configuration using EAP-SIM/AKA/AKA'"); + return false; + } + if (!config.validate()) { + Log.e(TAG, + "It is not a valid format for Passpoint Configuration with EAP-SIM/AKA/AKA'"); + return false; + } + + String imsi = simCredential.getImsi(); + if (imsi.length() < IMSIParameter.MCC_MNC_LENGTH) { + Log.e(TAG, "Invalid IMSI length: " + imsi.length()); + return false; + } + + int index = imsi.indexOf("*"); + if (index == -1) { + Log.e(TAG, "missing * in imsi"); + return false; + } + + if (hasCarrierProvider(imsi.substring(0, index))) { + Log.e(TAG, "It is already in the Provider list"); + return false; + } + + // Create a provider and install the necessary certificates and keys. + PasspointProvider newProvider = mObjectFactory.makePasspointProvider( + config, mKeyStore, mSimAccessor, mProviderIndex++, Process.WIFI_UID); + newProvider.setEphemeral(true); + + Log.d(TAG, "installed PasspointConfiguration for carrier : " + + config.getHomeSp().getFriendlyName()); + + mProviders.put(config.getHomeSp().getFqdn(), newProvider); + mWifiConfigManager.saveToStore(true /* forceWrite */); + return true; + } + + /** * Remove a Passpoint provider identified by the given FQDN. * * @param fqdn The FQDN of the provider to remove @@ -334,6 +556,19 @@ public class PasspointManager { } /** + * Remove the ephemeral providers that are created temporarily for a carrier. + */ + public void removeEphemeralProviders() { + for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) { + PasspointProvider provider = entry.getValue(); + if (provider != null && provider.isEphemeral()) { + mProviders.remove(entry.getKey()); + mWifiConfigManager.removePasspointConfiguredNetwork(entry.getKey()); + } + } + } + + /** * Return the installed Passpoint provider configurations. * * An empty list will be returned when no provider is installed. diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java index e18280c1d..86f3f0efe 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java @@ -16,18 +16,22 @@ package com.android.server.wifi.hotspot2; +import static com.android.server.wifi.hotspot2.Utils.isCarrierEapMethod; + import android.annotation.NonNull; import android.net.wifi.WifiConfiguration; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Process; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.LocalLog; import android.util.Pair; +import com.android.server.wifi.CarrierNetworkConfig; import com.android.server.wifi.NetworkUpdateResult; import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiConfigManager; import com.android.server.wifi.WifiNetworkSelector; -import com.android.server.wifi.WifiNetworkSelector.NetworkEvaluator.OnConnectableListener; import com.android.server.wifi.util.ScanResultUtil; import java.util.ArrayList; @@ -43,7 +47,8 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva private final PasspointManager mPasspointManager; private final WifiConfigManager mWifiConfigManager; private final LocalLog mLocalLog; - + private final CarrierNetworkConfig mCarrierNetworkConfig; + private final TelephonyManager mTelephonyManager; /** * Contained information for a Passpoint network candidate. */ @@ -60,10 +65,13 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva } public PasspointNetworkEvaluator(PasspointManager passpointManager, - WifiConfigManager wifiConfigManager, LocalLog localLog) { + WifiConfigManager wifiConfigManager, LocalLog localLog, + CarrierNetworkConfig carrierNetworkConfig, TelephonyManager telephonyManager) { mPasspointManager = passpointManager; mWifiConfigManager = wifiConfigManager; mLocalLog = localLog; + mCarrierNetworkConfig = carrierNetworkConfig; + mTelephonyManager = telephonyManager; } @Override @@ -82,6 +90,24 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva // Sweep the ANQP cache to remove any expired ANQP entries. mPasspointManager.sweepCache(); + // Creates an ephemeral Passpoint profile if it finds a matching Passpoint AP for MCC/MNC + // of the current carrier on the device. + if (mWifiConfigManager.isSimPresent() + && mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable() + && !mPasspointManager.hasCarrierProvider( + mTelephonyManager.getNetworkOperator())) { + int eapMethod = mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( + scanDetails); + if (isCarrierEapMethod(eapMethod)) { + PasspointConfiguration carrierConfig = + mPasspointManager.createEphemeralPasspointConfigForCarrier( + eapMethod); + if (carrierConfig != null) { + mPasspointManager.installEphemeralPasspointConfigForCarrier(carrierConfig); + } + } + } + // Go through each ScanDetail and find the best provider for each ScanDetail. List<PasspointNetworkCandidate> candidateList = new ArrayList<>(); for (ScanDetail scanDetail : scanDetails) { diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java index 334da7d73..3d73b533e 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java @@ -94,6 +94,12 @@ public class PasspointProvider { private boolean mHasEverConnected; private boolean mIsShared; + /** + * This is a flag to indicate if the Provider is created temporarily. + * Thus, it is not saved permanently unlike normal Passpoint profile. + */ + private boolean mIsEphemeral = false; + public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, SIMAccessor simAccessor, long providerId, int creatorUid) { this(config, keyStore, simAccessor, providerId, creatorUid, null, null, null, null, false, @@ -176,6 +182,18 @@ public class PasspointProvider { mHasEverConnected = hasEverConnected; } + public boolean isEphemeral() { + return mIsEphemeral; + } + + public void setEphemeral(boolean isEphemeral) { + mIsEphemeral = isEphemeral; + } + + public IMSIParameter getImsiParameter() { + return mImsiParameter; + } + /** * Install certificates and key based on current configuration. * Note: the certificates and keys in the configuration will get cleared once diff --git a/service/java/com/android/server/wifi/hotspot2/Utils.java b/service/java/com/android/server/wifi/hotspot2/Utils.java index 005af2a87..e523f2f0a 100644 --- a/service/java/com/android/server/wifi/hotspot2/Utils.java +++ b/service/java/com/android/server/wifi/hotspot2/Utils.java @@ -1,5 +1,11 @@ package com.android.server.wifi.hotspot2; +import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; +import static com.android.server.wifi.hotspot2.anqp.Constants.NIBBLE_MASK; + +import android.net.wifi.EAPConstants; + +import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.hotspot2.anqp.Constants; import java.nio.ByteBuffer; @@ -10,9 +16,6 @@ import java.util.LinkedList; import java.util.List; import java.util.TimeZone; -import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK; -import static com.android.server.wifi.hotspot2.anqp.Constants.NIBBLE_MASK; - public abstract class Utils { public static final long UNSET_TIME = -1; @@ -101,6 +104,46 @@ public abstract class Utils { return prefix; } + /** + * Creates a realm that consists of mcc and mnc. + * + * @param mccmnc MCC(Mobile Country Code) and MNC (Mobile Network Code) + * @return a realm that has the MCC and MNC as format (wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org). + */ + public static String getRealmForMccMnc(String mccmnc) { + // The length of mccmnc is 5 or 6. + if (mccmnc == null || (mccmnc.length() != (IMSIParameter.MCC_MNC_LENGTH - 1) + && mccmnc.length() != IMSIParameter.MCC_MNC_LENGTH)) { + return null; + } + + // Please refer to 3GPP TS 23.003 for the definition of Home network realm when making + // the home network realm using mcc/mnc. + // 1. Take first 5 or 6 digits, depending on whether a 2 or 3 digit MNC used and separate + // them into MCC and MNC; if the MNC is 2 digits then a zero shall be added at the + // beginning. + // 2. Create the wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org domain name. + String mcc = mccmnc.substring(0, 3); + String mnc = mccmnc.substring(3); + if (mnc.length() == 2) { + mnc = "0" + mnc; + } + return String.format("wlan.mnc%s.mcc%s.3gppnetwork.org", mnc, mcc); + } + + /** + * Check if the eapMethod is for EAP-Methods that carrier supports. + * + * @param eapMethod eap method to check + * @return {@code true} if the provided {@code eapMethod} belongs to the + * EAP-Methods(EAP-SIM/AKA/AKA'), {@code false} otherwise. + */ + public static boolean isCarrierEapMethod(int eapMethod) { + return eapMethod == EAPConstants.EAP_SIM + || eapMethod == EAPConstants.EAP_AKA + || eapMethod == EAPConstants.EAP_AKA_PRIME; + } + public static String roamingConsortiumsToString(long[] ois) { if (ois == null) { return "null"; diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 71793c974..28307813b 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -3199,6 +3199,22 @@ public class ClientModeImplTest { } /** + * Verify removing sim will also remove an ephemeral Passpoint Provider. + */ + @Test + public void testResetSimNetworkWhenRemovingSim() throws Exception { + // Switch to connect mode and verify wifi is reported as enabled + startSupplicantAndDispatchMessages(); + + // Indicate that sim is removed. + mCmi.sendMessage(ClientModeImpl.CMD_RESET_SIM_NETWORKS, false); + mLooper.dispatchAll(); + + verify(mPasspointManager).removeEphemeralProviders(); + verify(mWifiConfigManager).resetSimNetworks(eq(false)); + } + + /** * Verifies that WifiLastResortWatchdog is notified of FOURWAY_HANDSHAKE_TIMEOUT. */ @Test diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java index 134d27d0a..4d4ea4487 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java @@ -51,6 +51,9 @@ import java.util.Set; */ @SmallTest public class ANQPMatcherTest { + private static final String TEST_MCC_MNC = "123456"; + private static final String TEST_3GPP_FQDN = String.format("wlan.mnc%s.mcc%s.3gppnetwork.org", + TEST_MCC_MNC.substring(3), TEST_MCC_MNC.substring(0, 3)); /** * Verify that domain name match will fail when a null Domain Name ANQP element is provided. * @@ -362,4 +365,97 @@ public class ANQPMatcherTest { // The MCC-MNC provided in 3GPP Network ANQP element doesn't match the IMSI parameter. assertFalse(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList)); } + + /** + * Verify that it will return a EAP-Method from the NAI realm when there is a matched realm in + * the NAIRealm element. + */ + @Test + public void getEapMethodForNAIRealmWithCarrierInMatch() { + // Test data. + String realm = TEST_3GPP_FQDN; + int eapMethodID = EAPConstants.EAP_AKA; + + // Create a realm that has the EAP method + EAPMethod method = new EAPMethod(eapMethodID, null); + NAIRealmData realmData = new NAIRealmData( + Arrays.asList(new String[]{realm}), Arrays.asList(new EAPMethod[]{method})); + + // Setup NAI Realm element. + NAIRealmElement element = new NAIRealmElement( + Arrays.asList(new NAIRealmData[]{realmData})); + + assertEquals(EAPConstants.EAP_AKA, + ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element)); + } + + /** + * Verify that it will return -1 when there is a matched realm in the NAIRealm element, but it + * does not have a EAP Method. + */ + @Test + public void getEapMethodForNAIRealmWithCarrierInMatchButNotEapMethod() { + // Test data. + String realm = TEST_3GPP_FQDN; + NAIRealmData realmData = new NAIRealmData(Arrays.asList(new String[]{realm}), + new ArrayList<>()); + + // Setup NAI Realm element. + NAIRealmElement element = new NAIRealmElement( + Arrays.asList(new NAIRealmData[]{realmData})); + + assertEquals(-1, + ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element)); + } + + /** + * Verify that it will return -1 when there is a matched realm in the NAIRealm element, but it + * does have non-carrier EAP-method. + */ + @Test + public void getEapMethodForNAIRealmWithCarrierInMatchButNotCarrierEapMethod() { + // Test data. + String realm = TEST_3GPP_FQDN; + 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(-1, + ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element)); + } + + /** + * Verify that it will return -1 when there is no matched realm in the NAIRealm element. + */ + @Test + public void getEapMethodForNAIRealmWithCarrierInNoMatch() { + // 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(-1, + ANQPMatcher.getCarrierEapMethodFromMatchingNAIRealm(TEST_3GPP_FQDN, element)); + } } 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 35b66c2b8..2442751b4 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -30,6 +30,7 @@ import static android.net.wifi.WifiManager.EXTRA_URL; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -67,15 +68,18 @@ import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Looper; import android.os.UserHandle; import android.os.test.TestLooper; +import android.telephony.TelephonyManager; import android.util.Base64; import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.wifi.Clock; import com.android.server.wifi.FakeKeys; import com.android.server.wifi.IMSIParameter; import com.android.server.wifi.SIMAccessor; +import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiConfigManager; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiKeyStore; @@ -86,7 +90,10 @@ import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; import com.android.server.wifi.hotspot2.anqp.DomainNameElement; import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; import com.android.server.wifi.hotspot2.anqp.I18Name; +import com.android.server.wifi.hotspot2.anqp.NAIRealmData; +import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; +import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.InformationElementUtil.RoamingConsortium; @@ -121,7 +128,7 @@ public class PasspointManagerTest { private static final String TEST_FRIENDLY_NAME = "friendly name"; private static final String TEST_FRIENDLY_NAME2 = "second friendly name"; private static final String TEST_REALM = "realm.test.com"; - private static final String TEST_IMSI = "1234*"; + private static final String TEST_IMSI = "123456*"; private static final IMSIParameter TEST_IMSI_PARAM = IMSIParameter.build(TEST_IMSI); private static final long TEST_BSSID = 0x112233445566L; @@ -131,6 +138,9 @@ public class PasspointManagerTest { private static final String TEST_BSSID_STRING2 = "11:22:33:44:55:77"; private static final String TEST_SSID3 = "TestSSID3"; private static final String TEST_BSSID_STRING3 = "11:22:33:44:55:88"; + private static final String TEST_MCC_MNC = "123456"; + private static final String TEST_3GPP_FQDN = String.format("wlan.mnc%s.mcc%s.3gppnetwork.org", + TEST_MCC_MNC.substring(3), TEST_MCC_MNC.substring(0, 3)); private static final long TEST_HESSID = 0x5678L; private static final int TEST_ANQP_DOMAIN_ID = 0; @@ -286,16 +296,17 @@ public class PasspointManagerTest { * * @return {@link PasspointConfiguration} */ - private PasspointConfiguration createTestConfigWithSimCredential() { + private PasspointConfiguration createTestConfigWithSimCredential(String fqdn, String imsi, + String realm) { PasspointConfiguration config = new PasspointConfiguration(); HomeSp homeSp = new HomeSp(); - homeSp.setFqdn(TEST_FQDN); + homeSp.setFqdn(fqdn); homeSp.setFriendlyName(TEST_FRIENDLY_NAME); config.setHomeSp(homeSp); Credential credential = new Credential(); credential.setRealm(TEST_REALM); Credential.SimCredential simCredential = new Credential.SimCredential(); - simCredential.setImsi(TEST_IMSI); + simCredential.setImsi(imsi); simCredential.setEapType(EAPConstants.EAP_SIM); credential.setSimCredential(simCredential); config.setCredential(credential); @@ -320,6 +331,23 @@ public class PasspointManagerTest { } /** + * Helper function for adding a test provider with SIM credentials to the manager. Return the + * mock provider that's added to the manager. + * + * @return {@link PasspointProvider} + */ + private PasspointProvider addTestCarrierProvider(String fqdn, String imsi, String realm) { + PasspointConfiguration config = createTestConfigWithSimCredential(fqdn, imsi, realm); + PasspointProvider provider = createMockProvider(config); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider); + + assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID)); + + return provider; + } + + /** * Helper function for creating a ScanResult for testing. * * @return {@link ScanResult} @@ -372,6 +400,31 @@ public class PasspointManagerTest { } /** + * Helper function for generating {@link ScanDetail} for testing. + */ + private ScanDetail generateScanDetail(String ssid, String bssid, long hessid, int anqpDomaiId, + boolean isPasspoint) { + NetworkDetail networkDetail = mock(NetworkDetail.class); + + ScanDetail scanDetail = mock(ScanDetail.class); + ScanResult scanResult = new ScanResult(); + scanResult.SSID = ssid; + scanResult.BSSID = bssid; + scanResult.hessid = hessid; + scanResult.anqpDomainId = anqpDomaiId; + if (isPasspoint) { + lenient().when(networkDetail.isInterworking()).thenReturn(true); + scanResult.flags = ScanResult.FLAG_PASSPOINT_NETWORK; + } else { + lenient().when(networkDetail.isInterworking()).thenReturn(false); + } + + lenient().when(scanDetail.getScanResult()).thenReturn(scanResult); + lenient().when(scanDetail.getNetworkDetail()).thenReturn(networkDetail); + return scanDetail; + } + + /** * Verify that the ANQP elements will be added to the ANQP cache on receiving a successful * response. * @@ -587,7 +640,8 @@ public class PasspointManagerTest { */ @Test public void addRemoveProviderWithValidSimCredential() throws Exception { - PasspointConfiguration config = createTestConfigWithSimCredential(); + PasspointConfiguration config = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); PasspointProvider provider = createMockProvider(config); when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider); @@ -630,7 +684,8 @@ public class PasspointManagerTest { @Test public void addProviderWithExistingConfig() throws Exception { // Add a provider with the original configuration. - PasspointConfiguration origConfig = createTestConfigWithSimCredential(); + PasspointConfiguration origConfig = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); PasspointProvider origProvider = createMockProvider(origConfig); when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore), eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(origProvider); @@ -1518,4 +1573,184 @@ public class PasspointManagerTest { assertEquals(true, mManager.startSubscriptionProvisioning(TEST_UID, osuProvider, mCallback)); } + + /** + * Verify that it will return {@code null} if the EAP-Method provided is not a carrier + * EAP-Method. + */ + @Test + public void verifyCreateEphemeralPasspointConfigurationWithNonCarrierEapMethod() { + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + TelephonyManager.class).startMocking(); + try { + TelephonyManager telephonyManager = mock(TelephonyManager.class); + when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); + when(telephonyManager.getNetworkOperator()).thenReturn("123456"); + PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, + mWifiKeyStore, mClock, + mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, + mWifiMetrics); + + assertNull(passpointManager.createEphemeralPasspointConfigForCarrier( + EAPConstants.EAP_TLS)); + } finally { + session.finishMocking(); + } + } + + /** + * Verify that it creates an ephemeral Passpoint configuration for the carrier. + */ + @Test + public void verifyCreateEphemeralPasspointConfigurationWithCarrierEapMethod() { + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + TelephonyManager.class).startMocking(); + try { + TelephonyManager telephonyManager = mock(TelephonyManager.class); + String mccmnc = "123456"; + when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); + when(telephonyManager.getNetworkOperator()).thenReturn(mccmnc); + when(telephonyManager.getNetworkOperatorName()).thenReturn("test"); + + PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, + mWifiKeyStore, mClock, + mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, + mWifiMetrics); + + PasspointConfiguration result = + passpointManager.createEphemeralPasspointConfigForCarrier( + EAPConstants.EAP_AKA); + + assertNotNull(result); + assertTrue(result.validate()); + assertEquals(Utils.getRealmForMccMnc(mccmnc), result.getHomeSp().getFqdn()); + assertEquals(mccmnc + "*", result.getCredential().getSimCredential().getImsi()); + } finally { + session.finishMocking(); + } + } + + /** + * Verify that it will not install the Passpoint configuration with Non-Carrier EAP method. + */ + @Test + public void verifyInstallEphemeralPasspointConfigurationWithNonCarrierEapMethod() { + when(mWifiConfigManager.isSimPresent()).thenReturn(true); + PasspointConfiguration config = createTestConfigWithUserCredential("abc.com", "test"); + PasspointProvider provider = createMockProvider(config); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), anyInt())).thenReturn(provider); + + assertFalse(mManager.installEphemeralPasspointConfigForCarrier(config)); + } + + /** + * Verify that it installs the carrier Passpoint configuration successfully. + */ + @Test + public void verifyInstallEphemeralPasspointConfiguration() { + when(mWifiConfigManager.isSimPresent()).thenReturn(true); + PasspointConfiguration config = createTestConfigWithSimCredential(TEST_FQDN, TEST_IMSI, + TEST_REALM); + PasspointProvider provider = createMockProvider(config); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mSimAccessor), anyLong(), anyInt())).thenReturn(provider); + + assertTrue(mManager.installEphemeralPasspointConfigForCarrier(config)); + } + + /** + * Verify that it returns {@code true} when it has Carrier Provider. + */ + @Test + public void verifyHasProviderForCarrierWithMatch() { + addTestCarrierProvider(TEST_3GPP_FQDN, TEST_MCC_MNC, TEST_3GPP_FQDN); + + assertTrue(mManager.hasCarrierProvider(TEST_MCC_MNC)); + } + + /** + * Verify that it returns {@code false} when it does not have Carrier Provider. + */ + @Test + public void verifyHasProviderForCarrierWithNoMatch() { + addTestProvider(TEST_FQDN, TEST_FRIENDLY_NAME); + + assertFalse(mManager.hasCarrierProvider(TEST_MCC_MNC)); + } + + /** + * Verify that it returns a carrier EAP-method from NAI-Realm matched with the carrier. + */ + @Test + public void verifyFindEapMethodFromNAIRealmMatchedWithCarrierWithMatch() { + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + TelephonyManager.class).mockStatic( + InformationElementUtil.class).startMocking(); + try { + TelephonyManager telephonyManager = mock(TelephonyManager.class); + when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); + when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC); + when(mWifiConfigManager.isSimPresent()).thenReturn(true); + List<ScanDetail> scanDetails = new ArrayList<>(); + scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, TEST_HESSID, + TEST_ANQP_DOMAIN_ID, true)); + InformationElementUtil.Vsa vsa = new InformationElementUtil.Vsa(); + + // ANQP_DOMAIN_ID(TEST_ANQP_KEY) + vsa.anqpDomainID = TEST_ANQP_DOMAIN_ID; + when(InformationElementUtil.getHS2VendorSpecificIE(isNull())).thenReturn(vsa); + Map<ANQPElementType, ANQPElement> anqpElementMapOfAp1 = new HashMap<>(); + List<NAIRealmData> realmDataList = new ArrayList<>(); + realmDataList.add(new NAIRealmData(Arrays.asList(TEST_3GPP_FQDN), + Arrays.asList(new EAPMethod(EAPConstants.EAP_AKA, null)))); + anqpElementMapOfAp1.put(ANQPElementType.ANQPNAIRealm, + new NAIRealmElement(realmDataList)); + + ANQPData anqpData = new ANQPData(mClock, anqpElementMapOfAp1); + when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(anqpData); + + PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, + mWifiKeyStore, mClock, + mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, + mWifiMetrics); + + assertEquals(EAPConstants.EAP_AKA, + passpointManager.findEapMethodFromNAIRealmMatchedWithCarrier(scanDetails)); + } finally { + session.finishMocking(); + } + } + + /** + * Verify that it returns -1 when there is no NAI-Realm matched with the carrier. + */ + @Test + public void verifyFindEapMethodFromNAIRealmMatchedWithCarrierWithNoMatch() { + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + TelephonyManager.class).mockStatic( + InformationElementUtil.class).startMocking(); + try { + TelephonyManager telephonyManager = mock(TelephonyManager.class); + when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); + when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC); + when(mWifiConfigManager.isSimPresent()).thenReturn(true); + List<ScanDetail> scanDetails = new ArrayList<>(); + scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, 0, 0, false)); + + PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, + mWifiKeyStore, mClock, + mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, + mWifiMetrics); + + assertEquals(-1, + passpointManager.findEapMethodFromNAIRealmMatchedWithCarrier(scanDetails)); + } finally { + session.finishMocking(); + } + } } diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java index dc7e9bb3b..fe0b26bac 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; @@ -29,15 +30,18 @@ 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.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.HomeSp; +import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.server.wifi.CarrierNetworkConfig; import com.android.server.wifi.NetworkUpdateResult; import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiConfigManager; @@ -70,8 +74,11 @@ public class PasspointNetworkEvaluatorTest { private static final PasspointProvider TEST_PROVIDER2 = generateProvider(TEST_CONFIG2); @Mock PasspointManager mPasspointManager; + @Mock PasspointConfiguration mPasspointConfiguration; @Mock WifiConfigManager mWifiConfigManager; @Mock OnConnectableListener mOnConnectableListener; + @Mock TelephonyManager mTelephonyManager; + @Mock CarrierNetworkConfig mCarrierNetworkConfig; LocalLog mLocalLog; PasspointNetworkEvaluator mEvaluator; @@ -108,7 +115,7 @@ public class PasspointNetworkEvaluatorTest { * Helper function for generating {@link ScanDetail} for testing. * * @param ssid The SSID associated with the scan - * @param rssiLevel The RSSI level associated with the scan + * @param bssid The BSSID associated with the scan * @return {@link ScanDetail} */ private static ScanDetail generateScanDetail(String ssid, String bssid) { @@ -135,7 +142,9 @@ public class PasspointNetworkEvaluatorTest { initMocks(this); mLocalLog = new LocalLog(512); mEvaluator = new PasspointNetworkEvaluator(mPasspointManager, mWifiConfigManager, - mLocalLog); + mLocalLog, mCarrierNetworkConfig, mTelephonyManager); + when(mWifiConfigManager.isSimPresent()).thenReturn(true); + when(mTelephonyManager.getNetworkOperator()).thenReturn("123456"); } /** @@ -367,6 +376,103 @@ public class PasspointNetworkEvaluatorTest { } /** + * Verify that it never creates an ephemeral Passpoint Configuration when the carrier does not + * support encrypted IMSI. + */ + @Test + public void skipCreateEphemeralPasspointConfigurationWhenNoSupportEncryptedIMSI() { + // Setup ScanDetail and match providers. + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(false); + when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); + + assertEquals(null, mEvaluator.evaluateNetworks( + scanDetails, null, null, false, false, mOnConnectableListener)); + verify(mPasspointManager, never()).createEphemeralPasspointConfigForCarrier(anyInt()); + } + + /** + * Verify that it never creates an ephemeral Passpoint Configuration when there is no SIM on the + * device. + */ + @Test + public void skipCreateEphemeralPasspointConfigurationWithoutSIMCard() { + // Setup ScanDetail and match providers. + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mWifiConfigManager.isSimPresent()).thenReturn(false); + when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + + assertEquals(null, mEvaluator.evaluateNetworks( + scanDetails, null, null, false, false, mOnConnectableListener)); + verify(mPasspointManager, never()).createEphemeralPasspointConfigForCarrier(anyInt()); + } + + /** + * Verify that it never creates an ephemeral Passpoint Configuration when the profile for the + * carrier already exists. + */ + @Test + public void skipCreateEphemeralPasspointConfigurationWhenProfileExists() { + // Setup ScanDetail and match providers. + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] { + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(true); + + assertEquals(null, mEvaluator.evaluateNetworks( + scanDetails, null, null, false, false, mOnConnectableListener)); + verify(mPasspointManager, never()).createEphemeralPasspointConfigForCarrier(anyInt()); + } + + /** + * Verify that it creates an ephemeral Passpoint Configuration when a EAP-Method is found from + * NAI realms matched with the carrier. + */ + @Test + public void createEphemeralPasspointConfigurationWhenEapMethodIsFoundFromMatchingNAIRealm() { + // Setup ScanDetail + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{ + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); + when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( + any(List.class))).thenReturn( + EAPConstants.EAP_AKA); + + assertEquals(null, mEvaluator.evaluateNetworks( + scanDetails, null, null, false, false, mOnConnectableListener)); + verify(mPasspointManager).findEapMethodFromNAIRealmMatchedWithCarrier(any(List.class)); + verify(mPasspointManager).createEphemeralPasspointConfigForCarrier( + eq(EAPConstants.EAP_AKA)); + } + + /** + * Verify that it installs the ephemeral configuration when the config is created for the + * carrier. + */ + @Test + public void installEphemeralPasspointConfiguration() { + // Setup ScanDetail + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{ + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); + when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( + any(List.class))).thenReturn( + EAPConstants.EAP_AKA); + when(mPasspointManager.createEphemeralPasspointConfigForCarrier( + EAPConstants.EAP_AKA)).thenReturn(mPasspointConfiguration); + + assertEquals(null, mEvaluator.evaluateNetworks( + scanDetails, null, null, false, false, mOnConnectableListener)); + verify(mPasspointManager).installEphemeralPasspointConfigForCarrier( + eq(mPasspointConfiguration)); + } + + /** * Verify that when the current active network is matched, the scan info associated with * the network is updated. * |