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