diff options
Diffstat (limited to 'service/java/com')
15 files changed, 269 insertions, 98 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; + } } |