fix spurious disconnect while associating, dont set AP configuration with NULL SSID

Bug:14568953
Bug:17144830

Note: this CL address cases where framework sends spurious disconnect to driver while associating, this issue was only one of the root cause of 17144830, the remaining issues in this bug are "missing scan results" and "wifi chipset disconnects from AP due to missing beacons or receiving deauth from AP"

Change-Id: If888f11bb3c66b55ca8ff991651f9e4457aaea31
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 5e28fcf..eb35db8 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -100,8 +100,13 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case WifiStateMachine.CMD_SET_AP_CONFIG:
-                    mWifiApConfig = (WifiConfiguration) message.obj;
-                    transitionTo(mActiveState);
+                    WifiConfiguration config = (WifiConfiguration) message.obj;
+                    if (config.SSID != null) {
+                        mWifiApConfig = (WifiConfiguration) message.obj;
+                        transitionTo(mActiveState);
+                    } else {
+                        Log.e(TAG, "Try to setup AP config without SSID: " + message);
+                    }
                     break;
                 default:
                     return NOT_HANDLED;
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index 99c5505..febfda4 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -1045,6 +1045,32 @@
                         break;
                     }
                 }
+            } else if (key.contains("wpa_state=ASSOCIATING")
+                    || key.contains("wpa_state=ASSOCIATED")
+                    || key.contains("wpa_state=FOUR_WAY_HANDSHAKE")
+                    || key.contains("wpa_state=GROUP_KEY_HANDSHAKE")) {
+                if (DBG) {
+                    logDbg("attemptAutoJoin: bail out due to sup state " + key);
+                }
+                // After WifiStateMachine ask the supplicant to associate or reconnect
+                // we might still obtain scan results from supplicant
+                // however the supplicant state in the mWifiInfo and supplicant state tracker
+                // are updated when we get the supplicant state change message which can be
+                // processed after the SCAN_RESULT message, so at this point the framework doesn't
+                // know that supplicant is ASSOCIATING.
+                // A good fix for this race condition would be for the WifiStateMachine to add
+                // a new transient state where it expects to get the supplicant message indicating
+                // that it started the association process and within which critical operations
+                // like autojoin should be deleted.
+
+                // This transient state would remove the need for the roam Wathchdog which
+                // basically does that.
+
+                // At the moment, we just query the supplicant state synchronously with the
+                // mWifiNative.status() command, which allow us to know that
+                // supplicant has started association process, even though we didnt yet get the
+                // SUPPLICANT_STATE_CHANGE message.
+                return;
             }
         }
         if (DBG) {
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index eb7c0fe..4513f9d 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -365,7 +365,7 @@
      * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
      * ssid=QCA-HS20-R2-TEST
      * p2p_device_name=
-     * p2p_config_methods=0x0
+     * p2p_config_methods=0x0SET_NE
      * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
      * anqp_network_auth_type=010000
      * anqp_roaming_consortium=03506f9a05001bc504bd
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 55947ff..4f5a279 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -58,6 +58,7 @@
 import android.net.StaticIpConfiguration;
 import android.net.TrafficStats;
 import android.net.wifi.*;
+import android.net.wifi.SupplicantState;
 import android.net.wifi.WpsResult.Status;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.net.wifi.passpoint.IWifiPasspointManager;
@@ -2810,9 +2811,14 @@
             }
         }
         boolean attemptAutoJoin = true;
+        SupplicantState state = mWifiInfo.getSupplicantState();
         if (getCurrentState() == mRoamingState
                 || getCurrentState() == mObtainingIpState
-                || linkDebouncing) {
+                || linkDebouncing
+                || state == SupplicantState.ASSOCIATING
+                || state == SupplicantState.AUTHENTICATING
+                || state == SupplicantState.FOUR_WAY_HANDSHAKE
+                || state == SupplicantState.GROUP_HANDSHAKE) {
             // Dont attempt auto-joining again while we are already attempting to join
             // and/or obtaining Ip address
             attemptAutoJoin = false;
@@ -5638,6 +5644,15 @@
                     transitionTo(mObtainingIpState);
                     break;
                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+                    // Calling handleNetworkDisconnect here is redundant because we might already
+                    // have called it when leaving L2ConnectedState to go to disconnecting state
+                    // or thru other path
+                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
+                    // if they are valid, and only in this case call handleNEtworkDisconnect,
+                    // TODO: this should be fixed for a L MR release
+                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
+                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
+                    // at the chip etc...
                     if (DBG) log("ConnectModeState: Network connection lost ");
                     handleNetworkDisconnect();
                     transitionTo(mDisconnectedState);
@@ -5726,6 +5741,13 @@
             // For paranoia's sake, call handleNetworkDisconnect
             // only if BSSID is null or last networkId
             // is not invalid.
+            if (DBG) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
+                if (mLastBssid !=null) {
+                    sb.append(" ").append(mLastBssid);
+                }
+            }
             if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
                 handleNetworkDisconnect();
             }