[WPA3] Fix connectivity issues with PSK-SAE Transtion mode

Fix two reported issues regarding PSK-SAE transition mode:
1. When phone supports SAE, creating a manual saved network with PSK
and manually connecting would cause the phone to connect but Settings
does not display any connected AP. Phone would not autoconnect.
2. When phone doesn't support SAE, phone cannot connect to an AP in
PSK-SAE Transition mode because the framework always targets the highest
security.

Bug: 132278271
Test: Connect to WPA2 network
Test: Connect to WPA3 network (w/capable phone)
Test: Connect to WPA2/3 Transition w/SAE capable phone
Test: Connect to WPA2/3 Transition w/SAE not-capable phone
Change-Id: I2e2db7a7583b0a8fabbe927e6ab1d04e3edbc000
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index baa3544..4d332b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -179,7 +179,8 @@
     public static final int SECURITY_OWE = 4;
     public static final int SECURITY_SAE = 5;
     public static final int SECURITY_EAP_SUITE_B = 6;
-    public static final int SECURITY_MAX_VAL = 7; // Has to be the last
+    public static final int SECURITY_PSK_SAE_TRANSITION = 7;
+    public static final int SECURITY_MAX_VAL = 8; // Has to be the last
 
     private static final int PSK_UNKNOWN = 0;
     private static final int PSK_WPA = 1;
@@ -782,7 +783,8 @@
             ssid = bestResult.SSID;
             bssid = bestResult.BSSID;
             security = getSecurity(bestResult);
-            if (security == SECURITY_PSK || security == SECURITY_SAE) {
+            if (security == SECURITY_PSK || security == SECURITY_SAE
+                    || security == SECURITY_PSK_SAE_TRANSITION) {
                 pskType = getPskType(bestResult);
             }
             if (security == SECURITY_EAP) {
@@ -859,6 +861,7 @@
                 return concise ? context.getString(R.string.wifi_security_short_wep) :
                     context.getString(R.string.wifi_security_wep);
             case SECURITY_SAE:
+            case SECURITY_PSK_SAE_TRANSITION:
                 if (pskType == PSK_SAE) {
                     return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
                             context.getString(R.string.wifi_security_psk_sae);
@@ -1150,7 +1153,7 @@
         if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
             return networkId == info.getNetworkId();
         } else if (config != null) {
-            return TextUtils.equals(getKey(config), getKey());
+            return isKeyEqual(getKey(config));
         } else {
             // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
             // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
@@ -1223,6 +1226,26 @@
         mAccessPointListener = listener;
     }
 
+    private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK);
+    private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE);
+    private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION);
+
+    private boolean isKeyEqual(String compareTo) {
+        if (mKey == null) {
+            return false;
+        }
+
+        if (compareTo.endsWith(sPskSuffix) || compareTo.endsWith(sSaeSuffix)) {
+            if (mKey.endsWith(sPskSaeSuffix)) {
+                // Special handling for PSK-SAE transition mode. If the AP has advertised both,
+                // we compare the key with both PSK and SAE for a match.
+                return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
+                        compareTo.substring(0, mKey.lastIndexOf(',')));
+            }
+        }
+        return mKey.equals(compareTo);
+    }
+
     /**
      * Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI
      * scan result.
@@ -1240,7 +1263,7 @@
         if (mKey != null && !isPasspoint() && !isOsuProvider()) {
             for (ScanResult result : scanResults) {
                 String scanResultKey = AccessPoint.getKey(result);
-                if (mKey != null && !mKey.equals(scanResultKey)) {
+                if (!isKeyEqual(scanResultKey)) {
                     Log.d(TAG, String.format(
                                     "ScanResult %s\nkey of %s did not match current AP key %s",
                                     result, scanResultKey, mKey));
@@ -1546,6 +1569,8 @@
     private static int getSecurity(ScanResult result) {
         if (result.capabilities.contains("WEP")) {
             return SECURITY_WEP;
+        } else if (result.capabilities.contains("PSK+SAE")) {
+            return SECURITY_PSK_SAE_TRANSITION;
         } else if (result.capabilities.contains("SAE")) {
             return SECURITY_SAE;
         } else if (result.capabilities.contains("PSK")) {
@@ -1601,6 +1626,8 @@
             return "SUITE_B";
         } else if (security == SECURITY_OWE) {
             return "OWE";
+        } else if (security == SECURITY_PSK_SAE_TRANSITION) {
+            return "PSK+SAE";
         }
         return "NONE";
     }