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.AlarmManager;
     22 import android.content.Context;
     23 import android.net.wifi.ScanResult;
     24 import android.net.wifi.SupplicantState;
     25 import android.net.wifi.WifiConfiguration;
     26 import android.net.wifi.WifiInfo;
     27 import android.net.wifi.WifiScanner;
     28 import android.net.wifi.WifiScanner.PnoSettings;
     29 import android.net.wifi.WifiScanner.ScanSettings;
     30 import android.os.Handler;
     31 import android.os.Looper;
     32 import android.util.LocalLog;
     33 import android.util.Log;
     34 
     35 import com.android.internal.R;
     36 import com.android.internal.annotations.VisibleForTesting;
     37 import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
     38 import com.android.server.wifi.util.ScanResultUtil;
     39 
     40 import java.io.FileDescriptor;
     41 import java.io.PrintWriter;
     42 import java.util.ArrayList;
     43 import java.util.HashMap;
     44 import java.util.HashSet;
     45 import java.util.Iterator;
     46 import java.util.LinkedList;
     47 import java.util.List;
     48 import java.util.Map;
     49 import java.util.Set;
     50 
     51 /**
     52  * This class manages all the connectivity related scanning activities.
     53  *
     54  * When the screen is turned on or off, WiFi is connected or disconnected,
     55  * or on-demand, a scan is initiatiated and the scan results are passed
     56  * to WifiNetworkSelector for it to make a recommendation on which network
     57  * to connect to.
     58  */
     59 public class WifiConnectivityManager {
     60     public static final String WATCHDOG_TIMER_TAG =
     61             "WifiConnectivityManager Schedule Watchdog Timer";
     62     public static final String PERIODIC_SCAN_TIMER_TAG =
     63             "WifiConnectivityManager Schedule Periodic Scan Timer";
     64     public static final String RESTART_SINGLE_SCAN_TIMER_TAG =
     65             "WifiConnectivityManager Restart Single Scan";
     66     public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG =
     67             "WifiConnectivityManager Restart Scan";
     68 
     69     private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
     70     // Constants to indicate whether a scan should start immediately or
     71     // it should comply to the minimum scan interval rule.
     72     private static final boolean SCAN_IMMEDIATELY = true;
     73     private static final boolean SCAN_ON_SCHEDULE = false;
     74     // Periodic scan interval in milli-seconds. This is the scan
     75     // performed when screen is on.
     76     @VisibleForTesting
     77     public static final int PERIODIC_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
     78     // When screen is on and WiFi traffic is heavy, exponential backoff
     79     // connectivity scans are scheduled. This constant defines the maximum
     80     // scan interval in this scenario.
     81     @VisibleForTesting
     82     public static final int MAX_PERIODIC_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
     83     // PNO scan interval in milli-seconds. This is the scan
     84     // performed when screen is off and disconnected.
     85     private static final int DISCONNECTED_PNO_SCAN_INTERVAL_MS = 20 * 1000; // 20 seconds
     86     // PNO scan interval in milli-seconds. This is the scan
     87     // performed when screen is off and connected.
     88     private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
     89     // When a network is found by PNO scan but gets rejected by Wifi Network Selector due
     90     // to its low RSSI value, scan will be reschduled in an exponential back off manner.
     91     private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds
     92     private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds
     93     // Maximum number of retries when starting a scan failed
     94     @VisibleForTesting
     95     public static final int MAX_SCAN_RESTART_ALLOWED = 5;
     96     // Number of milli-seconds to delay before retry starting
     97     // a previously failed scan
     98     private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds
     99     // When in disconnected mode, a watchdog timer will be fired
    100     // every WATCHDOG_INTERVAL_MS to start a single scan. This is
    101     // to prevent caveat from things like PNO scan.
    102     private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes
    103     // Restricted channel list age out value.
    104     private static final int CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour
    105     // This is the time interval for the connection attempt rate calculation. Connection attempt
    106     // timestamps beyond this interval is evicted from the list.
    107     public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
    108     // Max number of connection attempts in the above time interval.
    109     public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
    110     // Packet tx/rx rates to determine if we want to do partial vs full scans.
    111     // TODO(b/31180330): Make these device configs.
    112     public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8;
    113     public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16;
    114 
    115     // WifiStateMachine has a bunch of states. From the
    116     // WifiConnectivityManager's perspective it only cares
    117     // if it is in Connected state, Disconnected state or in
    118     // transition between these two states.
    119     public static final int WIFI_STATE_UNKNOWN = 0;
    120     public static final int WIFI_STATE_CONNECTED = 1;
    121     public static final int WIFI_STATE_DISCONNECTED = 2;
    122     public static final int WIFI_STATE_TRANSITIONING = 3;
    123 
    124     // Saved network evaluator priority
    125     private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1;
    126     private static final int PASSPOINT_NETWORK_EVALUATOR_PRIORITY = 2;
    127     private static final int SCORED_NETWORK_EVALUATOR_PRIORITY = 3;
    128 
    129     // Log tag for this class
    130     private static final String TAG = "WifiConnectivityManager";
    131 
    132     private final WifiStateMachine mStateMachine;
    133     private final WifiScanner mScanner;
    134     private final WifiConfigManager mConfigManager;
    135     private final WifiInfo mWifiInfo;
    136     private final WifiConnectivityHelper mConnectivityHelper;
    137     private final WifiNetworkSelector mNetworkSelector;
    138     private final WifiLastResortWatchdog mWifiLastResortWatchdog;
    139     private final WifiMetrics mWifiMetrics;
    140     private final AlarmManager mAlarmManager;
    141     private final Handler mEventHandler;
    142     private final Clock mClock;
    143     private final LocalLog mLocalLog;
    144     private final LinkedList<Long> mConnectionAttemptTimeStamps;
    145 
    146     private boolean mDbg = false;
    147     private boolean mWifiEnabled = false;
    148     private boolean mWifiConnectivityManagerEnabled = true;
    149     private boolean mScreenOn = false;
    150     private int mWifiState = WIFI_STATE_UNKNOWN;
    151     private boolean mUntrustedConnectionAllowed = false;
    152     private int mScanRestartCount = 0;
    153     private int mSingleScanRestartCount = 0;
    154     private int mTotalConnectivityAttemptsRateLimited = 0;
    155     private String mLastConnectionAttemptBssid = null;
    156     private int mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
    157     private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
    158     private boolean mPnoScanStarted = false;
    159     private boolean mPeriodicScanTimerSet = false;
    160     // Device configs
    161     private boolean mEnableAutoJoinWhenAssociated;
    162     private boolean mWaitForFullBandScanResults = false;
    163 
    164     // PNO settings
    165     private int mMin5GHzRssi;
    166     private int mMin24GHzRssi;
    167     private int mInitialScoreMax;
    168     private int mCurrentConnectionBonus;
    169     private int mSameNetworkBonus;
    170     private int mSecureBonus;
    171     private int mBand5GHzBonus;
    172 
    173     // BSSID blacklist
    174     @VisibleForTesting
    175     public static final int BSSID_BLACKLIST_THRESHOLD = 3;
    176     @VisibleForTesting
    177     public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000;
    178     private static class BssidBlacklistStatus {
    179         // Number of times this BSSID has been rejected for association.
    180         public int counter;
    181         public boolean isBlacklisted;
    182         public long blacklistedTimeStamp = RESET_TIME_STAMP;
    183     }
    184     private Map<String, BssidBlacklistStatus> mBssidBlacklist =
    185             new HashMap<>();
    186 
    187     // Association failure reason codes
    188     @VisibleForTesting
    189     public static final int REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
    190 
    191     // A helper to log debugging information in the local log buffer, which can
    192     // be retrieved in bugreport.
    193     private void localLog(String log) {
    194         mLocalLog.log(log);
    195     }
    196 
    197     // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
    198     // if the start scan command failed. An timer is used here to make it a deferred retry.
    199     private final AlarmManager.OnAlarmListener mRestartScanListener =
    200             new AlarmManager.OnAlarmListener() {
    201                 public void onAlarm() {
    202                     startConnectivityScan(SCAN_IMMEDIATELY);
    203                 }
    204             };
    205 
    206     // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times
    207     // if the start scan command failed. An timer is used here to make it a deferred retry.
    208     private class RestartSingleScanListener implements AlarmManager.OnAlarmListener {
    209         private final boolean mIsFullBandScan;
    210 
    211         RestartSingleScanListener(boolean isFullBandScan) {
    212             mIsFullBandScan = isFullBandScan;
    213         }
    214 
    215         @Override
    216         public void onAlarm() {
    217             startSingleScan(mIsFullBandScan);
    218         }
    219     }
    220 
    221     // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS
    222     // if it is in the WIFI_STATE_DISCONNECTED state.
    223     private final AlarmManager.OnAlarmListener mWatchdogListener =
    224             new AlarmManager.OnAlarmListener() {
    225                 public void onAlarm() {
    226                     watchdogHandler();
    227                 }
    228             };
    229 
    230     // Due to b/28020168, timer based single scan will be scheduled
    231     // to provide periodic scan in an exponential backoff fashion.
    232     private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener =
    233             new AlarmManager.OnAlarmListener() {
    234                 public void onAlarm() {
    235                     periodicScanTimerHandler();
    236                 }
    237             };
    238 
    239     /**
    240      * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener.
    241      * Executes selection of potential network candidates, initiation of connection attempt to that
    242      * network.
    243      *
    244      * @return true - if a candidate is selected by WifiNetworkSelector
    245      *         false - if no candidate is selected by WifiNetworkSelector
    246      */
    247     private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
    248         // Check if any blacklisted BSSIDs can be freed.
    249         refreshBssidBlacklist();
    250 
    251         if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) {
    252             localLog(listenerName + " onResults: No network selection because linkDebouncing is "
    253                     + mStateMachine.isLinkDebouncing() + " and supplicantTransient is "
    254                     + mStateMachine.isSupplicantTransientState());
    255             return false;
    256         }
    257 
    258         localLog(listenerName + " onResults: start network selection");
    259 
    260         WifiConfiguration candidate =
    261                 mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
    262                 mStateMachine.isConnected(), mStateMachine.isDisconnected(),
    263                 mUntrustedConnectionAllowed);
    264         mWifiLastResortWatchdog.updateAvailableNetworks(
    265                 mNetworkSelector.getFilteredScanDetails());
    266         mWifiMetrics.countScanResults(scanDetails);
    267         if (candidate != null) {
    268             localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
    269             connectToNetwork(candidate);
    270             return true;
    271         } else {
    272             return false;
    273         }
    274     }
    275 
    276     // All single scan results listener.
    277     //
    278     // Note: This is the listener for all the available single scan results,
    279     //       including the ones initiated by WifiConnectivityManager and
    280     //       other modules.
    281     private class AllSingleScanListener implements WifiScanner.ScanListener {
    282         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
    283 
    284         public void clearScanDetails() {
    285             mScanDetails.clear();
    286         }
    287 
    288         @Override
    289         public void onSuccess() {
    290         }
    291 
    292         @Override
    293         public void onFailure(int reason, String description) {
    294             localLog("registerScanListener onFailure:"
    295                       + " reason: " + reason + " description: " + description);
    296         }
    297 
    298         @Override
    299         public void onPeriodChanged(int periodInMs) {
    300         }
    301 
    302         @Override
    303         public void onResults(WifiScanner.ScanData[] results) {
    304             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    305                 clearScanDetails();
    306                 mWaitForFullBandScanResults = false;
    307                 return;
    308             }
    309 
    310             // Full band scan results only.
    311             if (mWaitForFullBandScanResults) {
    312                 if (!results[0].isAllChannelsScanned()) {
    313                     localLog("AllSingleScanListener waiting for full band scan results.");
    314                     clearScanDetails();
    315                     return;
    316                 } else {
    317                     mWaitForFullBandScanResults = false;
    318                 }
    319             }
    320 
    321             boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener");
    322             clearScanDetails();
    323 
    324             // Update metrics to see if a single scan detected a valid network
    325             // while PNO scan didn't.
    326             // Note: We don't update the background scan metrics any more as it is
    327             //       not in use.
    328             if (mPnoScanStarted) {
    329                 if (wasConnectAttempted) {
    330                     mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
    331                 } else {
    332                     mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
    333                 }
    334             }
    335         }
    336 
    337         @Override
    338         public void onFullResult(ScanResult fullScanResult) {
    339             if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    340                 return;
    341             }
    342 
    343             if (mDbg) {
    344                 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID
    345                         + " capabilities " + fullScanResult.capabilities);
    346             }
    347 
    348             mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
    349         }
    350     }
    351 
    352     private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener();
    353 
    354     // Single scan results listener. A single scan is initiated when
    355     // DisconnectedPNO scan found a valid network and woke up
    356     // the system, or by the watchdog timer, or to form the timer based
    357     // periodic scan.
    358     //
    359     // Note: This is the listener for the single scans initiated by the
    360     //        WifiConnectivityManager.
    361     private class SingleScanListener implements WifiScanner.ScanListener {
    362         private final boolean mIsFullBandScan;
    363 
    364         SingleScanListener(boolean isFullBandScan) {
    365             mIsFullBandScan = isFullBandScan;
    366         }
    367 
    368         @Override
    369         public void onSuccess() {
    370         }
    371 
    372         @Override
    373         public void onFailure(int reason, String description) {
    374             localLog("SingleScanListener onFailure:"
    375                     + " reason: " + reason + " description: " + description);
    376 
    377             // reschedule the scan
    378             if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
    379                 scheduleDelayedSingleScan(mIsFullBandScan);
    380             } else {
    381                 mSingleScanRestartCount = 0;
    382                 localLog("Failed to successfully start single scan for "
    383                         + MAX_SCAN_RESTART_ALLOWED + " times");
    384             }
    385         }
    386 
    387         @Override
    388         public void onPeriodChanged(int periodInMs) {
    389             localLog("SingleScanListener onPeriodChanged: "
    390                     + "actual scan period " + periodInMs + "ms");
    391         }
    392 
    393         @Override
    394         public void onResults(WifiScanner.ScanData[] results) {
    395         }
    396 
    397         @Override
    398         public void onFullResult(ScanResult fullScanResult) {
    399         }
    400     }
    401 
    402     // PNO scan results listener for both disconected and connected PNO scanning.
    403     // A PNO scan is initiated when screen is off.
    404     private class PnoScanListener implements WifiScanner.PnoScanListener {
    405         private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
    406         private int mLowRssiNetworkRetryDelay =
    407                 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
    408 
    409         public void clearScanDetails() {
    410             mScanDetails.clear();
    411         }
    412 
    413         // Reset to the start value when either a non-PNO scan is started or
    414         // WifiNetworkSelector selects a candidate from the PNO scan results.
    415         public void resetLowRssiNetworkRetryDelay() {
    416             mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
    417         }
    418 
    419         @VisibleForTesting
    420         public int getLowRssiNetworkRetryDelay() {
    421             return mLowRssiNetworkRetryDelay;
    422         }
    423 
    424         @Override
    425         public void onSuccess() {
    426         }
    427 
    428         @Override
    429         public void onFailure(int reason, String description) {
    430             localLog("PnoScanListener onFailure:"
    431                     + " reason: " + reason + " description: " + description);
    432 
    433             // reschedule the scan
    434             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
    435                 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
    436             } else {
    437                 mScanRestartCount = 0;
    438                 localLog("Failed to successfully start PNO scan for "
    439                         + MAX_SCAN_RESTART_ALLOWED + " times");
    440             }
    441         }
    442 
    443         @Override
    444         public void onPeriodChanged(int periodInMs) {
    445             localLog("PnoScanListener onPeriodChanged: "
    446                     + "actual scan period " + periodInMs + "ms");
    447         }
    448 
    449         // Currently the PNO scan results doesn't include IE,
    450         // which contains information required by WifiNetworkSelector. Ignore them
    451         // for now.
    452         @Override
    453         public void onResults(WifiScanner.ScanData[] results) {
    454         }
    455 
    456         @Override
    457         public void onFullResult(ScanResult fullScanResult) {
    458         }
    459 
    460         @Override
    461         public void onPnoNetworkFound(ScanResult[] results) {
    462             for (ScanResult result: results) {
    463                 mScanDetails.add(ScanResultUtil.toScanDetail(result));
    464             }
    465 
    466             boolean wasConnectAttempted;
    467             wasConnectAttempted = handleScanResults(mScanDetails, "PnoScanListener");
    468             clearScanDetails();
    469             mScanRestartCount = 0;
    470 
    471             if (!wasConnectAttempted) {
    472                 // The scan results were rejected by WifiNetworkSelector due to low RSSI values
    473                 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) {
    474                     mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS;
    475                 }
    476                 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay);
    477 
    478                 // Set up the delay value for next retry.
    479                 mLowRssiNetworkRetryDelay *= 2;
    480             } else {
    481                 resetLowRssiNetworkRetryDelay();
    482             }
    483         }
    484     }
    485 
    486     private final PnoScanListener mPnoScanListener = new PnoScanListener();
    487 
    488     private class OnSavedNetworkUpdateListener implements
    489             WifiConfigManager.OnSavedNetworkUpdateListener {
    490         @Override
    491         public void onSavedNetworkAdded(int networkId) {
    492             updatePnoScan();
    493         }
    494         @Override
    495         public void onSavedNetworkEnabled(int networkId) {
    496             updatePnoScan();
    497         }
    498         @Override
    499         public void onSavedNetworkRemoved(int networkId) {
    500             updatePnoScan();
    501         }
    502         @Override
    503         public void onSavedNetworkUpdated(int networkId) {
    504             updatePnoScan();
    505         }
    506         @Override
    507         public void onSavedNetworkTemporarilyDisabled(int networkId) {
    508             mConnectivityHelper.removeNetworkIfCurrent(networkId);
    509         }
    510         @Override
    511         public void onSavedNetworkPermanentlyDisabled(int networkId) {
    512             mConnectivityHelper.removeNetworkIfCurrent(networkId);
    513             updatePnoScan();
    514         }
    515         private void updatePnoScan() {
    516             // Update the PNO scan network list when screen is off. Here we
    517             // rely on startConnectivityScan() to perform all the checks and clean up.
    518             if (!mScreenOn) {
    519                 localLog("Saved networks updated");
    520                 startConnectivityScan(false);
    521             }
    522         }
    523     }
    524 
    525     /**
    526      * WifiConnectivityManager constructor
    527      */
    528     WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
    529             WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
    530             WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper,
    531             WifiLastResortWatchdog wifiLastResortWatchdog, WifiMetrics wifiMetrics,
    532             Looper looper, Clock clock, LocalLog localLog, boolean enable,
    533             FrameworkFacade frameworkFacade,
    534             SavedNetworkEvaluator savedNetworkEvaluator,
    535             ScoredNetworkEvaluator scoredNetworkEvaluator,
    536             PasspointNetworkEvaluator passpointNetworkEvaluator) {
    537         mStateMachine = stateMachine;
    538         mScanner = scanner;
    539         mConfigManager = configManager;
    540         mWifiInfo = wifiInfo;
    541         mNetworkSelector = networkSelector;
    542         mConnectivityHelper = connectivityHelper;
    543         mLocalLog = localLog;
    544         mWifiLastResortWatchdog = wifiLastResortWatchdog;
    545         mWifiMetrics = wifiMetrics;
    546         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    547         mEventHandler = new Handler(looper);
    548         mClock = clock;
    549         mConnectionAttemptTimeStamps = new LinkedList<>();
    550 
    551         mMin5GHzRssi = context.getResources().getInteger(
    552                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
    553         mMin24GHzRssi = context.getResources().getInteger(
    554                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
    555         mBand5GHzBonus = context.getResources().getInteger(
    556                 R.integer.config_wifi_framework_5GHz_preference_boost_factor);
    557         mCurrentConnectionBonus = context.getResources().getInteger(
    558                 R.integer.config_wifi_framework_current_network_boost);
    559         mSameNetworkBonus = context.getResources().getInteger(
    560                 R.integer.config_wifi_framework_SAME_BSSID_AWARD);
    561         mSecureBonus = context.getResources().getInteger(
    562                 R.integer.config_wifi_framework_SECURITY_AWARD);
    563         int thresholdSaturatedRssi24 = context.getResources().getInteger(
    564                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
    565         mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
    566                 R.bool.config_wifi_framework_enable_associated_network_selection);
    567         mInitialScoreMax = (context.getResources().getInteger(
    568                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)
    569                     + context.getResources().getInteger(
    570                             R.integer.config_wifi_framework_RSSI_SCORE_OFFSET))
    571                 * context.getResources().getInteger(
    572                         R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
    573 
    574         localLog("PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
    575                 + " min24GHzRssi " + mMin24GHzRssi
    576                 + " currentConnectionBonus " + mCurrentConnectionBonus
    577                 + " sameNetworkBonus " + mSameNetworkBonus
    578                 + " secureNetworkBonus " + mSecureBonus
    579                 + " initialScoreMax " + mInitialScoreMax);
    580 
    581         boolean hs2Enabled = context.getResources().getBoolean(
    582                 R.bool.config_wifi_hotspot2_enabled);
    583         localLog("Passpoint is: " + (hs2Enabled ? "enabled" : "disabled"));
    584 
    585         // Register the network evaluators
    586         mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
    587                 SAVED_NETWORK_EVALUATOR_PRIORITY);
    588         if (hs2Enabled) {
    589             mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
    590                     PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
    591         }
    592         mNetworkSelector.registerNetworkEvaluator(scoredNetworkEvaluator,
    593                 SCORED_NETWORK_EVALUATOR_PRIORITY);
    594 
    595         // Register for all single scan results
    596         mScanner.registerScanListener(mAllSingleScanListener);
    597 
    598         // Listen to WifiConfigManager network update events
    599         mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener());
    600 
    601         mWifiConnectivityManagerEnabled = enable;
    602 
    603         localLog("ConnectivityScanManager initialized and "
    604                 + (enable ? "enabled" : "disabled"));
    605     }
    606 
    607     /**
    608      * This checks the connection attempt rate and recommends whether the connection attempt
    609      * should be skipped or not. This attempts to rate limit the rate of connections to
    610      * prevent us from flapping between networks and draining battery rapidly.
    611      */
    612     private boolean shouldSkipConnectionAttempt(Long timeMillis) {
    613         Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator();
    614         // First evict old entries from the queue.
    615         while (attemptIter.hasNext()) {
    616             Long connectionAttemptTimeMillis = attemptIter.next();
    617             if ((timeMillis - connectionAttemptTimeMillis)
    618                     > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) {
    619                 attemptIter.remove();
    620             } else {
    621                 // This list is sorted by timestamps, so we can skip any more checks
    622                 break;
    623             }
    624         }
    625         // If we've reached the max connection attempt rate, skip this connection attempt
    626         return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE);
    627     }
    628 
    629     /**
    630      * Add the current connection attempt timestamp to our queue of connection attempts.
    631      */
    632     private void noteConnectionAttempt(Long timeMillis) {
    633         mConnectionAttemptTimeStamps.addLast(timeMillis);
    634     }
    635 
    636     /**
    637      * This is used to clear the connection attempt rate limiter. This is done when the user
    638      * explicitly tries to connect to a specified network.
    639      */
    640     private void clearConnectionAttemptTimeStamps() {
    641         mConnectionAttemptTimeStamps.clear();
    642     }
    643 
    644     /**
    645      * Attempt to connect to a network candidate.
    646      *
    647      * Based on the currently connected network, this menthod determines whether we should
    648      * connect or roam to the network candidate recommended by WifiNetworkSelector.
    649      */
    650     private void connectToNetwork(WifiConfiguration candidate) {
    651         ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
    652         if (scanResultCandidate == null) {
    653             localLog("connectToNetwork: bad candidate - "  + candidate
    654                     + " scanResult: " + scanResultCandidate);
    655             return;
    656         }
    657 
    658         String targetBssid = scanResultCandidate.BSSID;
    659         String targetAssociationId = candidate.SSID + " : " + targetBssid;
    660 
    661         // Check if we are already connected or in the process of connecting to the target
    662         // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
    663         // in case the firmware automatically roamed to a BSSID different from what
    664         // WifiNetworkSelector selected.
    665         if (targetBssid != null
    666                 && (targetBssid.equals(mLastConnectionAttemptBssid)
    667                     || targetBssid.equals(mWifiInfo.getBSSID()))
    668                 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
    669             localLog("connectToNetwork: Either already connected "
    670                     + "or is connecting to " + targetAssociationId);
    671             return;
    672         }
    673 
    674         if (candidate.BSSID != null
    675                 && !candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY)
    676                 && !candidate.BSSID.equals(targetBssid)) {
    677             localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the "
    678                     + "config specified BSSID " + candidate.BSSID + ". Drop it!");
    679             return;
    680         }
    681 
    682         Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
    683         if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
    684             localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
    685             mTotalConnectivityAttemptsRateLimited++;
    686             return;
    687         }
    688         noteConnectionAttempt(elapsedTimeMillis);
    689 
    690         mLastConnectionAttemptBssid = targetBssid;
    691 
    692         WifiConfiguration currentConnectedNetwork = mConfigManager
    693                 .getConfiguredNetwork(mWifiInfo.getNetworkId());
    694         String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
    695                 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
    696 
    697         if (currentConnectedNetwork != null
    698                 && (currentConnectedNetwork.networkId == candidate.networkId
    699                 //TODO(b/36788683): re-enable linked configuration check
    700                 /* || currentConnectedNetwork.isLinked(candidate) */)) {
    701             // Framework initiates roaming only if firmware doesn't support
    702             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}.
    703             if (mConnectivityHelper.isFirmwareRoamingSupported()) {
    704                 // Keep this logging here for now to validate the firmware roaming behavior.
    705                 localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "."
    706                         + " The actual roaming target is up to the firmware.");
    707             } else {
    708                 localLog("connectToNetwork: Roaming to " + targetAssociationId + " from "
    709                         + currentAssociationId);
    710                 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate);
    711             }
    712         } else {
    713             // Framework specifies the connection target BSSID if firmware doesn't support
    714             // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the
    715             // candidate configuration contains a specified BSSID.
    716             if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null
    717                       || candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY))) {
    718                 targetBssid = WifiStateMachine.SUPPLICANT_BSSID_ANY;
    719                 localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid
    720                         + " from " + currentAssociationId);
    721             } else {
    722                 localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
    723                         + currentAssociationId);
    724             }
    725             mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid);
    726         }
    727     }
    728 
    729     // Helper for selecting the band for connectivity scan
    730     private int getScanBand() {
    731         return getScanBand(true);
    732     }
    733 
    734     private int getScanBand(boolean isFullBandScan) {
    735         if (isFullBandScan) {
    736             return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
    737         } else {
    738             // Use channel list instead.
    739             return WifiScanner.WIFI_BAND_UNSPECIFIED;
    740         }
    741     }
    742 
    743     // Helper for setting the channels for connectivity scan when band is unspecified. Returns
    744     // false if we can't retrieve the info.
    745     private boolean setScanChannels(ScanSettings settings) {
    746         WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration();
    747 
    748         if (config == null) {
    749             return false;
    750         }
    751 
    752         Set<Integer> freqs =
    753                 mConfigManager.fetchChannelSetForNetworkForPartialScan(
    754                         config.networkId, CHANNEL_LIST_AGE_MS, mWifiInfo.getFrequency());
    755 
    756         if (freqs != null && freqs.size() != 0) {
    757             int index = 0;
    758             settings.channels = new WifiScanner.ChannelSpec[freqs.size()];
    759             for (Integer freq : freqs) {
    760                 settings.channels[index++] = new WifiScanner.ChannelSpec(freq);
    761             }
    762             return true;
    763         } else {
    764             localLog("No scan channels for " + config.configKey() + ". Perform full band scan");
    765             return false;
    766         }
    767     }
    768 
    769     // Watchdog timer handler
    770     private void watchdogHandler() {
    771         // Schedule the next timer and start a single scan if we are in disconnected state.
    772         // Otherwise, the watchdog timer will be scheduled when entering disconnected
    773         // state.
    774         if (mWifiState == WIFI_STATE_DISCONNECTED) {
    775             localLog("start a single scan from watchdogHandler");
    776 
    777             scheduleWatchdogTimer();
    778             startSingleScan(true);
    779         }
    780     }
    781 
    782     // Start a single scan and set up the interval for next single scan.
    783     private void startPeriodicSingleScan() {
    784         long currentTimeStamp = mClock.getElapsedSinceBootMillis();
    785 
    786         if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) {
    787             long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp;
    788             if (msSinceLastScan < PERIODIC_SCAN_INTERVAL_MS) {
    789                 localLog("Last periodic single scan started " + msSinceLastScan
    790                         + "ms ago, defer this new scan request.");
    791                 schedulePeriodicScanTimer(PERIODIC_SCAN_INTERVAL_MS - (int) msSinceLastScan);
    792                 return;
    793             }
    794         }
    795 
    796         boolean isFullBandScan = true;
    797 
    798         // If the WiFi traffic is heavy, only partial scan is initiated.
    799         if (mWifiState == WIFI_STATE_CONNECTED
    800                 && (mWifiInfo.txSuccessRate > MAX_TX_PACKET_FOR_FULL_SCANS
    801                     || mWifiInfo.rxSuccessRate > MAX_RX_PACKET_FOR_FULL_SCANS)) {
    802             localLog("No full band scan due to ongoing traffic");
    803             isFullBandScan = false;
    804         }
    805 
    806         mLastPeriodicSingleScanTimeStamp = currentTimeStamp;
    807         startSingleScan(isFullBandScan);
    808         schedulePeriodicScanTimer(mPeriodicSingleScanInterval);
    809 
    810         // Set up the next scan interval in an exponential backoff fashion.
    811         mPeriodicSingleScanInterval *= 2;
    812         if (mPeriodicSingleScanInterval >  MAX_PERIODIC_SCAN_INTERVAL_MS) {
    813             mPeriodicSingleScanInterval = MAX_PERIODIC_SCAN_INTERVAL_MS;
    814         }
    815     }
    816 
    817     // Reset the last periodic single scan time stamp so that the next periodic single
    818     // scan can start immediately.
    819     private void resetLastPeriodicSingleScanTimeStamp() {
    820         mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
    821     }
    822 
    823     // Periodic scan timer handler
    824     private void periodicScanTimerHandler() {
    825         localLog("periodicScanTimerHandler");
    826 
    827         // Schedule the next timer and start a single scan if screen is on.
    828         if (mScreenOn) {
    829             startPeriodicSingleScan();
    830         }
    831     }
    832 
    833     // Start a single scan
    834     private void startSingleScan(boolean isFullBandScan) {
    835         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    836             return;
    837         }
    838 
    839         mPnoScanListener.resetLowRssiNetworkRetryDelay();
    840 
    841         ScanSettings settings = new ScanSettings();
    842         if (!isFullBandScan) {
    843             if (!setScanChannels(settings)) {
    844                 isFullBandScan = true;
    845             }
    846         }
    847         settings.band = getScanBand(isFullBandScan);
    848         settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
    849                             | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
    850         settings.numBssidsPerScan = 0;
    851 
    852         List<ScanSettings.HiddenNetwork> hiddenNetworkList =
    853                 mConfigManager.retrieveHiddenNetworkList();
    854         settings.hiddenNetworks =
    855                 hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
    856 
    857         SingleScanListener singleScanListener =
    858                 new SingleScanListener(isFullBandScan);
    859         mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE);
    860     }
    861 
    862     // Start a periodic scan when screen is on
    863     private void startPeriodicScan(boolean scanImmediately) {
    864         mPnoScanListener.resetLowRssiNetworkRetryDelay();
    865 
    866         // No connectivity scan if auto roaming is disabled.
    867         if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) {
    868             return;
    869         }
    870 
    871         // Due to b/28020168, timer based single scan will be scheduled
    872         // to provide periodic scan in an exponential backoff fashion.
    873         if (scanImmediately) {
    874             resetLastPeriodicSingleScanTimeStamp();
    875         }
    876         mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
    877         startPeriodicSingleScan();
    878     }
    879 
    880     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
    881     private void startDisconnectedPnoScan() {
    882         // TODO(b/29503772): Need to change this interface.
    883 
    884         // Initialize PNO settings
    885         PnoSettings pnoSettings = new PnoSettings();
    886         List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList();
    887         int listSize = pnoNetworkList.size();
    888 
    889         if (listSize == 0) {
    890             // No saved network
    891             localLog("No saved network for starting disconnected PNO.");
    892             return;
    893         }
    894 
    895         pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
    896         pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
    897         pnoSettings.min5GHzRssi = mMin5GHzRssi;
    898         pnoSettings.min24GHzRssi = mMin24GHzRssi;
    899         pnoSettings.initialScoreMax = mInitialScoreMax;
    900         pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
    901         pnoSettings.sameNetworkBonus = mSameNetworkBonus;
    902         pnoSettings.secureBonus = mSecureBonus;
    903         pnoSettings.band5GHzBonus = mBand5GHzBonus;
    904 
    905         // Initialize scan settings
    906         ScanSettings scanSettings = new ScanSettings();
    907         scanSettings.band = getScanBand();
    908         scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
    909         scanSettings.numBssidsPerScan = 0;
    910         scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS;
    911 
    912         mPnoScanListener.clearScanDetails();
    913 
    914         mScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
    915         mPnoScanStarted = true;
    916     }
    917 
    918     // Stop PNO scan.
    919     private void stopPnoScan() {
    920         if (mPnoScanStarted) {
    921             mScanner.stopPnoScan(mPnoScanListener);
    922         }
    923 
    924         mPnoScanStarted = false;
    925     }
    926 
    927     // Set up watchdog timer
    928     private void scheduleWatchdogTimer() {
    929         localLog("scheduleWatchdogTimer");
    930 
    931         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    932                             mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
    933                             WATCHDOG_TIMER_TAG,
    934                             mWatchdogListener, mEventHandler);
    935     }
    936 
    937     // Set up periodic scan timer
    938     private void schedulePeriodicScanTimer(int intervalMs) {
    939         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    940                             mClock.getElapsedSinceBootMillis() + intervalMs,
    941                             PERIODIC_SCAN_TIMER_TAG,
    942                             mPeriodicScanTimerListener, mEventHandler);
    943         mPeriodicScanTimerSet = true;
    944     }
    945 
    946     // Cancel periodic scan timer
    947     private void cancelPeriodicScanTimer() {
    948         if (mPeriodicScanTimerSet) {
    949             mAlarmManager.cancel(mPeriodicScanTimerListener);
    950             mPeriodicScanTimerSet = false;
    951         }
    952     }
    953 
    954     // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS
    955     private void scheduleDelayedSingleScan(boolean isFullBandScan) {
    956         localLog("scheduleDelayedSingleScan");
    957 
    958         RestartSingleScanListener restartSingleScanListener =
    959                 new RestartSingleScanListener(isFullBandScan);
    960         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    961                             mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS,
    962                             RESTART_SINGLE_SCAN_TIMER_TAG,
    963                             restartSingleScanListener, mEventHandler);
    964     }
    965 
    966     // Set up timer to start a delayed scan after msFromNow milli-seconds
    967     private void scheduleDelayedConnectivityScan(int msFromNow) {
    968         localLog("scheduleDelayedConnectivityScan");
    969 
    970         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    971                             mClock.getElapsedSinceBootMillis() + msFromNow,
    972                             RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
    973                             mRestartScanListener, mEventHandler);
    974 
    975     }
    976 
    977     // Start a connectivity scan. The scan method is chosen according to
    978     // the current screen state and WiFi state.
    979     private void startConnectivityScan(boolean scanImmediately) {
    980         localLog("startConnectivityScan: screenOn=" + mScreenOn
    981                 + " wifiState=" + stateToString(mWifiState)
    982                 + " scanImmediately=" + scanImmediately
    983                 + " wifiEnabled=" + mWifiEnabled
    984                 + " wifiConnectivityManagerEnabled="
    985                 + mWifiConnectivityManagerEnabled);
    986 
    987         if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
    988             return;
    989         }
    990 
    991         // Always stop outstanding connecivity scan if there is any
    992         stopConnectivityScan();
    993 
    994         // Don't start a connectivity scan while Wifi is in the transition
    995         // between connected and disconnected states.
    996         if (mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) {
    997             return;
    998         }
    999 
   1000         if (mScreenOn) {
   1001             startPeriodicScan(scanImmediately);
   1002         } else {
   1003             if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
   1004                 startDisconnectedPnoScan();
   1005             }
   1006         }
   1007 
   1008     }
   1009 
   1010     // Stop connectivity scan if there is any.
   1011     private void stopConnectivityScan() {
   1012         // Due to b/28020168, timer based single scan will be scheduled
   1013         // to provide periodic scan in an exponential backoff fashion.
   1014         cancelPeriodicScanTimer();
   1015         stopPnoScan();
   1016         mScanRestartCount = 0;
   1017     }
   1018 
   1019     /**
   1020      * Handler for screen state (on/off) changes
   1021      */
   1022     public void handleScreenStateChanged(boolean screenOn) {
   1023         localLog("handleScreenStateChanged: screenOn=" + screenOn);
   1024 
   1025         mScreenOn = screenOn;
   1026 
   1027         startConnectivityScan(SCAN_ON_SCHEDULE);
   1028     }
   1029 
   1030     /**
   1031      * Helper function that converts the WIFI_STATE_XXX constants to string
   1032      */
   1033     private static String stateToString(int state) {
   1034         switch (state) {
   1035             case WIFI_STATE_CONNECTED:
   1036                 return "connected";
   1037             case WIFI_STATE_DISCONNECTED:
   1038                 return "disconnected";
   1039             case WIFI_STATE_TRANSITIONING:
   1040                 return "transitioning";
   1041             default:
   1042                 return "unknown";
   1043         }
   1044     }
   1045 
   1046     /**
   1047      * Handler for WiFi state (connected/disconnected) changes
   1048      */
   1049     public void handleConnectionStateChanged(int state) {
   1050         localLog("handleConnectionStateChanged: state=" + stateToString(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             startConnectivityScan(SCAN_IMMEDIATELY);
   1060         } else {
   1061             startConnectivityScan(SCAN_ON_SCHEDULE);
   1062         }
   1063     }
   1064 
   1065     /**
   1066      * Handler when user toggles whether untrusted connection is allowed
   1067      */
   1068     public void setUntrustedConnectionAllowed(boolean allowed) {
   1069         localLog("setUntrustedConnectionAllowed: allowed=" + allowed);
   1070 
   1071         if (mUntrustedConnectionAllowed != allowed) {
   1072             mUntrustedConnectionAllowed = allowed;
   1073             startConnectivityScan(SCAN_IMMEDIATELY);
   1074         }
   1075     }
   1076 
   1077     /**
   1078      * Handler when user specifies a particular network to connect to
   1079      */
   1080     public void setUserConnectChoice(int netId) {
   1081         localLog("setUserConnectChoice: netId=" + netId);
   1082 
   1083         mNetworkSelector.setUserConnectChoice(netId);
   1084     }
   1085 
   1086     /**
   1087      * Handler to prepare for connection to a user or app specified network
   1088      */
   1089     public void prepareForForcedConnection(int netId) {
   1090         localLog("prepareForForcedConnection: netId=" + netId);
   1091 
   1092         clearConnectionAttemptTimeStamps();
   1093         clearBssidBlacklist();
   1094     }
   1095 
   1096     /**
   1097      * Handler for on-demand connectivity scan
   1098      */
   1099     public void forceConnectivityScan() {
   1100         localLog("forceConnectivityScan");
   1101 
   1102         mWaitForFullBandScanResults = true;
   1103         startSingleScan(true);
   1104     }
   1105 
   1106     /**
   1107      * Update the BSSID blacklist when a BSSID is enabled or disabled
   1108      *
   1109      * @param bssid the bssid to be enabled/disabled
   1110      * @param enable -- true enable the bssid
   1111      *               -- false disable the bssid
   1112      * @param reasonCode enable/disable reason code
   1113      * @return true if blacklist is updated; false otherwise
   1114      */
   1115     private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) {
   1116         // Remove the bssid from blacklist when it is enabled.
   1117         if (enable) {
   1118             return mBssidBlacklist.remove(bssid) != null;
   1119         }
   1120 
   1121         // Update the bssid's blacklist status when it is disabled because of
   1122         // association rejection.
   1123         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
   1124         if (status == null) {
   1125             // First time for this BSSID
   1126             status = new BssidBlacklistStatus();
   1127             mBssidBlacklist.put(bssid, status);
   1128         }
   1129 
   1130         status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis();
   1131         status.counter++;
   1132         if (!status.isBlacklisted) {
   1133             if (status.counter >= BSSID_BLACKLIST_THRESHOLD
   1134                     || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) {
   1135                 status.isBlacklisted = true;
   1136                 return true;
   1137             }
   1138         }
   1139         return false;
   1140     }
   1141 
   1142     /**
   1143      * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector
   1144      *
   1145      * @param bssid the bssid to be enabled/disabled
   1146      * @param enable -- true enable the bssid
   1147      *               -- false disable the bssid
   1148      * @param reasonCode enable/disable reason code
   1149      * @return true if blacklist is updated; false otherwise
   1150      */
   1151     public boolean trackBssid(String bssid, boolean enable, int reasonCode) {
   1152         localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code "
   1153                 + reasonCode);
   1154 
   1155         if (bssid == null) {
   1156             return false;
   1157         }
   1158 
   1159         if (!updateBssidBlacklist(bssid, enable, reasonCode)) {
   1160             return false;
   1161         }
   1162 
   1163         // Blacklist was updated, so update firmware roaming configuration.
   1164         updateFirmwareRoamingConfiguration();
   1165 
   1166         if (!enable) {
   1167             // Disabling a BSSID can happen when connection to the AP was rejected.
   1168             // We start another scan immediately so that WifiNetworkSelector can
   1169             // give us another candidate to connect to.
   1170             startConnectivityScan(SCAN_IMMEDIATELY);
   1171         }
   1172 
   1173         return true;
   1174     }
   1175 
   1176     /**
   1177      * Check whether a bssid is disabled
   1178      */
   1179     @VisibleForTesting
   1180     public boolean isBssidDisabled(String bssid) {
   1181         BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
   1182         return status == null ? false : status.isBlacklisted;
   1183     }
   1184 
   1185     /**
   1186      * Compile and return a hashset of the blacklisted BSSIDs
   1187      */
   1188     private HashSet<String> buildBssidBlacklist() {
   1189         HashSet<String> blacklistedBssids = new HashSet<String>();
   1190         for (String bssid : mBssidBlacklist.keySet()) {
   1191             if (isBssidDisabled(bssid)) {
   1192                 blacklistedBssids.add(bssid);
   1193             }
   1194         }
   1195 
   1196         return blacklistedBssids;
   1197     }
   1198 
   1199     /**
   1200      * Update firmware roaming configuration if the firmware roaming feature is supported.
   1201      * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always
   1202      * empty for now.
   1203      */
   1204     private void updateFirmwareRoamingConfiguration() {
   1205         if (!mConnectivityHelper.isFirmwareRoamingSupported()) {
   1206             return;
   1207         }
   1208 
   1209         int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid();
   1210         if (maxBlacklistSize <= 0) {
   1211             Log.wtf(TAG, "Invalid max BSSID blacklist size:  " + maxBlacklistSize);
   1212             return;
   1213         }
   1214 
   1215         ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist());
   1216         int blacklistSize = blacklistedBssids.size();
   1217 
   1218         if (blacklistSize > maxBlacklistSize) {
   1219             Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is "
   1220                     + maxBlacklistSize);
   1221 
   1222             blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0,
   1223                     maxBlacklistSize));
   1224             localLog("Trim down BSSID blacklist size from " + blacklistSize + " to "
   1225                     + blacklistedBssids.size());
   1226         }
   1227 
   1228         if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids,
   1229                 new ArrayList<String>())) {  // TODO(b/36488259): SSID whitelist management.
   1230             localLog("Failed to set firmware roaming configuration.");
   1231         }
   1232     }
   1233 
   1234     /**
   1235      * Refresh the BSSID blacklist
   1236      *
   1237      * Go through the BSSID blacklist and check if a BSSID has been blacklisted for
   1238      * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it.
   1239      */
   1240     private void refreshBssidBlacklist() {
   1241         if (mBssidBlacklist.isEmpty()) {
   1242             return;
   1243         }
   1244 
   1245         boolean updated = false;
   1246         Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator();
   1247         Long currentTimeStamp = mClock.getElapsedSinceBootMillis();
   1248 
   1249         while (iter.hasNext()) {
   1250             BssidBlacklistStatus status = iter.next();
   1251             if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp)
   1252                     >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) {
   1253                 iter.remove();
   1254                 updated = true;
   1255             }
   1256         }
   1257 
   1258         if (updated) {
   1259             updateFirmwareRoamingConfiguration();
   1260         }
   1261     }
   1262 
   1263     /**
   1264      * Clear the BSSID blacklist
   1265      */
   1266     private void clearBssidBlacklist() {
   1267         mBssidBlacklist.clear();
   1268         updateFirmwareRoamingConfiguration();
   1269     }
   1270 
   1271     /**
   1272      * Start WifiConnectivityManager
   1273      */
   1274     private void start() {
   1275         mConnectivityHelper.getFirmwareRoamingInfo();
   1276         clearBssidBlacklist();
   1277         startConnectivityScan(SCAN_IMMEDIATELY);
   1278     }
   1279 
   1280     /**
   1281      * Stop and reset WifiConnectivityManager
   1282      */
   1283     private void stop() {
   1284         stopConnectivityScan();
   1285         clearBssidBlacklist();
   1286         resetLastPeriodicSingleScanTimeStamp();
   1287         mLastConnectionAttemptBssid = null;
   1288         mWaitForFullBandScanResults = false;
   1289     }
   1290 
   1291     /**
   1292      * Update WifiConnectivityManager running state
   1293      *
   1294      * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager
   1295      * are enabled, otherwise stop it.
   1296      */
   1297     private void updateRunningState() {
   1298         if (mWifiEnabled && mWifiConnectivityManagerEnabled) {
   1299             localLog("Starting up WifiConnectivityManager");
   1300             start();
   1301         } else {
   1302             localLog("Stopping WifiConnectivityManager");
   1303             stop();
   1304         }
   1305     }
   1306 
   1307     /**
   1308      * Inform WiFi is enabled for connection or not
   1309      */
   1310     public void setWifiEnabled(boolean enable) {
   1311         localLog("Set WiFi " + (enable ? "enabled" : "disabled"));
   1312 
   1313         mWifiEnabled = enable;
   1314         updateRunningState();
   1315 
   1316     }
   1317 
   1318     /**
   1319      * Turn on/off the WifiConnectivityManager at runtime
   1320      */
   1321     public void enable(boolean enable) {
   1322         localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
   1323 
   1324         mWifiConnectivityManagerEnabled = enable;
   1325         updateRunningState();
   1326     }
   1327 
   1328     @VisibleForTesting
   1329     int getLowRssiNetworkRetryDelay() {
   1330         return mPnoScanListener.getLowRssiNetworkRetryDelay();
   1331     }
   1332 
   1333     @VisibleForTesting
   1334     long getLastPeriodicSingleScanTimeStamp() {
   1335         return mLastPeriodicSingleScanTimeStamp;
   1336     }
   1337 
   1338     /**
   1339      * Dump the local logs.
   1340      */
   1341     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1342         pw.println("Dump of WifiConnectivityManager");
   1343         pw.println("WifiConnectivityManager - Log Begin ----");
   1344         mLocalLog.dump(fd, pw, args);
   1345         pw.println("WifiConnectivityManager - Log End ----");
   1346     }
   1347 }
   1348