Check roaming network for Fast Re-Authentication

Sometimes fast re-authentication doesn't work when the user moves from
the outside into the range of AP because BSSID mismatch is caused.
When the user taps AP for first connection, network block sets BSSID
with "any". And when the user moves into the range of AP,
SavedNetworkEvaluator sets BSSID from scan result.
And current code does not care about roaming case. When device tries
to connect to different APs in the same ESS, Wi-Fi framework always
handles them as the same network and won't update the target BSSID to
supplicant. So supplicant always starts the connection with unexpected
BSSID.

The patch sets BSSID to supplicant if roaming network is detected.
It classifies roaming network if BSSID is different from first
connection.

Bug: 64043254
Test: Unit tests

Change-Id: I4a57e7c05aaee99781dd38ef0405fcfaffae10c8
Merged-In: I4a57e7c05aaee99781dd38ef0405fcfaffae10c8
(cherry-picked from 64aadb9aedb5f1bbf55f477d3ee8bf3f2dadaaf0)
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
index e9c20db..703c798 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -69,6 +69,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -404,7 +405,22 @@
     public boolean connectToNetwork(@NonNull WifiConfiguration config) {
         logd("connectToNetwork " + config.configKey());
         if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
-            logd("Network is already saved, will not trigger remove and add operation.");
+            String networkSelectionBSSID = config.getNetworkSelectionStatus()
+                    .getNetworkSelectionBSSID();
+            String networkSelectionBSSIDCurrent =
+                    mCurrentNetworkLocalConfig.getNetworkSelectionStatus()
+                            .getNetworkSelectionBSSID();
+            if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
+                logd("Network is already saved, will not trigger remove and add operation.");
+            } else {
+                logd("Network is already saved, but need to update BSSID.");
+                if (!setCurrentNetworkBssid(
+                        config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
+                    loge("Failed to set current network BSSID.");
+                    return false;
+                }
+                mCurrentNetworkLocalConfig = new WifiConfiguration(config);
+            }
         } else {
             mCurrentNetworkRemoteHandle = null;
             mCurrentNetworkLocalConfig = null;
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index c726f49..bbfde46 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -236,9 +236,10 @@
 
     /**
      * Check if the provided two networks are the same.
+     * Note: This does not check if network selection BSSID's are the same.
      *
-     * @param config      Configuration corresponding to a network.
-     * @param config1      Configuration corresponding to another network.
+     * @param config  Configuration corresponding to a network.
+     * @param config1 Configuration corresponding to another network.
      *
      * @return true if |config| and |config1| are the same network.
      *         false otherwise.
@@ -256,13 +257,6 @@
         if (!Objects.equals(config.SSID, config1.SSID)) {
             return false;
         }
-        String networkSelectionBSSID = config.getNetworkSelectionStatus()
-                .getNetworkSelectionBSSID();
-        String networkSelectionBSSID1 = config1.getNetworkSelectionStatus()
-                .getNetworkSelectionBSSID();
-        if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) {
-            return false;
-        }
         if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) {
             return false;
         }
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
index 2deef52..6c7b252 100644
--- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
@@ -54,6 +54,7 @@
 import android.net.wifi.WifiSsid;
 import android.os.IHwBinder;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.SparseArray;
 
 import com.android.server.wifi.hotspot2.AnqpEvent;
@@ -488,6 +489,28 @@
                 .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
     }
 
+    @Test
+    public void connectToNetworkWithSameNetworkButDifferentBssidUpdatesNetworkFromSupplicant()
+            throws Exception {
+        executeAndValidateInitializationSequence();
+        WifiConfiguration config = executeAndValidateConnectSequence(SUPPLICANT_NETWORK_ID, false);
+        String testBssid = "11:22:33:44:55:66";
+        when(mSupplicantStaNetworkMock.setBssid(eq(testBssid))).thenReturn(true);
+
+        // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
+        reset(mISupplicantStaIfaceMock);
+        setupMocksForConnectSequence(true /*haveExistingNetwork*/);
+        // Change the BSSID and connect to the same network.
+        assertFalse(TextUtils.equals(
+                testBssid, config.getNetworkSelectionStatus().getNetworkSelectionBSSID()));
+        config.getNetworkSelectionStatus().setNetworkSelectionBSSID(testBssid);
+        assertTrue(mDut.connectToNetwork(config));
+        verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
+        verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
+        verify(mISupplicantStaIfaceMock, never())
+                .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
+    }
+
     /**
      * Tests connection to a specified network failure due to network add.
      */
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index 506db91..2b3de3f 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -43,6 +43,8 @@
     static final int OTHER_USER_ID = 11;
     static final String TEST_SSID = "test_ssid";
     static final String TEST_SSID_1 = "test_ssid_1";
+    static final String TEST_BSSID = "aa:aa:11:22:cc:dd";
+    static final String TEST_BSSID_1 = "11:22:11:22:cc:dd";
     static final List<UserInfo> PROFILES = Arrays.asList(
             new UserInfo(CURRENT_USER_ID, "owner", 0),
             new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0));
@@ -252,6 +254,19 @@
     }
 
     /**
+     * Verify that WifiConfigurationUtil.isSameNetwork returns true when two WifiConfiguration
+     * objects have the same parameters but different network selection BSSID's.
+     */
+    @Test
+    public void testIsSameNetworkReturnsTrueOnSameNetworkWithDifferentBSSID() {
+        WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+        network.getNetworkSelectionStatus().setNetworkSelectionBSSID(TEST_BSSID);
+        WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+        network1.getNetworkSelectionStatus().setNetworkSelectionBSSID(TEST_BSSID_1);
+        assertTrue(WifiConfigurationUtil.isSameNetwork(network, network1));
+    }
+
+    /**
      * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
      * objects have the different SSIDs.
      */