fix handling DHCP status

Change-Id: Ib4dfc9a1f84d6faaaf71adf63c8e2457e95646fd
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index b01994c..d97a161 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -67,6 +67,9 @@
     /* for debug purpose only : the untrusted SSID we would be connected to if we had VPN */
     String lastUntrustedBSSID = null;
 
+    /* For debug purpose only: if the scored override a score */
+    boolean didOverride = false;
+
     // Lose the non-auth failure blacklisting after 8 hours
     private final static long loseBlackListHardMilli = 1000 * 60 * 60 * 8;
     // Lose some temporary blacklisting after 30 minutes
@@ -274,6 +277,8 @@
 
         WifiConfiguration currentNetwork = mWifiStateMachine.getCurrentWifiConfiguration();
         if (currentNetwork == null) {
+            // Return any absurdly high score, if we are not connected there is no current
+            // network to...
            return 1000;
         }
 
@@ -281,7 +286,7 @@
             return -2;
         }
 
-        int order = compareWifiConfigurations(currentNetwork, candidate);
+        int order = compareWifiConfigurationsTop(currentNetwork, candidate);
         return order;
     }
 
@@ -525,9 +530,9 @@
         // Apply Hysteresis, boost RSSI of current configuration
         if (null != currentConfiguration) {
             if (a.configKey().equals(currentConfiguration)) {
-                aRssiBoost += 15;
+                aRssiBoost += 20;
             } else if (b.configKey().equals(currentConfiguration)) {
-                bRssiBoost += 15;
+                bRssiBoost += 20;
             }
         }
 
@@ -588,9 +593,9 @@
         // looking up the score
         if (null != mCurrentConfigurationKey) {
             if (a.configKey().equals(mCurrentConfigurationKey)) {
-                aRssiBoost += 15;
+                aRssiBoost += 20;
             } else if (b.configKey().equals(mCurrentConfigurationKey)) {
-                bRssiBoost += 15;
+                bRssiBoost += 20;
             }
         }
         int scoreA = getConfigNetworkScore(a, 3000, aRssiBoost);
@@ -728,9 +733,29 @@
         return (rssi5 < -80 && rssi24 < -90);
     }
 
