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