diff options
author | Sunil Ravi <sunilravi@google.com> | 2020-02-21 19:00:28 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-21 19:00:28 +0000 |
commit | aebc25210b0dc86e0b9051a2cfb1135971a89e5a (patch) | |
tree | 05e9dae780883b8fdb706f0a8e79c9c8c8b774cc | |
parent | f17ae57eac419d42b290c9b8589693b6dcd387ae (diff) | |
parent | 853857e57a4595ab67345fc8a198bb7b06e2aa0b (diff) |
Merge "STA: Add support to connect to FILS enabled APs"
17 files changed, 1295 insertions, 175 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 8a49d027f..3dfb0e190 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384; 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; @@ -40,6 +42,7 @@ import android.net.DhcpResultsParcelable; import android.net.InvalidPacketException; import android.net.IpConfiguration; import android.net.KeepalivePacketData; +import android.net.Layer2PacketParcelable; import android.net.LinkProperties; import android.net.MacAddress; import android.net.MatchAllNetworkSpecifier; @@ -610,6 +613,8 @@ public class ClientModeImpl extends StateMachine { private static final int CMD_SAVE_NETWORK = BASE + 259; private static final int CMD_PKT_CNT_FETCH = BASE + 261; + /* Start connection to FILS AP*/ + static final int CMD_START_FILS_CONNECTION = BASE + 262; // For message logging. private static final Class[] sMessageClasses = { AsyncChannel.class, ClientModeImpl.class }; @@ -677,6 +682,14 @@ public class ClientModeImpl extends StateMachine { /* Network is not connected, supplicant assoc+auth is not complete */ private State mDisconnectedState = new DisconnectedState(); + /* + * FILS connection related variables. + */ + /* To indicate to IpClient whether HLP IEs were included or not in assoc request */ + private boolean mSentHLPs = false; + /* Tracks IpClient start state until (FILS_)NETWORK_CONNECTION_EVENT event */ + private boolean mIpClientWithPreConnection = false; + /** * One of {@link WifiManager#WIFI_STATE_DISABLED}, * {@link WifiManager#WIFI_STATE_DISABLING}, @@ -878,6 +891,8 @@ public class ClientModeImpl extends StateMachine { getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, + getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_DISCONNECTION_EVENT, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.RX_HS20_ANQP_ICON_EVENT, @@ -1013,6 +1028,11 @@ public class ClientModeImpl extends StateMachine { } @Override + public void onPreconnectionStart(List<Layer2PacketParcelable> packets) { + sendMessage(CMD_START_FILS_CONNECTION, 0, 0, packets); + } + + @Override public void onQuit() { mWaitForStopCv.open(); } @@ -1027,11 +1047,23 @@ public class ClientModeImpl extends StateMachine { } private void stopIpClient() { - /* Restore power save and suspend optimizations */ - handlePostDhcpSetup(); + if (mVerboseLoggingEnabled) { + log("stopIpClient IpClientWithPreConnection: " + mIpClientWithPreConnection); + } if (mIpClient != null) { + if (mIpClientWithPreConnection) { + mIpClient.notifyPreconnectionComplete(false); + } mIpClient.stop(); } + mIpClientWithPreConnection = false; + mSentHLPs = false; + } + + private void stopDhcpSetup() { + /* Restore power save and suspend optimizations */ + handlePostDhcpSetup(); + stopIpClient(); } /** @@ -1898,6 +1930,7 @@ public class ClientModeImpl extends StateMachine { } sb.append(" blacklist=" + Boolean.toString(mDidBlackListBSSID)); break; + case WifiMonitor.FILS_NETWORK_CONNECTION_EVENT: case WifiMonitor.NETWORK_CONNECTION_EVENT: sb.append(" "); sb.append(Integer.toString(msg.arg1)); @@ -2163,6 +2196,9 @@ public class ClientModeImpl extends StateMachine { case WifiMonitor.NETWORK_CONNECTION_EVENT: s = "NETWORK_CONNECTION_EVENT"; break; + case WifiMonitor.FILS_NETWORK_CONNECTION_EVENT: + s = "FILS_NETWORK_CONNECTION_EVENT"; + break; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: s = "NETWORK_DISCONNECTION_EVENT"; break; @@ -2621,7 +2657,7 @@ public class ClientModeImpl extends StateMachine { */ private void handleNetworkDisconnect() { if (mVerboseLoggingEnabled) { - log("handleNetworkDisconnect: Stopping DHCP and clearing IP" + log("handleNetworkDisconnect:" + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() @@ -2638,7 +2674,18 @@ public class ClientModeImpl extends StateMachine { clearTargetBssid("handleNetworkDisconnect"); - stopIpClient(); + // Don't stop DHCP if Fils connection is in progress. + if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID + && mTargetNetworkId != WifiConfiguration.INVALID_NETWORK_ID + && mLastNetworkId != mTargetNetworkId && mIpClientWithPreConnection) { + if (mVerboseLoggingEnabled) { + log("handleNetworkDisconnect: Don't stop IpClient as fils connection in progress: " + + " mLastNetworkId: " + mLastNetworkId + + " mTargetNetworkId" + mTargetNetworkId); + } + } else { + stopDhcpSetup(); + } mWifiScoreReport.stopConnectedNetworkScorer(); /* Reset data structures */ @@ -2714,6 +2761,21 @@ public class ClientModeImpl extends StateMachine { } } + void addLayer2PacketsToHlpReq(List<Layer2PacketParcelable> packets) { + List<Layer2PacketParcelable> mLayer2Packet = packets; + if ((mLayer2Packet != null) && (mLayer2Packet.size() > 0)) { + mWifiNative.flushAllHlp(mInterfaceName); + + for (int j = 0; j < mLayer2Packet.size(); j++) { + byte [] bytes = mLayer2Packet.get(j).payload; + byte [] payloadBytes = Arrays.copyOfRange(bytes, 12, bytes.length); + MacAddress dstAddress = mLayer2Packet.get(j).dstMacAddress; + + mWifiNative.addHlpReq(mInterfaceName, dstAddress, payloadBytes); + } + } + } + void handlePostDhcpSetup() { /* Restore power save and suspend optimizations */ setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true); @@ -3271,6 +3333,7 @@ public class ClientModeImpl extends StateMachine { case CMD_RECONNECT: case CMD_REASSOCIATE: case WifiMonitor.NETWORK_CONNECTION_EVENT: + case WifiMonitor.FILS_NETWORK_CONNECTION_EVENT: case WifiMonitor.NETWORK_DISCONNECTION_EVENT: case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: @@ -3485,6 +3548,7 @@ public class ClientModeImpl extends StateMachine { * Helper method to stop external services and clean up state from client mode. */ private void stopClientMode() { + handleNetworkDisconnect(); // exiting supplicant started state is now only applicable to client mode mWifiDiagnostics.stopLogging(mInterfaceName); @@ -3563,6 +3627,26 @@ public class ClientModeImpl extends StateMachine { return mLastBssid; } + void connectToNetwork(WifiConfiguration config) { + if ((config != null) && mWifiNative.connectToNetwork(mInterfaceName, config)) { + mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime(); + mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config); + mLastConnectAttemptTimestamp = mClock.getWallClockMillis(); + mIsAutoRoaming = false; + if (getCurrentState() != mDisconnectedState) { + transitionTo(mDisconnectingState); + } + } else { + loge("CMD_START_CONNECT Failed to start connection to network " + config); + mTargetWifiConfiguration = null; + stopIpClient(); + reportConnectionAttemptEnd( + WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, + WifiMetricsProto.ConnectionEvent.HLF_NONE, + WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); + } + } + class ConnectModeState extends State { @Override @@ -3631,6 +3715,7 @@ public class ClientModeImpl extends StateMachine { WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN; switch (message.what) { case WifiMonitor.ASSOCIATION_REJECTION_EVENT: + stopIpClient(); mWifiDiagnostics.captureBugReportData( WifiDiagnostics.REPORT_REASON_ASSOC_FAILURE); mDidBlackListBSSID = false; @@ -3668,6 +3753,7 @@ public class ClientModeImpl extends StateMachine { } break; case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: + stopIpClient(); mWifiDiagnostics.captureBugReportData( WifiDiagnostics.REPORT_REASON_AUTH_FAILURE); int disableReason = WifiConfiguration.NetworkSelectionStatus @@ -3848,6 +3934,7 @@ public class ClientModeImpl extends StateMachine { netId = message.arg1; int uid = message.arg2; bssid = (String) message.obj; + mSentHLPs = false; if (!hasConnectionRequests()) { if (mNetworkAgent == null) { @@ -3869,60 +3956,44 @@ public class ClientModeImpl extends StateMachine { loge("CMD_START_CONNECT and no config, bail out..."); break; } + mTargetNetworkId = netId; // Update scorecard while there is still state from existing connection int scanRssi = mWifiConfigManager.findScanRssi(netId, SCAN_RSSI_VALID_TIME_MS); mWifiScoreCard.noteConnectionAttempt(mWifiInfo, scanRssi, config.SSID); - mTargetNetworkId = netId; - setTargetBssid(config, bssid); mBssidBlocklistMonitor.updateFirmwareRoamingConfiguration(config.SSID); + updateWifiConfigOnStartConnection(config, bssid); + reportConnectionAttemptStart(config, mTargetRoamBSSID, WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED); - if (config.macRandomizationSetting - == WifiConfiguration.RANDOMIZATION_PERSISTENT - && isConnectedMacRandomizationEnabled()) { - configureRandomizedMacAddress(config); - } else { - setCurrentMacToFactoryMac(config); - } String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName); mWifiInfo.setMacAddress(currentMacAddress); Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address"); - if (config.enterpriseConfig != null - && config.enterpriseConfig.isAuthenticationSimBased() - && mTelephonyUtil.isImsiEncryptionInfoAvailable( - mTelephonyUtil.getBestMatchSubscriptionId(config)) - && TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) { - String anonAtRealm = mTelephonyUtil - .getAnonymousIdentityWith3GppRealm(config); - // Use anonymous@<realm> when pseudonym is not available - config.enterpriseConfig.setAnonymousIdentity(anonAtRealm); - } - - if (isWpa3SaeUpgradeEnabled() && config.allowedKeyManagement.get( - WifiConfiguration.KeyMgmt.WPA_PSK)) { - config = upgradeToWpa3IfPossible(config); - } + mTargetWifiConfiguration = config; + /* Check for FILS configuration again after updating the config */ + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256) + || config.allowedKeyManagement.get( + WifiConfiguration.KeyMgmt.FILS_SHA384)) { - if (mWifiNative.connectToNetwork(mInterfaceName, config)) { - mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime(); - mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config); - mLastConnectAttemptTimestamp = mClock.getWallClockMillis(); - mTargetWifiConfiguration = config; - mIsAutoRoaming = false; - if (getCurrentState() != mDisconnectedState) { - transitionTo(mDisconnectingState); + boolean isIpClientStarted = startIpClient(config, true); + if (isIpClientStarted) { + mIpClientWithPreConnection = true; + break; } - } else { - loge("CMD_START_CONNECT Failed to start connection to network " + config); - reportConnectionAttemptEnd( - WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, - WifiMetricsProto.ConnectionEvent.HLF_NONE, - WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN); - break; } + connectToNetwork(config); + break; + case CMD_START_FILS_CONNECTION: + List<Layer2PacketParcelable> packets; + packets = (List<Layer2PacketParcelable>) message.obj; + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Send HLP IEs to supplicant"); + } + addLayer2PacketsToHlpReq(packets); + config = mTargetWifiConfiguration; + connectToNetwork(config); break; case CMD_CONNECT_NETWORK: callbackIdentifier = message.arg2; @@ -3993,6 +4064,8 @@ public class ClientModeImpl extends StateMachine { } handleStatus = NOT_HANDLED; break; + case WifiMonitor.FILS_NETWORK_CONNECTION_EVENT: + mSentHLPs = true; case WifiMonitor.NETWORK_CONNECTION_EVENT: if (mVerboseLoggingEnabled) log("Network connection established"); mLastNetworkId = message.arg1; @@ -4166,6 +4239,14 @@ public class ClientModeImpl extends StateMachine { final boolean enabled = (message.arg1 > 0); mWifiNative.configureNeighborDiscoveryOffload(mInterfaceName, enabled); break; + case CMD_PRE_DHCP_ACTION: + case CMD_PRE_DHCP_ACTION_COMPLETE: + case CMD_POST_DHCP_ACTION: + case CMD_IPV4_PROVISIONING_SUCCESS: + case CMD_IP_CONFIGURATION_SUCCESSFUL: + case CMD_IPV4_PROVISIONING_FAILURE: + handleStatus = handleL3MessagesWhenNotConnected(message); + break; default: handleStatus = NOT_HANDLED; break; @@ -4179,6 +4260,38 @@ public class ClientModeImpl extends StateMachine { } } + private boolean handleL3MessagesWhenNotConnected(Message message) { + boolean handleStatus = HANDLED; + + if (!mIpClientWithPreConnection) { + return NOT_HANDLED; + } + + switch (message.what) { + case CMD_PRE_DHCP_ACTION: + handlePreDhcpSetup(); + break; + case CMD_PRE_DHCP_ACTION_COMPLETE: + if (mIpClient != null) { + mIpClient.completedPreDhcpAction(); + } + break; + case CMD_IPV4_PROVISIONING_FAILURE: + stopDhcpSetup(); + deferMessage(message); + break; + case CMD_POST_DHCP_ACTION: + case CMD_IPV4_PROVISIONING_SUCCESS: + case CMD_IP_CONFIGURATION_SUCCESSFUL: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + + return handleStatus; + } + private WifiNetworkAgentSpecifier createNetworkAgentSpecifier( @NonNull WifiConfiguration currentWifiConfiguration, @Nullable String currentBssid) { currentWifiConfiguration.BSSID = currentBssid; @@ -4474,10 +4587,6 @@ public class ClientModeImpl extends StateMachine { @Override public void exit() { - if (mIpClient != null) { - mIpClient.stop(); - } - // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState // Bug: 15347363 // For paranoia's sake, call handleNetworkDisconnect @@ -4490,9 +4599,6 @@ public class ClientModeImpl extends StateMachine { sb.append(" ").append(mLastBssid); } } - if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { - handleNetworkDisconnect(); - } mCountryCode.setReadyForChange(true); mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED); mWifiStateTracker.updateState(WifiStateTracker.DISCONNECTED); @@ -4600,6 +4706,7 @@ public class ClientModeImpl extends StateMachine { } break; case WifiMonitor.NETWORK_CONNECTION_EVENT: + case WifiMonitor.FILS_NETWORK_CONNECTION_EVENT: mWifiInfo.setBSSID((String) message.obj); mLastNetworkId = message.arg1; mWifiInfo.setNetworkId(mLastNetworkId); @@ -4826,83 +4933,13 @@ public class ClientModeImpl extends StateMachine { class ObtainingIpState extends State { @Override public void enter() { - final WifiConfiguration currentConfig = getCurrentWifiConfiguration(); - final boolean isUsingStaticIp = - (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC); - if (mVerboseLoggingEnabled) { - final String key = currentConfig.getKey(); - log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId) - + " " + key + " " - + " roam=" + mIsAutoRoaming - + " static=" + isUsingStaticIp); - } - - sendNetworkChangeBroadcast(DetailedState.OBTAINING_IPADDR); - - // We must clear the config BSSID, as the wifi chipset may decide to roam - // from this point on and having the BSSID specified in the network block would - // cause the roam to fail and the device to disconnect. - clearTargetBssid("ObtainingIpAddress"); - - // Stop IpClient in case we're switching from DHCP to static - // configuration or vice versa. - // - // When we transition from static configuration to DHCP in - // particular, we must tell ConnectivityService that we're - // disconnected, because DHCP might take a long time during which - // connectivity APIs such as getActiveNetworkInfo should not return - // CONNECTED. - stopIpClient(); - - if (mIpClient != null) { - mIpClient.setHttpProxy(currentConfig.getHttpProxy()); - if (!TextUtils.isEmpty(mContext.getResources().getString( - R.string.config_wifi_tcp_buffers))) { - mIpClient.setTcpBufferSizes(mContext.getResources().getString( - R.string.config_wifi_tcp_buffers)); - } - } - - WifiConfiguration config = getCurrentWifiConfiguration(); - ScanDetailCache scanDetailCache = - mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId); - ScanResult scanResult = null; - if (scanDetailCache != null && mLastBssid != null) { - scanResult = scanDetailCache.getScanResult(mLastBssid); - } - - final ProvisioningConfiguration prov; - ProvisioningConfiguration.ScanResultInfo scanResultInfo = null; - if (scanResult != null) { - final List<ScanResultInfo.InformationElement> ies = - new ArrayList<ScanResultInfo.InformationElement>(); - for (ScanResult.InformationElement ie : scanResult.getInformationElements()) { - ScanResultInfo.InformationElement scanResultInfoIe = - new ScanResultInfo.InformationElement(ie.getId(), ie.getBytes()); - ies.add(scanResultInfoIe); - } - scanResultInfo = new ProvisioningConfiguration.ScanResultInfo(scanResult.SSID, ies); - } - - if (!isUsingStaticIp) { - prov = new ProvisioningConfiguration.Builder() - .withPreDhcpAction() - .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName)) - .withNetwork(getCurrentNetwork()) - .withDisplayName(currentConfig.SSID) - .withScanResultInfo(scanResultInfo) - .build(); + WifiConfiguration currentConfig = getCurrentWifiConfiguration(); + if (mIpClientWithPreConnection && mIpClient != null) { + mIpClient.notifyPreconnectionComplete(mSentHLPs); + mIpClientWithPreConnection = false; + mSentHLPs = false; } else { - StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration(); - prov = new ProvisioningConfiguration.Builder() - .withStaticConfiguration(staticIpConfig) - .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName)) - .withNetwork(getCurrentNetwork()) - .withDisplayName(currentConfig.SSID) - .build(); - } - if (mIpClient != null) { - mIpClient.startProvisioning(prov); + startIpClient(currentConfig, false); } // Get Link layer stats so as we get fresh tx packet counters getWifiLinkLayerStats(); @@ -5441,6 +5478,7 @@ public class ClientModeImpl extends StateMachine { mWifiNative.disconnect(mInterfaceName); break; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: + stopIpClient(); if (message.arg2 == 15 /* FOURWAY_HANDSHAKE_TIMEOUT */) { String bssid = (message.obj == null) ? mTargetRoamBSSID : (String) message.obj; @@ -6068,8 +6106,70 @@ public class ClientModeImpl extends StateMachine { } } - private WifiConfiguration upgradeToWpa3IfPossible(@NonNull WifiConfiguration config) { - if (isWpa3SaeUpgradeOffloadEnabled()) { + /** + * @return true if this device supports FILS-SHA256 + */ + private boolean isFilsSha256Supported() { + return (mWifiNative.getSupportedFeatureSet(mInterfaceName) & WIFI_FEATURE_FILS_SHA256) != 0; + } + + /** + * @return true if this device supports FILS-SHA384 + */ + private boolean isFilsSha384Supported() { + return (mWifiNative.getSupportedFeatureSet(mInterfaceName) & WIFI_FEATURE_FILS_SHA384) != 0; + } + + /** + * Helper method to set the allowed key management schemes from + * scan result. + */ + private void updateAllowedKeyManagementSchemesFromScanResult( + WifiConfiguration config, ScanResult scanResult) { + if (isFilsSha256Supported() + && ScanResultUtil.isScanResultForFilsSha256Network(scanResult)) { + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.FILS_SHA256); + } + if (isFilsSha384Supported() + && ScanResultUtil.isScanResultForFilsSha384Network(scanResult)) { + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.FILS_SHA384); + } + } + /** + * Update wifi configuration based on the matching scan result. + * + * @param config Wifi configuration object. + * @param scanResult Scan result matching the network. + */ + private void updateWifiConfigFromMatchingScanResult(WifiConfiguration config, + ScanResult scanResult) { + updateAllowedKeyManagementSchemesFromScanResult(config, scanResult); + if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256) + || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA384)) { + config.enterpriseConfig.setFieldValue(WifiEnterpriseConfig.EAP_ERP, "1"); + } + } + + /** + * Update the wifi configuration before sending connect to + * supplicant/driver. + * + * @param config wifi configuration object. + * @param bssid BSSID to assocaite with. + */ + void updateWifiConfigOnStartConnection(WifiConfiguration config, String bssid) { + boolean canUpgradePskToSae = false; + boolean isFrameworkWpa3SaeUpgradePossible = false; + boolean isLegacyWpa2ApInScanResult = false; + + setTargetBssid(config, bssid); + + if (isWpa3SaeUpgradeEnabled() && config.allowedKeyManagement.get( + WifiConfiguration.KeyMgmt.WPA_PSK)) { + isFrameworkWpa3SaeUpgradePossible = true; + } + + if (isFrameworkWpa3SaeUpgradePossible && isWpa3SaeUpgradeOffloadEnabled()) { // Driver offload of upgrading legacy WPA/WPA2 connection to WPA3 if (mVerboseLoggingEnabled) { Log.d(TAG, "Driver upgrade legacy WPA/WPA2 connection to WPA3"); @@ -6077,52 +6177,64 @@ public class ClientModeImpl extends StateMachine { config.allowedAuthAlgorithms.clear(); // Note: KeyMgmt.WPA2_PSK is already enabled, enable SAE as well config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); - return config; + isFrameworkWpa3SaeUpgradePossible = false; } - - boolean canUpgradePskToSae = false; - // Check if network selection selected a good WPA3 candidate AP for a WPA2 // saved network. ScanResult scanResultCandidate = config.getNetworkSelectionStatus().getCandidate(); - if (scanResultCandidate != null) { + if (isFrameworkWpa3SaeUpgradePossible && scanResultCandidate != null) { ScanResultMatchInfo scanResultMatchInfo = ScanResultMatchInfo .fromScanResult(scanResultCandidate); if ((scanResultMatchInfo.networkType == WifiConfiguration.SECURITY_TYPE_SAE)) { canUpgradePskToSae = true; } else { // No SAE candidate - return config; + isFrameworkWpa3SaeUpgradePossible = false; } } - // Now check if there are any additional legacy WPA2 only APs in range. + /** + * Go through the matching scan results and update wifi config. + */ + ScanResultMatchInfo key1 = ScanResultMatchInfo.fromWifiConfiguration(config); ScanRequestProxy scanRequestProxy = mWifiInjector.getScanRequestProxy(); - for (ScanResult scanResult : scanRequestProxy.getScanResults()) { + List<ScanResult> scanResults = scanRequestProxy.getScanResults(); + for (ScanResult scanResult : scanResults) { if (!config.SSID.equals(ScanResultUtil.createQuotedSSID(scanResult.SSID))) { continue; } - if (ScanResultUtil.isScanResultForPskNetwork(scanResult) - && !ScanResultUtil.isScanResultForSaeNetwork(scanResult)) { - // Found a legacy WPA2 AP in range. Do not upgrade the connection to WPA3 to - // allow seamless roaming within the ESS. - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Found legacy WPA2 AP, do not upgrade to WPA3"); + if (isFrameworkWpa3SaeUpgradePossible && !isLegacyWpa2ApInScanResult) { + if (ScanResultUtil.isScanResultForPskNetwork(scanResult) + && !ScanResultUtil.isScanResultForSaeNetwork(scanResult)) { + // Found a legacy WPA2 AP in range. Do not upgrade the connection to WPA3 to + // allow seamless roaming within the ESS. + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Found legacy WPA2 AP, do not upgrade to WPA3"); + } + isLegacyWpa2ApInScanResult = true; + canUpgradePskToSae = false; + } + if (ScanResultUtil.isScanResultForSaeNetwork(scanResult) + && scanResultCandidate == null) { + // When the user manually selected a network from the Wi-Fi picker, evaluate + // if to upgrade based on the scan results. The most typical use case during + // the WPA3 transition mode is to have a WPA2/WPA3 AP in transition mode. In + // this case, we would like to upgrade the connection. + canUpgradePskToSae = true; } - canUpgradePskToSae = false; - break; } - if (ScanResultUtil.isScanResultForSaeNetwork(scanResult) - && scanResultCandidate == null) { - // When the user manually selected a network from the Wi-Fi picker, evaluate - // if to upgrade based on the scan results. The most typical use case during - // the WPA3 transition mode is to have a WPA2/WPA3 AP in transition mode. In - // this case, we would like to upgrade the connection. - canUpgradePskToSae = true; + + ScanResultMatchInfo key2 = ScanResultMatchInfo.fromScanResult(scanResult); + if (!key1.equals(key2)) { + continue; } + updateWifiConfigFromMatchingScanResult(config, scanResult); } - if (canUpgradePskToSae) { + if (isFrameworkWpa3SaeUpgradePossible && canUpgradePskToSae + && !(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256) + || config.allowedKeyManagement.get( + WifiConfiguration.KeyMgmt.FILS_SHA384))) { // Upgrade legacy WPA/WPA2 connection to WPA3 if (mVerboseLoggingEnabled) { Log.d(TAG, "Upgrade legacy WPA/WPA2 connection to WPA3"); @@ -6130,6 +6242,121 @@ public class ClientModeImpl extends StateMachine { config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); } - return config; + if (config.macRandomizationSetting + == WifiConfiguration.RANDOMIZATION_PERSISTENT + && isConnectedMacRandomizationEnabled()) { + configureRandomizedMacAddress(config); + } else { + setCurrentMacToFactoryMac(config); + } + + if (config.enterpriseConfig != null + && config.enterpriseConfig.isAuthenticationSimBased() + && mTelephonyUtil.isImsiEncryptionInfoAvailable( + mTelephonyUtil.getBestMatchSubscriptionId(config)) + && TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) { + String anonAtRealm = mTelephonyUtil + .getAnonymousIdentityWith3GppRealm(config); + // Use anonymous@<realm> when pseudonym is not available + config.enterpriseConfig.setAnonymousIdentity(anonAtRealm); + } } + + private boolean startIpClient(WifiConfiguration config, boolean isFilsConnection) { + if (mIpClient == null) { + return false; + } + + final boolean isUsingStaticIp = + (config.getIpAssignment() == IpConfiguration.IpAssignment.STATIC); + if (mVerboseLoggingEnabled) { + final String key = config.getKey(); + log("startIpClient netId=" + Integer.toString(mLastNetworkId) + + " " + key + " " + + " roam=" + mIsAutoRoaming + + " static=" + isUsingStaticIp + + " isFilsConnection=" + isFilsConnection); + } + + if (isFilsConnection) { + stopIpClient(); + if (isUsingStaticIp) { + mWifiNative.flushAllHlp(mInterfaceName); + return false; + } + final ProvisioningConfiguration prov = + new ProvisioningConfiguration.Builder() + .withPreDhcpAction() + .withPreconnection() + .withApfCapabilities( + mWifiNative.getApfCapabilities(mInterfaceName)) + .build(); + mIpClient.startProvisioning(prov); + } else { + sendNetworkChangeBroadcast(DetailedState.OBTAINING_IPADDR); + // We must clear the config BSSID, as the wifi chipset may decide to roam + // from this point on and having the BSSID specified in the network block would + // cause the roam to fail and the device to disconnect. + clearTargetBssid("ObtainingIpAddress"); + + // Stop IpClient in case we're switching from DHCP to static + // configuration or vice versa. + // + // When we transition from static configuration to DHCP in + // particular, we must tell ConnectivityService that we're + // disconnected, because DHCP might take a long time during which + // connectivity APIs such as getActiveNetworkInfo should not return + // CONNECTED. + stopDhcpSetup(); + + mIpClient.setHttpProxy(config.getHttpProxy()); + if (!TextUtils.isEmpty(mContext.getResources().getString( + R.string.config_wifi_tcp_buffers))) { + mIpClient.setTcpBufferSizes(mContext.getResources().getString( + R.string.config_wifi_tcp_buffers)); + } + + ScanDetailCache scanDetailCache = + mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId); + ScanResult scanResult = null; + if (scanDetailCache != null && mLastBssid != null) { + scanResult = scanDetailCache.getScanResult(mLastBssid); + } + + final ProvisioningConfiguration prov; + ProvisioningConfiguration.ScanResultInfo scanResultInfo = null; + if (scanResult != null) { + final List<ScanResultInfo.InformationElement> ies = + new ArrayList<ScanResultInfo.InformationElement>(); + for (ScanResult.InformationElement ie : scanResult.getInformationElements()) { + ScanResultInfo.InformationElement scanResultInfoIe = + new ScanResultInfo.InformationElement(ie.getId(), ie.getBytes()); + ies.add(scanResultInfoIe); + } + scanResultInfo = new ProvisioningConfiguration.ScanResultInfo(scanResult.SSID, ies); + } + + if (!isUsingStaticIp) { + prov = new ProvisioningConfiguration.Builder() + .withPreDhcpAction() + .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName)) + .withNetwork(getCurrentNetwork()) + .withDisplayName(config.SSID) + .withScanResultInfo(scanResultInfo) + .build(); + } else { + StaticIpConfiguration staticIpConfig = config.getStaticIpConfiguration(); + prov = new ProvisioningConfiguration.Builder() + .withStaticConfiguration(staticIpConfig) + .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName)) + .withNetwork(getCurrentNetwork()) + .withDisplayName(config.SSID) + .build(); + } + mIpClient.startProvisioning(prov); + } + + return true; + } + } diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java index 3d333c849..2d78514e4 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java @@ -70,7 +70,7 @@ abstract class SupplicantStaIfaceCallbackImpl extends ISupplicantStaIfaceCallbac /** * Converts the supplicant state received from HIDL to the equivalent framework state. */ - private static SupplicantState supplicantHidlStateToFrameworkState(int state) { + protected static SupplicantState supplicantHidlStateToFrameworkState(int state) { switch (state) { case ISupplicantStaIfaceCallback.State.DISCONNECTED: return SupplicantState.DISCONNECTED; diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java index f3d72dc24..463881bbd 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java @@ -18,9 +18,13 @@ package com.android.server.wifi; import android.annotation.NonNull; import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; import android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback.BssTmData; +import android.net.wifi.SupplicantState; import android.net.wifi.WifiManager; +import android.net.wifi.WifiSsid; import android.util.Log; +import com.android.server.wifi.util.NativeUtil; + import java.util.ArrayList; abstract class SupplicantStaIfaceCallbackV1_3Impl extends @@ -29,7 +33,9 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends private final SupplicantStaIfaceHal mStaIfaceHal; private final String mIfaceName; private final WifiMonitor mWifiMonitor; + private final SupplicantStaIfaceHal.SupplicantStaIfaceHalCallback mCallbackV10; private final SupplicantStaIfaceHal.SupplicantStaIfaceHalCallbackV1_2 mCallbackV12; + private boolean mStateIsFourwayV13 = false; // Used to help check for PSK password mismatch SupplicantStaIfaceCallbackV1_3Impl(@NonNull SupplicantStaIfaceHal staIfaceHal, @NonNull String ifaceName, @@ -40,6 +46,7 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends // Create an older callback for function delegation, // and it would cascadingly create older one. mCallbackV12 = mStaIfaceHal.new SupplicantStaIfaceHalCallbackV1_2(mIfaceName); + mCallbackV10 = mStaIfaceHal.new SupplicantStaIfaceHalCallback(mIfaceName); } @Override @@ -49,7 +56,9 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends @Override public void onNetworkRemoved(int id) { - mCallbackV12.onNetworkRemoved(id); + mStaIfaceHal.logCallback("onNetworkRemoved"); + // Reset 4way handshake state since network has been removed. + mStateIsFourwayV13 = false; } @Override @@ -86,7 +95,20 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends @Override public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) { - mCallbackV12.onDisconnected(bssid, locallyGenerated, reasonCode); + mStaIfaceHal.logCallback("onDisconnected"); + if (mStaIfaceHal.isVerboseLoggingEnabled()) { + Log.e(TAG, "onDisconnected 4way=" + mStateIsFourwayV13 + + " locallyGenerated=" + locallyGenerated + + " reasonCode=" + reasonCode); + } + if (mStateIsFourwayV13 + && (!locallyGenerated || reasonCode != ReasonCode.IE_IN_4WAY_DIFFERS)) { + mWifiMonitor.broadcastAuthenticationFailureEvent( + mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1); + } + mWifiMonitor.broadcastNetworkDisconnectionEvent( + mIfaceName, locallyGenerated ? 1 : 0, reasonCode, + NativeUtil.macAddressFromByteArray(bssid)); } @Override @@ -333,7 +355,25 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends @Override public void onStateChanged_1_3(int newState, byte[/* 6 */] bssid, int id, ArrayList<Byte> ssid, boolean filsHlpSent) { - mCallbackV12.onStateChanged(newState, bssid, id, ssid); + mStaIfaceHal.logCallback("onStateChanged_1_3"); + SupplicantState newSupplicantState = + mCallbackV10.supplicantHidlStateToFrameworkState(newState); + WifiSsid wifiSsid = + WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid)); + String bssidStr = NativeUtil.macAddressFromByteArray(bssid); + mStateIsFourwayV13 = + (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE); + if (newSupplicantState == SupplicantState.COMPLETED) { + if (filsHlpSent) { + mWifiMonitor.broadcastFilsNetworkConnectionEvent( + mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), bssidStr); + } else { + mWifiMonitor.broadcastNetworkConnectionEvent( + mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), bssidStr); + } + } + mWifiMonitor.broadcastSupplicantStateChangeEvent( + mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), + wifiSsid, bssidStr, newSupplicantState); } - } diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java index bf2472935..717d0d7b5 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java @@ -16,6 +16,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384; import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO; import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; @@ -2129,6 +2131,88 @@ public class SupplicantStaIfaceHal { } /** + * Flush all previously configured HLPs. + * + * @param ifaceName Name of the interface. + * @return true if request is sent successfully, false otherwise. + */ + public boolean flushAllHlp(@NonNull String ifaceName) { + synchronized (mLock) { + final String methodStr = "filsHlpFlushRequest"; + if (isV1_3()) { + ISupplicantStaIface iface = + checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr); + if (iface == null) { + return false; + } + + // Get a v1.3 supplicant STA Interface + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 = + getStaIfaceMockableV1_3(iface); + + if (staIfaceV13 == null) { + Log.e(TAG, methodStr + + ": ISupplicantStaIface is null, cannot flushAllHlp"); + return false; + } + try { + SupplicantStatus status = staIfaceV13.filsHlpFlushRequest(); + return checkStatusAndLogFailure(status, methodStr); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } else { + Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL"); + return false; + } + } + } + + /** + * Set FILS HLP packet. + * + * @param ifaceName Name of the interface. + * @param dst Destination MAC address. + * @param hlpPacket Hlp Packet data in hex. + * @return true if request is sent successfully, false otherwise. + */ + public boolean addHlpReq(@NonNull String ifaceName, byte [] dst, byte [] hlpPacket) { + synchronized (mLock) { + final String methodStr = "filsHlpAddRequest"; + if (isV1_3()) { + ISupplicantStaIface iface = + checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr); + if (iface == null) { + return false; + } + + // Get a v1.3 supplicant STA Interface + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface staIfaceV13 = + getStaIfaceMockableV1_3(iface); + + if (staIfaceV13 == null) { + Log.e(TAG, methodStr + + ": ISupplicantStaIface is null, cannot addHlpReq"); + return false; + } + try { + ArrayList<Byte> payload = NativeUtil.byteArrayToArrayList(hlpPacket); + SupplicantStatus status = staIfaceV13.filsHlpAddRequest(dst, payload); + return checkStatusAndLogFailure(status, methodStr); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } else { + Log.e(TAG, "Method " + methodStr + " is not supported in existing HAL"); + return false; + } + } + } + + + /** * Start WPS pin registrar operation with the specified peer and pin. * * @param ifaceName Name of the interface. @@ -2658,6 +2742,23 @@ public class SupplicantStaIfaceHal { } } + if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + .KeyMgmtMask.FILS_SHA256) != 0) { + advancedCapabilities |= WIFI_FEATURE_FILS_SHA256; + + if (mVerboseLoggingEnabled) { + Log.v(TAG, methodStr + ": FILS_SHA256 supported"); + } + } + if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + .KeyMgmtMask.FILS_SHA384) != 0) { + advancedCapabilities |= WIFI_FEATURE_FILS_SHA384; + + if (mVerboseLoggingEnabled) { + Log.v(TAG, methodStr + ": FILS_SHA384 supported"); + } + } + if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": Capability flags = " + keyMgmtCapabilities); } diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java index 02bda7e0f..f9ac13f52 100644 --- a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java @@ -384,7 +384,6 @@ public class SupplicantStaNetworkHal { Log.e(TAG, "failed to set Key Management"); return false; } - // Check and set SuiteB configurations. if (keyMgmtMask.get(WifiConfiguration.KeyMgmt.SUITE_B_192) && !saveSuiteBConfig(config)) { @@ -721,6 +720,15 @@ public class SupplicantStaNetworkHal { Log.e(TAG, "failed to set ocsp"); return false; } + /** EAP ERP */ + eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.EAP_ERP); + if (!TextUtils.isEmpty(eapParam) && eapParam.equals("1")) { + if (!setEapErp(true)) { + Log.e(TAG, ssid + ": failed to set eap erp"); + return false; + } + } + return true; } @@ -799,6 +807,14 @@ public class SupplicantStaNetworkHal { mask |= android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask .WAPI_CERT; break; + case WifiConfiguration.KeyMgmt.FILS_SHA256: + mask |= android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask + .FILS_SHA256; + break; + case WifiConfiguration.KeyMgmt.FILS_SHA384: + mask |= android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask + .FILS_SHA384; + break; case WifiConfiguration.KeyMgmt.WPA2_PSK: // This should never happen default: throw new IllegalArgumentException( @@ -1054,6 +1070,12 @@ public class SupplicantStaNetworkHal { mask = supplicantMaskValueToWifiConfigurationBitSet( mask, android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask .WAPI_CERT, bitset, WifiConfiguration.KeyMgmt.WAPI_CERT); + mask = supplicantMaskValueToWifiConfigurationBitSet( + mask, android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask + .FILS_SHA256, bitset, WifiConfiguration.KeyMgmt.FILS_SHA256); + mask = supplicantMaskValueToWifiConfigurationBitSet( + mask, android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork.KeyMgmtMask + .FILS_SHA384, bitset, WifiConfiguration.KeyMgmt.FILS_SHA384); if (mask != 0) { throw new IllegalArgumentException( "invalid key mgmt mask from supplicant: " + mask); @@ -1925,6 +1947,29 @@ public class SupplicantStaNetworkHal { } /** See ISupplicantStaNetwork.hal for documentation */ + private boolean setEapErp(boolean enable) { + synchronized (mLock) { + final String methodStr = "setEapErp"; + if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false; + try { + android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork + iSupplicantStaNetworkV13; + + iSupplicantStaNetworkV13 = getV1_3StaNetwork(); + if (iSupplicantStaNetworkV13 != null) { + /* Support for set ERP Requires HAL v1.3 or higher */ + SupplicantStatus status = iSupplicantStaNetworkV13.setEapErp(enable); + return checkStatusAndLogFailure(status, methodStr); + } else { + return false; + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + return false; + } + } + } + /** See ISupplicantStaNetwork.hal for documentation */ private boolean getSaePasswordId() { synchronized (mLock) { final String methodStr = "getSaePasswordId"; diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java index 72d8db21c..3ed456b01 100644 --- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java +++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java @@ -496,8 +496,8 @@ public class WifiConfigurationUtil { private static boolean validateKeyMgmt(BitSet keyMgmnt) { if (keyMgmnt.cardinality() > 1) { - if (keyMgmnt.cardinality() > 3) { - Log.e(TAG, "validateKeyMgmt failed: cardinality > 3"); + if (keyMgmnt.cardinality() > 4) { + Log.e(TAG, "validateKeyMgmt failed: cardinality > 4"); return false; } if (!keyMgmnt.get(WifiConfiguration.KeyMgmt.WPA_EAP)) { @@ -509,9 +509,11 @@ public class WifiConfigurationUtil { Log.e(TAG, "validateKeyMgmt failed: not PSK or 8021X"); return false; } - if (keyMgmnt.cardinality() == 3 - && !keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { - Log.e(TAG, "validateKeyMgmt failed: not SUITE_B_192"); + if (keyMgmnt.cardinality() == 4 + && (!keyMgmnt.get(WifiConfiguration.KeyMgmt.SUITE_B_192) + && !keyMgmnt.get(WifiConfiguration.KeyMgmt.FILS_SHA256) + && !keyMgmnt.get(WifiConfiguration.KeyMgmt.FILS_SHA384))) { + Log.e(TAG, "validateKeyMgmt failed: neither SUITE_B_192 nor FILS"); return false; } } diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 2ca87921c..4e006a2fc 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -100,6 +100,9 @@ public class WifiMonitor { /* MBO/OCE events */ public static final int MBO_OCE_BSS_TM_HANDLING_DONE = BASE + 71; + /* Fils network connection completed */ + public static final int FILS_NETWORK_CONNECTION_EVENT = BASE + 62; + /* WPS config errrors */ private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12; private static final int CONFIG_AUTH_FAILURE = 18; @@ -504,6 +507,17 @@ public class WifiMonitor { } /** + * Broadcast the fils network connection event to all the handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param networkId ID of the network in wpa_supplicant. + * @param bssid BSSID of the access point. + */ + public void broadcastFilsNetworkConnectionEvent(String iface, int networkId, String bssid) { + sendMessage(iface, FILS_NETWORK_CONNECTION_EVENT, networkId, 0, bssid); + } + + /** * Broadcast the network disconnection event to all the handlers registered for this event. * * @param iface Name of iface on which this occurred. diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index a80486016..0ec2c14aa 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -1985,6 +1985,26 @@ public class WifiNative { } /** + * Flush all previously configured HLPs. + * + * @return true if request is sent successfully, false otherwise. + */ + public boolean flushAllHlp(@NonNull String ifaceName) { + return mSupplicantStaIfaceHal.flushAllHlp(ifaceName); + } + + /** + * Set FILS HLP packet. + * + * @param dst Destination MAC address. + * @param hlpPacket Hlp Packet data in hex. + * @return true if request is sent successfully, false otherwise. + */ + public boolean addHlpReq(@NonNull String ifaceName, MacAddress dst, byte [] hlpPacket) { + return mSupplicantStaIfaceHal.addHlpReq(ifaceName, dst.toByteArray(), hlpPacket); + } + + /** * Initiate TDLS discover and setup or teardown with the specified peer. * * @param ifaceName Name of the interface. diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index b5bf82269..091707a7a 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -914,6 +914,8 @@ public class InformationElementUtil { private static final int RSN_AKM_OWE = 0x12ac0f00; private static final int RSN_AKM_EAP_SUITE_B_192 = 0x0cac0f00; private static final int RSN_OSEN = 0x019a6f50; + private static final int RSN_AKM_FILS_SHA256 = 0x0eac0f00; + private static final int RSN_AKM_FILS_SHA384 = 0x0fac0f00; private static final int WPA_CIPHER_NONE = 0x00f25000; private static final int WPA_CIPHER_TKIP = 0x02f25000; @@ -1016,6 +1018,12 @@ public class InformationElementUtil { case RSN_OSEN: rsnKeyManagement.add(ScanResult.KEY_MGMT_OSEN); break; + case RSN_AKM_FILS_SHA256: + rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA256); + break; + case RSN_AKM_FILS_SHA384: + rsnKeyManagement.add(ScanResult.KEY_MGMT_FILS_SHA384); + break; default: // do nothing break; @@ -1294,6 +1302,10 @@ public class InformationElementUtil { return "WAPI-PSK"; case ScanResult.KEY_MGMT_WAPI_CERT: return "WAPI-CERT"; + case ScanResult.KEY_MGMT_FILS_SHA256: + return "FILS-SHA256"; + case ScanResult.KEY_MGMT_FILS_SHA384: + return "FILS-SHA384"; default: return "?"; } diff --git a/service/java/com/android/server/wifi/util/ScanResultUtil.java b/service/java/com/android/server/wifi/util/ScanResultUtil.java index 2e9a00a6f..320490c0b 100644 --- a/service/java/com/android/server/wifi/util/ScanResultUtil.java +++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java @@ -127,6 +127,22 @@ public class ScanResultUtil { } /** + * Helper method to check if the provided |scanResult| corresponds to FILS SHA256 network. + * This checks if the provided capabilities string contains FILS-SHA256 or not. + */ + public static boolean isScanResultForFilsSha256Network(ScanResult scanResult) { + return scanResult.capabilities.contains("FILS-SHA256"); + } + + /** + * Helper method to check if the provided |scanResult| corresponds to FILS SHA384 network. + * This checks if the provided capabilities string contains FILS-SHA384 or not. + */ + public static boolean isScanResultForFilsSha384Network(ScanResult scanResult) { + return scanResult.capabilities.contains("FILS-SHA384"); + } + + /** * Helper method to check if the provided |scanResult| corresponds to an open network or not. * This checks if the provided capabilities string does not contain either of WEP, PSK, SAE * or EAP encryption types or not. diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 087259370..a31247be0 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -60,6 +60,7 @@ import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback; import android.net.ConnectivityManager; import android.net.DhcpResultsParcelable; import android.net.InetAddresses; +import android.net.Layer2PacketParcelable; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; @@ -151,6 +152,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -343,6 +345,7 @@ public class ClientModeImplTest extends WifiBaseTest { static final int sFreq = 2437; static final int sFreq1 = 5240; static final String WIFI_IFACE_NAME = "mockWlan"; + static final String sFilsSsid = "FILS-AP"; ClientModeImpl mCmi; HandlerThread mWifiCoreThread; @@ -2690,10 +2693,9 @@ public class ClientModeImplTest extends WifiBaseTest { when(mWifiNative.getMacAddress(WIFI_IFACE_NAME)) .thenReturn(TEST_LOCAL_MAC_ADDRESS.toString()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -2720,10 +2722,9 @@ public class ClientModeImplTest extends WifiBaseTest { assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -2767,11 +2768,10 @@ public class ClientModeImplTest extends WifiBaseTest { assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); - WifiConfiguration config = mock(WifiConfiguration.class); + WifiConfiguration config = new WifiConfiguration(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; config.setRandomizedMacAddress(MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS)); - when(config.getNetworkSelectionStatus()) - .thenReturn(new WifiConfiguration.NetworkSelectionStatus()); when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(0)).thenReturn(config); mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); @@ -3205,7 +3205,7 @@ public class ClientModeImplTest extends WifiBaseTest { disconnect(); mLooper.dispatchAll(); - verify(mWifiScoreCard).resetConnectionState(); + verify(mWifiScoreCard, times(1)).resetConnectionState(); verify(mWifiScoreCard, never()).noteWifiDisabled(any()); verify(mWifiHealthMonitor, never()).setWifiEnabled(false); @@ -3213,7 +3213,7 @@ public class ClientModeImplTest extends WifiBaseTest { mCmi.setWifiStateForApiCalls(WifiManager.WIFI_STATE_DISABLED); mCmi.setOperationalMode(ClientModeImpl.DISABLED_MODE, null); mLooper.dispatchAll(); - verify(mWifiScoreCard).resetConnectionState(); + verify(mWifiScoreCard, times(2)).resetConnectionState(); verify(mWifiHealthMonitor).setWifiEnabled(false); } @@ -4513,4 +4513,202 @@ public class ClientModeImplTest extends WifiBaseTest { assertTrue(mCmi.isWifiBandSupported(WifiScanner.WIFI_BAND_6_GHZ)); verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ); } + + /** + * Helper function for setting up fils test. + * + * @param isDriverSupportFils true if driver support fils. + * @return wifi configuration. + */ + private WifiConfiguration setupFilsTest(boolean isDriverSupportFils) { + assertEquals(ClientModeImpl.CONNECT_MODE, mCmi.getOperationalModeForTest()); + assertEquals(WifiManager.WIFI_STATE_ENABLED, mCmi.syncGetWifiState()); + + WifiConfiguration config = new WifiConfiguration(); + config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + config.SSID = ScanResultUtil.createQuotedSSID(sFilsSsid); + config.networkId = 1; + config.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); + config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; + + when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); + if (isDriverSupportFils) { + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( + WifiManager.WIFI_FEATURE_FILS_SHA256 | WifiManager.WIFI_FEATURE_FILS_SHA384); + } else { + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn((long) 0); + } + + return config; + } + + /** + * Helper function for setting up a scan result with FILS supported AP. + * + */ + private void setupFilsEnabledApInScanResult() { + String caps = "[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP]" + + "[RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP][ESS]"; + ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(sFilsSsid), + sFilsSsid, sBSSID, 1245, 0, caps, -78, 2412, 1025, 22, 33, 20, 0, 0, true); + ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID, + sFilsSsid.getBytes(StandardCharsets.UTF_8)); + scanResult.informationElements = new ScanResult.InformationElement[]{ie}; + List<ScanResult> scanResults = new ArrayList<>(); + scanResults.add(scanResult); + + when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); + } + + + /** + * Helper function to send CMD_START_FILS_CONNECTION along with HLP IEs. + * + */ + private void prepareFilsHlpPktAndSendStartConnect() { + Layer2PacketParcelable l2Packet = new Layer2PacketParcelable(); + l2Packet.dstMacAddress = TEST_GLOBAL_MAC_ADDRESS; + l2Packet.payload = new byte[] {0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, + 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x55, 0x66}; + mCmi.sendMessage(ClientModeImpl.CMD_START_FILS_CONNECTION, 0, 0, + Collections.singletonList(l2Packet)); + mLooper.dispatchAll(); + } + + /** + * Verifies that while connecting to AP, the logic looks into the scan result and + * looks for AP matching the network type and ssid and update the wificonfig with FILS + * AKM if supported. + * + * @throws Exception + */ + @Test + public void testFilsAKMUpdateBeforeConnect() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); + verify(mWifiNative, never()).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies that while connecting to AP, framework updates the wifi config with + * FILS AKM only if underlying driver support FILS feature. + * + * @throws Exception + */ + @Test + public void testFilsAkmIsNotAddedinWifiConfigIfDriverDoesNotSupportFils() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(false); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + assertFalse(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)); + verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + + /** + * Verifies that the HLP (DHCP) packets are send to wpa_supplicant + * prior to Fils connection. + * + * @throws Exception + */ + @Test + public void testFilsHlpUpdateBeforeFilsConnection() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative).flushAllHlp(eq(WIFI_IFACE_NAME)); + verify(mWifiNative).addHlpReq(eq(WIFI_IFACE_NAME), any(), any()); + verify(mWifiNative).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies that an association rejection in first FILS connect attempt doesn't block + * the second connection attempt. + * + * @throws Exception + */ + @Test + public void testFilsSecondConnectAttemptIsNotBLockedAfterAssocReject() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative, times(1)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + + mCmi.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT, 0, 2, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + prepareFilsHlpPktAndSendStartConnect(); + + verify(mWifiNative, times(2)).connectToNetwork(eq(WIFI_IFACE_NAME), eq(config)); + } + + /** + * Verifies Fils connection. + * + * @throws Exception + */ + @Test + public void testFilsConnection() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + WifiConfiguration config = setupFilsTest(true); + setupFilsEnabledApInScanResult(); + + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + prepareFilsHlpPktAndSendStartConnect(); + + mCmi.sendMessage(WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(sFilsSsid), + sBSSID, SupplicantState.COMPLETED)); + mLooper.dispatchAll(); + + assertEquals("ObtainingIpState", getCurrentState().getName()); + + DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); + dhcpResults.baseConfiguration = new StaticIpConfiguration(); + dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4"); + dhcpResults.baseConfiguration.ipAddress = + new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0); + dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8")); + dhcpResults.leaseDuration = 3600; + + injectDhcpSuccess(dhcpResults); + mLooper.dispatchAll(); + + WifiInfo wifiInfo = mCmi.getWifiInfo(); + assertNotNull(wifiInfo); + assertEquals(sBSSID, wifiInfo.getBSSID()); + assertTrue(WifiSsid.createFromAsciiEncoded(sFilsSsid).equals(wifiInfo.getWifiSsid())); + assertEquals("ConnectedState", getCurrentState().getName()); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index 2278d0dfe..64c6e0794 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -16,6 +16,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256; +import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384; import static android.net.wifi.WifiManager.WIFI_FEATURE_MBO; import static android.net.wifi.WifiManager.WIFI_FEATURE_OCE; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; @@ -1673,6 +1675,44 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { } /** + * Test FILS SHA256 key management support. + */ + @Test + public void testGetKeyMgmtCapabilitiesFilsSha256() throws Exception { + setupMocksForHalV1_3(); + + executeAndValidateInitializationSequenceV1_3(); + + doAnswer(new GetKeyMgmtCapabilities_1_3Answer(android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256)) + .when(mISupplicantStaIfaceMockV13).getKeyMgmtCapabilities_1_3(any( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getKeyMgmtCapabilities_1_3Callback.class)); + + assertEquals(WIFI_FEATURE_FILS_SHA256, + mDut.getAdvancedKeyMgmtCapabilities(WLAN0_IFACE_NAME)); + } + + /** + * Test FILS SHA384 key management support. + */ + @Test + public void testGetKeyMgmtCapabilitiesFilsSha384() throws Exception { + setupMocksForHalV1_3(); + + executeAndValidateInitializationSequenceV1_3(); + + doAnswer(new GetKeyMgmtCapabilities_1_3Answer(android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA384)) + .when(mISupplicantStaIfaceMockV13).getKeyMgmtCapabilities_1_3(any( + android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface + .getKeyMgmtCapabilities_1_3Callback.class)); + + assertEquals(WIFI_FEATURE_FILS_SHA384, + mDut.getAdvancedKeyMgmtCapabilities(WLAN0_IFACE_NAME)); + } + + /** * Test Easy Connect (DPP) calls return failure if hal version is less than 1_2 */ @Test @@ -2534,4 +2574,199 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { eq(WLAN0_IFACE_NAME), btmFrameDataCaptor.capture()); } + /** + * Tests the configuring of FILS HLP packet in supplicant. + */ + @Test + public void testAddHlpReq() throws Exception { + byte[] dstAddr = {0x45, 0x23, 0x12, 0x12, 0x12, 0x45}; + byte[] hlpPacket = {0x00, 0x01, 0x02, 0x03, 0x04, 0x12, 0x15, 0x34, 0x55, 0x12, + 0x12, 0x45, 0x23, 0x52, 0x32, 0x16, 0x15, 0x53, 0x62, 0x32, 0x32, 0x10}; + + setupMocksForHalV1_3(); + when(mISupplicantStaIfaceMockV13.filsHlpAddRequest(any(byte[].class), + any(ArrayList.class))).thenReturn(mStatusSuccess); + + // Fail before initialization is performed. + assertFalse(mDut.addHlpReq(WLAN0_IFACE_NAME, dstAddr, hlpPacket)); + verify(mISupplicantStaIfaceMockV13, never()).filsHlpAddRequest(any(byte[].class), + any(ArrayList.class)); + + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + ArrayList<Byte> hlpPayload = NativeUtil.byteArrayToArrayList(hlpPacket); + assertTrue(mDut.addHlpReq(WLAN0_IFACE_NAME, dstAddr, hlpPacket)); + verify(mISupplicantStaIfaceMockV13).filsHlpAddRequest(eq(dstAddr), eq(hlpPayload)); + } + + /** + * Tests the flushing of FILS HLP packet from supplicant. + */ + @Test + public void testFlushAllHlp() throws Exception { + + setupMocksForHalV1_3(); + when(mISupplicantStaIfaceMockV13.filsHlpFlushRequest()).thenReturn(mStatusSuccess); + + // Fail before initialization is performed. + assertFalse(mDut.flushAllHlp(WLAN0_IFACE_NAME)); + verify(mISupplicantStaIfaceMockV13, never()).filsHlpFlushRequest(); + + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + assertTrue(mDut.flushAllHlp(WLAN0_IFACE_NAME)); + verify(mISupplicantStaIfaceMockV13).filsHlpFlushRequest(); + } + + /** + * Tests the handling of state change V13 notification without + * any configured network. + */ + @Test + public void testonStateChangedV13CallbackWithNoConfiguredNetwork() throws Exception { + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.INACTIVE, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + // Can't compare WifiSsid instances because they lack an equals. + verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.INACTIVE)); + } + + /** + * Tests the handling of state change V13 notification to + * associated after configuring a network. + */ + @Test + public void testStateChangeV13ToAssociatedCallback() throws Exception { + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.ASSOCIATED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.ASSOCIATED)); + } + + /** + * Tests the handling of state change V13 notification to + * completed after configuring a network. + */ + @Test + public void testStateChangeV13ToCompletedCallback() throws Exception { + InOrder wifiMonitorInOrder = inOrder(mWifiMonitor); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.COMPLETED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + + wifiMonitorInOrder.verify(mWifiMonitor).broadcastNetworkConnectionEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID)); + wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED)); + } + + /** + * Tests the handling of incorrect network passwords, edge case + * when onStateChanged_1_3() is used. + * + * If the network is removed during 4-way handshake, do not call it a password mismatch. + */ + @Test + public void testNetworkRemovedDuring4wayWhenonStateChangedV13IsUsed() throws Exception { + executeAndValidateInitializationSequence(); + assertNotNull(mISupplicantStaIfaceCallback); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + int reasonCode = 3; + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE, + NativeUtil.macAddressToByteArray(BSSID), + SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + mISupplicantStaIfaceCallback.onNetworkRemoved(SUPPLICANT_NETWORK_ID); + mISupplicantStaIfaceCallback.onDisconnected( + NativeUtil.macAddressToByteArray(BSSID), true, reasonCode); + verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt(), + anyInt()); + } + + /** + * Tests the handling of incorrect network passwords when + * onStateChanged_1_3() is used, edge case. + * + * If the disconnect reason is "IE in 4way differs", do not call it a password mismatch. + */ + @Test + public void testIeDiffersWhenonStateChangedV13IsUsed() throws Exception { + executeAndValidateInitializationSequence(); + assertNotNull(mISupplicantStaIfaceCallback); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + + int reasonCode = ISupplicantStaIfaceCallback.ReasonCode.IE_IN_4WAY_DIFFERS; + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE, + NativeUtil.macAddressToByteArray(BSSID), + SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), false); + mISupplicantStaIfaceCallback.onDisconnected( + NativeUtil.macAddressToByteArray(BSSID), true, reasonCode); + verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt(), + anyInt()); + } + + /** + * Tests the handling of state change V13 notification to + * completed (with FILS HLP IE sent) after configuring a + * network. + */ + @Test + public void testStateChangeV13WithFilsHlpIESentToCompletedCallback() throws Exception { + InOrder wifiMonitorInOrder = inOrder(mWifiMonitor); + setupMocksForHalV1_3(); + executeAndValidateInitializationSequenceV1_3(); + assertNotNull(mISupplicantStaIfaceCallbackV13); + int frameworkNetworkId = 6; + executeAndValidateConnectSequence(frameworkNetworkId, false); + + mISupplicantStaIfaceCallbackV13.onStateChanged_1_3( + ISupplicantStaIfaceCallback.State.COMPLETED, + NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID, + NativeUtil.decodeSsid(SUPPLICANT_SSID), true); + + wifiMonitorInOrder.verify(mWifiMonitor).broadcastFilsNetworkConnectionEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID)); + wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent( + eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), + any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED)); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java index 949b8659f..616472c10 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java @@ -387,6 +387,29 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { } /** + * Tests the saving/loading of WifiConfiguration with FILS AKM + * to wpa_supplicant. + */ + @Test + public void testTLSWifiEnterpriseConfigWithFilsEapErp() throws Exception { + // Now expose the V1.3 ISupplicantStaNetwork + createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_3); + + WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); + config.enterpriseConfig = + WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2(); + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.FILS_SHA256); + config.enterpriseConfig.setFieldValue(WifiEnterpriseConfig.EAP_ERP, "1"); + testWifiConfigurationSaveLoad(config); + // Check the supplicant variables to ensure that we have added the FILS AKM. + assertTrue((mSupplicantVariables.keyMgmtMask & android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256) + == android.hardware.wifi.supplicant.V1_3 + .ISupplicantStaNetwork.KeyMgmtMask.FILS_SHA256); + verify(mISupplicantStaNetworkV13).setEapErp(eq(true)); + } + + /** * Tests the saving of WifiConfiguration to wpa_supplicant. */ @Test @@ -974,6 +997,14 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { Integer.parseInt(oppKeyCaching) == 1 ? true : false, mSupplicantVariables.eapProactiveKeyCaching); } + // There is no getter for this one, so check the supplicant variable. + String eapErp = + config.enterpriseConfig.getFieldValue(WifiEnterpriseConfig.EAP_ERP); + if (!TextUtils.isEmpty(eapErp)) { + assertEquals( + Integer.parseInt(eapErp) == 1 ? true : false, + mSupplicantVariables.eapErp); + } } /** @@ -1695,6 +1726,14 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { }).when(mISupplicantStaNetworkV13) .getWapiCertSuite(any(android.hardware.wifi.supplicant.V1_3 .ISupplicantStaNetwork.getWapiCertSuiteCallback.class)); + + /** EAP ERP */ + doAnswer(new AnswerWithArguments() { + public SupplicantStatus answer(boolean enable) throws RemoteException { + mSupplicantVariables.eapErp = enable; + return mStatusSuccess; + } + }).when(mISupplicantStaNetworkV13).setEapErp(any(boolean.class)); } private SupplicantStatus createSupplicantStatus(int code) { @@ -1761,5 +1800,6 @@ public class SupplicantStaNetworkHalTest extends WifiBaseTest { public int ocsp; public ArrayList<Byte> serializedPmkCache; public String wapiCertSuite; + public boolean eapErp; } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java index 40ca9b9a2..6652e9b0c 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java @@ -602,4 +602,23 @@ public class WifiMonitorTest extends WifiBaseTest { assertEquals(WifiMonitor.MBO_OCE_BSS_TM_HANDLING_DONE, messageCaptor.getValue().what); } + /** + * Broadcast fils network connection test. + */ + @Test + public void testBroadcastFilsNetworkConnectionEvent() { + mWifiMonitor.registerHandler( + WLAN_IFACE_NAME, WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, mHandlerSpy); + int networkId = NETWORK_ID; + String bssid = BSSID; + mWifiMonitor.broadcastFilsNetworkConnectionEvent(WLAN_IFACE_NAME, networkId, bssid); + mLooper.dispatchAll(); + + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mHandlerSpy).handleMessage(messageCaptor.capture()); + assertEquals(WifiMonitor.FILS_NETWORK_CONNECTION_EVENT, messageCaptor.getValue().what); + assertEquals(networkId, messageCaptor.getValue().arg1); + assertEquals(bssid, (String) messageCaptor.getValue().obj); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index 728af073a..ae8f1229e 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java @@ -1033,4 +1033,33 @@ public class WifiNativeTest extends WifiBaseTest { WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); verify(mWificondControl, never()).sendMgmtFrame(any(), any(), anyInt(), any(), any()); } + + /** + * Tests that WifiNative#addHlpReq() calls + * SupplicantStaIfaceHal#addHlpReq() + */ + @Test + public void testaddHlpReq() { + byte[] hlpPacket = { + 0x40, 0x00, 0x3c, 0x00, (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, + 0x33, 0x72, (byte) 0xf4, (byte) 0xf5, (byte) 0xe8, 0x51, (byte) 0x9e, 0x09, + (byte) 0xa8, (byte) 0xbd, 0x27, 0x5b, 0x33, 0x72, (byte) 0xb0, 0x66, + 0x00, 0x00 + }; + mWifiNative.addHlpReq(WIFI_IFACE_NAME, TEST_MAC_ADDRESS, hlpPacket); + + verify(mStaIfaceHal).addHlpReq(eq(WIFI_IFACE_NAME), + eq(TEST_MAC_ADDRESS.toByteArray()), eq(hlpPacket)); + } + + /** + * Tests that WifiNative#flushAllHlp() calls + * SupplicantStaIfaceHal#flushAllHlp() + */ + @Test + public void testflushAllHlp() { + mWifiNative.flushAllHlp(WIFI_IFACE_NAME); + + verify(mStaIfaceHal).flushAllHlp(eq(WIFI_IFACE_NAME)); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java index 4b98a59d9..874da7043 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java @@ -627,6 +627,88 @@ public class InformationElementUtilTest extends WifiBaseTest { } /** + * Test Capabilities.generateCapabilitiesString() with RSN IE, + * CCMP and FILS SHA256. Expect the function to return a string + * with the proper security information. + */ + @Test + public void buildCapabilities_rsnFilsSha256Element() { + InformationElement ieRsn = new InformationElement(); + ieRsn.id = InformationElement.EID_RSN; + ieRsn.bytes = new byte[] { + // RSNE Version (0x0001) + (byte) 0x01, (byte) 0x00, + // Group cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of cipher suites (1) + (byte) 0x01, (byte) 0x00, + // Cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of AKMs (3) + (byte) 0x03, (byte) 0x00, + // WPA AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x01, + // WPA SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x05, + // FILS SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x0E, + // RSN capabilities + (byte) 0x00, (byte) 0x00 }; + + InformationElement[] ies = new InformationElement[] { ieRsn }; + int beaconCap = 0x1 << 4; + + InformationElementUtil.Capabilities capabilities = + new InformationElementUtil.Capabilities(); + capabilities.from(ies, beaconCap, true); + String result = capabilities.generateCapabilitiesString(); + + assertEquals("[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP][RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP]", + result); + } + + /** + * Test Capabilities.generateCapabilitiesString() with RSN IE, + * CCMP and FILS SHA384. Expect the function to return a string + * with the proper security information. + */ + @Test + public void buildCapabilities_rsnFilsSha384Element() { + InformationElement ieRsn = new InformationElement(); + ieRsn.id = InformationElement.EID_RSN; + ieRsn.bytes = new byte[] { + // RSNE Version (0x0001) + (byte) 0x01, (byte) 0x00, + // Group cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of cipher suites (1) + (byte) 0x01, (byte) 0x00, + // Cipher suite: CCMP + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, + // Number of AKMs (3) + (byte) 0x03, (byte) 0x00, + // WPA AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x01, + // WPA SHA256 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x05, + // FILS SHA384 AKM + (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x0F, + // RSN capabilities + (byte) 0x00, (byte) 0x00 }; + + InformationElement[] ies = new InformationElement[] { ieRsn }; + int beaconCap = 0x1 << 4; + + InformationElementUtil.Capabilities capabilities = + new InformationElementUtil.Capabilities(); + capabilities.from(ies, beaconCap, true); + String result = capabilities.generateCapabilitiesString(); + + assertEquals("[WPA2-EAP+EAP-SHA256+FILS-SHA384-CCMP][RSN-EAP+EAP-SHA256+FILS-SHA384-CCMP]", + result); + } + + /** * Test Capabilities.generateCapabilitiesString() with both RSN and WPA1 IE which are malformed. * Expect the function to return a string with empty key management & pairswise cipher security * information. diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java index bc5f880b4..0f69316e7 100644 --- a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java +++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java @@ -203,6 +203,46 @@ public class ScanResultUtilTest extends WifiBaseTest { assertFalse(ScanResultUtil.isScanResultForPskSaeTransitionNetwork(input)); } + /** + * Test that provided network supports FILS SHA256 AKM. + */ + @Test + public void testFilsSha256AkmSupportedNetwork() { + final String ssid = "FILS-AP"; + String caps = "[WPA2-EAP+EAP-SHA256+FILS-SHA256-CCMP]" + + "[RSN-EAP+EAP-SHA256+FILS-SHA256-CCMP][ESS]"; + + ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, + "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, + 0, true); + + input.informationElements = new InformationElement[] { + createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8)) + }; + + assertTrue(ScanResultUtil.isScanResultForFilsSha256Network(input)); + } + + /** + * Test that provided network supports FILS SHA384 AKM. + */ + @Test + public void testFilsSha384AkmSupportedNetwork() { + final String ssid = "FILS-AP"; + String caps = "[WPA2-EAP+EAP-SHA384+FILS-SHA384-CCMP]" + + "[RSN-EAP+EAP-SHA384+FILS-SHA384-CCMP][ESS]"; + + ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid, + "ab:cd:01:ef:45:89", 1245, 0, caps, -78, 2450, 1025, 22, 33, 20, 0, + 0, true); + + input.informationElements = new InformationElement[] { + createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8)) + }; + + assertTrue(ScanResultUtil.isScanResultForFilsSha384Network(input)); + } + private static InformationElement createIE(int id, byte[] bytes) { InformationElement ie = new InformationElement(); ie.id = id; |