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