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 17package com.android.server.wifi; 18 19import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; 20 21import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE; 22 23import android.app.AlarmManager; 24import android.content.Context; 25import android.content.pm.PackageManager; 26import android.net.wifi.ScanResult; 27import android.net.wifi.SupplicantState; 28import android.net.wifi.WifiConfiguration; 29import android.net.wifi.WifiInfo; 30import android.net.wifi.WifiScanner; 31import android.net.wifi.WifiScanner.PnoSettings; 32import android.net.wifi.WifiScanner.ScanSettings; 33import android.os.Handler; 34import android.os.Looper; 35import android.os.Process; 36import android.os.WorkSource; 37import android.util.LocalLog; 38import android.util.Log; 39 40import com.android.internal.R; 41import com.android.internal.annotations.VisibleForTesting; 42import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator; 43import com.android.server.wifi.util.ScanResultUtil; 44 45import java.io.FileDescriptor; 46import java.io.PrintWriter; 47import java.util.ArrayList; 48import java.util.HashMap; 49import java.util.HashSet; 50import java.util.Iterator; 51import java.util.LinkedList; 52import java.util.List; 53import java.util.Map; 54import 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 */ 64public 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