rework scan logic so as to make use of delayed messages instead of scan alarm

Bug:18594119
Change-Id: I0d49fa2908d824de6adb125cf862de47f8ba5e91
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index a971dcd..f5f1eee 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -158,6 +158,7 @@
         int numScanResultsKnown = 0; // Record number of scan results we knew about
         WifiConfiguration associatedConfig = null;
         boolean didAssociate = false;
+        long now = System.currentTimeMillis();
 
         ArrayList<NetworkKey> unknownScanResults = new ArrayList<NetworkKey>();
 
@@ -224,7 +225,11 @@
                 if (associatedConfig != null && associatedConfig.SSID != null) {
                     if (VDBG) {
                         logDbg("addToScanCache save associated config "
-                                + associatedConfig.SSID + " with " + result.SSID);
+                                + associatedConfig.SSID + " with " + result.SSID
+                                + " status " + associatedConfig.autoJoinStatus
+                                + " reason " + associatedConfig.disableReason
+                                + " tsp " + associatedConfig.blackListTimestamp
+                                + " was " + (now - associatedConfig.blackListTimestamp));
                     }
                     mWifiStateMachine.sendMessage(
                             WifiStateMachine.CMD_AUTO_SAVE_NETWORK, associatedConfig);
@@ -232,7 +237,6 @@
                 }
             } else {
                 // If the scan result has been blacklisted fir 18 hours -> unblacklist
-                long now = System.currentTimeMillis();
                 if ((now - result.blackListTimestamp) > loseBlackListHardMilli) {
                     result.setAutoJoinStatus(ScanResult.ENABLED);
                 }
@@ -1129,6 +1133,8 @@
         didBailDueToWeakRssi = false;
         int networkSwitchType = AUTO_JOIN_IDLE;
 
+        long now = System.currentTimeMillis();
+
         String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
 
         // Reset the currentConfiguration Key, and set it only if WifiStateMachine and
@@ -1266,14 +1272,13 @@
                     logDbg("attemptAutoJoin skip candidate due to auto join status "
                             + Integer.toString(config.autoJoinStatus) + " key "
                             + config.configKey(true)
-                    + " reason " + config.disableReason);
+                            + " reason " + config.disableReason);
                 }
                 continue;
             }
 
             // Try to un-blacklist based on elapsed time
             if (config.blackListTimestamp > 0) {
-                long now = System.currentTimeMillis();
                 if (now < config.blackListTimestamp) {
                     /**
                      * looks like there was a change in the system clock since we black listed, and
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index c7b8ab3..0de9cf4 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -730,7 +730,7 @@
                     && (config.autoJoinStatus
                     <= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
 
-                // Wait for 20 minutes before reenabling config that have known, repeated connection
+                // Wait for 5 minutes before reenabling config that have known, repeated connection
                 // or DHCP failures
                 if (config.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE
                         || config.disableReason == WifiConfiguration.DISABLED_ASSOCIATION_REJECT
@@ -3066,8 +3066,9 @@
                 // Try to make a non verified WifiConfiguration, but only if the original
                 // configuration was not self already added
                 if (VDBG) {
-                    loge("associateWithConfiguration: will create " +
-                            result.SSID + " and associate it with: " + link.SSID);
+                    loge("associateWithConfiguration: try to create " +
+                            result.SSID + " and associate it with: " + link.SSID
+                            + " key " + link.configKey());
                 }
                 config = wifiConfigurationFromScanResult(result);
                 if (config != null) {
@@ -3077,6 +3078,10 @@
                     config.peerWifiConfiguration = link.configKey();
                     if (config.allowedKeyManagement.equals(link.allowedKeyManagement) &&
                             config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+                        if (VDBG && config != null) {
+                            loge("associateWithConfiguration: got a config from beacon"
+                                    + config.SSID + " key " + config.configKey());
+                        }
                         // Transfer the credentials from the configuration we are linking from
                         String psk = readNetworkVariableFromSupplicantFile(link.SSID, "psk");
                         if (psk != null) {
@@ -3108,6 +3113,10 @@
                     }
                     if (config != null) break;
                 }
+                if (VDBG && config != null) {
+                    loge("associateWithConfiguration: success, created: " + config.SSID
+                            + " key " + config.configKey());
+                }
             }
         }
         return config;
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 09920f5..9e283f6 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -862,6 +862,8 @@
     // Used for debug and stats gathering
     private static int sScanAlarmIntentCount = 0;
 
+    final static int frameworkMinScanIntervalSaneValue = 10000;
+
     public WifiStateMachine(Context context, String wlanInterface,
             WifiTrafficPoller trafficPoller){
         super("WifiStateMachine");
@@ -910,9 +912,13 @@
         mScanIntent = getPrivateBroadcast(ACTION_START_SCAN, SCAN_REQUEST);
         mBatchedScanIntervalIntent = getPrivateBroadcast(ACTION_REFRESH_BATCHED_SCAN, 0);
 
-        mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
+        // Make sure the interval is not configured less than 10 seconds
+        int period = mContext.getResources().getInteger(
                 R.integer.config_wifi_framework_scan_interval);
-
+        if (period < frameworkMinScanIntervalSaneValue) {
+            period = frameworkMinScanIntervalSaneValue;
+        }
+        mDefaultFrameworkScanIntervalMs = period;
         mDriverStopDelayMs = mContext.getResources().getInteger(
                 R.integer.config_wifi_driver_stop_delay);
 
@@ -949,10 +955,10 @@
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        sScanAlarmIntentCount++;
-                        startScan(SCAN_ALARM_SOURCE, -2, null, null);
+                        sScanAlarmIntentCount++; // Used for debug only
+                        startScan(SCAN_ALARM_SOURCE, mDelayedScanCounter.incrementAndGet(), null, null);
                         if (VDBG)
-                            loge("WiFiStateMachine SCAN ALARM");
+                            loge("WiFiStateMachine SCAN ALARM -> " + mDelayedScanCounter.get());
                     }
                 },
                 new IntentFilter(ACTION_START_SCAN));
@@ -1123,35 +1129,27 @@
      */
     private long mFrameworkScanIntervalMs = 10000;
 
-    private long mCurrentScanAlarmMs = 10000;
-    private void setScanAlarm(boolean enabled, int delayMilli) {
+    private AtomicInteger mDelayedScanCounter = new AtomicInteger();
+
+    private void setScanAlarm(boolean enabled) {
         if (PDBG) {
             loge("setScanAlarm " + enabled
-                    + " period " + mCurrentScanAlarmMs
-                    + " initial delay " + delayMilli);
+                    + " period " + mDefaultFrameworkScanIntervalMs
+                    + " mBackgroundScanSupported " + mBackgroundScanSupported);
         }
-        if (mCurrentScanAlarmMs <= 0) enabled = false;
+        if (mBackgroundScanSupported == false) {
+            // Scan alarm is only used for background scans if they are not
+            // offloaded to the wifi chipset, hence enable the scan alarm
+            // gicing us RTC_WAKEUP of backgroundScan is NOT supported
+            enabled = true;
+        }
+
         if (enabled == mAlarmEnabled) return;
         if (enabled) {
-            long initialDelayMilli;
-            if (delayMilli <= 0) {
-                // scan now
-                startScan(SCAN_ALARM_SOURCE, 0, null, null);
-                initialDelayMilli = mCurrentScanAlarmMs;
-            } else {
-                initialDelayMilli = delayMilli;
-            }
-
-            int type = AlarmManager.RTC;
-
             /* Set RTC_WAKEUP alarms if PNO is not supported - because no one is */
             /* going to wake up the host processor to look for access points */
-            if (mBackgroundScanSupported == false)
-                type = AlarmManager.RTC_WAKEUP;
-
-            mAlarmManager.setRepeating(type,
-                    System.currentTimeMillis() + initialDelayMilli,
-                    mCurrentScanAlarmMs,
+            mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+                    System.currentTimeMillis() + mDefaultFrameworkScanIntervalMs,
                     mScanIntent);
             mAlarmEnabled = true;
         } else {
@@ -1160,6 +1158,53 @@
         }
     }
 
+    private void cancelDelayedScan() {
+        mDelayedScanCounter.incrementAndGet();
+        loge("cancelDelayedScan -> " + mDelayedScanCounter);
+    }
+
+    private boolean checkAndRestartDelayedScan(int counter, boolean restart, int milli,
+                                   ScanSettings settings, WorkSource workSource) {
+        if (counter != mDelayedScanCounter.get()) {
+            return false;
+        }
+        if (restart)
+            startDelayedScan(milli, settings, workSource);
+        return true;
+    }
+
+    private void startDelayedScan(int milli, ScanSettings settings, WorkSource workSource) {
+        if (milli <= 0) return;
+        /**
+         * The cases where the scan alarm should be run are :
+         * - DisconnectedState && screenOn => used delayed timer
+         * - DisconnectedState && !screenOn && mBackgroundScanSupported => PNO
+         * - DisconnectedState && !screenOn && !mBackgroundScanSupported => used RTC_WAKEUP Alarm
+         * - ConnectedState && screenOn => used delayed timer
+         */
+
+        mDelayedScanCounter.incrementAndGet();
+        if (mScreenOn &&
+                (getCurrentState() == mDisconnectedState
+                || getCurrentState() == mConnectedState)) {
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
+            bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
+            bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
+            sendMessageDelayed(CMD_START_SCAN, SCAN_ALARM_SOURCE,
+                    mDelayedScanCounter.get(), bundle, milli);
+            if (DBG) loge("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
+        } else if (mBackgroundScanSupported == false
+                && !mScreenOn && getCurrentState() == mDisconnectedState) {
+            setScanAlarm(true);
+            if (DBG) loge("startDelayedScan start scan alarm -> "
+                    + mDelayedScanCounter + " milli " + milli);
+        } else {
+            if (DBG) loge("startDelayedScan unhandled -> "
+                    + mDelayedScanCounter + " milli " + milli);
+        }
+    }
+
     private boolean setRandomMacOui() {
         String oui = mContext.getResources().getString(
                 R.string.config_wifi_random_mac_oui, GOOGLE_OUI);
@@ -1592,7 +1637,7 @@
     private static int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
     private static int MESSAGE_HANDLING_STATUS_REFUSED = -1;
     private static int MESSAGE_HANDLING_STATUS_FAIL = -2;
-    private static int MESSAGE_HANDLING_STATUS_BUFFERED = -3;
+    private static int MESSAGE_HANDLING_STATUS_OBSOLETE = -3;
     private static int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
     private static int MESSAGE_HANDLING_STATUS_DISCARD = -5;
     private static int MESSAGE_HANDLING_STATUS_LOOPED = -6;
@@ -1602,9 +1647,11 @@
 
     //TODO: this is used only to track connection attempts, however the link state and packet per
     //TODO: second logic should be folded into that
-    private boolean isScanAllowed() {
+    private boolean checkOrDeferScanAllowed(Message msg) {
         long now = System.currentTimeMillis();
         if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) {
+            Message dmsg = Message.obtain(msg);
+            sendMessageDelayed(dmsg, 11000 - (now - lastConnectAttempt));
             return false;
         }
         return true;
@@ -1667,7 +1714,6 @@
                 mRxTime = stats.rx_time;
                 mRunningBeaconCount = stats.beacon_rx;
                 if (dbg) {
-                    loge("WifiLinkLayerStats:");
                     loge(stats.toString());
                 }
             }
@@ -1717,7 +1763,8 @@
                 loge(ts + " noteScanStart" + workSource.toString()
                         + " uid " + Integer.toString(callingUid));
             } else {
-                loge(ts + " noteScanstart no scan source");
+                loge(ts + " noteScanstart no scan source"
+                        + " uid " + Integer.toString(callingUid));
             }
         }
         startRadioScanStats();
@@ -1742,9 +1789,11 @@
         if (DBG) {
             String ts = String.format("[%,d ms]", now);
             if (mScanWorkSource != null)
-                loge(ts + " noteScanEnd " + mScanWorkSource.toString());
+                loge(ts + " noteScanEnd " + mScanWorkSource.toString()
+                        + " onTime=" + mOnTimeThisScan);
             else
-                loge(ts + " noteScanEnd no scan source");
+                loge(ts + " noteScanEnd no scan source"
+                        + " onTime=" + mOnTimeThisScan);
         }
         if (mScanWorkSource != null) {
             try {
@@ -2412,6 +2461,7 @@
         WifiConfiguration config;
         Long now;
         String report;
+        String key;
         StringBuilder sb = new StringBuilder();
         if (mScreenOn) {
             sb.append("!");
@@ -2448,6 +2498,7 @@
                 if (lastScanDuration != 0) {
                     sb.append(" dur:").append(lastScanDuration);
                 }
+                sb.append(" cnt=").append(mDelayedScanCounter);
                 sb.append(" rssi=").append(mWifiInfo.getRssi());
                 sb.append(" f=").append(mWifiInfo.getFrequency());
                 sb.append(" sc=").append(mWifiInfo.score);
@@ -2568,6 +2619,11 @@
                     sb.append(",").append(mRxTime);
                 }
                 sb.append(String.format(" bcn=%d", mRunningBeaconCount));
+                sb.append(String.format(" con=%d", mConnectionRequests));
+                key = mWifiConfigStore.getLastSelectedConfiguration();
+                if (key != null) {
+                    sb.append(" last=").append(key);
+                }
                 break;
             case WifiMonitor.NETWORK_CONNECTION_EVENT:
                 sb.append(" ");
@@ -2581,6 +2637,10 @@
                     sb.append(" ").append(config.configKey());
                 }
                 sb.append(printTime());
+                key = mWifiConfigStore.getLastSelectedConfiguration();
+                if (key != null) {
+                    sb.append(" last=").append(key);
+                }
                 break;
             case CMD_TARGET_BSSID:
             case CMD_ASSOCIATED_BSSID:
@@ -2764,7 +2824,7 @@
                 sb.append(Integer.toString(msg.arg1));
                 sb.append(" ");
                 sb.append(Integer.toString(msg.arg2));
-                String key = mWifiConfigStore.getLastSelectedConfiguration();
+                key = mWifiConfigStore.getLastSelectedConfiguration();
                 if (key != null) {
                     sb.append(" last=").append(key);
                 }
@@ -2934,7 +2994,6 @@
         mScreenOn = screenOn;
         if (PDBG) {
             loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
-                    + " mCurrentScanAlarmMs = " + Long.toString(mCurrentScanAlarmMs)
                     + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
                     + " state " + getCurrentState().getName()
                     + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
@@ -2955,38 +3014,41 @@
         getWifiLinkLayerStats(false);
         mOnTimeScreenStateChange = mOnTime;
         lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
+        mEnableBackgroundScan = false;
+        cancelDelayedScan();
 
         if (screenOn) {
+            setScanAlarm(false);
             clearBlacklist();
 
             fullBandConnectedTimeIntervalMilli = mWifiConfigStore.associatedPartialScanPeriodMilli;
-            // Start the scan alarm so as to enable autojoin
+            // In either Disconnectedstate or ConnectedState,
+            // start the scan alarm so as to enable autojoin
             if (getCurrentState() == mConnectedState
                     && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
-                mCurrentScanAlarmMs = mWifiConfigStore.associatedPartialScanPeriodMilli;
-                // Scan after 200ms
-                setScanAlarm(true, 200);
+                // Scan after 500ms
+                startDelayedScan(500, null, null);
             } else if (getCurrentState() == mDisconnectedState) {
-                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
                 // Scan after 200ms
-                setScanAlarm(true, 200);
+                startDelayedScan(200, null, null);
             }
-        } else {
-            setScanAlarm(false, 0);
+        } else if (startBackgroundScanIfNeeded) {
+            // Screen Off and Disconnected and chipset doesn't support scan offload
+            //              => start scan alarm
+            // Screen Off and Disconnected and chipset does support scan offload
+            //              => will use scan offload (i.e. background scan)
+            if (!mBackgroundScanSupported) {
+                setScanAlarm(true);
+            } else {
+                mEnableBackgroundScan = true;
+            }
         }
-
-        if (mBackgroundScanSupported) {
-            mEnableBackgroundScan = (screenOn == false);
-        }
-
         if (DBG) logd("backgroundScan enabled=" + mEnableBackgroundScan
                 + " startBackgroundScanIfNeeded:" + startBackgroundScanIfNeeded);
-
         if (startBackgroundScanIfNeeded) {
             // to scan for them in background, we need all networks enabled
             enableBackgroundScan(mEnableBackgroundScan);
         }
-
         if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
     }
 
@@ -3381,10 +3443,15 @@
             attemptAutoJoin = false;
         }
         if (DBG) {
+            String selection = mWifiConfigStore.getLastSelectedConfiguration();
+            if (selection == null) {
+                selection = "<none>";
+            }
             loge("wifi setScanResults state" + getCurrentState()
                     + " sup_state=" + state
                     + " debouncing=" + linkDebouncing
-                    + " mConnectionRequests=" + mConnectionRequests);
+                    + " mConnectionRequests=" + mConnectionRequests
+                    + " selection=" + selection);
         }
         if (attemptAutoJoin) {
             messageHandlingStatus = MESSAGE_HANDLING_STATUS_PROCESSED;
@@ -4220,7 +4287,14 @@
         linkDebouncing = false;
         /* Reset roaming parameters */
         mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
-        fullBandConnectedTimeIntervalMilli = 20 * 1000; // Start scans at 20 seconds interval
+
+        /**
+         *  fullBandConnectedTimeIntervalMilli:
+         *  - start scans at mWifiConfigStore.associatedPartialScanPeriodMilli seconds interval
+         *  - exponentially increase to mWifiConfigStore.associatedFullScanMaxIntervalMilli
+         *  Initialize to sane value = 20 seconds
+         */
+        fullBandConnectedTimeIntervalMilli = 20 * 1000;
 
         setNetworkDetailedState(DetailedState.DISCONNECTED);
         if (mNetworkAgent != null) {
@@ -5227,7 +5301,7 @@
         public void enter() {
 
             if (PDBG) {
-                loge("Driverstarted State enter");
+                loge("DriverStartedState enter");
             }
             mIsRunning = true;
             mInDelayedStop = false;
@@ -6877,6 +6951,8 @@
                         if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
                             noteWifiDisabledWhileAssociated();
                         }
+                        mWifiConfigStore.
+                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
                     }
                     break;
                 case CMD_SET_COUNTRY_CODE:
@@ -6892,6 +6968,20 @@
                               + " RSSI=" + mWifiInfo.getRssi());
                     //}
                     if (message.arg1 == SCAN_ALARM_SOURCE) {
+                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
+                        // not be processed) and restart the scan if needed
+                        boolean shouldScan =
+                                mScreenOn && mWifiConfigStore.enableAutoJoinScanWhenAssociated;
+                        if (!checkAndRestartDelayedScan(message.arg2,
+                                shouldScan,
+                                mWifiConfigStore.associatedPartialScanPeriodMilli, null, null)) {
+                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
+                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
+                                    + message.arg1
+                                    + " " + message.arg2 + ", " + mDelayedScanCounter
+                                    + " -> obsolete");
+                            return HANDLED;
+                        }
                         boolean tryFullBandScan = false;
                         boolean restrictChannelList = false;
                         long now_ms = System.currentTimeMillis();
@@ -6997,13 +7087,14 @@
                                                 WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
                                 }
                             }