-    /**
-     * attemptRoam function implements the core of the same SSID switching algorithm
-     */
+    int compareWifiConfigurationsTop(WifiConfiguration a, WifiConfiguration b) {
+        int scorerOrder = compareWifiConfigurationsWithScorer(a, b);
+        int order = compareWifiConfigurations(a, b);
+
+        if (scorerOrder * order < 0) {
+            // For debugging purpose, remember that an override happened
+            // during that autojoin Attempt
+            didOverride = true;
+            a.numScorerOverride++;
+            b.numScorerOverride++;
+        }
+
+        if (scorerOrder != 0) {
+            // If the scorer came up with a result then use the scorer's result, else use
+            // the order provided by the base comparison function
+            order = scorerOrder;
+        }
+        return order;
+    }
+
+        /**
+         * attemptRoam function implements the core of the same SSID switching algorithm
+         */
     ScanResult attemptRoam(WifiConfiguration current, int age) {
         ScanResult a = null;
         if (current == null) {
@@ -871,6 +896,7 @@
      *
      * @param config
      * @return score
+     * @return score
      */
     int getConfigNetworkScore(WifiConfiguration config, int age, int rssiBoost) {
 
@@ -898,7 +924,7 @@
      * attemptAutoJoin() function implements the core of the a network switching algorithm
      */
     void attemptAutoJoin() {
-        boolean didOverride = false;
+        didOverride = false;
         int networkSwitchType = AUTO_JOIN_IDLE;
 
         String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
@@ -1083,8 +1109,8 @@
 
             if (lastSelectedConfiguration == null ||
                     !config.configKey().equals(lastSelectedConfiguration)) {
-                // Don't try to autojoin a network that is too far
-                // If that configuration is
+                // Don't try to autojoin a network that is too far but
+                // If that configuration is a user's choice however, try anyway
                 if (config.visibility == null) {
                     continue;
                 }
@@ -1117,26 +1143,10 @@
                     logDbg("attemptAutoJoin will compare candidate  " + candidate.configKey()
                             + " with " + config.configKey());
                 }
-
-                int scorerOrder = compareWifiConfigurationsWithScorer(candidate, config);
-                int order = compareWifiConfigurations(candidate, config);
-
-                if (scorerOrder * order < 0) {
-                    // For debugging purpose, remember that an override happened
-                    // during that autojoin Attempt
-                    didOverride = true;
-                    candidate.numScorerOverride++;
-                    config.numScorerOverride++;
-                }
-
-                if (scorerOrder != 0) {
-                    // If the scorer came up with a result then use the scorer's result, else use
-                    // the order provided by the base comparison function
-                    order = scorerOrder;
-                }
+                int order = compareWifiConfigurationsTop(candidate, config);
 
                 // The lastSelectedConfiguration is the configuration the user has manually selected
-                // thru thru WifiPicker, or that a 3rd party app asked us to connect to via the
+                // thru WifiPicker, or that a 3rd party app asked us to connect to via the
                 // enableNetwork with disableOthers=true WifiManager API
                 // As this is a direct user choice, we strongly prefer this configuration,
                 // hence give +/-100
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index 69808a0..97aad0f 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -44,6 +44,7 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyStore;
@@ -222,6 +223,18 @@
             WifiEnterpriseConfig.ENGINE_KEY, WifiEnterpriseConfig.ENGINE_ID_KEY,
             WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY };
 
+
+    /**
+     * The maximum number of times we will retry a connection to an access point
+     * for which we have failed in acquiring an IP address from DHCP. A value of
+     * N means that we will make N+1 connection attempts in all.
+     * <p>
+     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+     * value if a Settings value is not present.
+     */
+    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+
     private final LocalLog mLocalLog;
     private final WpaConfigFileObserver mFileObserver;
 
@@ -390,7 +403,7 @@
                 result.averageRssi(previousRssi, previousSeen,
                         WifiAutoJoinController.mScanResultMaximumAge);
                 if (VDBG) {
-                    loge("updateConfiguration freq=" + result.level
+                    loge("updateConfiguration freq=" + result.frequency
                         + " BSSID=" + result.BSSID
                         + " RSSI=" + result.level
                         + " " + config.configKey());
@@ -2820,6 +2833,12 @@
         return found;
     }
 
+    int getMaxDhcpRetries() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
+                DEFAULT_MAX_DHCP_RETRIES);
+    }
+
     void handleSSIDStateChange(int netId, boolean enabled, String message) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
         if (config != null) {
@@ -2880,6 +2899,25 @@
                                 loge("blacklisted " + config.configKey() + " to "
                                         + Integer.toString(config.autoJoinStatus));
                             }
+                        } else if (message.contains("DHCP FAILURE")) {
+                            config.numConnectionFailures++;
+                            config.lastConnectionFailure = System.currentTimeMillis();
+                            int maxRetries = getMaxDhcpRetries();
+                            // maxRetries == 0 means keep trying forever
+                            if (maxRetries > 0 && config.numConnectionFailures > maxRetries) {
+                                /**
+                                 * If we've exceeded the maximum number of retries for DHCP
+                                 * to a given network, disable the network
+                                 */
+                                config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
+                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
+                            }
+                            if (DBG) {
+                                loge("blacklisted " + config.configKey() + " to "
+                                        + config.autoJoinStatus
+                                        + " due to DHCP failure, count="
+                                        + config.numConnectionFailures);
+                            }
                         }
                         message.replace("\n", "");
                         message.replace("\r", "");
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 2a97b98..21b7bc9 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -185,7 +185,6 @@
     private boolean mEnableRssiPolling = false;
     private boolean mEnableBackgroundScan = false;
     private int mRssiPollToken = 0;
-    private int mReconnectCount = 0;
     /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
     * In CONNECT_MODE, the STA can scan and connect to an access point
     * In SCAN_ONLY_MODE, the STA can only scan for access points
@@ -523,16 +522,6 @@
     private static final int SUCCESS = 1;
     private static final int FAILURE = -1;
 
-    /**
-     * The maximum number of times we will retry a connection to an access point
-     * for which we have failed in acquiring an IP address from DHCP. A value of
-     * N means that we will make N+1 connection attempts in all.
-     * <p>
-     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
-     * value if a Settings value is not present.
-     */
-    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
     /* Tracks if suspend optimizations need to be disabled by DHCP,
      * screen or due to high perf mode.
      * When any of them needs to disable it, we keep the suspend optimizations
@@ -2053,7 +2042,6 @@
         pw.println("mLastSignalLevel " + mLastSignalLevel);
         pw.println("mLastBssid " + mLastBssid);
         pw.println("mLastNetworkId " + mLastNetworkId);
-        pw.println("mReconnectCount " + mReconnectCount);
         pw.println("mOperationalMode " + mOperationalMode);
         pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
         pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
@@ -2285,6 +2273,19 @@
                     }
                 }
                 break;
+            case CMD_IP_CONFIGURATION_LOST:
+                int count = -1;
+                WifiConfiguration c = getCurrentWifiConfiguration();
+                if (c != null) count = c.numConnectionFailures;
+                sb.append(" ");
+                sb.append(Integer.toString(msg.arg1));
+                sb.append(" ");
+                sb.append(Integer.toString(msg.arg2));
+                sb.append(" failures: ");
+                sb.append(Integer.toString(count));
+                sb.append("/");
+                sb.append(Integer.toString(mWifiConfigStore.getMaxDhcpRetries()));
+                break;
             default:
                 sb.append(" ");
                 sb.append(Integer.toString(msg.arg1));
@@ -3001,7 +3002,7 @@
      * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
      * - HTTP proxy: the wifi config store.
      */
-    private void updateLinkProperties() {
+    private void updateLinkProperties(boolean failure) {
         LinkProperties newLp = new LinkProperties();
 
         // Interface name and proxy are locally configured.
@@ -3048,6 +3049,38 @@
             if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
         }
 
+        if (DBG) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("updateLinkProperties nid: " + mLastNetworkId);
+            sb.append(" state: " + detailedState);
+            if (failure) sb.append(" failure");
+
+            if (mLinkProperties != null) {
+                if (mLinkProperties.hasIPv4Address()) {
+                    sb.append(" v4");
+                }
+                if (mLinkProperties.hasGlobalIPv6Address()) {
+                    sb.append(" v6");
+                }
+                if (mLinkProperties.hasIPv4DefaultRoute()) {
+                    sb.append(" v4r");
+                }
+                if (mLinkProperties.hasIPv6DefaultRoute()) {
+                    sb.append(" v6r");
+                }
+                if (mLinkProperties.hasIPv4DnsServer()) {
+                    sb.append(" v4dns");
+                }
+                if (mLinkProperties.hasIPv6DnsServer()) {
+                    sb.append(" v6dns");
+                }
+                if (mLinkProperties.isProvisioned()) {
+                    sb.append(" isprov");
+                }
+            }
+            loge(sb.toString());
+        }
+
         // If we just configured or lost IP configuration, do the needful.
         // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
         // here because those should only be called if we're attempting to connect or already
@@ -3055,10 +3088,10 @@
         // Also, when roaming we don't pass through a not-provisioned state but
         // still need to realize we have an IP_CONFIGURATION_SUCCESSFUL.
         if (isProvisioned &&
-                (!wasProvisioned || detailedState == DetailedState.OBTAINING_IPADDR)) {
+                (!wasProvisioned || getCurrentState() == mObtainingIpState)) {
             sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
         } else if (!isProvisioned && (wasProvisioned
-                || detailedState == DetailedState.OBTAINING_IPADDR)) {
+                || failure && getCurrentState() == mObtainingIpState)) {
             sendMessage(CMD_IP_CONFIGURATION_LOST);
         } else if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
             // If anything has changed, and we're already connected, send out a notification.
