diff options
6 files changed, 214 insertions, 10 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index cf8acb4f0..7314bb853 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -2985,6 +2985,31 @@ public class ClientModeImpl extends StateMachine { handleConnectionAttemptEndForDiagnostics(level2FailureCode); } + /* If this connection attempt fails after 802.1x stage, clear intermediate cached data. */ + void clearNetworkCachedDataIfNeeded(WifiConfiguration config, int reason) { + if (config == null) return; + + switch(reason) { + case 14: // MICHAEL_MIC_FAILURE + case 15: // 4WAY_HANDSHAKE_TIMEOUT + case 16: // GROUP_KEY_UPDATE_TIMEOUT + case 17: // IE_IN_4WAY_DIFFERS + case 18: // GROUP_CIPHER_NOT_VALID + case 19: // PAIRWISE_CIPHER_NOT_VALID + case 20: // AKMP_NOT_VALID + case 23: // IEEE_802_1X_AUTH_FAILED + case 24: // CIPHER_SUITE_REJECTED + case 29: // BAD_CIPHER_OR_AKM + case 45: // PEERKEY_MISMATCH + case 49: // INVALID_PMKID + mWifiNative.removeNetworkCachedData(config.networkId); + break; + default: + logi("Keep PMK cache for network disconnection reason " + reason); + break; + } + } + /** * Returns the sufficient RSSI for the frequency that this network is last seen on. */ @@ -3247,6 +3272,9 @@ public class ClientModeImpl extends StateMachine { mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config); boolean setMacSuccess = mWifiNative.setMacAddress(mInterfaceName, newMac); + if (setMacSuccess) { + mWifiNative.removeNetworkCachedDataIfNeeded(config.networkId, newMac); + } Log.d(TAG, "ConnectedMacRandomization SSID(" + config.getPrintableSsid() + "). setMacAddress(" + newMac.toString() + ") from " + currentMacString + " = " + setMacSuccess); @@ -3265,6 +3293,7 @@ public class ClientModeImpl extends StateMachine { String currentMacStr = mWifiNative.getMacAddress(mInterfaceName); if (!TextUtils.equals(currentMacStr, factoryMac.toString())) { if (mWifiNative.setMacAddress(mInterfaceName, factoryMac)) { + mWifiNative.removeNetworkCachedDataIfNeeded(config.networkId, factoryMac); mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config); } else { Log.e(TAG, "Failed to set MAC address to " + "'" + factoryMac.toString() + "'"); @@ -4213,6 +4242,7 @@ public class ClientModeImpl extends StateMachine { // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode // at the chip etc... if (mVerboseLoggingEnabled) log("ConnectModeState: Network connection lost "); + clearNetworkCachedDataIfNeeded(getTargetWifiConfiguration(), message.arg2); handleNetworkDisconnect(); transitionTo(mDisconnectedState); break; @@ -5242,6 +5272,7 @@ public class ClientModeImpl extends StateMachine { + " BSSID=" + bssid + " target=" + target); } + clearNetworkCachedDataIfNeeded(getTargetWifiConfiguration(), message.arg2); if (bssid != null && bssid.equals(mTargetBssid)) { handleNetworkDisconnect(); transitionTo(mDisconnectedState); @@ -5397,6 +5428,7 @@ public class ClientModeImpl extends StateMachine { mWifiDiagnostics.captureBugReportData( WifiDiagnostics.REPORT_REASON_UNEXPECTED_DISCONNECT); } + boolean localGen = message.arg1 == 1; if (!localGen) { // ignore disconnects initiated by wpa_supplicant. mWifiScoreCard.noteNonlocalDisconnect(message.arg2); @@ -5600,6 +5632,7 @@ public class ClientModeImpl extends StateMachine { getTargetSsid(), bssid, WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION); } + clearNetworkCachedDataIfNeeded(getTargetWifiConfiguration(), message.arg2); break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: StateChangeResult stateChangeResult = (StateChangeResult) message.obj; diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java index 5af65d0a5..cac84b543 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackV1_3Impl.java @@ -199,9 +199,13 @@ abstract class SupplicantStaIfaceCallbackV1_3Impl extends if (WifiConfigurationUtil.isConfigForPskNetwork(curConfig)) return; - mStaIfaceHal.addPmkCacheEntry(curConfig.networkId, expirationTimeInSec, serializedEntry); + mStaIfaceHal.addPmkCacheEntry(mIfaceName, + curConfig.networkId, expirationTimeInSec, serializedEntry); mStaIfaceHal.logCallback( - "onPmkCacheAdded: update pmk cache for config id " + curConfig.networkId); + "onPmkCacheAdded: update pmk cache for config id " + + curConfig.networkId + + " on " + + mIfaceName); } @Override diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java index 4380f6ab4..657b081dd 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java @@ -43,6 +43,7 @@ import android.hardware.wifi.supplicant.V1_3.WifiTechnology; import android.hardware.wifi.supplicant.V1_3.WpaDriverCapabilitiesMask; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; +import android.net.MacAddress; import android.net.wifi.ScanResult; import android.net.wifi.WifiAnnotations.WifiStandard; import android.net.wifi.WifiConfiguration; @@ -170,10 +171,12 @@ public class SupplicantStaIfaceHal { static class PmkCacheStoreData { public long expirationTimeInSec; public ArrayList<Byte> data; + public MacAddress macAddress; - PmkCacheStoreData(long timeInSec, ArrayList<Byte> serializedData) { + PmkCacheStoreData(long timeInSec, ArrayList<Byte> serializedData, MacAddress macAddress) { expirationTimeInSec = timeInSec; data = serializedData; + this.macAddress = macAddress; } } @@ -1053,6 +1056,24 @@ public class SupplicantStaIfaceHal { } /** + * Clear HAL cached data if MAC address is changed. + * + * @param networkId network id of the network to be checked. + * @param curMacAddress current MAC address + */ + public void removeNetworkCachedDataIfNeeded(int networkId, MacAddress curMacAddress) { + synchronized (mLock) { + PmkCacheStoreData pmkData = mPmkCacheEntries.get(networkId); + + if (pmkData == null) return; + + if (curMacAddress.equals(pmkData.macAddress)) return; + + removeNetworkCachedData(networkId); + } + } + + /** * Remove all networks from supplicant * * @param ifaceName Name of the interface. @@ -2636,10 +2657,21 @@ public class SupplicantStaIfaceHal { } protected void addPmkCacheEntry( + String ifaceName, int networkId, long expirationTimeInSec, ArrayList<Byte> serializedEntry) { - mPmkCacheEntries.put(networkId, - new PmkCacheStoreData(expirationTimeInSec, serializedEntry)); - updatePmkCacheExpiration(); + String macAddressStr = getMacAddress(ifaceName); + if (macAddressStr == null) { + Log.w(TAG, "Omit PMK cache due to no valid MAC address on " + ifaceName); + return; + } + try { + MacAddress macAddress = MacAddress.fromString(macAddressStr); + mPmkCacheEntries.put(networkId, + new PmkCacheStoreData(expirationTimeInSec, serializedEntry, macAddress)); + updatePmkCacheExpiration(); + } catch (IllegalArgumentException ex) { + Log.w(TAG, "Invalid MAC address string " + macAddressStr); + } } protected void removePmkCacheEntry(int networkId) { diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 08abafa61..d0f2753a5 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -2416,6 +2416,15 @@ public class WifiNative { mSupplicantStaIfaceHal.removeNetworkCachedData(networkId); } + /** Clear HAL cached data for |networkId| if MAC address is changed. + * + * @param networkId network id of the network to be checked. + * @param curMacAddress current MAC address + */ + public void removeNetworkCachedDataIfNeeded(int networkId, MacAddress curMacAddress) { + mSupplicantStaIfaceHal.removeNetworkCachedDataIfNeeded(networkId, curMacAddress); + } + /* * DPP */ diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 0c48521e1..8f36770e6 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -4997,4 +4997,81 @@ public class ClientModeImplTest extends WifiBaseTest { verifyNoMoreInteractions(mNetworkAgentHandler); } + + /* + * Verify that network cached data is cleared correctly in + * disconnected state. + */ + @Test + public void testNetworkCachedDataIsClearedCorrectlyInDisconnectedState() throws Exception { + // Setup CONNECT_MODE & a WifiConfiguration + initializeAndAddNetworkAndVerifySuccess(); + mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + // got UNSPECIFIED during this connection attempt + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 1, sBSSID); + mLooper.dispatchAll(); + + assertEquals("DisconnectedState", getCurrentState().getName()); + verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); + + // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); + mLooper.dispatchAll(); + + assertEquals("DisconnectedState", getCurrentState().getName()); + verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); + } + + /* + * Verify that network cached data is cleared correctly in + * disconnected state. + */ + @Test + public void testNetworkCachedDataIsClearedCorrectlyInObtainingIpState() throws Exception { + initializeAndAddNetworkAndVerifySuccess(); + + verify(mWifiNative).removeAllNetworks(WIFI_IFACE_NAME); + + IActionListener connectActionListener = mock(IActionListener.class); + mCmi.connect(null, 0, mock(Binder.class), connectActionListener, 0, Binder.getCallingUid()); + mLooper.dispatchAll(); + verify(connectActionListener).onSuccess(); + + verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt(), any()); + + mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED)); + mLooper.dispatchAll(); + + assertEquals("ObtainingIpState", getCurrentState().getName()); + + // got 4WAY_HANDSHAKE_TIMEOUT during this connection attempt + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); + mLooper.dispatchAll(); + + verify(mWifiNative).removeNetworkCachedData(FRAMEWORK_NETWORK_ID); + } + + /* + * Verify that network cached data is NOT cleared in ConnectedState. + */ + @Test + public void testNetworkCachedDataIsClearedIf4WayHandshakeFailure() throws Exception { + when(mWifiScoreCard.detectAbnormalDisconnection()) + .thenReturn(WifiHealthMonitor.REASON_SHORT_CONNECTION_NONLOCAL); + InOrder inOrderWifiLockManager = inOrder(mWifiLockManager); + connect(); + inOrderWifiLockManager.verify(mWifiLockManager).updateWifiClientConnected(true); + + // got 4WAY_HANDSHAKE_TIMEOUT + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 15, sBSSID); + mLooper.dispatchAll(); + verify(mWifiNative, never()).removeNetworkCachedData(anyInt()); + } + } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index facb7f9ad..95f88bbf1 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -52,6 +52,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.test.MockAnswerUtil; +import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.content.Context; import android.hardware.wifi.V1_0.WifiChannelWidthInMhz; import android.hardware.wifi.supplicant.V1_0.ISupplicant; @@ -69,6 +70,7 @@ import android.hardware.wifi.supplicant.V1_3.ISupplicantStaIfaceCallback.BssTmDa import android.hardware.wifi.supplicant.V1_3.WifiTechnology; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; +import android.net.MacAddress; import android.net.wifi.ScanResult; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; @@ -126,6 +128,8 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { private static final int ICON_FILE_SIZE = 72; private static final String HS20_URL = "http://blahblah"; private static final long PMK_CACHE_EXPIRATION_IN_SEC = 1024; + private static final byte[] CONNECTED_MAC_ADDRESS_BYTES = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; private @Mock IServiceManager mServiceManagerMock; private @Mock ISupplicant mISupplicantMock; @@ -251,6 +255,13 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { any(IServiceNotification.Stub.class))).thenReturn(true); when(mISupplicantMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(true); + doAnswer(new AnswerWithArguments() { + public void answer(ISupplicantStaIface.getMacAddressCallback cb) { + cb.onValues(mStatusSuccess, CONNECTED_MAC_ADDRESS_BYTES); + } + }) + .when(mISupplicantStaIfaceMock) + .getMacAddress(any(ISupplicantStaIface.getMacAddressCallback.class)); mHandler = spy(new Handler(mLooper.getLooper())); mDut = new SupplicantStaIfaceHalSpy(); } @@ -1759,7 +1770,8 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { config.networkId = testFrameworkNetworkId; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); PmkCacheStoreData pmkCacheData = - new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>(), + MacAddress.fromBytes(CONNECTED_MAC_ADDRESS_BYTES)); mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); @@ -1820,7 +1832,8 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { config.networkId = testFrameworkNetworkId; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); PmkCacheStoreData pmkCacheData = - new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>(), + MacAddress.fromBytes(CONNECTED_MAC_ADDRESS_BYTES)); mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); @@ -1850,7 +1863,8 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { config.networkId = testFrameworkNetworkId; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); PmkCacheStoreData pmkCacheData = - new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>(), + MacAddress.fromBytes(CONNECTED_MAC_ADDRESS_BYTES)); mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); @@ -1877,7 +1891,8 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { config.networkId = testFrameworkNetworkId; config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); PmkCacheStoreData pmkCacheData = - new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>()); + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>(), + MacAddress.fromBytes(CONNECTED_MAC_ADDRESS_BYTES)); mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); @@ -1896,6 +1911,40 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { } /** + * Tests the PMK cache is removed and not set if MAC address is changed. + */ + @Test + public void testRemovePmkEntryOnMacAddressChanged() throws Exception { + int testFrameworkNetworkId = 9; + long testStartSeconds = PMK_CACHE_EXPIRATION_IN_SEC / 2; + WifiConfiguration config = new WifiConfiguration(); + config.networkId = testFrameworkNetworkId; + config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + // Assume we have a PMK cache with a different MAC address. + final byte[] previouisConnectedMacAddressBytes = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x09}; + PmkCacheStoreData pmkCacheData = + new PmkCacheStoreData(PMK_CACHE_EXPIRATION_IN_SEC, new ArrayList<Byte>(), + MacAddress.fromBytes(previouisConnectedMacAddressBytes)); + mDut.mPmkCacheEntries.put(testFrameworkNetworkId, pmkCacheData); + when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartSeconds * 1000L); + + setupMocksForHalV1_3(); + setupMocksForPmkCache(); + setupMocksForConnectSequence(false); + + // When MAC is not changed, PMK cache should NOT be removed. + mDut.removeNetworkCachedDataIfNeeded(testFrameworkNetworkId, + MacAddress.fromBytes(previouisConnectedMacAddressBytes)); + assertEquals(pmkCacheData, mDut.mPmkCacheEntries.get(testFrameworkNetworkId)); + + // When MAC is changed, PMK cache should be removed. + mDut.removeNetworkCachedDataIfNeeded(testFrameworkNetworkId, + MacAddress.fromBytes(CONNECTED_MAC_ADDRESS_BYTES)); + assertNull(mDut.mPmkCacheEntries.get(testFrameworkNetworkId)); + } + + /** * Test getConnectionCapabilities * Should fail if running HAL lower than V1_3 */ |