+
                         } else {
                             loge("CMD_START_SCAN : connected mode and no configuration");
                             messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
                         }
                     } else {
                         // Not scan alarm source
-                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
+                        return NOT_HANDLED;
                     }
                     break;
                     /* Ignore connection to same network */
@@ -7147,6 +7238,8 @@
                 }
                 obtainingIpWatchdogCount++;
                 loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
+                // Get Link layer stats so as we get fresh tx packet counters
+                getWifiLinkLayerStats(true);
                 sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
                         obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
             } else {
@@ -7283,7 +7376,7 @@
                 log("RoamingState Enter"
                         + " mScreenOn=" + mScreenOn );
             }
-            setScanAlarm(false, 0);
+            setScanAlarm(false);
 
             // Make sure we disconnect if roaming fails
             roamWatchdogCount++;
@@ -7414,11 +7507,8 @@
             }
             if (mScreenOn
                     && mWifiConfigStore.enableAutoJoinScanWhenAssociated) {
-                mCurrentScanAlarmMs = mWifiConfigStore.associatedPartialScanPeriodMilli;
-                // Scan after 200ms
-                setScanAlarm(true, 200);
-            } else {
-                mCurrentScanAlarmMs = 0;
+                // restart scan alarm
+                startDelayedScan(mWifiConfigStore.associatedPartialScanPeriodMilli, null, null);
             }
             registerConnected();
             lastConnectAttempt = 0;