@@ -3134,12 +3167,6 @@
           return address;
       }
 
-    private int getMaxDhcpRetries() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                                      Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
-                                      DEFAULT_MAX_DHCP_RETRIES);
-    }
-
     private void sendScanResultsAvailableBroadcast() {
         noteScanEnd();
         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
@@ -3387,7 +3414,7 @@
     private void handleIPv4Success(DhcpResults dhcpResults) {
 
         if (PDBG) {
-            loge("handleIPv4Success <" + dhcpResults.toString()
+            loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString()
                     + "> linkaddress num " + dhcpResults.linkProperties.getLinkAddresses().size());
             for (LinkAddress linkAddress : dhcpResults.linkProperties.getLinkAddresses()) {
                 loge("link address " + linkAddress.toString());
@@ -3424,12 +3451,16 @@
         }
         mWifiInfo.setInetAddress(addr);
         mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
-        updateLinkProperties();
+        updateLinkProperties(false);
     }
 
     private void handleSuccessfulIpConfiguration() {
         mLastSignalLevel = -1; // Force update of signal strength
-        mReconnectCount = 0; // Reset IP failure tracking
+        WifiConfiguration c = getCurrentWifiConfiguration();
+        // Reset IP failure tracking
+        if (c != null) {
+            c.numConnectionFailures = 0;
+        }
     }
 
     private void handleIPv4Failure() {
@@ -3438,31 +3469,22 @@
                  mDhcpResults.linkProperties.clear();
              }
         }
-        updateLinkProperties();
+        if (PDBG) {
+            loge("wifistatemachine handleIPv4Failure");
+        }
+        updateLinkProperties(true);
     }
 
     private void handleIpConfigurationLost() {
         mWifiInfo.setInetAddress(null);
         mWifiInfo.setMeteredHint(false);
-        /**
-         * If we've exceeded the maximum number of retries for DHCP
-         * to a given network, disable the network
-         */
-        int maxRetries = getMaxDhcpRetries();
-        // maxRetries == 0 means keep trying forever
-        if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
-            loge("Failed " +
-                    mReconnectCount + " times, Disabling " + mLastNetworkId);
-            mWifiConfigStore.disableNetwork(mLastNetworkId,
-                    WifiConfiguration.DISABLED_DHCP_FAILURE);
-            mReconnectCount = 0;
-        }
+
+        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false, "DHCP FAILURE");
 
         /* DHCP times out after about 30 seconds, we do a
-         * disconnect and an immediate reconnect to try again
+         * disconnect thru supplicant, we will let autojoin retry connecting to the network
          */
         mWifiNative.disconnect();
