Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wifi;
     18 
     19 import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE;
     20 
     21 import android.app.ActivityManager;
     22 import android.app.AlarmManager;
     23 import android.content.Context;
     24 import android.net.wifi.ScanResult;
     25 import android.net.wifi.SupplicantState;
     26 import android.net.wifi.WifiConfiguration;
     27 import android.net.wifi.WifiInfo;
     28 import android.net.wifi.WifiManager;
     29 import android.net.wifi.WifiScanner;
     30 import android.net.wifi.WifiScanner.PnoSettings;
     31 import android.net.wifi.WifiScanner.ScanSettings;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.util.LocalLog;
     35 import android.util.Log;
     36 
     37 import com.android.internal.R;
     38 import com.android.internal.annotations.VisibleForTesting;
     39 import com.android.server.wifi.util.ScanDetailUtil;
     40 
     41 import java.io.FileDescriptor;
     42 import java.io.PrintWriter;
     43 import java.util.ArrayList;
     44 import java.util.HashSet;
     45 import java.util.Iterator;
     46 import java.util.LinkedList;
     47 import java.util.List;
     48 import java.util.Set;
     49 
     50 /**
     51  * This class manages all the connectivity related scanning activities.
     52  *
     53  * When the screen is turned on or off, WiFi is connected or disconnected,
     54  * or on-demand, a scan is initiatiated and the scan results are passed
     55  * to QNS for it to make a recommendation on which network to connect to.
     56  */
     57 public class WifiConnectivityManager {
     58     public static final String WATCHDOG_TIMER_TAG =
     59             "WifiConnectivityManager Schedule Watchdog Timer";
     60     public static final String PERIODIC_SCAN_TIMER_TAG =
     61             "WifiConnectivityManager Schedule Periodic Scan Timer";
     62     public static final String RESTART_SINGLE_SCAN_TIMER_TAG =
     63             "WifiConnectivityManager Restart Single Scan";
     64     public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG =
     65             "WifiConnectivityManager Restart Scan";
     66 
     67     private static final String TAG = "WifiConnectivityManager";
     68     private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
     69     // Constants to indicate whether a scan should start immediately or
     70     // it should comply to the minimum scan interval rule.
     71     private static final boolean SCAN_IMMEDIATELY = true;
     72     private static final boolean SCAN_ON_SCHEDULE = false;
     73     // Periodic scan interval in milli-seconds. This is the scan
     74     // performed when screen is on.
     75     @VisibleForTesting
     76     public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
     77     // When screen is on and WiFi traffic is heavy, exponential backoff
     78     // connectivity scans are scheduled. This constant defines the maximum
     79     // scan interval in this scenario.
     80     @VisibleForTesting
     81     public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
     82     // PNO scan interval in milli-seconds. This is the scan
     83     // performed when screen is off and disconnected.
     84     private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
     85     // PNO scan interval in milli-seconds. This is the scan
     86     // performed when screen is off and connected.
     87     private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
     88     // When a network is found by PNO scan but gets rejected by QNS due to its
     89     // low RSSI value, scan will be reschduled in an exponential back off manner.
     90     private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds
     91     private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds
     92     // Maximum number of retries when starting a scan failed
     93     @VisibleForTesting
     94     public static final int MAX_SCAN_RESTART_ALLOWED = 5;
     95     // Number of milli-seconds to delay before retry starting
     96     // a previously failed scan
     97     private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds
     98     // When in disconnected mode, a watchdog timer will be fired
     99     // every WATCHDOG_INTERVAL_MS to start a single scan. This is
    100     // to prevent caveat from things like PNO scan.
    101     private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes
    102     // Restricted channel list age out value.
    103     private static final int CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour
    104     // This is the time interval for the connection attempt rate calculation. Connection attempt
    105     // timestamps beyond this interval is evicted from the list.
    106     public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
    107     // Max number of connection attempts in the above time interval.
    108     public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
    109 
    110     // WifiStateMachine has a bunch of states. From the
    111     // WifiConnectivityManager's perspective it only cares
    112     // if it is in Connected state, Disconnected state or in
    113     // transition between these two states.
    114     public static final int WIFI_STATE_UNKNOWN = 0;
    115     public static final int WIFI_STATE_CONNECTED = 1;
    116     public static final int WIFI_STATE_DISCONNECTED = 2;
    117     public static final int WIFI_STATE_TRANSITIONING = 3;
    118 
    119     // Due to b/28020168, timer based single scan will be scheduled
    120     // to provide periodic scan in an exponential backoff fashion.
    121     private static final boolean ENABLE_BACKGROUND_SCAN = false;
    122     // Flag to turn on connected PNO, when needed
    123     private static final boolean ENABLE_CONNECTED_PNO_SCAN = false;
    124 
    125     private final WifiStateMachine mStateMachine;
    126     private final WifiScanner mScanner;
    127     private final WifiConfigManager mConfigManager;
    128     private final WifiInfo mWifiInfo;
    129     private final WifiQualifiedNetworkSelector mQualifiedNetworkSelector;
    130     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
    131     private final WifiMetrics mWifiMetrics;
    132     private final AlarmManager mAlarmManager;
    133     private final Handler mEventHandler;
    134     private final Clock mClock;
    135     private final LocalLog mLocalLog =
    136             new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
    137     private final LinkedList<Long> mConnectionAttemptTimeStamps;
    138 
    139     private boolean mDbg = false;
    140     private boolean mWifiEnabled = false;
    141     private boolean mWifiConnectivityManagerEnabled = true;
    142     private boolean mScreenOn = false;
    143     private int mWifiState = WIFI_STATE_UNKNOWN;
    144     private boolean mUntrustedConnectionAllowed = false;
    145     private int mScanRestartCount = 0;
    146     private int mSingleScanRestartCount = 0;
    147     private int mTotalConnectivityAttemptsRateLimited = 0;
    148     private String mLastConnectionAttemptBssid = null;
    149     private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
    150     private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
    151     private boolean mPnoScanStarted = false;
    152     private boolean mPeriodicScanTimerSet = false;
    153     private boolean mWaitForFullBandScanResults = false;
    154 
    155     // PNO settings
    156     private int mMin5GHzRssi;
    157     private int mMin24GHzRssi;
    158     private int mInitialScoreMax;
    159     private int mCurrentConnectionBonus;
    160     private int mSameNetworkBonus;
    161     private int mSecureBonus;
    162     private int mBand5GHzBonus;
    163 
    164     // A helper to log debugging information in the local log buffer, which can
    165     // be retrieved in bugreport.
    166     private void localLog(String log) {
    167         mLocalLog.log(log);
    168     }
    169 
    170     // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
    171     // if the start scan command failed. An timer is used here to make it a deferred retry.
    172     private final AlarmManager.OnAlarmListener mRestartScanListener =
    173             new AlarmManager.OnAlarmListener() {
    174                 public void onAlarm() {
    175                     startConnectivityScan(SCAN_IMMEDIATELY);
    176                 }
    177             };
    178 
    179     // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
    180     // if the start scan command failed. An timer is used here to make it a deferred retry.
    181     private class RestartSingleScanListener implements AlarmManager.OnAlarmListener {
    182         private final boolean mIsFullBandScan;
    183 
    184         RestartSingleScanListener(boolean isFullBandScan) {
    185             mIsFullBandScan = isFullBandScan;
    186         }
    187 
    188         @Override
    189         public void onAlarm() {
    190             startSingleScan(mIsFullBandScan);
    191         }
    192     }
    193 
    194     // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS
    195     // if it is in the WIFI_STATE_DISCONNECTED state.
    196     private final AlarmManager.OnAlarmListener mWatchdogListener =
    197             new AlarmManager.OnAlarmListener() {
    198                 public void onAlarm() {
    199                     watchdogHandler();
    200                 }
    201             };
    202 
    203     // Due to b/28020168, timer based single scan will be scheduled
    204     // to provide periodic scan in an exponential backoff fashion.
    205     private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
    206             new AlarmManager.OnAlarmListener() {
    207                 public void onAlarm() {
    208                     periodicScanTimerHandler();
    209                 }
    210             };
    211 
    212     /**
    213      * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
    214      * Executes selection of potential network candidates, initiation of connection attempt to that
    215      * network.
    216      *
    217      * @return true - if a candidate is selected by QNS
    218      *         false - if no candidate is selected by QNS
    219      */
    220     private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
    221         localLog(listenerName + " onResults: start QNS");
    222         WifiConfiguration candidate =
    223                 mQualifiedNetworkSelector.selectQualifiedNetwork(false,
    224                 mUntrustedConnectionAllowed, scanDetails,
    225                 mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
    226                 mStateMachine.isDisconnected(),
    227                 mStateMachine.isSupplicantTransientState());
    228         mWifiLastResortWatchdog.updateAvailableNetworks(
    229                 mQualifiedNetworkSelector.getFilteredScanDetails());
    230         mWifiMetrics.countScanResults(scanDetails);
    231         if (candidate != null) {
    232             localLog(listenerName + ": QNS candidate-" + candidate.SSID);
    233             connectToNetwork(candidate);
    234             return true;
    235         } else {
    236             return false;
    237         }
    238     }
    239 
    240     // Periodic scan results listener. A periodic scan is initiated when
    241     // screen is on.
    242     private class PeriodicScanListener implements WifiScanner.ScanListener {
    243         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
    244 
    245         public void clearScanDetails() {
    246             mScanDetails.clear();
    247         }
    248 
    249         @Override
    250         public void onSuccess() {
    251             localLog("PeriodicScanListener onSuccess");
    252         }
    253 
    254         @Override
    255         public void onFailure(int reason, String description) {
    256             Log.e(TAG, "PeriodicScanListener onFailure:"
    257                           + " reason: " + reason
    258                           + " description: " + description);
    259 
    260             // reschedule the scan
    261             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
    262                 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
    263             } else {
    264                 mScanRestartCount = 0;
    265                 Log.e(TAG, "Failed to successfully start periodic scan for "
    266                           + MAX_SCAN_RESTART_ALLOWED + " times");
    267             }
    268         }
    269 
    270         @Override
    271         public void onPeriodChanged(int periodInMs) {
    272             localLog("PeriodicScanListener onPeriodChanged: "
    273                           + "actual scan period " + periodInMs + "ms");
    274         }
    275 
    276         @Override
    277         public void onResults(WifiScanner.ScanData[] results) {
    278             handleScanResults(mScanDetails, "PeriodicScanListener");
    279             clearScanDetails();
    280             mScanRestartCount = 0;
    281         }
    282 
    283         @Override
    284         public void onFullResult(ScanResult fullScanResult) {
    285             if (mDbg) {
    286                 localLog("PeriodicScanListener onFullResult: "
    287                             + fullScanResult.SSID + " capabilities "
    288                             + fullScanResult.capabilities);
    289             }
    290 
    291             mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
    292         }
    293     }
    294 
    295     private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener();
    296 
    297     // All single scan results listener.
    298     //
    299     // Note: This is the listener for all the available single scan results,
    300     //       including the ones initiated by WifiConnectivityManager and
    301     //       other modules.
    302     private class AllSingleScanListener implements WifiScanner.ScanListener {
    303         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
    304 
    305         public void clearScanDetails() {
    306             mScanDetails.clear();
    307         }
    308 
    309         @Override
    310         public void onSuccess() {
    311             localLog("registerScanListener onSuccess");
    312         }
    313 
    314         @Override
    315         public void onFailure(int reason, String description) {
    316             Log.e(TAG, "registerScanListener onFailure:"
    317                           + " reason: " + reason
    318                           + " description: " + description);
    319         }
    320 
    321         @Override
    322         public void onPeriodChanged(int periodInMs) {
    323         }
    324 
    325         @Override
    326         public void onResults(WifiScanner.ScanData[] results) {
    327             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    328                 clearScanDetails();
    329                 mWaitForFullBandScanResults = false;
    330                 return;
    331             }
    332 
    333             // Full band scan results only.
    334             if (mWaitForFullBandScanResults) {
    335                 if (!results[0].isAllChannelsScanned()) {
    336                     localLog("AllSingleScanListener waiting for full band scan results.");
    337                     clearScanDetails();
    338                     return;
    339                 } else {
    340                     mWaitForFullBandScanResults = false;
    341                 }
    342             }
    343 
    344             boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener");
    345             clearScanDetails();
    346 
    347             // Update metrics to see if a single scan detected a valid network
    348             // while PNO scan didn't.
    349             // Note: We don't update the background scan metrics any more as it is
    350             //       not in use.
    351             if (mPnoScanStarted) {
    352                 if (wasConnectAttempted) {
    353                     mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
    354                 } else {
    355                     mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
    356                 }
    357             }
    358         }
    359 
    360         @Override
    361         public void onFullResult(ScanResult fullScanResult) {
    362             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    363                 return;
    364             }
    365 
    366             if (mDbg) {
    367                 localLog("AllSingleScanListener onFullResult: "
    368                             + fullScanResult.SSID + " capabilities "
    369                             + fullScanResult.capabilities);
    370             }
    371 
    372             mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
    373         }
    374     }
    375 
    376     private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener();
    377 
    378     // Single scan results listener. A single scan is initiated when
    379     // Disconnected/ConnectedPNO scan found a valid network and woke up
    380     // the system, or by the watchdog timer, or to form the timer based
    381     // periodic scan.
    382     //
    383     // Note: This is the listener for the single scans initiated by the
    384     //        WifiConnectivityManager.
    385     private class SingleScanListener implements WifiScanner.ScanListener {
    386         private final boolean mIsFullBandScan;
    387 
    388         SingleScanListener(boolean isFullBandScan) {
    389             mIsFullBandScan = isFullBandScan;
    390         }
    391 
    392         @Override
    393         public void onSuccess() {
    394             localLog("SingleScanListener onSuccess");
    395         }
    396 
    397         @Override
    398         public void onFailure(int reason, String description) {
    399             Log.e(TAG, "SingleScanListener onFailure:"
    400                           + " reason: " + reason
    401                           + " description: " + description);
    402 
    403             // reschedule the scan
    404             if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
    405                 scheduleDelayedSingleScan(mIsFullBandScan);
    406             } else {
    407                 mSingleScanRestartCount = 0;
    408                 Log.e(TAG, "Failed to successfully start single scan for "
    409                           + MAX_SCAN_RESTART_ALLOWED + " times");
    410             }
    411         }
    412 
    413         @Override
    414         public void onPeriodChanged(int periodInMs) {
    415             localLog("SingleScanListener onPeriodChanged: "
    416                           + "actual scan period " + periodInMs + "ms");
    417         }
    418 
    419         @Override
    420         public void onResults(WifiScanner.ScanData[] results) {
    421         }
    422 
    423         @Override
    424         public void onFullResult(ScanResult fullScanResult) {
    425         }
    426     }
    427 
    428     // re-enable this when b/27695292 is fixed
    429     // private final SingleScanListener mSingleScanListener = new SingleScanListener();
    430 
    431     // PNO scan results listener for both disconected and connected PNO scanning.
    432     // A PNO scan is initiated when screen is off.
    433     private class PnoScanListener implements WifiScanner.PnoScanListener {
    434         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
    435         private int mLowRssiNetworkRetryDelay =
    436                 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
    437 
    438         public void clearScanDetails() {
    439             mScanDetails.clear();
    440         }
    441 
    442         // Reset to the start value when either a non-PNO scan is started or
    443         // QNS selects a candidate from the PNO scan results.
    444         public void resetLowRssiNetworkRetryDelay() {
    445             mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
    446         }
    447 
    448         @VisibleForTesting
    449         public int getLowRssiNetworkRetryDelay() {
    450             return mLowRssiNetworkRetryDelay;
    451         }
    452 
    453         @Override
    454         public void onSuccess() {
    455             localLog("PnoScanListener onSuccess");
    456         }
    457 
    458         @Override
    459         public void onFailure(int reason, String description) {
    460             Log.e(TAG, "PnoScanListener onFailure:"
    461                           + " reason: " + reason
    462                           + " description: " + description);
    463 
    464             // reschedule the scan
    465             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
    466                 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
    467             } else {
    468                 mScanRestartCount = 0;
    469                 Log.e(TAG, "Failed to successfully start PNO scan for "
    470                           + MAX_SCAN_RESTART_ALLOWED + " times");
    471             }
    472         }
    473 
    474         @Override
    475         public void onPeriodChanged(int periodInMs) {
    476             localLog("PnoScanListener onPeriodChanged: "
    477                           + "actual scan period " + periodInMs + "ms");
    478         }
    479 
    480         // Currently the PNO scan results doesn't include IE,
    481         // which contains information required by QNS. Ignore them
    482         // for now.
    483         @Override
    484         public void onResults(WifiScanner.ScanData[] results) {
    485         }
    486 
    487         @Override
    488         public void onFullResult(ScanResult fullScanResult) {
    489         }
    490 
    491         @Override
    492         public void onPnoNetworkFound(ScanResult[] results) {
    493             localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length);
    494 
    495             for (ScanResult result: results) {
    496                 mScanDetails.add(ScanDetailUtil.toScanDetail(result));
    497             }
    498 
    499             boolean wasConnectAttempted;
    500             wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener");
    501             clearScanDetails();
    502             mScanRestartCount = 0;
    503 
    504             if (!wasConnectAttempted) {
    505                 // The scan results were rejected by QNS due to low RSSI values
    506                 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) {
    507                     mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS;
    508                 }
    509                 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay);
    510 
    511                 // Set up the delay value for next retry.
    512                 mLowRssiNetworkRetryDelay *= 2;
    513             } else {
    514                 resetLowRssiNetworkRetryDelay();
    515             }
    516         }
    517     }
    518 
    519     private final PnoScanListener mPnoScanListener = new PnoScanListener();
    520 
    521     /**
    522      * WifiConnectivityManager constructor
    523      */
    524     public WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
    525                 WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
    526                 WifiQualifiedNetworkSelector qualifiedNetworkSelector,
    527                 WifiInjector wifiInjector, Looper looper, boolean enable) {
    528         mStateMachine = stateMachine;
    529         mScanner = scanner;
    530         mConfigManager = configManager;
    531         mWifiInfo = wifiInfo;
    532         mQualifiedNetworkSelector = qualifiedNetworkSelector;
    533         mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
    534         mWifiMetrics = wifiInjector.getWifiMetrics();
    535         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    536         mEventHandler = new Handler(looper);
    537         mClock = wifiInjector.getClock();
    538         mConnectionAttemptTimeStamps = new LinkedList<>();
    539 
    540         mMin5GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI;
    541         mMin24GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI;
    542         mBand5GHzBonus = WifiQualifiedNetworkSelector.BAND_AWARD_5GHz;
    543         mCurrentConnectionBonus = mConfigManager.mCurrentNetworkBoost.get();
    544         mSameNetworkBonus = context.getResources().getInteger(
    545                                 R.integer.config_wifi_framework_SAME_BSSID_AWARD);
    546         mSecureBonus = context.getResources().getInteger(
    547                             R.integer.config_wifi_framework_SECURITY_AWARD);
    548         mInitialScoreMax = (mConfigManager.mThresholdSaturatedRssi24.get()
    549                             + WifiQualifiedNetworkSelector.RSSI_SCORE_OFFSET)
    550                             * WifiQualifiedNetworkSelector.RSSI_SCORE_SLOPE;
    551 
    552         Log.i(TAG, "PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
    553                     + " min24GHzRssi " + mMin24GHzRssi
    554                     + " currentConnectionBonus " + mCurrentConnectionBonus
    555                     + " sameNetworkBonus " + mSameNetworkBonus
    556                     + " secureNetworkBonus " + mSecureBonus
    557                     + " initialScoreMax " + mInitialScoreMax);
    558 
    559         // Register for all single scan results
    560         mScanner.registerScanListener(mAllSingleScanListener);
    561 
    562         mWifiConnectivityManagerEnabled = enable;
    563 
    564         Log.i(TAG, "ConnectivityScanManager initialized and "
    565                 + (enable ? "enabled" : "disabled"));
    566     }
    567 
    568     /**
    569      * This checks the connection attempt rate and recommends whether the connection attempt
    570      * should be skipped or not. This attempts to rate limit the rate of connections to
    571      * prevent us from flapping between networks and draining battery rapidly.
    572      */
    573     private boolean shouldSkipConnectionAttempt(Long timeMillis) {
    574         Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator();
    575         // First evict old entries from the queue.
    576         while (attemptIter.hasNext()) {
    577             Long connectionAttemptTimeMillis = attemptIter.next();
    578             if ((timeMillis - connectionAttemptTimeMillis)
    579                     > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) {
    580                 attemptIter.remove();
    581             } else {
    582                 // This list is sorted by timestamps, so we can skip any more checks
    583                 break;
    584             }
    585         }
    586         // If we've reached the max connection attempt rate, skip this connection attempt
    587         return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE);
    588     }
    589 
    590     /**
    591      * Add the current connection attempt timestamp to our queue of connection attempts.
    592      */
    593     private void noteConnectionAttempt(Long timeMillis) {
    594         mConnectionAttemptTimeStamps.addLast(timeMillis);
    595     }
    596 
    597     /**
    598      * This is used to clear the connection attempt rate limiter. This is done when the user
    599      * explicitly tries to connect to a specified network.
    600      */
    601     private void clearConnectionAttemptTimeStamps() {
    602         mConnectionAttemptTimeStamps.clear();
    603     }
    604 
    605     /**
    606      * Attempt to connect to a network candidate.
    607      *
    608      * Based on the currently connected network, this menthod determines whether we should
    609      * connect or roam to the network candidate recommended by QNS.
    610      */
    611     private void connectToNetwork(WifiConfiguration candidate) {
    612         ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
    613         if (scanResultCandidate == null) {
    614             Log.e(TAG, "connectToNetwork: bad candidate - "  + candidate
    615                     + " scanResult: " + scanResultCandidate);
    616             return;
    617         }
    618 
    619         String targetBssid = scanResultCandidate.BSSID;
    620         String targetAssociationId = candidate.SSID + " : " + targetBssid;
    621 
    622         // Check if we are already connected or in the process of connecting to the target
    623         // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
    624         // in case the firmware automatically roamed to a BSSID different from what QNS
    625         // selected.
    626         if (targetBssid != null
    627                 && (targetBssid.equals(mLastConnectionAttemptBssid)
    628                     || targetBssid.equals(mWifiInfo.getBSSID()))
    629                 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
    630             localLog("connectToNetwork: Either already connected "
    631                     + "or is connecting to " + targetAssociationId);
    632             return;
    633         }
    634 
    635         Long elapsedTimeMillis = mClock.elapsedRealtime();
    636         if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
    637             localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
    638             mTotalConnectivityAttemptsRateLimited++;
    639             return;
    640         }
    641         noteConnectionAttempt(elapsedTimeMillis);
    642 
    643         mLastConnectionAttemptBssid = targetBssid;
    644 
    645         WifiConfiguration currentConnectedNetwork = mConfigManager
    646                 .getWifiConfiguration(mWifiInfo.getNetworkId());
    647         String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
    648                 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
    649 
    650         if (currentConnectedNetwork != null
    651                 && (currentConnectedNetwork.networkId == candidate.networkId
    652                 || currentConnectedNetwork.isLinked(candidate))) {
    653             localLog("connectToNetwork: Roaming from " + currentAssociationId + " to "
    654                         + targetAssociationId);
    655             mStateMachine.autoRoamToNetwork(candidate.networkId, scanResultCandidate);
    656         } else {
    657             localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to "
    658                         + targetAssociationId);
    659             mStateMachine.autoConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID);
    660         }
    661     }
    662 
    663     // Helper for selecting the band for connectivity scan
    664     private int getScanBand() {
    665         return getScanBand(true);
    666     }
    667 
    668     private int getScanBand(boolean isFullBandScan) {
    669         if (isFullBandScan) {
    670             int freqBand = mStateMachine.getFrequencyBand();
    671             if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_5GHZ) {
    672                 return WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS;
    673             } else if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ) {
    674                 return WifiScanner.WIFI_BAND_24_GHZ;
    675             } else {
    676                 return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
    677             }
    678         } else {
    679             // Use channel list instead.
    680             return WifiScanner.WIFI_BAND_UNSPECIFIED;
    681         }
    682     }
    683 
    684     // Helper for setting the channels for connectivity scan when band is unspecified. Returns
    685     // false if we can't retrieve the info.
    686     private boolean setScanChannels(ScanSettings settings) {
    687         WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration();
    688 
    689         if (config == null) {
    690             return false;
    691         }
    692 
    693         HashSet<Integer> freqs = mConfigManager.makeChannelList(config, CHANNEL_LIST_AGE_MS);
    694 
    695         if (freqs != null && freqs.size() != 0) {
    696             int index = 0;
    697             settings.channels = new WifiScanner.ChannelSpec[freqs.size()];
    698             for (Integer freq : freqs) {
    699                 settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
    700             }
    701             return true;
    702         } else {
    703             localLog("No scan channels for " + config.configKey() + ". Perform full band scan");
    704             return false;
    705         }
    706     }
    707 
    708     // Watchdog timer handler
    709     private void watchdogHandler() {
    710         localLog("watchdogHandler");
    711 
    712         // Schedule the next timer and start a single scan if we are in disconnected state.
    713         // Otherwise, the watchdog timer will be scheduled when entering disconnected
    714         // state.
    715         if (mWifiState == WIFI_STATE_DISCONNECTED) {
    716             Log.i(TAG, "start a single scan from watchdogHandler");
    717 
    718             scheduleWatchdogTimer();
    719             startSingleScan(true);
    720         }
    721     }
    722 
    723     // Start a single scan and set up the interval for next single scan.
    724     private void startPeriodicSingleScan() {
    725         long currentTimeStamp = mClock.elapsedRealtime();
    726 
    727         if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
    728             long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
    729             if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) {
    730                 localLog("Last periodic single scan started " + msSinceLastScan
    731                         + "ms ago, defer this new scan request.");
    732                 schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan);
    733                 return;
    734             }
    735         }
    736 
    737         boolean isFullBandScan = true;
    738 
    739         // If the WiFi traffic is heavy, only partial scan is initiated.
    740         if (mWifiState == WIFI_STATE_CONNECTED
    741                 && (mWifiInfo.txSuccessRate
    742                             > mConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS
    743                     || mWifiInfo.rxSuccessRate
    744                             > mConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS)) {
    745             localLog("No full band scan due to heavy traffic, txSuccessRate="
    746                         + mWifiInfo.txSuccessRate + " rxSuccessRate="
    747                         + mWifiInfo.rxSuccessRate);
    748             isFullBandScan = false;
    749         }
    750 
    751         mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
    752         startSingleScan(isFullBandScan);
    753         schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
    754 
    755         // Set up the next scan interval in an exponential backoff fashion.
    756         mPeriodicSingleScanInterval *= 2;
    757         if (mPeriodicSingleScanInterval >  MAX_PERIODIC_SCAN_INTERVAL_MS) {
    758             mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS;
    759         }
    760     }
    761 
    762     // Reset the last periodic single scan time stamp so that the next periodic single
    763     // scan can start immediately.
    764     private void resetLastPeriodicSingleScanTimeStamp() {
    765         mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
    766     }
    767 
    768     // Periodic scan timer handler
    769     private void periodicScanTimerHandler() {
    770         localLog("periodicScanTimerHandler");
    771 
    772         // Schedule the next timer and start a single scan if screen is on.
    773         if (mScreenOn) {
    774             startPeriodicSingleScan();
    775         }
    776     }
    777 
    778     // Start a single scan
    779     private void startSingleScan(boolean isFullBandScan) {
    780         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    781             return;
    782         }
    783 
    784         mPnoScanListener.resetLowRssiNetworkRetryDelay();
    785 
    786         ScanSettings settings = new ScanSettings();
    787         if (!isFullBandScan) {
    788             if (!setScanChannels(settings)) {
    789                 isFullBandScan = true;
    790             }
    791         }
    792         settings.band = getScanBand(isFullBandScan);
    793         settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
    794                             | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
    795         settings.numBssidsPerScan = 0;
    796 
    797         //Retrieve the list of hidden networkId's to scan for.
    798         Set<Integer> hiddenNetworkIds = mConfigManager.getHiddenConfiguredNetworkIds();
    799         if (hiddenNetworkIds != null && hiddenNetworkIds.size() > 0) {
    800             int i = 0;
    801             settings.hiddenNetworkIds = new int[hiddenNetworkIds.size()];
    802             for (Integer netId : hiddenNetworkIds) {
    803                 settings.hiddenNetworkIds[i++] = netId;
    804             }
    805         }
    806 
    807         // re-enable this when b/27695292 is fixed
    808         // mSingleScanListener.clearScanDetails();
    809         // mScanner.startScan(settings, mSingleScanListener, WIFI_WORK_SOURCE);
    810         SingleScanListener singleScanListener =
    811                 new SingleScanListener(isFullBandScan);
    812         mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE);
    813     }
    814 
    815     // Start a periodic scan when screen is on
    816     private void startPeriodicScan(boolean scanImmediately) {
    817         mPnoScanListener.resetLowRssiNetworkRetryDelay();
    818 
    819         // No connectivity scan if auto roaming is disabled.
    820         if (mWifiState == WIFI_STATE_CONNECTED
    821                 && !mConfigManager.getEnableAutoJoinWhenAssociated()) {
    822             return;
    823         }
    824 
    825         // Due to b/28020168, timer based single scan will be scheduled
    826         // to provide periodic scan in an exponential backoff fashion.
    827         if (!ENABLE_BACKGROUND_SCAN) {
    828             if (scanImmediately) {
    829                 resetLastPeriodicSingleScanTimeStamp();
    830             }
    831             mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
    832             startPeriodicSingleScan();
    833         } else {
    834             ScanSettings settings = new ScanSettings();
    835             settings.band = getScanBand();
    836             settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
    837                                 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
    838             settings.numBssidsPerScan = 0;
    839             settings.periodInMs = PERIODIC_SCAN_INTERVAL_MS;
    840 
    841             mPeriodicScanListener.clearScanDetails();
    842             mScanner.startBackgroundScan(settings, mPeriodicScanListener, WIFI_WORK_SOURCE);
    843         }
    844     }
    845 
    846     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
    847     private void startDisconnectedPnoScan() {
    848         // Initialize PNO settings
    849         PnoSettings pnoSettings = new PnoSettings();
    850         ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
    851                 mConfigManager.retrieveDisconnectedPnoNetworkList();
    852         int listSize = pnoNetworkList.size();
    853 
    854         if (listSize == 0) {
    855             // No saved network
    856             localLog("No saved network for starting disconnected PNO.");
    857             return;
    858         }
    859 
    860         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
    861         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
    862         pnoSettings.min5GHzRssi = mMin5GHzRssi;
    863         pnoSettings.min24GHzRssi = mMin24GHzRssi;
    864         pnoSettings.initialScoreMax = mInitialScoreMax;
    865         pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
    866         pnoSettings.sameNetworkBonus = mSameNetworkBonus;
    867         pnoSettings.secureBonus = mSecureBonus;
    868         pnoSettings.band5GHzBonus = mBand5GHzBonus;
    869 
    870         // Initialize scan settings
    871         ScanSettings scanSettings = new ScanSettings();
    872         scanSettings.band = getScanBand();
    873         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
    874         scanSettings.numBssidsPerScan = 0;
    875         scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS;
    876         // TODO: enable exponential back off scan later to further save energy
    877         // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
    878 
    879         mPnoScanListener.clearScanDetails();
    880 
    881         mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
    882         mPnoScanStarted = true;
    883     }
    884 
    885     // Start a ConnectedPNO scan when screen is off and Wifi is connected
    886     private void startConnectedPnoScan() {
    887         // Disable ConnectedPNO for now due to b/28020168
    888         if (!ENABLE_CONNECTED_PNO_SCAN) {
    889             return;
    890         }
    891 
    892         // Initialize PNO settings
    893         PnoSettings pnoSettings = new PnoSettings();
    894         ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
    895                 mConfigManager.retrieveConnectedPnoNetworkList();
    896         int listSize = pnoNetworkList.size();
    897 
    898         if (listSize == 0) {
    899             // No saved network
    900             localLog("No saved network for starting connected PNO.");
    901             return;
    902         }
    903 
    904         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
    905         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
    906         pnoSettings.min5GHzRssi = mMin5GHzRssi;
    907         pnoSettings.min24GHzRssi = mMin24GHzRssi;
    908         pnoSettings.initialScoreMax = mInitialScoreMax;
    909         pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
    910         pnoSettings.sameNetworkBonus = mSameNetworkBonus;
    911         pnoSettings.secureBonus = mSecureBonus;
    912         pnoSettings.band5GHzBonus = mBand5GHzBonus;
    913 
    914         // Initialize scan settings
    915         ScanSettings scanSettings = new ScanSettings();
    916         scanSettings.band = getScanBand();
    917         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
    918         scanSettings.numBssidsPerScan = 0;
    919         scanSettings.periodInMs = CONNECTED_PNO_SCAN_INTERVAL_MS;
    920         // TODO: enable exponential back off scan later to further save energy
    921         // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
    922 
    923         mPnoScanListener.clearScanDetails();
    924 
    925         mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
    926         mPnoScanStarted = true;
    927     }
    928 
    929     // Stop a PNO scan. This includes both DisconnectedPNO and ConnectedPNO scans.
    930     private void stopPnoScan() {
    931         if (mPnoScanStarted) {
    932             mScanner.stopPnoScan(mPnoScanListener);
    933         }
    934 
    935         mPnoScanStarted = false;
    936     }
    937 
    938     // Set up watchdog timer
    939     private void scheduleWatchdogTimer() {
    940         Log.i(TAG, "scheduleWatchdogTimer");
    941 
    942         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    943                             mClock.elapsedRealtime() + WATCHDOG_INTERVAL_MS,
    944                             WATCHDOG_TIMER_TAG,
    945                             mWatchdogListener, mEventHandler);
    946     }
    947 
    948     // Set up periodic scan timer
    949     private void schedulePeriodicScanTimer(int intervalMs) {
    950         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    951                             mClock.elapsedRealtime() + intervalMs,
    952                             PERIODIC_SCAN_TIMER_TAG,
    953                             mPeriodicScanTimerListener, mEventHandler);
    954         mPeriodicScanTimerSet = true;
    955     }
    956 
    957     // Cancel periodic scan timer
    958     private void cancelPeriodicScanTimer() {
    959         if (mPeriodicScanTimerSet) {
    960             mAlarmManager.cancel(mPeriodicScanTimerListener);
    961             mPeriodicScanTimerSet = false;
    962         }
    963     }
    964 
    965     // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
    966     private void scheduleDelayedSingleScan(boolean isFullBandScan) {
    967         localLog("scheduleDelayedSingleScan");
    968 
    969         RestartSingleScanListener restartSingleScanListener =
    970                 new RestartSingleScanListener(isFullBandScan);
    971         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    972                             mClock.elapsedRealtime() + RESTART_SCAN_DELAY_MS,
    973                             RESTART_SINGLE_SCAN_TIMER_TAG,
    974                             restartSingleScanListener, mEventHandler);
    975     }
    976 
    977     // Set up timer to start a delayed scan after msFromNow milli-seconds
    978     private void scheduleDelayedConnectivityScan(int msFromNow) {
    979         localLog("scheduleDelayedConnectivityScan");
    980 
    981         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    982                             mClock.elapsedRealtime() + msFromNow,
    983                             RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
    984                             mRestartScanListener, mEventHandler);
    985 
    986     }
    987 
    988     // Start a connectivity scan. The scan method is chosen according to
    989     // the current screen state and WiFi state.
    990     private void startConnectivityScan(boolean scanImmediately) {
    991         localLog("startConnectivityScan: screenOn=" + mScreenOn
    992                         + " wifiState=" + mWifiState
    993                         + " scanImmediately=" + scanImmediately
    994                         + " wifiEnabled=" + mWifiEnabled
    995                         + " wifiConnectivityManagerEnabled="
    996                         + mWifiConnectivityManagerEnabled);
    997 
    998         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    999             return;
   1000         }
   1001 
   1002         // Always stop outstanding connecivity scan if there is any
   1003         stopConnectivityScan();
   1004 
   1005         // Don't start a connectivity scan while Wifi is in the transition
   1006         // between connected and disconnected states.
   1007         if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
   1008             return;
   1009         }
   1010 
   1011         if (mScreenOn) {
   1012             startPeriodicScan(scanImmediately);
   1013         } else { // screenOff
   1014             if (mWifiState == WIFI_STATE_CONNECTED) {
   1015                 startConnectedPnoScan();
   1016             } else {
   1017                 startDisconnectedPnoScan();
   1018             }
   1019         }
   1020     }
   1021 
   1022     // Stop connectivity scan if there is any.
   1023     private void stopConnectivityScan() {
   1024         // Due to b/28020168, timer based single scan will be scheduled
   1025         // to provide periodic scan in an exponential backoff fashion.
   1026         if (!ENABLE_BACKGROUND_SCAN) {
   1027             cancelPeriodicScanTimer();
   1028         } else {
   1029             mScanner.stopBackgroundScan(mPeriodicScanListener);
   1030         }
   1031         stopPnoScan();
   1032         mScanRestartCount = 0;
   1033     }
   1034 
   1035     /**
   1036      * Handler for screen state (on/off) changes
   1037      */
   1038     public void handleScreenStateChanged(boolean screenOn) {
   1039         localLog("handleScreenStateChanged: screenOn=" + screenOn);
   1040 
   1041         mScreenOn = screenOn;
   1042 
   1043         startConnectivityScan(SCAN_ON_SCHEDULE);
   1044     }
   1045 
   1046     /**
   1047      * Handler for WiFi state (connected/disconnected) changes
   1048      */
   1049     public void handleConnectionStateChanged(int state) {
   1050         localLog("handleConnectionStateChanged: state=" + state);
   1051 
   1052         mWifiState = state;
   1053 
   1054         // Reset BSSID of last connection attempt and kick off
   1055         // the watchdog timer if entering disconnected state.
   1056         if (mWifiState == WIFI_STATE_DISCONNECTED) {
   1057             mLastConnectionAttemptBssid = null;
   1058             scheduleWatchdogTimer();
   1059         }
   1060 
   1061         startConnectivityScan(SCAN_ON_SCHEDULE);
   1062     }
   1063 
   1064     /**
   1065      * Handler when user toggles whether untrusted connection is allowed
   1066      */
   1067     public void setUntrustedConnectionAllowed(boolean allowed) {
   1068         Log.i(TAG, "setUntrustedConnectionAllowed: allowed=" + allowed);
   1069 
   1070         if (mUntrustedConnectionAllowed != allowed) {
   1071             mUntrustedConnectionAllowed = allowed;
   1072             startConnectivityScan(SCAN_IMMEDIATELY);
   1073         }
   1074     }
   1075 
   1076     /**
   1077      * Handler when user specifies a particular network to connect to
   1078      */
   1079     public void connectToUserSelectNetwork(int netId, boolean persistent) {
   1080         Log.i(TAG, "connectToUserSelectNetwork: netId=" + netId
   1081                    + " persist=" + persistent);
   1082 
   1083         mQualifiedNetworkSelector.userSelectNetwork(netId, persistent);
   1084 
   1085         clearConnectionAttemptTimeStamps();
   1086     }
   1087 
   1088     /**
   1089      * Handler for on-demand connectivity scan
   1090      */
   1091     public void forceConnectivityScan() {
   1092         Log.i(TAG, "forceConnectivityScan");
   1093 
   1094         mWaitForFullBandScanResults = true;
   1095         startSingleScan(true);
   1096     }
   1097 
   1098     /**
   1099      * Track whether a BSSID should be enabled or disabled for QNS
   1100      */
   1101     public boolean trackBssid(String bssid, boolean enable) {
   1102         Log.i(TAG, "trackBssid: " + (enable ? "enable " : "disable ") + bssid);
   1103 
   1104         boolean ret = mQualifiedNetworkSelector
   1105                             .enableBssidForQualityNetworkSelection(bssid, enable);
   1106 
   1107         if (ret && !enable) {
   1108             // Disabling a BSSID can happen when the AP candidate to connect to has
   1109             // no capacity for new stations. We start another scan immediately so that QNS
   1110             // can give us another candidate to connect to.
   1111             startConnectivityScan(SCAN_IMMEDIATELY);
   1112         }
   1113 
   1114         return ret;
   1115     }
   1116 
   1117     /**
   1118      * Set band preference when doing scan and making connection
   1119      */
   1120     public void setUserPreferredBand(int band) {
   1121         Log.i(TAG, "User band preference: " + band);
   1122 
   1123         mQualifiedNetworkSelector.setUserPreferredBand(band);
   1124         startConnectivityScan(SCAN_IMMEDIATELY);
   1125     }
   1126 
   1127     /**
   1128      * Inform WiFi is enabled for connection or not
   1129      */
   1130     public void setWifiEnabled(boolean enable) {
   1131         Log.i(TAG, "Set WiFi " + (enable ? "enabled" : "disabled"));
   1132 
   1133         mWifiEnabled = enable;
   1134 
   1135         if (!mWifiEnabled) {
   1136             stopConnectivityScan();
   1137             resetLastPeriodicSingleScanTimeStamp();
   1138             mLastConnectionAttemptBssid = null;
   1139             mWaitForFullBandScanResults = false;
   1140         } else if (mWifiConnectivityManagerEnabled) {
   1141            startConnectivityScan(SCAN_IMMEDIATELY);
   1142         }
   1143     }
   1144 
   1145     /**
   1146      * Turn on/off the WifiConnectivityMangager at runtime
   1147      */
   1148     public void enable(boolean enable) {
   1149         Log.i(TAG, "Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
   1150 
   1151         mWifiConnectivityManagerEnabled = enable;
   1152 
   1153         if (!mWifiConnectivityManagerEnabled) {
   1154             stopConnectivityScan();
   1155             resetLastPeriodicSingleScanTimeStamp();
   1156             mLastConnectionAttemptBssid = null;
   1157             mWaitForFullBandScanResults = false;
   1158         } else if (mWifiEnabled) {
   1159            startConnectivityScan(SCAN_IMMEDIATELY);
   1160         }
   1161     }
   1162 
   1163     /**
   1164      * Enable/disable verbose logging
   1165      */
   1166     public void enableVerboseLogging(int verbose) {
   1167         mDbg = verbose > 0;
   1168     }
   1169 
   1170     /**
   1171      * Dump the local log buffer
   1172      */
   1173     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1174         pw.println("Dump of WifiConnectivityManager");
   1175         pw.println("WifiConnectivityManager - Log Begin ----");
   1176         pw.println("WifiConnectivityManager - Number of connectivity attempts rate limited: "
   1177                 + mTotalConnectivityAttemptsRateLimited);
   1178         mLocalLog.dump(fd, pw, args);
   1179         pw.println("WifiConnectivityManager - Log End ----");
   1180     }
   1181 
   1182     @VisibleForTesting
   1183     int getLowRssiNetworkRetryDelay() {
   1184         return mPnoScanListener.getLowRssiNetworkRetryDelay();
   1185     }
   1186 
   1187     @VisibleForTesting
   1188     long getLastPeriodicSingleScanTimeStamp() {
   1189         return mLastPeriodicSingleScanTimeStamp;
   1190     }
   1191 }
   1192