@@ -7619,7 +7709,7 @@
         @Override
         public void exit() {
             loge("WifiStateMachine: Leaving Connected state");
-            setScanAlarm(false, 0);
+            setScanAlarm(false);
             mLastDriverRoamAttempt = 0;
         }
     }
@@ -7628,7 +7718,6 @@
 
         @Override
         public void enter() {
-            mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
 
             if (PDBG) {
                 loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
@@ -7658,7 +7747,7 @@
                     }
                     break;
                 case CMD_START_SCAN:
-                    // Ignore scans while disconnecting
+                    deferMessage(message);
                     return HANDLED;
                 case CMD_DISCONNECTING_WATCHDOG_TIMER:
                     if (disconnectingWatchdogCount == message.arg1) {
@@ -7682,11 +7771,6 @@
             }
             return HANDLED;
         }
-
-        @Override
-        public void exit() {
-            mCurrentScanAlarmMs = 0;
-        }
     }
 
     class DisconnectedState extends State {
@@ -7699,17 +7783,10 @@
                 return;
             }
 
-            // Loose the last selection choice
-            // mWifiAutoJoinController.setLastSelectedConfiguration
-            // (WifiConfiguration.INVALID_NETWORK_ID);
-
             mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                     Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
                     mDefaultFrameworkScanIntervalMs);
 
