diff options
27 files changed, 562 insertions, 255 deletions
diff --git a/service/java/com/android/server/wifi/CarrierNetworkConfig.java b/service/java/com/android/server/wifi/CarrierNetworkConfig.java index cf39e0a7b..2d99d14d9 100644 --- a/service/java/com/android/server/wifi/CarrierNetworkConfig.java +++ b/service/java/com/android/server/wifi/CarrierNetworkConfig.java @@ -53,6 +53,7 @@ public class CarrierNetworkConfig { private static final int ENCODED_SSID_INDEX = 0; private static final int EAP_TYPE_INDEX = 1; private static final int CONFIG_ELEMENT_SIZE = 2; + private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier"); private boolean mDbg = false; @@ -61,6 +62,12 @@ public class CarrierNetworkConfig { private boolean mIsCarrierImsiEncryptionInfoAvailable = false; private ImsiEncryptionInfo mLastImsiEncryptionInfo = null; // used for dumpsys only + // RFC2045: adds Line Feed at each 76 chars and encode it. + public static final int ENCODING_METHOD_RFC_2045 = 2045; + + // RFC4648: encodes whole data into one string. + public static final int ENCODING_METHOD_RFC_4648 = 4648; + /** * Enable/disable verbose logging. */ @@ -118,6 +125,15 @@ public class CarrierNetworkConfig { } /** + * @return the base64 encoding flag with a carrier AP, or -1 if the specified AP is not + * associate with a carrier network. + */ + public int getBase64EncodingFlag(String ssid) { + NetworkInfo info = mCarrierNetworkMap.get(ssid); + return info == null ? -1 : info.mBase64EncodingFlag; + } + + /** * @return True if carrier IMSI encryption info is available, False otherwise. */ public boolean isCarrierEncryptionInfoAvailable() { @@ -157,16 +173,19 @@ public class CarrierNetworkConfig { private static class NetworkInfo { final int mEapType; final String mCarrierName; + final int mBase64EncodingFlag; - NetworkInfo(int eapType, String carrierName) { + NetworkInfo(int eapType, String carrierName, int base64EncodingFlag) { mEapType = eapType; mCarrierName = carrierName; + mBase64EncodingFlag = base64EncodingFlag; } @Override public String toString() { return new StringBuffer("NetworkInfo: eap=").append(mEapType).append( - ", carrier=").append(mCarrierName).toString(); + ", carrier=").append(mCarrierName).append("base64EncodingFlag=").append( + mBase64EncodingFlag).toString(); } } @@ -230,22 +249,35 @@ public class CarrierNetworkConfig { return; } + int encodeMethod = carrierConfig.getInt( + CarrierConfigManager.KEY_IMSI_ENCODING_METHOD_INT, ENCODING_METHOD_RFC_2045); + if (encodeMethod != ENCODING_METHOD_RFC_2045 && encodeMethod != ENCODING_METHOD_RFC_4648) { + Log.e(TAG, "Invalid encoding method type: " + encodeMethod); + return; + } + for (String networkConfig : networkConfigs) { String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR); + if (configArr.length != CONFIG_ELEMENT_SIZE) { Log.e(TAG, "Ignore invalid config: " + networkConfig); continue; } + try { + int flag = Base64.DEFAULT; + if (encodeMethod == ENCODING_METHOD_RFC_4648) { + flag = Base64.NO_WRAP; + } String ssid = new String(Base64.decode( - configArr[ENCODED_SSID_INDEX], Base64.DEFAULT)); + configArr[ENCODED_SSID_INDEX], flag)); int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX])); // Verify EAP type, must be a SIM based EAP type. if (eapType == -1) { Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]); continue; } - mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName)); + mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName, flag)); } catch (NumberFormatException e) { Log.e(TAG, "Failed to parse EAP type: '" + configArr[EAP_TYPE_INDEX] + "' " + e.getMessage()); diff --git a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java index b052c7d94..323cfc0b9 100644 --- a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java @@ -110,6 +110,17 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { } config.enterpriseConfig.setEapMethod(eapType); + // Check if we already have a network with the same credentials in WifiConfigManager + // database. If yes, we should check if the network is currently blacklisted. + WifiConfiguration existingNetwork = + mWifiConfigManager.getConfiguredNetwork(config.configKey()); + if (existingNetwork != null + && !existingNetwork.getNetworkSelectionStatus().isNetworkEnabled() + && !mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId)) { + mLocalLog.log(TAG + ": Ignoring blacklisted network: " + + WifiNetworkSelector.toNetworkString(existingNetwork)); + continue; + } // Add the newly created WifiConfiguration to WifiConfigManager. NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); @@ -131,7 +142,10 @@ public class CarrierNetworkEvaluator implements NetworkEvaluator { config = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId()); - WifiConfiguration.NetworkSelectionStatus nss = config.getNetworkSelectionStatus(); + WifiConfiguration.NetworkSelectionStatus nss = null; + if (config != null) { + nss = config.getNetworkSelectionStatus(); + } if (nss == null) { mLocalLog.log(TAG + ": null network selection status for: " + config); continue; diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index ff17e410e..64f800571 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -17,7 +17,6 @@ package com.android.server.wifi; import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; @@ -811,6 +810,7 @@ public class ClientModeImpl extends StateMachine { mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler()); mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this); + mLinkProperties = new LinkProperties(); mMcastLockManagerFilterController = new McastLockManagerFilterController(); @@ -1590,19 +1590,20 @@ public class ClientModeImpl extends StateMachine { } /** - * Method to trigger a disconnect. - * Note: To be used from within the wifi stack. + * Disconnect from Access Point */ - public void disconnectCommandInternal() { - sendMessage(CMD_DISCONNECT, 0 /* fromExternal */); + public void disconnectCommand() { + sendMessage(CMD_DISCONNECT); } /** * Method to trigger a disconnect. - * Note: To be used from public API surface. + * + * @param uid UID of requesting caller + * @param reason disconnect reason */ - public void disconnectCommandExternal() { - sendMessage(CMD_DISCONNECT, 1 /* fromExternal */); + public void disconnectCommand(int uid, int reason) { + sendMessage(CMD_DISCONNECT, uid, reason); } /** @@ -3675,7 +3676,7 @@ public class ClientModeImpl extends StateMachine { if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case CMD_USER_UNLOCK: @@ -4133,7 +4134,7 @@ public class ClientModeImpl extends StateMachine { netId = message.arg1; if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case CMD_ENABLE_NETWORK: @@ -4157,7 +4158,7 @@ public class ClientModeImpl extends StateMachine { replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED); if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } } else { loge("Failed to disable network"); @@ -4172,7 +4173,7 @@ public class ClientModeImpl extends StateMachine { if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } } break; @@ -4186,7 +4187,8 @@ public class ClientModeImpl extends StateMachine { // Pair<identity, encrypted identity> Pair<String, String> identityPair = TelephonyUtil.getSimIdentity(getTelephonyManager(), - new TelephonyUtil(), mTargetWifiConfiguration); + new TelephonyUtil(), mTargetWifiConfiguration, + mWifiInjector.getCarrierNetworkConfig()); Log.i(TAG, "SUP_REQUEST_IDENTITY: identityPair=" + identityPair); if (identityPair != null && identityPair.first != null) { mWifiNative.simIdentityResponse(mInterfaceName, netId, @@ -4332,7 +4334,7 @@ public class ClientModeImpl extends StateMachine { if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network. - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case CMD_REMOVE_USER_CONFIGURATIONS: @@ -4341,7 +4343,7 @@ public class ClientModeImpl extends StateMachine { if (removedNetworkIds.contains(mTargetNetworkId) || removedNetworkIds.contains(mLastNetworkId)) { // Disconnect and let autojoin reselect a new network. - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case WifiManager.CONNECT_NETWORK: @@ -4422,7 +4424,7 @@ public class ClientModeImpl extends StateMachine { netId = message.arg1; if (netId == mTargetNetworkId || netId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case CMD_ASSOCIATED_BSSID: @@ -4486,7 +4488,7 @@ public class ClientModeImpl extends StateMachine { } else { logw("Connected to unknown networkId " + mLastNetworkId + ", disconnecting..."); - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } break; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: @@ -4519,7 +4521,7 @@ public class ClientModeImpl extends StateMachine { if (isProviderOwnedNetwork(mTargetNetworkId, fqdn) || isProviderOwnedNetwork(mLastNetworkId, fqdn)) { logd("Disconnect from current network since its provider is updated"); - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } replyToMessage(message, message.what, SUCCESS); } else { @@ -4532,7 +4534,7 @@ public class ClientModeImpl extends StateMachine { if (isProviderOwnedNetwork(mTargetNetworkId, fqdn) || isProviderOwnedNetwork(mLastNetworkId, fqdn)) { logd("Disconnect from current network since its provider is removed"); - disconnectCommandInternal(); + sendMessage(CMD_DISCONNECT); } mWifiConfigManager.removePasspointConfiguredNetwork(fqdn); replyToMessage(message, message.what, SUCCESS); @@ -5062,17 +5064,8 @@ public class ClientModeImpl extends StateMachine { } break; case CMD_DISCONNECT: - boolean fromExternal = message.arg1 == 1; - if (fromExternal) { - mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_API); - // For external disconnect requests, temporarily disable the network. - mWifiConfigManager.updateNetworkSelectionStatus( - mLastNetworkId, DISABLED_BY_WIFI_MANAGER_DISCONNECT); - } else { - mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_GENERIC); - } + mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, + StaEvent.DISCONNECT_GENERIC); mWifiNative.disconnect(mInterfaceName); transitionTo(mDisconnectingState); break; diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 92bec08a3..81b513920 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX; - import android.annotation.Nullable; import android.app.ActivityManager; import android.app.admin.DeviceAdminInfo; @@ -128,7 +126,6 @@ public class WifiConfigManager { 1, // threshold for DISABLED_NO_INTERNET_TEMPORARY 1, // threshold for DISABLED_WPS_START 6, // threshold for DISABLED_TLS_VERSION_MISMATCH - 1, // threshold for DISABLED_BY_WIFI_MANAGER_DISCONNECT 1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS 1, // threshold for DISABLED_NO_INTERNET_PERMANENT 1, // threshold for DISABLED_BY_WIFI_MANAGER @@ -153,7 +150,6 @@ public class WifiConfigManager { 10 * 60 * 1000, // threshold for DISABLED_NO_INTERNET_TEMPORARY 0 * 60 * 1000, // threshold for DISABLED_WPS_START Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION - 1 * 60 * 60 * 1000, // threshold for DISABLED_BY_WIFI_MANAGER_DISCONNECT Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET_PERMANENT Integer.MAX_VALUE, // threshold for DISABLED_BY_WIFI_MANAGER @@ -940,11 +936,7 @@ public class WifiConfigManager { internalConfig.allowedGroupManagementCiphers = (BitSet) externalConfig.allowedGroupManagementCiphers.clone(); } - if (externalConfig.allowedSuiteBCiphers != null - && !externalConfig.allowedSuiteBCiphers.isEmpty()) { - internalConfig.allowedSuiteBCiphers = - (BitSet) externalConfig.allowedSuiteBCiphers.clone(); - } + // allowedSuiteBCiphers is set internally according to the certificate type // Copy over the |IpConfiguration| parameters if set. if (externalConfig.getIpConfiguration() != null) { @@ -1568,7 +1560,7 @@ public class WifiConfigManager { if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) { setNetworkSelectionEnabled(config); setNetworkStatus(config, WifiConfiguration.Status.ENABLED); - } else if (reason < NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX) { + } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { setNetworkSelectionTemporarilyDisabled(config, reason); } else { setNetworkSelectionPermanentlyDisabled(config, reason); @@ -2796,7 +2788,8 @@ public class WifiConfigManager { if (TelephonyUtil.isSimConfig(config)) { Pair<String, String> currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, - new TelephonyUtil(), config); + new TelephonyUtil(), config, + mWifiInjector.getCarrierNetworkConfig()); if (mVerboseLoggingEnabled) { Log.d(TAG, "New identity for config " + config + ": " + currentIdentity); } diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java index a5425dbc1..b288ccb24 100644 --- a/service/java/com/android/server/wifi/WifiDiagnostics.java +++ b/service/java/com/android/server/wifi/WifiDiagnostics.java @@ -530,8 +530,6 @@ class WifiDiagnostics extends BaseWifiDiagnostics { } private boolean flushDump(int errorCode) { - if (mBuildProperties.isUserBuild()) return false; - if (errorCode == REPORT_REASON_USER_ACTION) return false; long currentTime = mClock.getWallClockMillis(); diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 755a80fb6..d45f78e6b 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -484,6 +484,10 @@ public class WifiInjector { return mPasspointManager; } + public CarrierNetworkConfig getCarrierNetworkConfig() { + return mCarrierNetworkConfig; + } + public WakeupController getWakeupController() { return mWakeupController; } diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java index 3b8c5bbd7..a22be9b51 100644 --- a/service/java/com/android/server/wifi/WifiKeyStore.java +++ b/service/java/com/android/server/wifi/WifiKeyStore.java @@ -26,10 +26,13 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.security.Key; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -250,6 +253,21 @@ public class WifiKeyStore { } } + + /** + * @param certData byte array of the certificate + */ + private X509Certificate buildCACertificate(byte[] certData) { + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + InputStream inputStream = new ByteArrayInputStream(certData); + X509Certificate caCertificateX509 = (X509Certificate) certificateFactory + .generateCertificate(inputStream); + return caCertificateX509; + } catch (CertificateException e) { + return null; + } + } /** * Update/Install keys for given enterprise network. * @@ -277,6 +295,61 @@ public class WifiKeyStore { return false; } } + + // For WPA3-Enterprise 192-bit networks, set the SuiteBCipher field based on the + // CA certificate type. Suite-B requires SHA384, reject other certs. + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { + // Read the first CA certificate, and initialize + byte[] certData = mKeyStore.get( + Credentials.CA_CERTIFICATE + config.enterpriseConfig.getCaCertificateAlias(), + android.os.Process.WIFI_UID); + + if (certData == null) { + Log.e(TAG, "Failed reading CA certificate for Suite-B"); + return false; + } + + X509Certificate x509CaCert = buildCACertificate(certData); + + if (x509CaCert != null) { + String sigAlgOid = x509CaCert.getSigAlgOID(); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Signature algorithm: " + sigAlgOid); + } + config.allowedSuiteBCiphers.clear(); + + // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates + // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 + // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term + // Suite-B was already coined in the IEEE 802.11-2016 specification for + // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates + // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally + // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, + // we are supporting both types here. + if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { + // sha384WithRSAEncryption + config.allowedSuiteBCiphers.set( + WifiConfiguration.SuiteBCipher.ECDHE_RSA); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Selecting Suite-B RSA"); + } + } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { + // ecdsa-with-SHA384 + config.allowedSuiteBCiphers.set( + WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Selecting Suite-B ECDSA"); + } + } else { + Log.e(TAG, "Invalid CA certificate type for Suite-B: " + + sigAlgOid); + return false; + } + } else { + Log.e(TAG, "Invalid CA certificate for Suite-B"); + return false; + } + } return true; } } diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index 174379e33..a8e554f2d 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -940,7 +940,7 @@ public class WifiNetworkFactory extends NetworkFactory { // Invoked at the termination of current connected request processing. private void teardownForConnectedNetwork() { Log.i(TAG, "Disconnecting from network on reset"); - mWifiInjector.getClientModeImpl().disconnectCommandInternal(); + mWifiInjector.getClientModeImpl().disconnectCommand(); mConnectedSpecificNetworkRequest = null; mConnectedSpecificNetworkRequestSpecifier = null; // ensure there is no active request in progress. diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java index ad09dce8e..a633a0c71 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java +++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java @@ -504,7 +504,7 @@ public class WifiNetworkSuggestionsManager { if (mActiveNetworkSuggestionsMatchingConnection.isEmpty()) { Log.i(TAG, "Only network suggestion matching the connected network removed. " + "Disconnecting..."); - mWifiInjector.getClientModeImpl().disconnectCommandInternal(); + mWifiInjector.getClientModeImpl().disconnectCommand(); } } } diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 276ead4a4..a7d6f4b85 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -1644,7 +1644,7 @@ public class WifiServiceImpl extends BaseWifiService { return false; } mLog.info("disconnect uid=%").c(Binder.getCallingUid()).flush(); - mClientModeImpl.disconnectCommandExternal(); + mClientModeImpl.disconnectCommand(); return true; } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index d23905729..e874ed1a5 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -338,7 +338,7 @@ public class PasspointManager { return -1; } - String mccMnc = mTelephonyManager.getNetworkOperator(); + String mccMnc = mTelephonyManager.getSimOperator(); if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) { return -1; } @@ -399,7 +399,7 @@ public class PasspointManager { * {@code null} otherwise. */ public PasspointConfiguration createEphemeralPasspointConfigForCarrier(int eapMethod) { - String mccMnc = mTelephonyManager.getNetworkOperator(); + String mccMnc = mTelephonyManager.getSimOperator(); if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) { Log.e(TAG, "invalid length of mccmnc"); return null; @@ -418,7 +418,7 @@ public class PasspointManager { PasspointConfiguration config = new PasspointConfiguration(); HomeSp homeSp = new HomeSp(); homeSp.setFqdn(domain); - homeSp.setFriendlyName(mTelephonyManager.getNetworkOperatorName()); + homeSp.setFriendlyName(mTelephonyManager.getSimOperatorName()); config.setHomeSp(homeSp); Credential credential = new Credential(); diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java index df17389fb..fe882f630 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java @@ -33,6 +33,7 @@ import com.android.server.wifi.ScanDetail; import com.android.server.wifi.WifiConfigManager; import com.android.server.wifi.WifiNetworkSelector; import com.android.server.wifi.util.ScanResultUtil; +import com.android.server.wifi.util.TelephonyUtil; import java.util.ArrayList; import java.util.List; @@ -96,11 +97,11 @@ public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEva 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() + // of the current MNO carrier on the device. + if ((TelephonyUtil.getCarrierType(mTelephonyManager) == TelephonyUtil.CARRIER_MNO_TYPE) && mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable() && !mPasspointManager.hasCarrierProvider( - mTelephonyManager.getNetworkOperator())) { + mTelephonyManager.getSimOperator())) { int eapMethod = mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( scanDetails); if (isCarrierEapMethod(eapMethod)) { diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java index 664824a2a..bf9e6d390 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java @@ -29,6 +29,7 @@ import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.ProvisioningCallback; import android.net.wifi.hotspot2.omadm.PpsMoParser; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; @@ -81,12 +82,12 @@ public class PasspointProvisioner { private final WfaKeyStore mWfaKeyStore; private final PasspointObjectFactory mObjectFactory; private final SystemInfo mSystemInfo; - private RedirectListener mRedirectListener; private int mCurrentSessionId = 0; private int mCallingUid; private boolean mVerboseLoggingEnabled = false; private WifiManager mWifiManager; private PasspointManager mPasspointManager; + private Looper mLooper; PasspointProvisioner(Context context, WifiNative wifiNative, PasspointObjectFactory objectFactory, PasspointManager passpointManager) { @@ -107,12 +108,12 @@ public class PasspointProvisioner { * @param looper Looper on which the Provisioning state machine will run */ public void init(Looper looper) { + mLooper = looper; mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - mProvisioningStateMachine.start(new Handler(looper)); + mProvisioningStateMachine.start(new Handler(mLooper)); mOsuNetworkConnection.init(mProvisioningStateMachine.getHandler()); // Offload the heavy load job to another thread mProvisioningStateMachine.getHandler().post(() -> { - mRedirectListener = RedirectListener.createInstance(looper); mWfaKeyStore.load(); mOsuServerConnection.init(mObjectFactory.getSSLContext(TLS_VERSION), mObjectFactory.getTrustManagerImpl(mWfaKeyStore.get())); @@ -142,10 +143,6 @@ public class PasspointProvisioner { */ public boolean startSubscriptionProvisioning(int callingUid, OsuProvider provider, IProvisioningCallback callback) { - if (mRedirectListener == null) { - Log.e(TAG, "RedirectListener is not possible to run"); - return false; - } mCallingUid = callingUid; Log.v(TAG, "Provisioning started with " + provider.toString()); @@ -181,12 +178,20 @@ public class PasspointProvisioner { private String mSessionId; private String mWebUrl; private PasspointConfiguration mPasspointConfiguration; + private RedirectListener mRedirectListener; + private HandlerThread mRedirectHandlerThread; + private Handler mRedirectStartStopHandler; /** * Initializes and starts the state machine with a handler to handle incoming events */ public void start(Handler handler) { mHandler = handler; + if (mRedirectHandlerThread == null) { + mRedirectHandlerThread = new HandlerThread("RedirectListenerHandler"); + mRedirectHandlerThread.start(); + mRedirectStartStopHandler = new Handler(mRedirectHandlerThread.getLooper()); + } } /** @@ -216,9 +221,17 @@ public class PasspointProvisioner { } resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_PROVISIONING_ABORTED); } + mProvisioningCallback = callback; + mRedirectListener = RedirectListener.createInstance(mLooper); + + if (mRedirectListener == null) { + resetStateMachineForFailure( + ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER); + return; + } + if (!mOsuServerConnection.canValidateServer()) { Log.w(TAG, "Provisioning is not possible"); - mProvisioningCallback = callback; resetStateMachineForFailure( ProvisioningCallback.OSU_FAILURE_PROVISIONING_NOT_AVAILABLE); return; @@ -228,12 +241,10 @@ public class PasspointProvisioner { serverUrl = new URL(provider.getServerUri().toString()); } catch (MalformedURLException e) { Log.e(TAG, "Invalid Server URL"); - mProvisioningCallback = callback; resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_SERVER_URL_INVALID); return; } mServerUrl = serverUrl; - mProvisioningCallback = callback; mOsuProvider = provider; if (mOsuProvider.getOsuSsid() == null) { // Find a best matching OsuProvider that has an OSU SSID from current scanResults @@ -376,7 +387,7 @@ public class PasspointProvisioner { invokeProvisioningCallback(PROVISIONING_STATUS, ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED); - mRedirectListener.stopServer(); + mRedirectListener.stopServer(mRedirectStartStopHandler); secondSoapExchange(); } @@ -395,7 +406,7 @@ public class PasspointProvisioner { resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_PROVISIONING_ABORTED); return; } - mRedirectListener.stopServer(); + mRedirectListener.stopServer(mRedirectStartStopHandler); resetStateMachineForFailure( ProvisioningCallback.OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER); } @@ -754,7 +765,7 @@ public class PasspointProvisioner { } mProvisioningStateMachine.handleTimeOutForRedirectResponse(); } - })) { + }, mRedirectStartStopHandler)) { Log.e(TAG, "fails to start redirect listener"); resetStateMachineForFailure( ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER); @@ -944,12 +955,15 @@ public class PasspointProvisioner { } private void resetStateMachine() { - mRedirectListener.stopServer(); + if (mRedirectListener != null) { + mRedirectListener.stopServer(mRedirectStartStopHandler); + } mOsuNetworkConnection.setEventCallback(null); mOsuNetworkConnection.disconnectIfNeeded(); mOsuServerConnection.setEventCallback(null); mOsuServerConnection.cleanup(); mPasspointConfiguration = null; + mProvisioningCallback = null; changeState(STATE_INIT); } diff --git a/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java b/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java index 03c3ebebb..75a226efd 100644 --- a/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java +++ b/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java @@ -17,9 +17,7 @@ package com.android.server.wifi.hotspot2.soap; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.util.Log; @@ -47,10 +45,8 @@ public class RedirectListener extends NanoHTTPD { static final int USER_TIMEOUT_MILLIS = 4 * 60 * 1000; private static final String TAG = "PasspointRedirectListener"; - private final String mPath; private final URL mServerUrl; - private final Handler mStartStopHandler; private final Handler mHandler; private Runnable mTimeOutTask; private RedirectCallback mRedirectCallback; @@ -72,7 +68,7 @@ public class RedirectListener extends NanoHTTPD { } @VisibleForTesting - /* package */ RedirectListener(Looper looper, @Nullable Looper startStopLooper, int port) + /* package */ RedirectListener(Looper looper, int port) throws IOException { super(InetAddress.getLocalHost().getHostAddress(), port); @@ -82,12 +78,6 @@ public class RedirectListener extends NanoHTTPD { mServerUrl = new URL("http", getHostname(), port, mPath); mHandler = new Handler(looper); mTimeOutTask = () -> mRedirectCallback.onRedirectTimedOut(); - if (startStopLooper == null) { - HandlerThread redirectHandlerThread = new HandlerThread("RedirectListenerHandler"); - redirectHandlerThread.start(); - startStopLooper = redirectHandlerThread.getLooper(); - } - mStartStopHandler = new Handler(startStopLooper); } /** @@ -99,8 +89,14 @@ public class RedirectListener extends NanoHTTPD { public static RedirectListener createInstance(@NonNull Looper looper) { RedirectListener redirectListener; try { - redirectListener = new RedirectListener(looper, null, - new ServerSocket(0, 1, InetAddress.getLocalHost()).getLocalPort()); + ServerSocket serverSocket = new ServerSocket(0, 1, InetAddress.getLocalHost()); + redirectListener = new RedirectListener(looper, serverSocket.getLocalPort()); + redirectListener.setServerSocketFactory(() -> { + // Close current server socket so that new server socket is able to bind the port + // in the start() of NanoHTTPD. + serverSocket.close(); + return new ServerSocket(); + }); } catch (IOException e) { Log.e(TAG, "fails to create an instance: " + e); return null; @@ -112,21 +108,26 @@ public class RedirectListener extends NanoHTTPD { * Start redirect listener * * @param callback to be notified when the redirect request is received or timed out. - * @return {@code true} in success, {@code false} if the {@code callback} is {@code null} or the - * server is already running. + * @param startHandler handler on which the start code is executed. + * @return {@code true} in success, {@code false} if the {@code callback} and {@code + * startHandler} are {@code null} or the server is already running. */ - public boolean startServer(@NonNull RedirectCallback callback) { + public boolean startServer(@NonNull RedirectCallback callback, @NonNull Handler startHandler) { if (callback == null) { return false; } + if (startHandler == null) { + return false; + } + if (isAlive()) { Log.e(TAG, "redirect listener is already running"); return false; } mRedirectCallback = callback; - mStartStopHandler.post(() -> { + startHandler.post(() -> { try { start(); } catch (IOException e) { @@ -139,13 +140,18 @@ public class RedirectListener extends NanoHTTPD { /** * Stop redirect listener + * + * @param stopHandler handler on which the stop code is executed. */ - public void stopServer() { + public void stopServer(@NonNull Handler stopHandler) { if (mHandler.hasCallbacks(mTimeOutTask)) { mHandler.removeCallbacks(mTimeOutTask); } + if (stopHandler == null) { + return; + } if (isServerAlive()) { - mStartStopHandler.post(() -> stop()); + stopHandler.post(() -> stop()); } } diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java index 597aa71ad..d453c91ea 100644 --- a/service/java/com/android/server/wifi/util/TelephonyUtil.java +++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java @@ -16,6 +16,7 @@ package com.android.server.wifi.util; +import android.annotation.NonNull; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.telephony.ImsiEncryptionInfo; @@ -25,6 +26,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.CarrierNetworkConfig; import com.android.server.wifi.WifiNative; import java.security.InvalidKeyException; @@ -45,6 +47,10 @@ public class TelephonyUtil { public static final String DEFAULT_EAP_PREFIX = "\0"; + public static final int CARRIER_INVALID_TYPE = -1; + public static final int CARRIER_MNO_TYPE = 0; // Mobile Network Operator + public static final int CARRIER_MVNO_TYPE = 1; // Mobile Virtual Network Operator + private static final String THREE_GPP_NAI_REALM_FORMAT = "wlan.mnc%s.mcc%s.3gppnetwork.org"; // IMSI encryption method: RSA-OAEP with SHA-256 hash function @@ -73,16 +79,22 @@ public class TelephonyUtil { * * @param tm TelephonyManager instance * @param config WifiConfiguration that indicates what sort of authentication is necessary + * @param telephonyUtil TelephonyUtil instance + * @param carrierNetworkConfig CarrierNetworkConfig instance * @return Pair<identify, encrypted identity> or null if the SIM is not available * or config is invalid */ public static Pair<String, String> getSimIdentity(TelephonyManager tm, - TelephonyUtil telephonyUtil, - WifiConfiguration config) { + TelephonyUtil telephonyUtil, + WifiConfiguration config, CarrierNetworkConfig carrierNetworkConfig) { if (tm == null) { Log.e(TAG, "No valid TelephonyManager"); return null; } + if (carrierNetworkConfig == null) { + Log.e(TAG, "No valid CarrierNetworkConfig"); + return null; + } String imsi = tm.getSubscriberId(); String mccMnc = ""; @@ -104,8 +116,15 @@ public class TelephonyUtil { return null; } + int base64EncodingFlag = carrierNetworkConfig.getBase64EncodingFlag(config.SSID); + if (base64EncodingFlag == -1) { + // no encrypted IMSI identity. + return Pair.create(identity, ""); + } String encryptedIdentity = buildEncryptedIdentity(telephonyUtil, - getSimMethodForConfig(config), imsi, mccMnc, imsiEncryptionInfo); + getSimMethodForConfig(config), imsi, mccMnc, imsiEncryptionInfo, + base64EncodingFlag); + // In case of failure for encryption, set empty string if (encryptedIdentity == null) encryptedIdentity = ""; return Pair.create(identity, encryptedIdentity); @@ -116,15 +135,17 @@ public class TelephonyUtil { * a Base64 encoded string. * * @param key The public key to use for encryption + * @param encodingFlag base64 encoding flag * @return Base64 encoded string, or null if encryption failed */ @VisibleForTesting - public String encryptDataUsingPublicKey(PublicKey key, byte[] data) { + public String encryptDataUsingPublicKey(PublicKey key, byte[] data, int encodingFlag) { try { Cipher cipher = Cipher.getInstance(IMSI_CIPHER_TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = cipher.doFinal(data); - return Base64.encodeToString(encryptedBytes, 0, encryptedBytes.length, Base64.DEFAULT); + + return Base64.encodeToString(encryptedBytes, 0, encryptedBytes.length, encodingFlag); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { Log.e(TAG, "Encryption failed: " + e.getMessage()); @@ -138,14 +159,16 @@ public class TelephonyUtil { * "0" - EAP-AKA Identity * "1" - EAP-SIM Identity * "6" - EAP-AKA' Identity + * * @param eapMethod EAP authentication method: EAP-SIM, EAP-AKA, EAP-AKA' * @param imsi The IMSI retrieved from the SIM * @param mccMnc The MCC MNC identifier retrieved from the SIM * @param imsiEncryptionInfo The IMSI encryption info retrieved from the SIM + * @param base64EncodingFlag base64 encoding flag */ private static String buildEncryptedIdentity(TelephonyUtil telephonyUtil, int eapMethod, - String imsi, String mccMnc, - ImsiEncryptionInfo imsiEncryptionInfo) { + String imsi, String mccMnc, + ImsiEncryptionInfo imsiEncryptionInfo, int base64EncodingFlag) { if (imsiEncryptionInfo == null) { return null; } @@ -155,9 +178,10 @@ public class TelephonyUtil { return null; } imsi = prefix + imsi; + // Build and return the encrypted identity. String encryptedImsi = telephonyUtil.encryptDataUsingPublicKey( - imsiEncryptionInfo.getPublicKey(), imsi.getBytes()); + imsiEncryptionInfo.getPublicKey(), imsi.getBytes(), base64EncodingFlag); if (encryptedImsi == null) { Log.e(TAG, "Failed to encrypt IMSI"); return null; @@ -621,4 +645,23 @@ public class TelephonyUtil { return null; } } + + /** + * Get the carrier type of current SIM. + * + * @param tm {@link TelephonyManager} instance + * @return carrier type of current active sim, {{@link #CARRIER_INVALID_TYPE}} if sim is not + * ready or {@code tm} is {@code null} + */ + public static int getCarrierType(@NonNull TelephonyManager tm) { + if (tm == null || tm.getSimState() != TelephonyManager.SIM_STATE_READY) { + return CARRIER_INVALID_TYPE; + } + + // If two APIs return the same carrier ID, then is considered as MNO, otherwise MVNO + if (tm.getCarrierIdFromSimMccMnc() == tm.getSimCarrierId()) { + return CARRIER_MNO_TYPE; + } + return CARRIER_MVNO_TYPE; + } } diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java index 6e731e626..cdae4190d 100644 --- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java +++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java @@ -80,14 +80,17 @@ public class CarrierNetworkConfigTest { * * @param ssid The SSID of the carrier network * @param eapType The EAP type of the carrier network + * @param encodingMethod base64 encoding method * @return {@link PersistableBundle} containing carrier config */ - private PersistableBundle generateTestConfig(String ssid, int eapType) { + private PersistableBundle generateTestConfig(String ssid, int eapType, int encodingMethod) { PersistableBundle bundle = new PersistableBundle(); String networkConfig = new String(Base64.encode(ssid.getBytes(), Base64.DEFAULT)) + "," + eapType; bundle.putStringArray(CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY, new String[] {networkConfig}); + bundle.putInt(CarrierConfigManager.KEY_IMSI_ENCODING_METHOD_INT, encodingMethod); + return bundle; } @@ -103,7 +106,8 @@ public class CarrierNetworkConfigTest { .thenReturn(mSubscriptionManager); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) - .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE)); + .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE, + CarrierNetworkConfig.ENCODING_METHOD_RFC_2045)); when(mSubscriptionManager.getActiveSubscriptionInfoList()) .thenReturn(Arrays.asList(new SubscriptionInfo[] {TEST_SUBSCRIPTION_INFO})); when(mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN)) @@ -135,6 +139,7 @@ public class CarrierNetworkConfigTest { assertTrue(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID)); assertEquals(TEST_INTERNAL_EAP_TYPE, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID)); assertEquals(TEST_CARRIER_NAME, mCarrierNetworkConfig.getCarrierName(TEST_SSID)); + assertEquals(Base64.DEFAULT, mCarrierNetworkConfig.getBase64EncodingFlag(TEST_SSID)); } /** @@ -198,7 +203,8 @@ public class CarrierNetworkConfigTest { when(mSubscriptionManager.getActiveSubscriptionInfoList()) .thenReturn(Arrays.asList(new SubscriptionInfo[] {updatedSubscriptionInfo})); when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) - .thenReturn(generateTestConfig(updatedSsid, updatedStandardEapType)); + .thenReturn(generateTestConfig(updatedSsid, updatedStandardEapType, + CarrierNetworkConfig.ENCODING_METHOD_RFC_2045)); mBroadcastReceiver.onReceive(mContext, new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); @@ -206,11 +212,14 @@ public class CarrierNetworkConfigTest { assertFalse(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID)); assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID)); assertEquals(null, mCarrierNetworkConfig.getCarrierName(TEST_SSID)); + assertEquals(-1, mCarrierNetworkConfig.getBase64EncodingFlag(TEST_SSID)); // Verify that updated SSID is associated with a carrier network. assertTrue(mCarrierNetworkConfig.isCarrierNetwork(updatedSsid)); assertEquals(updatedInternalEapType, mCarrierNetworkConfig.getNetworkEapType(updatedSsid)); assertEquals(updatedCarrierName, mCarrierNetworkConfig.getCarrierName(updatedSsid)); + assertEquals(Base64.DEFAULT, + mCarrierNetworkConfig.getBase64EncodingFlag(updatedSsid)); } /** @@ -242,4 +251,36 @@ public class CarrierNetworkConfigTest { mContentObserver.onChange(false); assertTrue(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()); } + + /** + * Verify that base64Encoding type should be {@link Base64#NO_WRAP} when carrier configuration + * defines RFC4648 for encoding method. + */ + @Test + public void verifyBase64EncodingTypeWithRfc4648() { + when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) + .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE, + CarrierNetworkConfig.ENCODING_METHOD_RFC_4648)); + mBroadcastReceiver.onReceive(mContext, + new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + assertEquals(Base64.NO_WRAP, mCarrierNetworkConfig.getBase64EncodingFlag(TEST_SSID)); + } + + /** + * Verify that carrier network config is not generated when carrier configuration defines + * unsupported encoding method. + */ + @Test + public void verifyBase64EncodingTypeWithUnsupportedEncodingMethod() { + String ssid = "invalid carrier AP"; + when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID)) + .thenReturn(generateTestConfig(ssid, TEST_STANDARD_EAP_TYPE, 123)); + mBroadcastReceiver.onReceive(mContext, + new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + + assertFalse(mCarrierNetworkConfig.isCarrierNetwork(ssid)); + assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(ssid)); + assertEquals(null, mCarrierNetworkConfig.getCarrierName(ssid)); + assertEquals(-1, mCarrierNetworkConfig.getBase64EncodingFlag(ssid)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java index ef8ceb130..0c61e1719 100644 --- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java @@ -122,8 +122,8 @@ public class CarrierNetworkEvaluatorTest { private AddOrUpdateNetworkAnswer mAddOrUpdateNetworkAnswer; - private void configureNewSsid(int networkId, ScanDetail scanDetail, boolean isEphemeral, - boolean isSaved) { + private WifiConfiguration configureNewSsid(int networkId, ScanDetail scanDetail, + boolean isEphemeral, boolean isSaved) { WifiConfiguration newConfig = ScanResultUtil.createNetworkFromScanResult( scanDetail.getScanResult()); assertTrue("" + newConfig, WifiConfigurationUtil.validate(newConfig, true)); @@ -140,6 +140,7 @@ public class CarrierNetworkEvaluatorTest { anyInt())).thenReturn(true); when(mWifiConfigManager.getConfiguredNetwork(networkId)).thenReturn(newConfig); mAddOrUpdateNetworkAnswer.addConfig(newConfig, networkId); + return newConfig; } /** Sets up test. */ @@ -334,4 +335,41 @@ public class CarrierNetworkEvaluatorTest { verify(mConnectableListener, never()).onConnectable(any(), any(), anyInt()); assertNull(selected); } + + /** + * One carrier Wi-Fi networks visible and cert installed but ssid is blacklisted. + * + * Desired behavior: no networks connectable or selected + */ + @Test + public void testAvailableButBlacklisted() { + String[] ssids = {CARRIER1_SSID}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {2470}; + String[] caps = {"[WPA2-EAP-CCMP]"}; + int[] levels = {10}; + + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + + List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(ssids, bssids, + freqs, caps, levels, mClock); + WifiConfiguration blacklisted = + configureNewSsid(CARRIER1_NET_ID, scanDetails.get(0), true, false); + blacklisted.getNetworkSelectionStatus() + .setNetworkSelectionStatus( + WifiConfiguration.NetworkSelectionStatus + .NETWORK_SELECTION_PERMANENTLY_DISABLED); + when(mWifiConfigManager.getConfiguredNetwork(eq(blacklisted.configKey()))) + .thenReturn(blacklisted); + when(mWifiConfigManager.tryEnableNetwork(CARRIER1_NET_ID)) + .thenReturn(false); + + WifiConfiguration selected = mDut.evaluateNetworks(scanDetails, null, null, false, false, + mConnectableListener); + verify(mWifiConfigManager).getConfiguredNetwork(eq(blacklisted.configKey())); + + verify(mConnectableListener, never()).onConnectable(any(), any(), anyInt()); + assertNull(selected); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index d05ec6e9e..d0092c020 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -3377,40 +3377,4 @@ public class ClientModeImplTest { assertEquals(OP_PACKAGE_NAME, mCmi.getWifiInfo().getNetworkSuggestionOrSpecifierPackageName()); } - - /** - * Verifies the handling of disconnect initiated from API surface when connected to a network. - */ - @Test - public void testExternalDisconnectWhenConnected() throws Exception { - connect(); - - mCmi.disconnectCommandExternal(); // Simulate settings invoking this. - mLooper.dispatchAll(); - - verify(mWifiNative).disconnect(WIFI_IFACE_NAME); - verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_API); - // verify that we temp blacklist the network. - verify(mWifiConfigManager).updateNetworkSelectionStatus(0, - WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT); - } - - /** - * Verifies the handling of disconnect initiated internally when connected to a network. - */ - @Test - public void testInternalDisconnectWhenConnected() throws Exception { - connect(); - - mCmi.disconnectCommandInternal(); // Internal stack initiated disconnect. - mLooper.dispatchAll(); - - verify(mWifiNative).disconnect(WIFI_IFACE_NAME); - verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_GENERIC); - // verify that we don't temp blacklist the network. - verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(0, - WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT); - } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index 2fe85902e..36e7d482e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -16,8 +16,6 @@ package com.android.server.wifi; -import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX; - import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -129,6 +127,7 @@ public class WifiConfigManagerTest { @Mock private RandomizedMacStoreData mRandomizedMacStoreData; @Mock private WifiConfigManager.OnSavedNetworkUpdateListener mWcmListener; @Mock private FrameworkFacade mFrameworkFacade; + @Mock private CarrierNetworkConfig mCarrierNetworkConfig; private MockResources mResources; private InOrder mContextConfigStoreMockOrder; @@ -207,6 +206,7 @@ public class WifiConfigManagerTest { when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog); when(mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate()) .thenReturn(false); + when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig); createWifiConfigManager(); mWifiConfigManager.setOnSavedNetworkUpdateListener(mWcmListener); ArgumentCaptor<ContentObserver> observerCaptor = @@ -4980,7 +4980,7 @@ public class WifiConfigManagerTest { NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP, retrievedDisableTime); verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED); - } else if (reason < NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX) { + } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) { // For temporarily disabled networks, we need to ensure that the current status remains // until the threshold is crossed. assertEquals(temporaryDisableReasonCounter, retrievedDisableReasonCounter); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java index 0091ae106..60b91753e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java @@ -567,7 +567,7 @@ public class WifiNetworkFactoryTest { // Release the network request. mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); // Verify that we did not trigger a disconnect because we've not yet connected. - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); // Re-enable connectivity manager . verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false); @@ -1405,7 +1405,7 @@ public class WifiNetworkFactoryTest { // Now release the network request. mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); // Verify that we triggered a disconnect. - verify(mClientModeImpl).disconnectCommandInternal(); + verify(mClientModeImpl).disconnectCommand(); // Re-enable connectivity manager . verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false); } @@ -1580,12 +1580,12 @@ public class WifiNetworkFactoryTest { verify(mWifiConnectivityManager, times(1)).setSpecificNetworkRequestInProgress(true); verify(mWifiScanner, times(2)).startScan(any(), any(), any()); // we shouldn't disconnect until the user accepts the next request. - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); // Remove the connected request1 & ensure we disconnect. mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); - verify(mClientModeImpl).disconnectCommandInternal(); + verify(mClientModeImpl).disconnectCommand(); verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl, mAlarmManager); @@ -1630,7 +1630,7 @@ public class WifiNetworkFactoryTest { // We shouldn't explicitly disconnect, the new connection attempt will implicitly disconnect // from the connected network. - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); // Remove the stale request1 & ensure nothing happens (because it was replaced by the // second request) @@ -1643,7 +1643,7 @@ public class WifiNetworkFactoryTest { // Now remove the rejected request2, ensure we disconnect & re-enable auto-join. mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2); mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); - verify(mClientModeImpl).disconnectCommandInternal(); + verify(mClientModeImpl).disconnectCommand(); verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false); verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl, @@ -1679,12 +1679,12 @@ public class WifiNetworkFactoryTest { // we shouldn't disconnect/re-enable auto-join until the connected request is released. verify(mWifiConnectivityManager, never()).setSpecificNetworkRequestInProgress(false); - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); // Remove the connected request1 & ensure we disconnect & ensure auto-join is re-enabled. mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1); mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest); - verify(mClientModeImpl).disconnectCommandInternal(); + verify(mClientModeImpl).disconnectCommand(); verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false); verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl, diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java index dd78012d3..ec35b1f8b 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java @@ -1257,7 +1257,7 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove the network suggestion and ensure we did not trigger a disconnect. assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS, mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1)); - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); } /** @@ -1293,11 +1293,11 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove one of the apps and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); // Now remove the other app and ensure we trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_2); - verify(mClientModeImpl).disconnectCommandInternal(); + verify(mClientModeImpl).disconnectCommand(); } /** @@ -1324,7 +1324,7 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove the app and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); } /** @@ -1356,7 +1356,7 @@ public class WifiNetworkSuggestionsManagerTest { // Now remove the app and ensure we did not trigger a disconnect. mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1); - verify(mClientModeImpl, never()).disconnectCommandInternal(); + verify(mClientModeImpl, never()).disconnectCommand(); } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 937ebdc7d..d02ed91b6 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -2814,7 +2814,7 @@ public class WifiServiceImplTest { doThrow(new SecurityException()).when(mAppOpsManager) .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME); assertTrue(mWifiServiceImpl.disconnect(TEST_PACKAGE_NAME)); - verify(mClientModeImpl).disconnectCommandExternal(); + verify(mClientModeImpl).disconnectCommand(); } /** @@ -2826,7 +2826,7 @@ public class WifiServiceImplTest { public void testDisconnectWithChangeWifiStatePerm() throws Exception { assertFalse(mWifiServiceImpl.disconnect(TEST_PACKAGE_NAME)); verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl, never()).disconnectCommandExternal(); + verify(mClientModeImpl, never()).disconnectCommand(); } /** @@ -2845,7 +2845,7 @@ public class WifiServiceImplTest { } verifyCheckChangePermission(TEST_PACKAGE_NAME); - verify(mClientModeImpl, never()).disconnectCommandExternal(); + verify(mClientModeImpl, never()).disconnectCommand(); } @Test 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 2442751b4..8503c450c 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -1586,7 +1586,7 @@ public class PasspointManagerTest { try { TelephonyManager telephonyManager = mock(TelephonyManager.class); when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); - when(telephonyManager.getNetworkOperator()).thenReturn("123456"); + when(telephonyManager.getSimOperator()).thenReturn("123456"); PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock, mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, @@ -1611,8 +1611,8 @@ public class PasspointManagerTest { 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"); + when(telephonyManager.getSimOperator()).thenReturn(mccmnc); + when(telephonyManager.getSimOperatorName()).thenReturn("test"); PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock, @@ -1693,7 +1693,7 @@ public class PasspointManagerTest { try { TelephonyManager telephonyManager = mock(TelephonyManager.class); when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); - when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC); + when(telephonyManager.getSimOperator()).thenReturn(TEST_MCC_MNC); when(mWifiConfigManager.isSimPresent()).thenReturn(true); List<ScanDetail> scanDetails = new ArrayList<>(); scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, TEST_HESSID, @@ -1737,7 +1737,7 @@ public class PasspointManagerTest { try { TelephonyManager telephonyManager = mock(TelephonyManager.class); when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager); - when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC); + when(telephonyManager.getSimOperator()).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)); 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 76a2e653e..61d0c3722 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java @@ -145,7 +145,7 @@ public class PasspointNetworkEvaluatorTest { mEvaluator = new PasspointNetworkEvaluator(mPasspointManager, mWifiConfigManager, mLocalLog, mCarrierNetworkConfig, mTelephonyManager); when(mWifiConfigManager.isSimPresent()).thenReturn(true); - when(mTelephonyManager.getNetworkOperator()).thenReturn("123456"); + when(mTelephonyManager.getSimOperator()).thenReturn("123456"); } /** @@ -412,6 +412,31 @@ public class PasspointNetworkEvaluatorTest { } /** + * Verify that it never creates an ephemeral Passpoint Configuration when the carrier is not + * MNO. + */ + @Test + public void skipCreateEphemeralPasspointConfigurationForNonMNO() { + // Setup ScanDetail and match providers. + List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{ + generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + + // MVNO carrier is enabled. + when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1); + when(mTelephonyManager.getSimCarrierId()).thenReturn(20); + 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, never()).createEphemeralPasspointConfigForCarrier(anyInt()); + } + + /** * Verify that it never creates an ephemeral Passpoint Configuration when the profile for the * carrier already exists. */ @@ -437,6 +462,11 @@ public class PasspointNetworkEvaluatorTest { // Setup ScanDetail List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{ generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + + // MNO carrier is enabled. + when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1); + when(mTelephonyManager.getSimCarrierId()).thenReturn(1); when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( @@ -459,6 +489,11 @@ public class PasspointNetworkEvaluatorTest { // Setup ScanDetail List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{ generateScanDetail(TEST_SSID1, TEST_BSSID1)}); + when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + + // MNO carrier is enabled. + when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1); + when(mTelephonyManager.getSimCarrierId()).thenReturn(1); when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false); when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier( diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java index 0dcf5224c..c83023e66 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java @@ -199,12 +199,10 @@ public class PasspointProvisionerTest { mSession = ExtendedMockito.mockitoSession().mockStatic( RedirectListener.class).mockStatic(PpsMoParser.class).mockStatic( UpdateResponseMessage.class).startMocking(); - - when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn( - mRedirectListener); + when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn(mRedirectListener); when(mRedirectListener.getServerUrl()).thenReturn(new URL(TEST_REDIRECT_URL)); when(mRedirectListener.startServer( - any(RedirectListener.RedirectCallback.class))).thenReturn(true); + any(RedirectListener.RedirectCallback.class), any(Handler.class))).thenReturn(true); when(mRedirectListener.isAlive()).thenReturn(true); when(mWifiManager.isWifiEnabled()).thenReturn(true); when(mObjectFactory.makeOsuNetworkConnection(any(Context.class))) @@ -356,7 +354,8 @@ public class PasspointProvisionerTest { verify(mCallback).onProvisioningStatus( ProvisioningCallback.OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE); verify(mRedirectListener, atLeastOnce()) - .startServer(mOnRedirectReceivedArgumentCaptor.capture()); + .startServer(mOnRedirectReceivedArgumentCaptor.capture(), + any(Handler.class)); mRedirectReceivedListener = mOnRedirectReceivedArgumentCaptor.getValue(); verifyNoMoreInteractions(mCallback); } else if (step == STEP_WAIT_FOR_SECOND_SOAP_RESPONSE) { @@ -368,7 +367,7 @@ public class PasspointProvisionerTest { mRedirectReceivedListener.onRedirectReceived(); mLooper.dispatchAll(); - verify(mRedirectListener, atLeastOnce()).stopServer(); + verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class)); verify(mCallback).onProvisioningStatus( ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED); verify(mCallback).onProvisioningStatus( @@ -520,6 +519,35 @@ public class PasspointProvisionerTest { } /** + * Verifies existing provisioning flow is aborted when failing to create an instance of {@link + * RedirectListener}. + */ + @Test + public void verifyRedirectStartFailure() throws RemoteException { + when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn(null); + mPasspointProvisioner.init(mLooper.getLooper()); + verify(mOsuNetworkConnection).init(mHandlerCaptor.capture()); + + mHandler = mHandlerCaptor.getValue(); + assertEquals(mHandler.getLooper(), mLooper.getLooper()); + + mLooper.dispatchAll(); + + assertTrue(mPasspointProvisioner.startSubscriptionProvisioning( + TEST_UID, mOsuProvider, mCallback)); + + mLooper.dispatchAll(); + + // Since creating an instance of RedirectListener, directly move to FAILED_STATE + verify(mCallback).onProvisioningFailure( + ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER); + + // Failure case, no more runnable posted + verifyNoMoreInteractions(mCallback); + + } + + /** * Verifies that if connection attempt to OSU AP fails, corresponding error callback is invoked. */ @Test @@ -775,7 +803,7 @@ public class PasspointProvisionerTest { mRedirectReceivedListener.onRedirectTimedOut(); mLooper.dispatchAll(); - verify(mRedirectListener, atLeastOnce()).stopServer(); + verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class)); verify(mCallback).onProvisioningFailure( ProvisioningCallback.OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER); // No further runnable posted @@ -794,7 +822,7 @@ public class PasspointProvisionerTest { mRedirectReceivedListener.onRedirectReceived(); mLooper.dispatchAll(); - verify(mRedirectListener, atLeastOnce()).stopServer(); + verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class)); verify(mCallback).onProvisioningStatus( ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED); verify(mCallback).onProvisioningStatus( diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java index 308bb47e8..d61a59d6f 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; +import android.os.Handler; import android.os.Looper; import android.os.test.TestLooper; @@ -47,6 +48,7 @@ public class RedirectListenerTest { private RedirectListenerSpy mRedirectListener; private URL mServerUrl; private TestLooper mLooper = new TestLooper(); + private Handler mHandler = new Handler(mLooper.getLooper()); @Mock RedirectListener.RedirectCallback mListener; @Mock NanoHTTPD.IHTTPSession mIHTTPSession; @@ -55,7 +57,7 @@ public class RedirectListenerTest { private class RedirectListenerSpy extends RedirectListener { boolean mIsStart = false; RedirectListenerSpy(Looper looper, int port) throws IOException { - super(looper, looper, port); + super(looper, port); } @Override @@ -80,20 +82,19 @@ public class RedirectListenerTest { @Before public void setUp() throws Exception { initMocks(this); - mRedirectListener = new RedirectListenerSpy(mLooper.getLooper(), TEST_PORT); mServerUrl = mRedirectListener.getServerUrl(); } private void verifyStartServer() { - mRedirectListener.startServer(mListener); + mRedirectListener.startServer(mListener, mHandler); mLooper.dispatchAll(); assertTrue(mRedirectListener.mIsStart); } private void verifyStopServer() { - mRedirectListener.stopServer(); + mRedirectListener.stopServer(mHandler); mLooper.dispatchAll(); assertFalse(mRedirectListener.mIsStart); diff --git a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java index 112add873..07c917dfb 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java @@ -17,7 +17,6 @@ package com.android.server.wifi.util; import static org.junit.Assert.*; -import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.Mockito.*; import android.net.wifi.WifiConfiguration; @@ -29,23 +28,41 @@ import android.util.Pair; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.server.wifi.CarrierNetworkConfig; import com.android.server.wifi.WifiConfigurationTestUtil; import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData; import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.security.PublicKey; +import javax.crypto.Cipher; + /** * Unit tests for {@link com.android.server.wifi.util.TelephonyUtil}. */ @SmallTest public class TelephonyUtilTest { + private TelephonyUtil mTelephonyUtil; + + @Mock + CarrierNetworkConfig mCarrierNetworkConfig; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mCarrierNetworkConfig.getBase64EncodingFlag(anyString())).thenReturn(-1); + mTelephonyUtil = new TelephonyUtil(); + } @Test public void getSimIdentityEapSim() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); final Pair<String, String> expectedIdentity = Pair.create( "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); @@ -54,18 +71,17 @@ public class TelephonyUtilTest { when(tm.getSimOperator()).thenReturn("321456"); when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM, - WifiEnterpriseConfig.Phase2.NONE))); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP, - WifiEnterpriseConfig.Phase2.SIM))); + WifiEnterpriseConfig.Phase2.SIM), mCarrierNetworkConfig)); } @Test public void getSimIdentityEapAka() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); final Pair<String, String> expectedIdentity = Pair.create( "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); when(tm.getSubscriberId()).thenReturn("3214561234567890"); @@ -74,18 +90,17 @@ public class TelephonyUtilTest { when(tm.getSimOperator()).thenReturn("321456"); when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, - WifiEnterpriseConfig.Phase2.NONE))); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP, - WifiEnterpriseConfig.Phase2.AKA))); + WifiEnterpriseConfig.Phase2.AKA), mCarrierNetworkConfig)); } @Test public void getSimIdentityEapAkaPrime() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); final Pair<String, String> expectedIdentity = Pair.create( "63214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); @@ -94,76 +109,95 @@ public class TelephonyUtilTest { when(tm.getSimOperator()).thenReturn("321456"); when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork( WifiEnterpriseConfig.Eap.AKA_PRIME, - WifiEnterpriseConfig.Phase2.NONE))); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP, - WifiEnterpriseConfig.Phase2.AKA_PRIME))); + WifiEnterpriseConfig.Phase2.AKA_PRIME), mCarrierNetworkConfig)); } /** - * Verify that an expected identity is returned when using the encrypted IMSI. - * - * @throws Exception + * Verify that an expected identity is returned when using the encrypted IMSI encoded by + * RFC4648. */ @Test - public void getEncryptedIdentityImsi() throws Exception { + public void getEncryptedIdentityImsiWithRfc4648() throws Exception { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); - String encryptedImsi = "EncryptedIMSI"; + Cipher cipher = mock(Cipher.class); + PublicKey key = null; + int flag = Base64.NO_WRAP; + String imsi = "3214561234567890"; + String encryptedImsi = Base64.encodeToString(imsi.getBytes(), 0, imsi.getBytes().length, + flag); String encryptedIdentity = "\0" + encryptedImsi + "@wlan.mnc456.mcc321.3gppnetwork.org"; final Pair<String, String> expectedIdentity = Pair.create( "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", encryptedIdentity); - PublicKey key = null; - - when(tm.getSubscriberId()).thenReturn("3214561234567890"); - when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); - when(tm.getSimOperator()).thenReturn("321456"); - ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456", - TelephonyManager.KEY_TYPE_WLAN, null, key, null); - when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN))) - .thenReturn(info); - - when(telephonyUtil.encryptDataUsingPublicKey(any(), any())).thenReturn(encryptedImsi); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, - WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, - WifiEnterpriseConfig.Phase2.NONE))); + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + Cipher.class).startMocking(); + try { + when(Cipher.getInstance(anyString())).thenReturn(cipher); + when(cipher.doFinal(any(byte[].class))).thenReturn(imsi.getBytes()); + when(tm.getSubscriberId()).thenReturn(imsi); + when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + when(tm.getSimOperator()).thenReturn("321456"); + ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456", + TelephonyManager.KEY_TYPE_WLAN, null, key, null); + when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN))) + .thenReturn(info); + when(mCarrierNetworkConfig.getBase64EncodingFlag(anyString())).thenReturn(flag); + + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, + WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); + } finally { + session.finishMocking(); + } } /** - * Verify that an expected identity is returned when using the encrypted IMSI with key - * identifier. - * - * @throws Exception + * Verify that an expected identity is returned when using the encrypted IMSI encoded by RFC2045 + * with key identifier. */ @Test - public void getEncryptedIdentityKeyIdentifier() throws Exception { + public void getEncryptedIdentityKeyIdentifierWithRfc2045() throws Exception { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); + Cipher cipher = mock(Cipher.class); + int flag = Base64.DEFAULT; PublicKey key = null; String keyIdentifier = "key=testKey"; - String encryptedImsi = "EncryptedIMSI"; + String imsi = "3214561234567890"; + String encryptedImsi = Base64.encodeToString(imsi.getBytes(), 0, imsi.getBytes().length, + flag); String encryptedIdentity = "\0" + encryptedImsi + "@wlan.mnc456.mcc321.3gppnetwork.org," + keyIdentifier; final Pair<String, String> expectedIdentity = Pair.create( "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", encryptedIdentity); - when(tm.getSubscriberId()).thenReturn("3214561234567890"); - when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); - when(tm.getSimOperator()).thenReturn("321456"); - ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456", - TelephonyManager.KEY_TYPE_WLAN, keyIdentifier, key, null); - when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN))) - .thenReturn(info); - - when(telephonyUtil.encryptDataUsingPublicKey(any(), any())).thenReturn(encryptedImsi); - - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, - WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, - WifiEnterpriseConfig.Phase2.NONE))); + // static mocking + MockitoSession session = ExtendedMockito.mockitoSession().mockStatic( + Cipher.class).startMocking(); + try { + when(Cipher.getInstance(anyString())).thenReturn(cipher); + when(cipher.doFinal(any(byte[].class))).thenReturn(imsi.getBytes()); + when(tm.getSubscriberId()).thenReturn(imsi); + when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + when(tm.getSimOperator()).thenReturn("321456"); + ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456", + TelephonyManager.KEY_TYPE_WLAN, keyIdentifier, key, null); + when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN))) + .thenReturn(info); + when(mCarrierNetworkConfig.getBase64EncodingFlag(anyString())).thenReturn(flag); + + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, + WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); + } finally { + session.finishMocking(); + } } /** @@ -174,8 +208,6 @@ public class TelephonyUtilTest { @Test public void getEncryptedIdentityFailed() throws Exception { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); - PublicKey key = null; String imsi = "3214561234567890"; final Pair<String, String> expectedIdentity = Pair.create( "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", ""); @@ -183,22 +215,17 @@ public class TelephonyUtilTest { when(tm.getSubscriberId()).thenReturn("3214561234567890"); when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); when(tm.getSimOperator()).thenReturn("321456"); - ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456", - TelephonyManager.KEY_TYPE_WLAN, null, key, null); when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN))) - .thenReturn(info); - when(telephonyUtil.encryptDataUsingPublicKey(any(), aryEq(imsi.getBytes()))) .thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA, - WifiEnterpriseConfig.Phase2.NONE))); + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); } @Test public void getSimIdentity2DigitMnc() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); final Pair<String, String> expectedIdentity = Pair.create( "1321560123456789@wlan.mnc056.mcc321.3gppnetwork.org", ""); @@ -207,15 +234,14 @@ public class TelephonyUtilTest { when(tm.getSimOperator()).thenReturn("32156"); when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM, - WifiEnterpriseConfig.Phase2.NONE))); + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); } @Test public void getSimIdentityUnknownMccMnc() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); final Pair<String, String> expectedIdentity = Pair.create( "13214560123456789@wlan.mnc456.mcc321.3gppnetwork.org", ""); @@ -224,36 +250,39 @@ public class TelephonyUtilTest { when(tm.getSimOperator()).thenReturn(null); when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null); - assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM, - WifiEnterpriseConfig.Phase2.NONE))); + WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig)); } @Test public void getSimIdentityWithNoTelephonyManager() { assertEquals(null, TelephonyUtil.getSimIdentity(null, null, WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE))); + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE), + mCarrierNetworkConfig)); } @Test public void getSimIdentityNonTelephonyConfig() { TelephonyManager tm = mock(TelephonyManager.class); - TelephonyUtil telephonyUtil = mock(TelephonyUtil.class); when(tm.getSubscriberId()).thenReturn("321560123456789"); when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); when(tm.getSimOperator()).thenReturn("32156"); - assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM))); - assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM), + mCarrierNetworkConfig)); + assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2))); - assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil, + WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2), + mCarrierNetworkConfig)); + assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil, WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE))); + WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE), + mCarrierNetworkConfig)); assertEquals(null, TelephonyUtil.getSimIdentity( - tm, telephonyUtil, new WifiConfiguration())); + tm, mTelephonyUtil, new WifiConfiguration(), mCarrierNetworkConfig)); } @Test |