-        mWifiNative.reconnect();
     }
 
     /* Current design is to not set the config on a running hostapd but instead
@@ -3758,7 +3780,7 @@
                     break;
                 /* Link configuration (IP address, DNS, ...) changes notified via netlink */
                 case CMD_NETLINK_UPDATE:
-                    updateLinkProperties();
+                    updateLinkProperties(false);
                     break;
                 case CMD_IP_CONFIGURATION_SUCCESSFUL:
                 case CMD_IP_CONFIGURATION_LOST:
@@ -4881,6 +4903,12 @@
             case WifiManager.WPS_COMPLETED:
                 s = "WPS_COMPLETED";
                 break;
+            case CMD_IP_CONFIGURATION_LOST:
+                s = "CMD_IP_CONFIGURATION_LOST";
+                break;
+            case CMD_IP_CONFIGURATION_SUCCESSFUL:
+                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
+                break;
             default:
                 s = "what:" + Integer.toString(message.what);
                 break;
@@ -5375,7 +5403,7 @@
         public void exit() {
             // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
             // Bug: 15347363
-            // For paranoia's sake, call handleNEtworkDisconnect only if BSSID is null or last networkId
+            // For paranoia's sake, call handleNetworkDisconnect only if BSSID is null or last networkId
             // is not invalid.
             if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
                 handleNetworkDisconnect();
@@ -5393,12 +5421,19 @@
               case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                   handlePostDhcpSetup();
                   if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
-                      if (DBG) log("DHCP successful");
+                      if (DBG) log("WifiStateMachine DHCP successful");
                       handleIPv4Success((DhcpResults) message.obj);
                       // We advance to mVerifyingLinkState because handleIPv4Success will call
                       // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
                   } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
-                      if (DBG) log("DHCP failed");
+                      if (DBG) {
+                          int count = -1;
+                          WifiConfiguration config = getCurrentWifiConfiguration();
+                          if (config != null) {
+                              count = config.numConnectionFailures;
+                          }
+                          log("WifiStateMachine DHCP failure count=" + count);
+                      }
                       handleIPv4Failure();
                       // As above, we transition to mDisconnectingState via updateLinkProperties.
                   }
@@ -5563,7 +5598,7 @@
                         }
                         if (result.hasProxyChanged()) {
                             log("Reconfiguring proxy on connection");
-                            updateLinkProperties();
+                            updateLinkProperties(false);
                         }
                     }