-            if (mScreenOn)
-                mCurrentScanAlarmMs = mDisconnectedScanPeriodMs;
-
             if (PDBG) {
                 loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
                         + " mEnableBackgroundScan= " + mEnableBackgroundScan
@@ -7720,23 +7797,28 @@
             /** clear the roaming state, if we were roaming, we failed */
             mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
 
-            /**
-             * - screen dark and PNO supported => scan alarm disabled
-             * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period
-             */
-            if ((mScreenOn == false) && mEnableBackgroundScan) {
-
-                /* If a regular scan result is pending, do not initiate background
-                 * scan until the scan results are returned. This is needed because
-                 * initiating a background scan will cancel the regular scan and
-                 * scan results will not be returned until background scanning is
-                 * cleared
+            if (mScreenOn) {
+                /**
+                 * screen lit and => delayed timer
                  */
-                if (!mIsScanOngoing) {
-                    enableBackgroundScan(true);
-                }
+                startDelayedScan(mDisconnectedScanPeriodMs, null, null);
             } else {
-                setScanAlarm(true, 200);
+                /**
+                 * screen dark and PNO supported => scan alarm disabled
+                 */
+                if (mEnableBackgroundScan) {
+                    /* If a regular scan result is pending, do not initiate background
+                     * scan until the scan results are returned. This is needed because
+                     * initiating a background scan will cancel the regular scan and
+                     * scan results will not be returned until background scanning is
+                     * cleared
+                     */
+                    if (!mIsScanOngoing) {
+                        enableBackgroundScan(true);
+                    }
+                } else {
+                    setScanAlarm(true);
+                }
             }
 
             /**
@@ -7805,16 +7887,32 @@
                     ret = NOT_HANDLED;
                     break;
                 case CMD_START_SCAN:
-                    if (!isScanAllowed()) {
-                        // Ignore the scan request
+                    if (!checkOrDeferScanAllowed(message)) {
+                        // The scan request was rescheduled
+                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
                         return HANDLED;
                     }
                     /* Disable background scan temporarily during a regular scan */
                     if (mEnableBackgroundScan) {
                         enableBackgroundScan(false);
                     }
-                    /* Handled in parent state */
-                    ret = NOT_HANDLED;
+                    if (message.arg1 == SCAN_ALARM_SOURCE) {
+                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
+                        // not be processed) and restart the scan
+                        if (!checkAndRestartDelayedScan(message.arg2,
+                                true, mDisconnectedScanPeriodMs, null, null)) {
+                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
+                            loge("WifiStateMachine Disconnected CMD_START_SCAN source "
+                                    + message.arg1
+                                    + " " + message.arg2 + ", " + mDelayedScanCounter
+                                    + " -> obsolete");
+                            return HANDLED;
+                        }
+                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
+                        ret = HANDLED;
+                    } else {
+                        ret = NOT_HANDLED;
+                    }
                     break;
                 case WifiMonitor.SCAN_RESULTS_EVENT:
                     /* Re-enable background scan when a pending scan result is received */
@@ -7866,8 +7964,7 @@
             if (mEnableBackgroundScan) {
                 enableBackgroundScan(false);
             }
-            mCurrentScanAlarmMs = 0;
-            setScanAlarm(false, 0);
+            setScanAlarm(false);
         }
     }