1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; 21 22 import android.annotation.NonNull; 23 import android.app.AlarmManager; 24 import android.content.Context; 25 import android.net.MacAddress; 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.WifiManager; 31 import android.net.wifi.WifiManager.DeviceMobilityState; 32 import android.net.wifi.WifiNetworkSuggestion; 33 import android.net.wifi.WifiScanner; 34 import android.net.wifi.WifiScanner.PnoSettings; 35 import android.net.wifi.WifiScanner.ScanSettings; 36 import android.net.wifi.hotspot2.PasspointConfiguration; 37 import android.os.Handler; 38 import android.os.HandlerExecutor; 39 import android.os.Process; 40 import android.os.WorkSource; 41 import android.util.ArrayMap; 42 import android.util.LocalLog; 43 import android.util.Log; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.server.wifi.util.ScanResultUtil; 48 import com.android.wifi.resources.R; 49 50 import java.io.FileDescriptor; 51 import java.io.PrintWriter; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Collections; 55 import java.util.HashSet; 56 import java.util.Iterator; 57 import java.util.LinkedList; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.Set; 61 import java.util.stream.Collectors; 62 63 /** 64 * This class manages all the connectivity related scanning activities. 65 * 66 * When the screen is turned on or off, WiFi is connected or disconnected, 67 * or on-demand, a scan is initiatiated and the scan results are passed 68 * to WifiNetworkSelector for it to make a recommendation on which network 69 * to connect to. 70 */ 71 public class WifiConnectivityManager { 72 public static final String WATCHDOG_TIMER_TAG = 73 "WifiConnectivityManager Schedule Watchdog Timer"; 74 public static final String PERIODIC_SCAN_TIMER_TAG = 75 "WifiConnectivityManager Schedule Periodic Scan Timer"; 76 public static final String RESTART_SINGLE_SCAN_TIMER_TAG = 77 "WifiConnectivityManager Restart Single Scan"; 78 public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG = 79 "WifiConnectivityManager Restart Scan"; 80 public static final String DELAYED_PARTIAL_SCAN_TIMER_TAG = 81 "WifiConnectivityManager Schedule Delayed Partial Scan Timer"; 82 83 private static final long RESET_TIME_STAMP = Long.MIN_VALUE; 84 // Constants to indicate whether a scan should start immediately or 85 // it should comply to the minimum scan interval rule. 86 private static final boolean SCAN_IMMEDIATELY = true; 87 private static final boolean SCAN_ON_SCHEDULE = false; 88 89 // PNO scan interval in milli-seconds. This is the scan 90 // performed when screen is off and connected. 91 private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 92 // When a network is found by PNO scan but gets rejected by Wifi Network Selector due 93 // to its low RSSI value, scan will be reschduled in an exponential back off manner. 94 private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds 95 private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds 96 // Maximum number of retries when starting a scan failed 97 @VisibleForTesting 98 public static final int MAX_SCAN_RESTART_ALLOWED = 5; 99 // Number of milli-seconds to delay before retry starting 100 // a previously failed scan 101 private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds 102 // When in disconnected mode, a watchdog timer will be fired 103 // every WATCHDOG_INTERVAL_MS to start a single scan. This is 104 // to prevent caveat from things like PNO scan. 105 private static final int WATCHDOG_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes 106 // Restricted channel list age out value. 107 private static final long CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour 108 // This is the time interval for the connection attempt rate calculation. Connection attempt 109 // timestamps beyond this interval is evicted from the list. 110 public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins 111 // Max number of connection attempts in the above time interval. 112 public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6; 113 private static final int TEMP_BSSID_BLOCK_DURATION = 10 * 1000; // 10 seconds 114 // Maximum age of frequencies last seen to be included in pno scans. (30 days) 115 private static final long MAX_PNO_SCAN_FREQUENCY_AGE_MS = (long) 1000 * 3600 * 24 * 30; 116 // ClientModeImpl 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 // Initial scan state, used to manage performing partial scans in initial scans 126 // Initial scans are the first scan after enabling Wifi or turning on screen when disconnected 127 private static final int INITIAL_SCAN_STATE_START = 0; 128 private static final int INITIAL_SCAN_STATE_AWAITING_RESPONSE = 1; 129 private static final int INITIAL_SCAN_STATE_COMPLETE = 2; 130 131 // Log tag for this class 132 private static final String TAG = "WifiConnectivityManager"; 133 private static final String ALL_SINGLE_SCAN_LISTENER = "AllSingleScanListener"; 134 private static final String PNO_SCAN_LISTENER = "PnoScanListener"; 135 136 private final Context mContext; 137 private final ClientModeImpl mStateMachine; 138 private final WifiInjector mWifiInjector; 139 private final WifiConfigManager mConfigManager; 140 private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; 141 private final WifiInfo mWifiInfo; 142 private final WifiConnectivityHelper mConnectivityHelper; 143 private final WifiNetworkSelector mNetworkSelector; 144 private final WifiLastResortWatchdog mWifiLastResortWatchdog; 145 private final OpenNetworkNotifier mOpenNetworkNotifier; 146 private final WifiMetrics mWifiMetrics; 147 private final AlarmManager mAlarmManager; 148 private final Handler mEventHandler; 149 private final Clock mClock; 150 private final ScoringParams mScoringParams; 151 private final LocalLog mLocalLog; 152 private final LinkedList<Long> mConnectionAttemptTimeStamps; 153 private final BssidBlocklistMonitor mBssidBlocklistMonitor; 154 private WifiScanner mScanner; 155 private WifiScoreCard mWifiScoreCard; 156 157 private boolean mDbg = false; 158 private boolean mVerboseLoggingEnabled = false; 159 private boolean mWifiEnabled = false; 160 private boolean mAutoJoinEnabled = false; // disabled by default, enabled by external triggers 161 private boolean mRunning = false; 162 private boolean mScreenOn = false; 163 private int mWifiState = WIFI_STATE_UNKNOWN; 164 private int mInitialScanState = INITIAL_SCAN_STATE_COMPLETE; 165 private boolean mAutoJoinEnabledExternal = true; // enabled by default 166 private boolean mUntrustedConnectionAllowed = false; 167 private boolean mTrustedConnectionAllowed = false; 168 private boolean mSpecificNetworkRequestInProgress = false; 169 private int mScanRestartCount = 0; 170 private int mSingleScanRestartCount = 0; 171 private int mTotalConnectivityAttemptsRateLimited = 0; 172 private String mLastConnectionAttemptBssid = null; 173 private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 174 private long mLastNetworkSelectionTimeStamp = RESET_TIME_STAMP; 175 private boolean mPnoScanStarted = false; 176 private boolean mPeriodicScanTimerSet = false; 177 private boolean mDelayedPartialScanTimerSet = false; 178 179 // Used for Initial Scan metrics 180 private boolean mFailedInitialPartialScan = false; 181 private int mInitialPartialScanChannelCount; 182 183 // Device configs 184 private boolean mWaitForFullBandScanResults = false; 185 186 // Scanning Schedules 187 // Default schedule used in case of invalid configuration 188 private static final int[] DEFAULT_SCANNING_SCHEDULE_SEC = {20, 40, 80, 160}; 189 private int[] mConnectedSingleScanScheduleSec; 190 private int[] mDisconnectedSingleScanScheduleSec; 191 private int[] mConnectedSingleSavedNetworkSingleScanScheduleSec; 192 private List<WifiCandidates.Candidate> mLatestCandidates = null; 193 private long mLatestCandidatesTimestampMs = 0; 194 195 private final Object mLock = new Object(); 196 197 @GuardedBy("mLock") 198 private int[] mCurrentSingleScanScheduleSec; 199 200 private int mCurrentSingleScanScheduleIndex; 201 private WifiChannelUtilization mWifiChannelUtilization; 202 // Cached WifiCandidates used in high mobility state to avoid connecting to APs that are 203 // moving relative to the user. 204 private CachedWifiCandidates mCachedWifiCandidates = null; 205 private @DeviceMobilityState int mDeviceMobilityState = 206 WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN; 207 208 // A helper to log debugging information in the local log buffer, which can 209 // be retrieved in bugreport. localLog(String log)210 private void localLog(String log) { 211 mLocalLog.log(log); 212 if (mVerboseLoggingEnabled) Log.v(TAG, log); 213 } 214 215 /** 216 * Enable verbose logging for WifiConnectivityManager. 217 */ enableVerboseLogging(boolean verbose)218 public void enableVerboseLogging(boolean verbose) { 219 mVerboseLoggingEnabled = verbose; 220 } 221 222 // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 223 // if the start scan command failed. A timer is used here to make it a deferred retry. 224 private final AlarmManager.OnAlarmListener mRestartScanListener = 225 new AlarmManager.OnAlarmListener() { 226 public void onAlarm() { 227 startConnectivityScan(SCAN_IMMEDIATELY); 228 } 229 }; 230 231 // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 232 // if the start scan command failed. An timer is used here to make it a deferred retry. 233 private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { 234 private final boolean mIsFullBandScan; 235 RestartSingleScanListener(boolean isFullBandScan)236 RestartSingleScanListener(boolean isFullBandScan) { 237 mIsFullBandScan = isFullBandScan; 238 } 239 240 @Override onAlarm()241 public void onAlarm() { 242 startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE); 243 } 244 } 245 246 // As a watchdog mechanism, a single scan will be scheduled every WATCHDOG_INTERVAL_MS 247 // if it is in the WIFI_STATE_DISCONNECTED state. 248 private final AlarmManager.OnAlarmListener mWatchdogListener = 249 new AlarmManager.OnAlarmListener() { 250 public void onAlarm() { 251 watchdogHandler(); 252 } 253 }; 254 255 // Due to b/28020168, timer based single scan will be scheduled 256 // to provide periodic scan in an exponential backoff fashion. 257 private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener = 258 new AlarmManager.OnAlarmListener() { 259 public void onAlarm() { 260 periodicScanTimerHandler(); 261 } 262 }; 263 264 private final AlarmManager.OnAlarmListener mDelayedPartialScanTimerListener = 265 new AlarmManager.OnAlarmListener() { 266 public void onAlarm() { 267 if (mCachedWifiCandidates == null 268 || mCachedWifiCandidates.frequencies == null 269 || mCachedWifiCandidates.frequencies.size() == 0) { 270 return; 271 } 272 ScanSettings settings = new ScanSettings(); 273 settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; 274 settings.band = getScanBand(false); 275 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 276 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 277 settings.numBssidsPerScan = 0; 278 int index = 0; 279 settings.channels = 280 new WifiScanner.ChannelSpec[mCachedWifiCandidates.frequencies.size()]; 281 for (Integer freq : mCachedWifiCandidates.frequencies) { 282 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 283 } 284 SingleScanListener singleScanListener = new SingleScanListener(false); 285 mScanner.startScan(settings, new HandlerExecutor(mEventHandler), 286 singleScanListener, WIFI_WORK_SOURCE); 287 mWifiMetrics.incrementConnectivityOneshotScanCount(); 288 } 289 }; 290 291 /** 292 * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener. 293 * Executes selection of potential network candidates, initiation of connection attempt to that 294 * network. 295 * 296 * @return true - if a candidate is selected by WifiNetworkSelector 297 * false - if no candidate is selected by WifiNetworkSelector 298 */ handleScanResults(List<ScanDetail> scanDetails, String listenerName, boolean isFullScan)299 private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName, 300 boolean isFullScan) { 301 mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization( 302 mStateMachine.getWifiLinkLayerStats(), WifiChannelUtilization.UNKNOWN_FREQ); 303 304 updateUserDisabledList(scanDetails); 305 306 // Check if any blocklisted BSSIDs can be freed. 307 mBssidBlocklistMonitor.tryEnablingBlockedBssids(scanDetails); 308 Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklistForSsid( 309 mWifiInfo.getSSID()); 310 311 if (mStateMachine.isSupplicantTransientState()) { 312 localLog(listenerName 313 + " onResults: No network selection because supplicantTransientState is " 314 + mStateMachine.isSupplicantTransientState()); 315 return false; 316 } 317 318 localLog(listenerName + " onResults: start network selection"); 319 320 List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan( 321 scanDetails, bssidBlocklist, mWifiInfo, mStateMachine.isConnected(), 322 mStateMachine.isDisconnected(), mUntrustedConnectionAllowed); 323 mLatestCandidates = candidates; 324 mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis(); 325 326 if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT 327 && mContext.getResources().getBoolean( 328 R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) { 329 candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan); 330 } 331 332 WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates); 333 mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); 334 mWifiLastResortWatchdog.updateAvailableNetworks( 335 mNetworkSelector.getConnectableScanDetails()); 336 mWifiMetrics.countScanResults(scanDetails); 337 if (candidate != null) { 338 localLog(listenerName + ": WNS candidate-" + candidate.SSID); 339 connectToNetwork(candidate); 340 return true; 341 } else { 342 if (mWifiState == WIFI_STATE_DISCONNECTED) { 343 mOpenNetworkNotifier.handleScanResults( 344 mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); 345 } 346 return false; 347 } 348 } 349 filterCandidatesHighMovement( List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan)350 private List<WifiCandidates.Candidate> filterCandidatesHighMovement( 351 List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan) { 352 boolean isNotPartialScan = isFullScan || listenerName.equals(PNO_SCAN_LISTENER); 353 if (candidates == null || candidates.isEmpty()) { 354 // No connectable networks nearby or network selection is unnecessary 355 if (isNotPartialScan) { 356 mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 357 null); 358 } 359 return null; 360 } 361 362 long minimumTimeBetweenScansMs = mContext.getResources().getInteger( 363 R.integer.config_wifiHighMovementNetworkSelectionOptimizationScanDelayMs); 364 if (mCachedWifiCandidates != null && mCachedWifiCandidates.candidateRssiMap != null) { 365 // cached candidates are too recent, wait for next scan 366 if (mClock.getElapsedSinceBootMillis() - mCachedWifiCandidates.timeSinceBootMs 367 < minimumTimeBetweenScansMs) { 368 mWifiMetrics.incrementNumHighMovementConnectionSkipped(); 369 return null; 370 } 371 372 int rssiDelta = mContext.getResources().getInteger(R.integer 373 .config_wifiHighMovementNetworkSelectionOptimizationRssiDelta); 374 List<WifiCandidates.Candidate> filteredCandidates = candidates.stream().filter( 375 item -> mCachedWifiCandidates.candidateRssiMap.containsKey(item.getKey()) 376 && Math.abs(mCachedWifiCandidates.candidateRssiMap.get(item.getKey()) 377 - item.getScanRssi()) < rssiDelta) 378 .collect(Collectors.toList()); 379 380 if (!filteredCandidates.isEmpty()) { 381 if (isNotPartialScan) { 382 mCachedWifiCandidates = 383 new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 384 candidates); 385 } 386 mWifiMetrics.incrementNumHighMovementConnectionStarted(); 387 return filteredCandidates; 388 } 389 } 390 391 // Either no cached candidates, or all candidates got filtered out. 392 // Update the cached candidates here and schedule a delayed partial scan. 393 if (isNotPartialScan) { 394 mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 395 candidates); 396 localLog("Found " + candidates.size() + " candidates at high mobility state. " 397 + "Re-doing scan to confirm network quality."); 398 scheduleDelayedPartialScan(minimumTimeBetweenScansMs); 399 } 400 mWifiMetrics.incrementNumHighMovementConnectionSkipped(); 401 return null; 402 } 403 updateUserDisabledList(List<ScanDetail> scanDetails)404 private void updateUserDisabledList(List<ScanDetail> scanDetails) { 405 List<String> results = new ArrayList<>(); 406 List<ScanResult> passpointAp = new ArrayList<>(); 407 for (ScanDetail scanDetail : scanDetails) { 408 results.add(ScanResultUtil.createQuotedSSID(scanDetail.getScanResult().SSID)); 409 if (!scanDetail.getScanResult().isPasspointNetwork()) { 410 continue; 411 } 412 passpointAp.add(scanDetail.getScanResult()); 413 } 414 if (!passpointAp.isEmpty()) { 415 results.addAll(new ArrayList<>(mWifiInjector.getPasspointManager() 416 .getAllMatchingPasspointProfilesForScanResults(passpointAp).keySet())); 417 } 418 mConfigManager.updateUserDisabledList(results); 419 } 420 421 /** 422 * Set whether bluetooth is in the connected state 423 */ setBluetoothConnected(boolean isBluetoothConnected)424 public void setBluetoothConnected(boolean isBluetoothConnected) { 425 mNetworkSelector.setBluetoothConnected(isBluetoothConnected); 426 } 427 428 private class CachedWifiCandidates { 429 public final long timeSinceBootMs; 430 public final Map<WifiCandidates.Key, Integer> candidateRssiMap; 431 public final Set<Integer> frequencies; 432 CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates)433 CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates) { 434 this.timeSinceBootMs = timeSinceBootMs; 435 if (candidates == null) { 436 this.candidateRssiMap = null; 437 this.frequencies = null; 438 } else { 439 this.candidateRssiMap = new ArrayMap<WifiCandidates.Key, Integer>(); 440 this.frequencies = new HashSet<Integer>(); 441 for (WifiCandidates.Candidate c : candidates) { 442 candidateRssiMap.put(c.getKey(), c.getScanRssi()); 443 frequencies.add(c.getFrequency()); 444 } 445 } 446 } 447 } 448 449 // All single scan results listener. 450 // 451 // Note: This is the listener for all the available single scan results, 452 // including the ones initiated by WifiConnectivityManager and 453 // other modules. 454 private class AllSingleScanListener implements WifiScanner.ScanListener { 455 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 456 private int mNumScanResultsIgnoredDueToSingleRadioChain = 0; 457 clearScanDetails()458 public void clearScanDetails() { 459 mScanDetails.clear(); 460 mNumScanResultsIgnoredDueToSingleRadioChain = 0; 461 } 462 463 @Override onSuccess()464 public void onSuccess() { 465 } 466 467 @Override onFailure(int reason, String description)468 public void onFailure(int reason, String description) { 469 localLog("registerScanListener onFailure:" 470 + " reason: " + reason + " description: " + description); 471 } 472 473 @Override onPeriodChanged(int periodInMs)474 public void onPeriodChanged(int periodInMs) { 475 } 476 477 @Override onResults(WifiScanner.ScanData[] results)478 public void onResults(WifiScanner.ScanData[] results) { 479 if (!mWifiEnabled || !mAutoJoinEnabled) { 480 clearScanDetails(); 481 mWaitForFullBandScanResults = false; 482 return; 483 } 484 485 // We treat any full band scans (with DFS or not) as "full". 486 boolean isFullBandScanResults = false; 487 if (results != null && results.length > 0) { 488 isFullBandScanResults = 489 WifiScanner.isFullBandScan(results[0].getBandScanned(), true); 490 } 491 // Full band scan results only. 492 if (mWaitForFullBandScanResults) { 493 if (!isFullBandScanResults) { 494 localLog("AllSingleScanListener waiting for full band scan results."); 495 clearScanDetails(); 496 return; 497 } else { 498 mWaitForFullBandScanResults = false; 499 } 500 } 501 if (results != null && results.length > 0) { 502 mWifiMetrics.incrementAvailableNetworksHistograms(mScanDetails, 503 isFullBandScanResults); 504 } 505 if (mNumScanResultsIgnoredDueToSingleRadioChain > 0) { 506 Log.i(TAG, "Number of scan results ignored due to single radio chain scan: " 507 + mNumScanResultsIgnoredDueToSingleRadioChain); 508 } 509 boolean wasConnectAttempted = handleScanResults(mScanDetails, 510 ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults); 511 clearScanDetails(); 512 513 // Update metrics to see if a single scan detected a valid network 514 // while PNO scan didn't. 515 // Note: We don't update the background scan metrics any more as it is 516 // not in use. 517 if (mPnoScanStarted) { 518 if (wasConnectAttempted) { 519 mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); 520 } else { 521 mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); 522 } 523 } 524 525 // Check if we are in the middle of initial partial scan 526 if (mInitialScanState == INITIAL_SCAN_STATE_AWAITING_RESPONSE) { 527 // Done with initial scan 528 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE); 529 530 if (wasConnectAttempted) { 531 Log.i(TAG, "Connection attempted with the reduced initial scans"); 532 schedulePeriodicScanTimer( 533 getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex)); 534 mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, true); 535 mInitialPartialScanChannelCount = 0; 536 } else { 537 Log.i(TAG, "Connection was not attempted, issuing a full scan"); 538 startConnectivityScan(SCAN_IMMEDIATELY); 539 mFailedInitialPartialScan = true; 540 } 541 } else if (mInitialScanState == INITIAL_SCAN_STATE_COMPLETE) { 542 if (mFailedInitialPartialScan && wasConnectAttempted) { 543 // Initial scan failed, but following full scan succeeded 544 mWifiMetrics.reportInitialPartialScan(mInitialPartialScanChannelCount, false); 545 } 546 mFailedInitialPartialScan = false; 547 mInitialPartialScanChannelCount = 0; 548 } 549 } 550 551 @Override onFullResult(ScanResult fullScanResult)552 public void onFullResult(ScanResult fullScanResult) { 553 if (!mWifiEnabled || !mAutoJoinEnabled) { 554 return; 555 } 556 557 if (mDbg) { 558 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID 559 + " capabilities " + fullScanResult.capabilities); 560 } 561 562 // When the scan result has radio chain info, ensure we throw away scan results 563 // not received with both radio chains (if |mUseSingleRadioChainScanResults| is false). 564 if (!mContext.getResources().getBoolean( 565 R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection) 566 && fullScanResult.radioChainInfos != null 567 && fullScanResult.radioChainInfos.length == 1) { 568 // Keep track of the number of dropped scan results for logging. 569 mNumScanResultsIgnoredDueToSingleRadioChain++; 570 return; 571 } 572 573 mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult)); 574 } 575 } 576 577 private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); 578 579 // Single scan results listener. A single scan is initiated when 580 // DisconnectedPNO scan found a valid network and woke up 581 // the system, or by the watchdog timer, or to form the timer based 582 // periodic scan. 583 // 584 // Note: This is the listener for the single scans initiated by the 585 // WifiConnectivityManager. 586 private class SingleScanListener implements WifiScanner.ScanListener { 587 private final boolean mIsFullBandScan; 588 SingleScanListener(boolean isFullBandScan)589 SingleScanListener(boolean isFullBandScan) { 590 mIsFullBandScan = isFullBandScan; 591 } 592 593 @Override onSuccess()594 public void onSuccess() { 595 } 596 597 @Override onFailure(int reason, String description)598 public void onFailure(int reason, String description) { 599 localLog("SingleScanListener onFailure:" 600 + " reason: " + reason + " description: " + description); 601 602 // reschedule the scan 603 if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 604 scheduleDelayedSingleScan(mIsFullBandScan); 605 } else { 606 mSingleScanRestartCount = 0; 607 localLog("Failed to successfully start single scan for " 608 + MAX_SCAN_RESTART_ALLOWED + " times"); 609 } 610 } 611 612 @Override onPeriodChanged(int periodInMs)613 public void onPeriodChanged(int periodInMs) { 614 localLog("SingleScanListener onPeriodChanged: " 615 + "actual scan period " + periodInMs + "ms"); 616 } 617 618 @Override onResults(WifiScanner.ScanData[] results)619 public void onResults(WifiScanner.ScanData[] results) { 620 mSingleScanRestartCount = 0; 621 } 622 623 @Override onFullResult(ScanResult fullScanResult)624 public void onFullResult(ScanResult fullScanResult) { 625 } 626 } 627 628 // PNO scan results listener for both disconnected and connected PNO scanning. 629 // A PNO scan is initiated when screen is off. 630 private class PnoScanListener implements WifiScanner.PnoScanListener { 631 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 632 private int mLowRssiNetworkRetryDelay = 633 LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 634 clearScanDetails()635 public void clearScanDetails() { 636 mScanDetails.clear(); 637 } 638 639 // Reset to the start value when either a non-PNO scan is started or 640 // WifiNetworkSelector selects a candidate from the PNO scan results. resetLowRssiNetworkRetryDelay()641 public void resetLowRssiNetworkRetryDelay() { 642 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS; 643 } 644 645 @VisibleForTesting getLowRssiNetworkRetryDelay()646 public int getLowRssiNetworkRetryDelay() { 647 return mLowRssiNetworkRetryDelay; 648 } 649 650 @Override onSuccess()651 public void onSuccess() { 652 } 653 654 @Override onFailure(int reason, String description)655 public void onFailure(int reason, String description) { 656 localLog("PnoScanListener onFailure:" 657 + " reason: " + reason + " description: " + description); 658 659 // reschedule the scan 660 if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 661 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS); 662 } else { 663 mScanRestartCount = 0; 664 localLog("Failed to successfully start PNO scan for " 665 + MAX_SCAN_RESTART_ALLOWED + " times"); 666 } 667 } 668 669 @Override onPeriodChanged(int periodInMs)670 public void onPeriodChanged(int periodInMs) { 671 localLog("PnoScanListener onPeriodChanged: " 672 + "actual scan period " + periodInMs + "ms"); 673 } 674 675 // Currently the PNO scan results doesn't include IE, 676 // which contains information required by WifiNetworkSelector. Ignore them 677 // for now. 678 @Override onResults(WifiScanner.ScanData[] results)679 public void onResults(WifiScanner.ScanData[] results) { 680 } 681 682 @Override onFullResult(ScanResult fullScanResult)683 public void onFullResult(ScanResult fullScanResult) { 684 } 685 686 @Override onPnoNetworkFound(ScanResult[] results)687 public void onPnoNetworkFound(ScanResult[] results) { 688 for (ScanResult result: results) { 689 if (result.informationElements == null) { 690 localLog("Skipping scan result with null information elements"); 691 continue; 692 } 693 mScanDetails.add(ScanResultUtil.toScanDetail(result)); 694 } 695 696 boolean wasConnectAttempted; 697 wasConnectAttempted = handleScanResults(mScanDetails, PNO_SCAN_LISTENER, false); 698 clearScanDetails(); 699 mScanRestartCount = 0; 700 701 if (!wasConnectAttempted) { 702 // The scan results were rejected by WifiNetworkSelector due to low RSSI values 703 if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) { 704 mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS; 705 } 706 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelay); 707 708 // Set up the delay value for next retry. 709 mLowRssiNetworkRetryDelay *= 2; 710 } else { 711 resetLowRssiNetworkRetryDelay(); 712 } 713 } 714 } 715 716 private final PnoScanListener mPnoScanListener = new PnoScanListener(); 717 718 private class OnNetworkUpdateListener implements 719 WifiConfigManager.OnNetworkUpdateListener { 720 @Override onNetworkAdded(WifiConfiguration config)721 public void onNetworkAdded(WifiConfiguration config) { 722 triggerScanOnNetworkChanges(); 723 } 724 @Override onNetworkEnabled(WifiConfiguration config)725 public void onNetworkEnabled(WifiConfiguration config) { 726 triggerScanOnNetworkChanges(); 727 } 728 @Override onNetworkRemoved(WifiConfiguration config)729 public void onNetworkRemoved(WifiConfiguration config) { 730 triggerScanOnNetworkChanges(); 731 } 732 @Override onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig)733 public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig) { 734 triggerScanOnNetworkChanges(); 735 } 736 @Override onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason)737 public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) { } 738 739 @Override onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason)740 public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) { 741 triggerScanOnNetworkChanges(); 742 } 743 } 744 745 private class OnSuggestionUpdateListener implements 746 WifiNetworkSuggestionsManager.OnSuggestionUpdateListener { 747 @Override onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions)748 public void onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions) { 749 triggerScanOnNetworkChanges(); 750 } 751 752 @Override onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions)753 public void onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions) { 754 triggerScanOnNetworkChanges(); 755 } 756 } 757 758 /** 759 * WifiConnectivityManager constructor 760 */ WifiConnectivityManager(Context context, ScoringParams scoringParams, ClientModeImpl stateMachine, WifiInjector injector, WifiConfigManager configManager, WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, WifiInfo wifiInfo, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, WifiMetrics wifiMetrics, Handler handler, Clock clock, LocalLog localLog, WifiScoreCard scoreCard)761 WifiConnectivityManager(Context context, ScoringParams scoringParams, 762 ClientModeImpl stateMachine, 763 WifiInjector injector, WifiConfigManager configManager, 764 WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, WifiInfo wifiInfo, 765 WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, 766 WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, 767 WifiMetrics wifiMetrics, Handler handler, 768 Clock clock, LocalLog localLog, WifiScoreCard scoreCard) { 769 mContext = context; 770 mStateMachine = stateMachine; 771 mWifiInjector = injector; 772 mConfigManager = configManager; 773 mWifiNetworkSuggestionsManager = wifiNetworkSuggestionsManager; 774 mWifiInfo = wifiInfo; 775 mNetworkSelector = networkSelector; 776 mConnectivityHelper = connectivityHelper; 777 mLocalLog = localLog; 778 mWifiLastResortWatchdog = wifiLastResortWatchdog; 779 mOpenNetworkNotifier = openNetworkNotifier; 780 mWifiMetrics = wifiMetrics; 781 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 782 mEventHandler = handler; 783 mClock = clock; 784 mScoringParams = scoringParams; 785 mConnectionAttemptTimeStamps = new LinkedList<>(); 786 787 // Listen to WifiConfigManager network update events 788 mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()); 789 // Listen to WifiNetworkSuggestionsManager suggestion update events 790 mWifiNetworkSuggestionsManager.addOnSuggestionUpdateListener( 791 new OnSuggestionUpdateListener()); 792 mBssidBlocklistMonitor = mWifiInjector.getBssidBlocklistMonitor(); 793 mWifiChannelUtilization = mWifiInjector.getWifiChannelUtilizationScan(); 794 mNetworkSelector.setWifiChannelUtilization(mWifiChannelUtilization); 795 mWifiScoreCard = scoreCard; 796 } 797 798 /** Initialize single scanning schedules, and validate them */ initializeScanningSchedule(int state)799 private int[] initializeScanningSchedule(int state) { 800 int[] scheduleSec; 801 802 if (state == WIFI_STATE_CONNECTED) { 803 scheduleSec = mContext.getResources().getIntArray( 804 R.array.config_wifiConnectedScanIntervalScheduleSec); 805 } else if (state == WIFI_STATE_DISCONNECTED) { 806 scheduleSec = mContext.getResources().getIntArray( 807 R.array.config_wifiDisconnectedScanIntervalScheduleSec); 808 } else { 809 scheduleSec = null; 810 } 811 812 boolean invalidConfig = false; 813 if (scheduleSec == null || scheduleSec.length == 0) { 814 invalidConfig = true; 815 } else { 816 for (int val : scheduleSec) { 817 if (val <= 0) { 818 invalidConfig = true; 819 break; 820 } 821 } 822 } 823 if (!invalidConfig) { 824 return scheduleSec; 825 } 826 827 Log.e(TAG, "Configuration for wifi scanning schedule is mis-configured," 828 + "using default schedule"); 829 return DEFAULT_SCANNING_SCHEDULE_SEC; 830 } 831 832 /** 833 * This checks the connection attempt rate and recommends whether the connection attempt 834 * should be skipped or not. This attempts to rate limit the rate of connections to 835 * prevent us from flapping between networks and draining battery rapidly. 836 */ shouldSkipConnectionAttempt(Long timeMillis)837 private boolean shouldSkipConnectionAttempt(Long timeMillis) { 838 Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator(); 839 // First evict old entries from the queue. 840 while (attemptIter.hasNext()) { 841 Long connectionAttemptTimeMillis = attemptIter.next(); 842 if ((timeMillis - connectionAttemptTimeMillis) 843 > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) { 844 attemptIter.remove(); 845 } else { 846 // This list is sorted by timestamps, so we can skip any more checks 847 break; 848 } 849 } 850 // If we've reached the max connection attempt rate, skip this connection attempt 851 return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE); 852 } 853 854 /** 855 * Add the current connection attempt timestamp to our queue of connection attempts. 856 */ noteConnectionAttempt(Long timeMillis)857 private void noteConnectionAttempt(Long timeMillis) { 858 mConnectionAttemptTimeStamps.addLast(timeMillis); 859 } 860 861 /** 862 * This is used to clear the connection attempt rate limiter. This is done when the user 863 * explicitly tries to connect to a specified network. 864 */ clearConnectionAttemptTimeStamps()865 private void clearConnectionAttemptTimeStamps() { 866 mConnectionAttemptTimeStamps.clear(); 867 } 868 869 /** 870 * Attempt to connect to a network candidate. 871 * 872 * Based on the currently connected network, this menthod determines whether we should 873 * connect or roam to the network candidate recommended by WifiNetworkSelector. 874 */ connectToNetwork(WifiConfiguration candidate)875 private void connectToNetwork(WifiConfiguration candidate) { 876 ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate(); 877 if (scanResultCandidate == null) { 878 localLog("connectToNetwork: bad candidate - " + candidate 879 + " scanResult: " + scanResultCandidate); 880 return; 881 } 882 883 String targetBssid = scanResultCandidate.BSSID; 884 String targetAssociationId = candidate.SSID + " : " + targetBssid; 885 886 // Check if we are already connected or in the process of connecting to the target 887 // BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just 888 // in case the firmware automatically roamed to a BSSID different from what 889 // WifiNetworkSelector selected. 890 if (targetBssid != null 891 && (targetBssid.equals(mLastConnectionAttemptBssid) 892 || targetBssid.equals(mWifiInfo.getBSSID())) 893 && SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) { 894 localLog("connectToNetwork: Either already connected " 895 + "or is connecting to " + targetAssociationId); 896 return; 897 } 898 899 if (candidate.BSSID != null 900 && !candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY) 901 && !candidate.BSSID.equals(targetBssid)) { 902 localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the " 903 + "config specified BSSID " + candidate.BSSID + ". Drop it!"); 904 return; 905 } 906 907 long elapsedTimeMillis = mClock.getElapsedSinceBootMillis(); 908 if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) { 909 localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!"); 910 mTotalConnectivityAttemptsRateLimited++; 911 return; 912 } 913 noteConnectionAttempt(elapsedTimeMillis); 914 915 mLastConnectionAttemptBssid = targetBssid; 916 917 WifiConfiguration currentConnectedNetwork = mConfigManager 918 .getConfiguredNetwork(mWifiInfo.getNetworkId()); 919 String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" : 920 (mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID()); 921 922 if (currentConnectedNetwork != null 923 && (currentConnectedNetwork.networkId == candidate.networkId 924 //TODO(b/36788683): re-enable linked configuration check 925 /* || currentConnectedNetwork.isLinked(candidate) */)) { 926 // Framework initiates roaming only if firmware doesn't support 927 // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}. 928 if (mConnectivityHelper.isFirmwareRoamingSupported()) { 929 // Keep this logging here for now to validate the firmware roaming behavior. 930 localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "." 931 + " The actual roaming target is up to the firmware."); 932 } else { 933 localLog("connectToNetwork: Roaming to " + targetAssociationId + " from " 934 + currentAssociationId); 935 mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate); 936 } 937 } else { 938 // Framework specifies the connection target BSSID if firmware doesn't support 939 // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the 940 // candidate configuration contains a specified BSSID. 941 if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null 942 || candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY))) { 943 targetBssid = ClientModeImpl.SUPPLICANT_BSSID_ANY; 944 localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid 945 + " from " + currentAssociationId); 946 } else { 947 localLog("connectToNetwork: Connect to " + targetAssociationId + " from " 948 + currentAssociationId); 949 } 950 mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid); 951 } 952 } 953 954 // Helper for selecting the band for connectivity scan getScanBand()955 private int getScanBand() { 956 return getScanBand(true); 957 } 958 getScanBand(boolean isFullBandScan)959 private int getScanBand(boolean isFullBandScan) { 960 if (isFullBandScan) { 961 return WifiScanner.WIFI_BAND_ALL; 962 } else { 963 // Use channel list instead. 964 return WifiScanner.WIFI_BAND_UNSPECIFIED; 965 } 966 } 967 968 // Helper for setting the channels for connectivity scan when band is unspecified. Returns 969 // false if we can't retrieve the info. 970 // If connected, return channels used for the connected network 971 // If disconnected, return channels used for any network. setScanChannels(ScanSettings settings)972 private boolean setScanChannels(ScanSettings settings) { 973 Set<Integer> freqs; 974 975 WifiConfiguration config = mStateMachine.getCurrentWifiConfiguration(); 976 if (config == null) { 977 long ageInMillis = 1000 * 60 * mContext.getResources().getInteger( 978 R.integer.config_wifiInitialPartialScanChannelCacheAgeMins); 979 int maxCount = mContext.getResources().getInteger( 980 R.integer.config_wifiInitialPartialScanChannelMaxCount); 981 freqs = fetchChannelSetForPartialScan(maxCount, ageInMillis); 982 } else { 983 freqs = fetchChannelSetForNetworkForPartialScan(config.networkId); 984 } 985 986 if (freqs != null && freqs.size() != 0) { 987 int index = 0; 988 settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; 989 for (Integer freq : freqs) { 990 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 991 } 992 return true; 993 } else { 994 localLog("No history scan channels found, Perform full band scan"); 995 return false; 996 } 997 } 998 999 /** 1000 * Add the channels into the channel set with a size limit. 1001 * If maxCount equals to 0, will add all available channels into the set. 1002 * @param channelSet Target set for adding channel to. 1003 * @param config Network for query channel from WifiScoreCard 1004 * @param maxCount Size limit of the set. If equals to 0, means no limit. 1005 * @param ageInMillis Only consider channel info whose timestamps are younger than this value. 1006 * @return True if all available channels for this network are added, otherwise false. 1007 */ addChannelFromWifiScoreCard(@onNull Set<Integer> channelSet, @NonNull WifiConfiguration config, int maxCount, long ageInMillis)1008 private boolean addChannelFromWifiScoreCard(@NonNull Set<Integer> channelSet, 1009 @NonNull WifiConfiguration config, int maxCount, long ageInMillis) { 1010 WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID); 1011 for (Integer channel : network.getFrequencies(ageInMillis)) { 1012 if (maxCount > 0 && channelSet.size() >= maxCount) { 1013 localLog("addChannelFromWifiScoreCard: size limit reached for network:" 1014 + config.SSID); 1015 return false; 1016 } 1017 channelSet.add(channel); 1018 } 1019 return true; 1020 } 1021 1022 /** 1023 * Fetch channel set for target network. 1024 */ 1025 @VisibleForTesting fetchChannelSetForNetworkForPartialScan(int networkId)1026 public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId) { 1027 WifiConfiguration config = mConfigManager.getConfiguredNetwork(networkId); 1028 if (config == null) { 1029 return null; 1030 } 1031 final int maxNumActiveChannelsForPartialScans = mContext.getResources().getInteger( 1032 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels); 1033 Set<Integer> channelSet = new HashSet<>(); 1034 // First add the currently connected network channel. 1035 if (mWifiInfo.getFrequency() > 0) { 1036 channelSet.add(mWifiInfo.getFrequency()); 1037 } 1038 // Then get channels for the network. 1039 addChannelFromWifiScoreCard(channelSet, config, maxNumActiveChannelsForPartialScans, 1040 CHANNEL_LIST_AGE_MS); 1041 return channelSet; 1042 } 1043 1044 /** 1045 * Fetch channel set for all saved and suggestion non-passpoint network for partial scan. 1046 */ 1047 @VisibleForTesting fetchChannelSetForPartialScan(int maxCount, long ageInMillis)1048 public Set<Integer> fetchChannelSetForPartialScan(int maxCount, long ageInMillis) { 1049 List<WifiConfiguration> networks = getAllScanOptimizationNetworks(); 1050 if (networks.isEmpty()) { 1051 return null; 1052 } 1053 1054 // Sort the networks with the most frequent ones at the front of the network list. 1055 Collections.sort(networks, mConfigManager.getScanListComparator()); 1056 1057 Set<Integer> channelSet = new HashSet<>(); 1058 1059 for (WifiConfiguration config : networks) { 1060 if (!addChannelFromWifiScoreCard(channelSet, config, maxCount, ageInMillis)) { 1061 return channelSet; 1062 } 1063 } 1064 1065 return channelSet; 1066 } 1067 1068 // Watchdog timer handler watchdogHandler()1069 private void watchdogHandler() { 1070 // Schedule the next timer and start a single scan if we are in disconnected state. 1071 // Otherwise, the watchdog timer will be scheduled when entering disconnected 1072 // state. 1073 if (mWifiState == WIFI_STATE_DISCONNECTED) { 1074 localLog("start a single scan from watchdogHandler"); 1075 1076 scheduleWatchdogTimer(); 1077 startSingleScan(true, WIFI_WORK_SOURCE); 1078 } 1079 } 1080 triggerScanOnNetworkChanges()1081 private void triggerScanOnNetworkChanges() { 1082 if (mScreenOn) { 1083 // Update scanning schedule if needed 1084 if (updateSingleScanningSchedule()) { 1085 localLog("Saved networks / suggestions updated impacting single scan schedule"); 1086 startConnectivityScan(false); 1087 } 1088 } else { 1089 // Update the PNO scan network list when screen is off. Here we 1090 // rely on startConnectivityScan() to perform all the checks and clean up. 1091 localLog("Saved networks / suggestions updated impacting pno scan"); 1092 startConnectivityScan(false); 1093 } 1094 } 1095 1096 // Start a single scan and set up the interval for next single scan. startPeriodicSingleScan()1097 private void startPeriodicSingleScan() { 1098 // Reaching here with scanning schedule is null means this is a false timer alarm 1099 if (getSingleScanningSchedule() == null) { 1100 return; 1101 } 1102 1103 long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 1104 1105 if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) { 1106 long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp; 1107 if (msSinceLastScan < getScheduledSingleScanIntervalMs(0)) { 1108 localLog("Last periodic single scan started " + msSinceLastScan 1109 + "ms ago, defer this new scan request."); 1110 schedulePeriodicScanTimer( 1111 getScheduledSingleScanIntervalMs(0) - (int) msSinceLastScan); 1112 return; 1113 } 1114 } 1115 1116 boolean isScanNeeded = true; 1117 boolean isFullBandScan = true; 1118 1119 boolean isShortTimeSinceLastNetworkSelection = 1120 ((currentTimeStamp - mLastNetworkSelectionTimeStamp) 1121 <= 1000 * mContext.getResources().getInteger( 1122 R.integer.config_wifiConnectedHighRssiScanMinimumWindowSizeSec)); 1123 1124 boolean isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection = 1125 mNetworkSelector.hasSufficientLinkQuality(mWifiInfo) 1126 && mNetworkSelector.hasInternetOrExpectNoInternet(mWifiInfo) 1127 && isShortTimeSinceLastNetworkSelection; 1128 // Check it is one of following conditions to skip scan (with firmware roaming) 1129 // or do partial scan only (without firmware roaming). 1130 // 1) Network is sufficient 1131 // 2) link is good, internet status is acceptable 1132 // and it is a short time since last network selection 1133 // 3) There is active stream such that scan will be likely disruptive 1134 if (mWifiState == WIFI_STATE_CONNECTED 1135 && (mNetworkSelector.isNetworkSufficient(mWifiInfo) 1136 || isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection 1137 || mNetworkSelector.hasActiveStream(mWifiInfo))) { 1138 // If only partial scan is proposed and firmware roaming control is supported, 1139 // we will not issue any scan because firmware roaming will take care of 1140 // intra-SSID roam. 1141 if (mConnectivityHelper.isFirmwareRoamingSupported()) { 1142 localLog("No partial scan because firmware roaming is supported."); 1143 isScanNeeded = false; 1144 } else { 1145 localLog("No full band scan because current network is sufficient"); 1146 isFullBandScan = false; 1147 } 1148 } 1149 1150 if (isScanNeeded) { 1151 mLastPeriodicSingleScanTimeStamp = currentTimeStamp; 1152 1153 if (mWifiState == WIFI_STATE_DISCONNECTED 1154 && mInitialScanState == INITIAL_SCAN_STATE_START) { 1155 startSingleScan(false, WIFI_WORK_SOURCE); 1156 1157 // Note, initial partial scan may fail due to lack of channel history 1158 // Hence, we verify state before changing to AWIATING_RESPONSE 1159 if (mInitialScanState == INITIAL_SCAN_STATE_START) { 1160 setInitialScanState(INITIAL_SCAN_STATE_AWAITING_RESPONSE); 1161 mWifiMetrics.incrementInitialPartialScanCount(); 1162 } 1163 // No scheduling for another scan (until we get the results) 1164 return; 1165 } 1166 1167 startSingleScan(isFullBandScan, WIFI_WORK_SOURCE); 1168 schedulePeriodicScanTimer( 1169 getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex)); 1170 1171 // Set up the next scan interval in an exponential backoff fashion. 1172 mCurrentSingleScanScheduleIndex++; 1173 } else { 1174 // Since we already skipped this scan, keep the same scan interval for next scan. 1175 schedulePeriodicScanTimer( 1176 getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex)); 1177 } 1178 } 1179 1180 // Retrieve a value from single scanning schedule in ms getScheduledSingleScanIntervalMs(int index)1181 private int getScheduledSingleScanIntervalMs(int index) { 1182 synchronized (mLock) { 1183 if (mCurrentSingleScanScheduleSec == null) { 1184 Log.e(TAG, "Invalid attempt to get schedule interval, Schedule array is null "); 1185 1186 // Use a default value 1187 return DEFAULT_SCANNING_SCHEDULE_SEC[0] * 1000; 1188 } 1189 1190 if (index >= mCurrentSingleScanScheduleSec.length) { 1191 index = mCurrentSingleScanScheduleSec.length - 1; 1192 } 1193 1194 return mCurrentSingleScanScheduleSec[index] * 1000; 1195 } 1196 } 1197 1198 // Set the single scanning schedule setSingleScanningSchedule(int[] scheduleSec)1199 private void setSingleScanningSchedule(int[] scheduleSec) { 1200 synchronized (mLock) { 1201 mCurrentSingleScanScheduleSec = scheduleSec; 1202 } 1203 } 1204 1205 // Get the single scanning schedule getSingleScanningSchedule()1206 private int[] getSingleScanningSchedule() { 1207 synchronized (mLock) { 1208 return mCurrentSingleScanScheduleSec; 1209 } 1210 } 1211 1212 // Update the single scanning schedule if needed, and return true if update occurs updateSingleScanningSchedule()1213 private boolean updateSingleScanningSchedule() { 1214 if (mWifiState != WIFI_STATE_CONNECTED) { 1215 // No need to update the scanning schedule 1216 return false; 1217 } 1218 1219 boolean shouldUseSingleSavedNetworkSchedule = useSingleSavedNetworkSchedule(); 1220 1221 if (mCurrentSingleScanScheduleSec == mConnectedSingleScanScheduleSec 1222 && shouldUseSingleSavedNetworkSchedule) { 1223 mCurrentSingleScanScheduleSec = mConnectedSingleSavedNetworkSingleScanScheduleSec; 1224 return true; 1225 } 1226 if (mCurrentSingleScanScheduleSec == mConnectedSingleSavedNetworkSingleScanScheduleSec 1227 && !shouldUseSingleSavedNetworkSchedule) { 1228 mCurrentSingleScanScheduleSec = mConnectedSingleScanScheduleSec; 1229 return true; 1230 } 1231 return false; 1232 } 1233 1234 // Set initial scan state setInitialScanState(int state)1235 private void setInitialScanState(int state) { 1236 Log.i(TAG, "SetInitialScanState to : " + state); 1237 mInitialScanState = state; 1238 } 1239 1240 // Reset the last periodic single scan time stamp so that the next periodic single 1241 // scan can start immediately. resetLastPeriodicSingleScanTimeStamp()1242 private void resetLastPeriodicSingleScanTimeStamp() { 1243 mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 1244 } 1245 1246 // Periodic scan timer handler periodicScanTimerHandler()1247 private void periodicScanTimerHandler() { 1248 localLog("periodicScanTimerHandler"); 1249 1250 // Schedule the next timer and start a single scan if screen is on. 1251 if (mScreenOn) { 1252 startPeriodicSingleScan(); 1253 } 1254 } 1255 1256 // Start a single scan startForcedSingleScan(boolean isFullBandScan, WorkSource workSource)1257 private void startForcedSingleScan(boolean isFullBandScan, WorkSource workSource) { 1258 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 1259 1260 ScanSettings settings = new ScanSettings(); 1261 if (!isFullBandScan) { 1262 if (!setScanChannels(settings)) { 1263 isFullBandScan = true; 1264 // Skip the initial scan since no channel history available 1265 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE); 1266 } else { 1267 mInitialPartialScanChannelCount = settings.channels.length; 1268 } 1269 } 1270 settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; // always do high accuracy scans. 1271 settings.band = getScanBand(isFullBandScan); 1272 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 1273 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1274 settings.numBssidsPerScan = 0; 1275 settings.hiddenNetworks.clear(); 1276 // retrieve the list of hidden network SSIDs from saved network to scan for 1277 settings.hiddenNetworks.addAll(mConfigManager.retrieveHiddenNetworkList()); 1278 // retrieve the list of hidden network SSIDs from Network suggestion to scan for 1279 settings.hiddenNetworks.addAll(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList()); 1280 1281 SingleScanListener singleScanListener = 1282 new SingleScanListener(isFullBandScan); 1283 mScanner.startScan( 1284 settings, new HandlerExecutor(mEventHandler), singleScanListener, workSource); 1285 mWifiMetrics.incrementConnectivityOneshotScanCount(); 1286 } 1287 startSingleScan(boolean isFullBandScan, WorkSource workSource)1288 private void startSingleScan(boolean isFullBandScan, WorkSource workSource) { 1289 if (!mWifiEnabled || !mAutoJoinEnabled) { 1290 return; 1291 } 1292 startForcedSingleScan(isFullBandScan, workSource); 1293 } 1294 1295 // Start a periodic scan when screen is on startPeriodicScan(boolean scanImmediately)1296 private void startPeriodicScan(boolean scanImmediately) { 1297 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 1298 1299 // No connectivity scan if auto roaming is disabled. 1300 if (mWifiState == WIFI_STATE_CONNECTED && !mContext.getResources().getBoolean( 1301 R.bool.config_wifi_framework_enable_associated_network_selection)) { 1302 return; 1303 } 1304 1305 // Due to b/28020168, timer based single scan will be scheduled 1306 // to provide periodic scan in an exponential backoff fashion. 1307 if (scanImmediately) { 1308 resetLastPeriodicSingleScanTimeStamp(); 1309 } 1310 mCurrentSingleScanScheduleIndex = 0; 1311 startPeriodicSingleScan(); 1312 } 1313 deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)1314 private int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) { 1315 switch (state) { 1316 case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN: 1317 case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT: 1318 case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT: 1319 return mContext.getResources() 1320 .getInteger(R.integer.config_wifiMovingPnoScanIntervalMillis); 1321 case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY: 1322 return mContext.getResources() 1323 .getInteger(R.integer.config_wifiStationaryPnoScanIntervalMillis); 1324 default: 1325 return -1; 1326 } 1327 } 1328 1329 /** 1330 * Pass device mobility state to WifiChannelUtilization and 1331 * alter the PNO scan interval based on the current device mobility state. 1332 * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase 1333 * the interval between scans. Decrease the interval between scans if the device begins to move 1334 * again. 1335 * @param newState the new device mobility state 1336 */ setDeviceMobilityState(@eviceMobilityState int newState)1337 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 1338 int oldDeviceMobilityState = mDeviceMobilityState; 1339 localLog("Device mobility state changed. state=" + newState); 1340 int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState); 1341 if (newPnoScanIntervalMs < 0) { 1342 Log.e(TAG, "Invalid device mobility state: " + newState); 1343 return; 1344 } 1345 mDeviceMobilityState = newState; 1346 mWifiChannelUtilization.setDeviceMobilityState(newState); 1347 1348 int oldPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(oldDeviceMobilityState); 1349 if (newPnoScanIntervalMs == oldPnoScanIntervalMs) { 1350 if (mPnoScanStarted) { 1351 mWifiMetrics.logPnoScanStop(); 1352 mWifiMetrics.enterDeviceMobilityState(newState); 1353 mWifiMetrics.logPnoScanStart(); 1354 } else { 1355 mWifiMetrics.enterDeviceMobilityState(newState); 1356 } 1357 } else { 1358 Log.d(TAG, "PNO Scan Interval changed to " + newPnoScanIntervalMs + " ms."); 1359 1360 if (mPnoScanStarted) { 1361 Log.d(TAG, "Restarting PNO Scan with new scan interval"); 1362 stopPnoScan(); 1363 mWifiMetrics.enterDeviceMobilityState(newState); 1364 startDisconnectedPnoScan(); 1365 } else { 1366 mWifiMetrics.enterDeviceMobilityState(newState); 1367 } 1368 } 1369 } 1370 1371 // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected startDisconnectedPnoScan()1372 private void startDisconnectedPnoScan() { 1373 // Initialize PNO settings 1374 PnoSettings pnoSettings = new PnoSettings(); 1375 List<PnoSettings.PnoNetwork> pnoNetworkList = retrievePnoNetworkList(); 1376 int listSize = pnoNetworkList.size(); 1377 1378 if (listSize == 0) { 1379 // No saved network 1380 localLog("No saved network for starting disconnected PNO."); 1381 return; 1382 } 1383 1384 pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; 1385 pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); 1386 pnoSettings.min6GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_6_GHZ_START_FREQ_MHZ); 1387 pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_5_GHZ_START_FREQ_MHZ); 1388 pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi( 1389 ScanResult.BAND_24_GHZ_START_FREQ_MHZ); 1390 1391 // Initialize scan settings 1392 ScanSettings scanSettings = new ScanSettings(); 1393 scanSettings.band = getScanBand(); 1394 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; 1395 scanSettings.numBssidsPerScan = 0; 1396 scanSettings.periodInMs = deviceMobilityStateToPnoScanIntervalMs(mDeviceMobilityState); 1397 1398 mPnoScanListener.clearScanDetails(); 1399 1400 mScanner.startDisconnectedPnoScan( 1401 scanSettings, pnoSettings, new HandlerExecutor(mEventHandler), mPnoScanListener); 1402 mPnoScanStarted = true; 1403 mWifiMetrics.logPnoScanStart(); 1404 } 1405 getAllScanOptimizationNetworks()1406 private @NonNull List<WifiConfiguration> getAllScanOptimizationNetworks() { 1407 List<WifiConfiguration> networks = mConfigManager.getSavedNetworks(-1); 1408 networks.addAll(mWifiNetworkSuggestionsManager.getAllScanOptimizationSuggestionNetworks()); 1409 // remove all auto-join disabled or network selection disabled network. 1410 networks.removeIf(config -> !config.allowAutojoin 1411 || !config.getNetworkSelectionStatus().isNetworkEnabled()); 1412 return networks; 1413 } 1414 1415 /** 1416 * Retrieve the PnoNetworks from Saved and suggestion non-passpoint network. 1417 */ 1418 @VisibleForTesting retrievePnoNetworkList()1419 public List<PnoSettings.PnoNetwork> retrievePnoNetworkList() { 1420 List<WifiConfiguration> networks = getAllScanOptimizationNetworks(); 1421 1422 if (networks.isEmpty()) { 1423 return Collections.EMPTY_LIST; 1424 } 1425 Collections.sort(networks, mConfigManager.getScanListComparator()); 1426 boolean pnoFrequencyCullingEnabled = mContext.getResources() 1427 .getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled); 1428 1429 List<PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); 1430 Set<WifiScanner.PnoSettings.PnoNetwork> pnoSet = new HashSet<>(); 1431 for (WifiConfiguration config : networks) { 1432 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 1433 WifiConfigurationUtil.createPnoNetwork(config); 1434 if (pnoSet.contains(pnoNetwork)) { 1435 continue; 1436 } 1437 pnoList.add(pnoNetwork); 1438 pnoSet.add(pnoNetwork); 1439 if (!pnoFrequencyCullingEnabled) { 1440 continue; 1441 } 1442 Set<Integer> channelList = new HashSet<>(); 1443 addChannelFromWifiScoreCard(channelList, config, 0, 1444 MAX_PNO_SCAN_FREQUENCY_AGE_MS); 1445 pnoNetwork.frequencies = channelList.stream().mapToInt(Integer::intValue).toArray(); 1446 localLog("retrievePnoNetworkList " + pnoNetwork.ssid + ":" 1447 + Arrays.toString(pnoNetwork.frequencies)); 1448 } 1449 return pnoList; 1450 } 1451 1452 // Stop PNO scan. stopPnoScan()1453 private void stopPnoScan() { 1454 if (!mPnoScanStarted) return; 1455 1456 mScanner.stopPnoScan(mPnoScanListener); 1457 mPnoScanStarted = false; 1458 mWifiMetrics.logPnoScanStop(); 1459 } 1460 1461 // Set up watchdog timer scheduleWatchdogTimer()1462 private void scheduleWatchdogTimer() { 1463 localLog("scheduleWatchdogTimer"); 1464 1465 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1466 mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS, 1467 WATCHDOG_TIMER_TAG, 1468 mWatchdogListener, mEventHandler); 1469 } 1470 1471 // Schedules a delayed partial scan, which will scan the frequencies in mCachedWifiCandidates. scheduleDelayedPartialScan(long delayMillis)1472 private void scheduleDelayedPartialScan(long delayMillis) { 1473 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1474 mClock.getElapsedSinceBootMillis() + delayMillis, DELAYED_PARTIAL_SCAN_TIMER_TAG, 1475 mDelayedPartialScanTimerListener, mEventHandler); 1476 mDelayedPartialScanTimerSet = true; 1477 } 1478 1479 // Cancel the delayed partial scan timer. cancelDelayedPartialScan()1480 private void cancelDelayedPartialScan() { 1481 if (mDelayedPartialScanTimerSet) { 1482 mAlarmManager.cancel(mDelayedPartialScanTimerListener); 1483 mDelayedPartialScanTimerSet = false; 1484 } 1485 } 1486 1487 // Set up periodic scan timer schedulePeriodicScanTimer(int intervalMs)1488 private void schedulePeriodicScanTimer(int intervalMs) { 1489 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1490 mClock.getElapsedSinceBootMillis() + intervalMs, 1491 PERIODIC_SCAN_TIMER_TAG, 1492 mPeriodicScanTimerListener, mEventHandler); 1493 mPeriodicScanTimerSet = true; 1494 } 1495 1496 // Cancel periodic scan timer cancelPeriodicScanTimer()1497 private void cancelPeriodicScanTimer() { 1498 if (mPeriodicScanTimerSet) { 1499 mAlarmManager.cancel(mPeriodicScanTimerListener); 1500 mPeriodicScanTimerSet = false; 1501 } 1502 } 1503 1504 // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS scheduleDelayedSingleScan(boolean isFullBandScan)1505 private void scheduleDelayedSingleScan(boolean isFullBandScan) { 1506 localLog("scheduleDelayedSingleScan"); 1507 1508 RestartSingleScanListener restartSingleScanListener = 1509 new RestartSingleScanListener(isFullBandScan); 1510 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1511 mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS, 1512 RESTART_SINGLE_SCAN_TIMER_TAG, 1513 restartSingleScanListener, mEventHandler); 1514 } 1515 1516 // Set up timer to start a delayed scan after msFromNow milli-seconds scheduleDelayedConnectivityScan(int msFromNow)1517 private void scheduleDelayedConnectivityScan(int msFromNow) { 1518 localLog("scheduleDelayedConnectivityScan"); 1519 1520 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1521 mClock.getElapsedSinceBootMillis() + msFromNow, 1522 RESTART_CONNECTIVITY_SCAN_TIMER_TAG, 1523 mRestartScanListener, mEventHandler); 1524 1525 } 1526 1527 // Start a connectivity scan. The scan method is chosen according to 1528 // the current screen state and WiFi state. startConnectivityScan(boolean scanImmediately)1529 private void startConnectivityScan(boolean scanImmediately) { 1530 localLog("startConnectivityScan: screenOn=" + mScreenOn 1531 + " wifiState=" + stateToString(mWifiState) 1532 + " scanImmediately=" + scanImmediately 1533 + " wifiEnabled=" + mWifiEnabled 1534 + " wifiConnectivityManagerEnabled=" 1535 + mAutoJoinEnabled); 1536 1537 if (!mWifiEnabled || !mAutoJoinEnabled) { 1538 return; 1539 } 1540 1541 // Always stop outstanding connecivity scan if there is any 1542 stopConnectivityScan(); 1543 1544 // Don't start a connectivity scan while Wifi is in the transition 1545 // between connected and disconnected states. 1546 if ((mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) 1547 || (getSingleScanningSchedule() == null)) { 1548 return; 1549 } 1550 1551 if (mScreenOn) { 1552 startPeriodicScan(scanImmediately); 1553 } else { 1554 if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) { 1555 startDisconnectedPnoScan(); 1556 } 1557 } 1558 1559 } 1560 1561 // Stop connectivity scan if there is any. stopConnectivityScan()1562 private void stopConnectivityScan() { 1563 // Due to b/28020168, timer based single scan will be scheduled 1564 // to provide periodic scan in an exponential backoff fashion. 1565 cancelPeriodicScanTimer(); 1566 cancelDelayedPartialScan(); 1567 stopPnoScan(); 1568 } 1569 1570 /** 1571 * Handler for screen state (on/off) changes 1572 */ handleScreenStateChanged(boolean screenOn)1573 public void handleScreenStateChanged(boolean screenOn) { 1574 localLog("handleScreenStateChanged: screenOn=" + screenOn); 1575 1576 mScreenOn = screenOn; 1577 1578 if (mWifiState == WIFI_STATE_DISCONNECTED 1579 && mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) { 1580 setInitialScanState(INITIAL_SCAN_STATE_START); 1581 } 1582 1583 mOpenNetworkNotifier.handleScreenStateChanged(screenOn); 1584 1585 startConnectivityScan(SCAN_ON_SCHEDULE); 1586 } 1587 1588 /** 1589 * Helper function that converts the WIFI_STATE_XXX constants to string 1590 */ stateToString(int state)1591 private static String stateToString(int state) { 1592 switch (state) { 1593 case WIFI_STATE_CONNECTED: 1594 return "connected"; 1595 case WIFI_STATE_DISCONNECTED: 1596 return "disconnected"; 1597 case WIFI_STATE_TRANSITIONING: 1598 return "transitioning"; 1599 default: 1600 return "unknown"; 1601 } 1602 } 1603 1604 /** 1605 * Check if Single saved network schedule should be used 1606 * This is true if the one of the following is satisfied: 1607 * 1. Device has a total of 1 network whether saved, passpoint, or suggestion. 1608 * 2. The device is connected to that network. 1609 */ useSingleSavedNetworkSchedule()1610 private boolean useSingleSavedNetworkSchedule() { 1611 WifiConfiguration currentNetwork = mStateMachine.getCurrentWifiConfiguration(); 1612 if (currentNetwork == null) { 1613 localLog("Current network is missing, may caused by remove network and disconnecting "); 1614 return false; 1615 } 1616 List<WifiConfiguration> savedNetworks = 1617 mConfigManager.getSavedNetworks(Process.WIFI_UID); 1618 // If we have multiple saved networks, then no need to proceed 1619 if (savedNetworks.size() > 1) { 1620 return false; 1621 } 1622 1623 List<PasspointConfiguration> passpointNetworks = 1624 mWifiInjector.getPasspointManager().getProviderConfigs(Process.WIFI_UID, true); 1625 // If we have multiple networks (saved + passpoint), then no need to proceed 1626 if (passpointNetworks.size() + savedNetworks.size() > 1) { 1627 return false; 1628 } 1629 1630 Set<WifiNetworkSuggestion> suggestionsNetworks = 1631 mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions(); 1632 // If total size not equal to 1, then no need to proceed 1633 if (passpointNetworks.size() + savedNetworks.size() + suggestionsNetworks.size() != 1) { 1634 return false; 1635 } 1636 1637 // Next verify that this network is the one device is connected to 1638 int currentNetworkId = currentNetwork.networkId; 1639 1640 // If we have a single saved network, and we are connected to it, return true. 1641 if (savedNetworks.size() == 1) { 1642 return (savedNetworks.get(0).networkId == currentNetworkId); 1643 } 1644 1645 // If we have a single passpoint network, and we are connected to it, return true. 1646 if (passpointNetworks.size() == 1) { 1647 String passpointKey = passpointNetworks.get(0).getUniqueId(); 1648 WifiConfiguration config = mConfigManager.getConfiguredNetwork(passpointKey); 1649 return (config != null && config.networkId == currentNetworkId); 1650 } 1651 1652 // If we have a single suggestion network, and we are connected to it, return true. 1653 WifiNetworkSuggestion network = suggestionsNetworks.iterator().next(); 1654 String suggestionKey = network.getWifiConfiguration().getKey(); 1655 WifiConfiguration config = mConfigManager.getConfiguredNetwork(suggestionKey); 1656 return (config != null && config.networkId == currentNetworkId); 1657 } 1658 initSingleSavedNetworkSchedule()1659 private int[] initSingleSavedNetworkSchedule() { 1660 int[] schedule = mContext.getResources().getIntArray( 1661 R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec); 1662 if (schedule == null || schedule.length == 0) { 1663 return null; 1664 } 1665 1666 for (int val : schedule) { 1667 if (val <= 0) { 1668 return null; 1669 } 1670 } 1671 return schedule; 1672 } 1673 1674 /** 1675 * Handler for WiFi state (connected/disconnected) changes 1676 */ handleConnectionStateChanged(int state)1677 public void handleConnectionStateChanged(int state) { 1678 localLog("handleConnectionStateChanged: state=" + stateToString(state)); 1679 1680 if (mConnectedSingleScanScheduleSec == null) { 1681 mConnectedSingleScanScheduleSec = initializeScanningSchedule(WIFI_STATE_CONNECTED); 1682 } 1683 if (mDisconnectedSingleScanScheduleSec == null) { 1684 mDisconnectedSingleScanScheduleSec = 1685 initializeScanningSchedule(WIFI_STATE_DISCONNECTED); 1686 } 1687 if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) { 1688 mConnectedSingleSavedNetworkSingleScanScheduleSec = 1689 initSingleSavedNetworkSchedule(); 1690 if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) { 1691 mConnectedSingleSavedNetworkSingleScanScheduleSec = mConnectedSingleScanScheduleSec; 1692 } 1693 } 1694 1695 mWifiState = state; 1696 1697 // Reset BSSID of last connection attempt and kick off 1698 // the watchdog timer if entering disconnected state. 1699 if (mWifiState == WIFI_STATE_DISCONNECTED) { 1700 mLastConnectionAttemptBssid = null; 1701 scheduleWatchdogTimer(); 1702 // Switch to the disconnected scanning schedule 1703 setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec); 1704 startConnectivityScan(SCAN_IMMEDIATELY); 1705 } else if (mWifiState == WIFI_STATE_CONNECTED) { 1706 if (useSingleSavedNetworkSchedule()) { 1707 // Switch to Single-Saved-Network connected schedule 1708 setSingleScanningSchedule(mConnectedSingleSavedNetworkSingleScanScheduleSec); 1709 } else { 1710 // Switch to connected single scanning schedule 1711 setSingleScanningSchedule(mConnectedSingleScanScheduleSec); 1712 } 1713 startConnectivityScan(SCAN_ON_SCHEDULE); 1714 } else { 1715 // Intermediate state, no applicable single scanning schedule 1716 setSingleScanningSchedule(null); 1717 startConnectivityScan(SCAN_ON_SCHEDULE); 1718 } 1719 } 1720 1721 /** 1722 * Handler when a WiFi connection attempt ended. 1723 * 1724 * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code. 1725 * @param bssid the failed network. 1726 * @param ssid identifies the failed network. 1727 */ handleConnectionAttemptEnded(int failureCode, @NonNull String bssid, @NonNull String ssid)1728 public void handleConnectionAttemptEnded(int failureCode, @NonNull String bssid, 1729 @NonNull String ssid) { 1730 if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) { 1731 String ssidUnquoted = (mWifiInfo.getWifiSsid() == null) 1732 ? null 1733 : mWifiInfo.getWifiSsid().toString(); 1734 mOpenNetworkNotifier.handleWifiConnected(ssidUnquoted); 1735 } else { 1736 mOpenNetworkNotifier.handleConnectionFailure(); 1737 retryConnectionOnLatestCandidates(bssid, ssid); 1738 } 1739 } 1740 retryConnectionOnLatestCandidates(String bssid, String ssid)1741 private void retryConnectionOnLatestCandidates(String bssid, String ssid) { 1742 try { 1743 if (mLatestCandidates == null || mLatestCandidates.size() == 0 1744 || mClock.getElapsedSinceBootMillis() - mLatestCandidatesTimestampMs 1745 > TEMP_BSSID_BLOCK_DURATION) { 1746 mLatestCandidates = null; 1747 return; 1748 } 1749 MacAddress macAddress = MacAddress.fromString(bssid); 1750 int prevNumCandidates = mLatestCandidates.size(); 1751 mLatestCandidates = mLatestCandidates.stream() 1752 .filter(candidate -> !macAddress.equals(candidate.getKey().bssid)) 1753 .collect(Collectors.toList()); 1754 if (prevNumCandidates == mLatestCandidates.size()) { 1755 return; 1756 } 1757 WifiConfiguration candidate = mNetworkSelector.selectNetwork(mLatestCandidates); 1758 if (candidate != null) { 1759 localLog("Automatic retry on the next best WNS candidate-" + candidate.SSID); 1760 // Make sure that the failed BSSID is blocked for at least TEMP_BSSID_BLOCK_DURATION 1761 // to prevent the supplicant from trying it again. 1762 mBssidBlocklistMonitor.blockBssidForDurationMs(bssid, ssid, 1763 TEMP_BSSID_BLOCK_DURATION, 1764 BssidBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, 0); 1765 connectToNetwork(candidate); 1766 } 1767 } catch (IllegalArgumentException e) { 1768 localLog("retryConnectionOnLatestCandidates: failed to create MacAddress from bssid=" 1769 + bssid); 1770 mLatestCandidates = null; 1771 return; 1772 } 1773 } 1774 1775 // Enable auto-join if WifiConnectivityManager is enabled & we have any pending generic network 1776 // request (trusted or untrusted) and no specific network request in progress. checkAllStatesAndEnableAutoJoin()1777 private void checkAllStatesAndEnableAutoJoin() { 1778 // if auto-join was disabled externally, don't re-enable for any triggers. 1779 // External triggers to disable always trumps any internal state. 1780 setAutoJoinEnabled(mAutoJoinEnabledExternal 1781 && (mUntrustedConnectionAllowed || mTrustedConnectionAllowed) 1782 && !mSpecificNetworkRequestInProgress); 1783 startConnectivityScan(SCAN_IMMEDIATELY); 1784 } 1785 1786 /** 1787 * Triggered when {@link WifiNetworkFactory} has a pending general network request. 1788 */ setTrustedConnectionAllowed(boolean allowed)1789 public void setTrustedConnectionAllowed(boolean allowed) { 1790 localLog("setTrustedConnectionAllowed: allowed=" + allowed); 1791 1792 if (mTrustedConnectionAllowed != allowed) { 1793 mTrustedConnectionAllowed = allowed; 1794 checkAllStatesAndEnableAutoJoin(); 1795 } 1796 } 1797 1798 1799 /** 1800 * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request. 1801 */ setUntrustedConnectionAllowed(boolean allowed)1802 public void setUntrustedConnectionAllowed(boolean allowed) { 1803 localLog("setUntrustedConnectionAllowed: allowed=" + allowed); 1804 1805 if (mUntrustedConnectionAllowed != allowed) { 1806 mUntrustedConnectionAllowed = allowed; 1807 checkAllStatesAndEnableAutoJoin(); 1808 } 1809 } 1810 1811 /** 1812 * Triggered when {@link WifiNetworkFactory} is processing a specific network request. 1813 */ setSpecificNetworkRequestInProgress(boolean inProgress)1814 public void setSpecificNetworkRequestInProgress(boolean inProgress) { 1815 localLog("setsetSpecificNetworkRequestInProgress : inProgress=" + inProgress); 1816 1817 if (mSpecificNetworkRequestInProgress != inProgress) { 1818 mSpecificNetworkRequestInProgress = inProgress; 1819 checkAllStatesAndEnableAutoJoin(); 1820 } 1821 } 1822 1823 /** 1824 * Handler when user specifies a particular network to connect to 1825 */ setUserConnectChoice(int netId)1826 public void setUserConnectChoice(int netId) { 1827 localLog("setUserConnectChoice: netId=" + netId); 1828 1829 mNetworkSelector.setUserConnectChoice(netId); 1830 } 1831 1832 /** 1833 * Handler to prepare for connection to a user or app specified network 1834 */ prepareForForcedConnection(int netId)1835 public void prepareForForcedConnection(int netId) { 1836 WifiConfiguration config = mConfigManager.getConfiguredNetwork(netId); 1837 if (config == null) { 1838 return; 1839 } 1840 localLog("prepareForForcedConnection: SSID=" + config.SSID); 1841 1842 clearConnectionAttemptTimeStamps(); 1843 mBssidBlocklistMonitor.clearBssidBlocklistForSsid(config.SSID); 1844 } 1845 1846 /** 1847 * Handler for on-demand connectivity scan 1848 */ forceConnectivityScan(WorkSource workSource)1849 public void forceConnectivityScan(WorkSource workSource) { 1850 if (!mWifiEnabled) return; 1851 localLog("forceConnectivityScan in request of " + workSource); 1852 1853 clearConnectionAttemptTimeStamps(); 1854 mWaitForFullBandScanResults = true; 1855 startForcedSingleScan(true, workSource); 1856 } 1857 1858 /** 1859 * Helper method to populate WifiScanner handle. This is done lazily because 1860 * WifiScanningService is started after WifiService. 1861 */ retrieveWifiScanner()1862 private void retrieveWifiScanner() { 1863 if (mScanner != null) return; 1864 mScanner = mWifiInjector.getWifiScanner(); 1865 checkNotNull(mScanner); 1866 // Register for all single scan results 1867 mScanner.registerScanListener(new HandlerExecutor(mEventHandler), mAllSingleScanListener); 1868 } 1869 1870 /** 1871 * Start WifiConnectivityManager 1872 */ start()1873 private void start() { 1874 if (mRunning) return; 1875 retrieveWifiScanner(); 1876 mConnectivityHelper.getFirmwareRoamingInfo(); 1877 mBssidBlocklistMonitor.clearBssidBlocklist(); 1878 mWifiChannelUtilization.init(mStateMachine.getWifiLinkLayerStats()); 1879 1880 if (mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) { 1881 setInitialScanState(INITIAL_SCAN_STATE_START); 1882 } 1883 1884 mRunning = true; 1885 mLatestCandidates = null; 1886 mLatestCandidatesTimestampMs = 0; 1887 } 1888 1889 /** 1890 * Stop and reset WifiConnectivityManager 1891 */ stop()1892 private void stop() { 1893 if (!mRunning) return; 1894 mRunning = false; 1895 stopConnectivityScan(); 1896 resetLastPeriodicSingleScanTimeStamp(); 1897 mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); 1898 mLastConnectionAttemptBssid = null; 1899 mWaitForFullBandScanResults = false; 1900 mLatestCandidates = null; 1901 mLatestCandidatesTimestampMs = 0; 1902 mScanRestartCount = 0; 1903 } 1904 1905 /** 1906 * Update WifiConnectivityManager running state 1907 * 1908 * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager 1909 * are enabled, otherwise stop it. 1910 */ updateRunningState()1911 private void updateRunningState() { 1912 if (mWifiEnabled && mAutoJoinEnabled) { 1913 localLog("Starting up WifiConnectivityManager"); 1914 start(); 1915 } else { 1916 localLog("Stopping WifiConnectivityManager"); 1917 stop(); 1918 } 1919 } 1920 1921 /** 1922 * Inform WiFi is enabled for connection or not 1923 */ setWifiEnabled(boolean enable)1924 public void setWifiEnabled(boolean enable) { 1925 localLog("Set WiFi " + (enable ? "enabled" : "disabled")); 1926 1927 if (mWifiEnabled && !enable) { 1928 mNetworkSelector.resetOnDisable(); 1929 mBssidBlocklistMonitor.clearBssidBlocklist(); 1930 } 1931 mWifiEnabled = enable; 1932 updateRunningState(); 1933 } 1934 1935 /** 1936 * Turn on/off the WifiConnectivityManager at runtime 1937 */ setAutoJoinEnabled(boolean enable)1938 private void setAutoJoinEnabled(boolean enable) { 1939 mAutoJoinEnabled = enable; 1940 updateRunningState(); 1941 } 1942 1943 /** 1944 * Turn on/off the auto join at runtime 1945 */ setAutoJoinEnabledExternal(boolean enable)1946 public void setAutoJoinEnabledExternal(boolean enable) { 1947 localLog("Set auto join " + (enable ? "enabled" : "disabled")); 1948 1949 if (mAutoJoinEnabledExternal != enable) { 1950 mAutoJoinEnabledExternal = enable; 1951 checkAllStatesAndEnableAutoJoin(); 1952 } 1953 } 1954 1955 @VisibleForTesting getLowRssiNetworkRetryDelay()1956 int getLowRssiNetworkRetryDelay() { 1957 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 1958 } 1959 1960 @VisibleForTesting getLastPeriodicSingleScanTimeStamp()1961 long getLastPeriodicSingleScanTimeStamp() { 1962 return mLastPeriodicSingleScanTimeStamp; 1963 } 1964 1965 /** 1966 * Dump the local logs. 1967 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)1968 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1969 pw.println("Dump of WifiConnectivityManager"); 1970 pw.println("WifiConnectivityManager - Log Begin ----"); 1971 mLocalLog.dump(fd, pw, args); 1972 pw.println("WifiConnectivityManager - Log End ----"); 1973 mOpenNetworkNotifier.dump(fd, pw, args); 1974 mBssidBlocklistMonitor.dump(fd, pw, args); 1975 } 1976 } 1977