1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import static android.net.wifi.WifiConfiguration.RANDOMIZATION_NONE; 20 21 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; 22 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED; 23 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; 24 import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.app.AlarmManager; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.net.MacAddress; 34 import android.net.wifi.ScanResult; 35 import android.net.wifi.WifiConfiguration; 36 import android.net.wifi.WifiInfo; 37 import android.net.wifi.WifiManager; 38 import android.net.wifi.WifiManager.DeviceMobilityState; 39 import android.net.wifi.WifiNetworkSuggestion; 40 import android.net.wifi.WifiScanner; 41 import android.net.wifi.WifiScanner.PnoSettings; 42 import android.net.wifi.WifiScanner.ScanSettings; 43 import android.net.wifi.hotspot2.PasspointConfiguration; 44 import android.os.Handler; 45 import android.os.HandlerExecutor; 46 import android.os.PowerManager; 47 import android.os.Process; 48 import android.os.WorkSource; 49 import android.util.ArrayMap; 50 import android.util.LocalLog; 51 import android.util.Log; 52 53 import com.android.internal.annotations.GuardedBy; 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.modules.utils.build.SdkLevel; 56 import com.android.server.wifi.hotspot2.PasspointManager; 57 import com.android.server.wifi.util.ScanResultUtil; 58 import com.android.wifi.resources.R; 59 60 import java.io.FileDescriptor; 61 import java.io.PrintWriter; 62 import java.util.ArrayList; 63 import java.util.Collections; 64 import java.util.HashSet; 65 import java.util.Iterator; 66 import java.util.LinkedList; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Objects; 70 import java.util.Set; 71 import java.util.stream.Collectors; 72 import java.util.stream.Stream; 73 74 /** 75 * This class manages all the connectivity related scanning activities. 76 * 77 * When the screen is turned on or off, WiFi is connected or disconnected, 78 * or on-demand, a scan is initiatiated and the scan results are passed 79 * to WifiNetworkSelector for it to make a recommendation on which network 80 * to connect to. 81 */ 82 public class WifiConnectivityManager { 83 public static final String WATCHDOG_TIMER_TAG = 84 "WifiConnectivityManager Schedule Watchdog Timer"; 85 public static final String PERIODIC_SCAN_TIMER_TAG = 86 "WifiConnectivityManager Schedule Periodic Scan Timer"; 87 public static final String RESTART_SINGLE_SCAN_TIMER_TAG = 88 "WifiConnectivityManager Restart Single Scan"; 89 public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG = 90 "WifiConnectivityManager Restart Scan"; 91 public static final String DELAYED_PARTIAL_SCAN_TIMER_TAG = 92 "WifiConnectivityManager Schedule Delayed Partial Scan Timer"; 93 94 private static final long RESET_TIME_STAMP = Long.MIN_VALUE; 95 // Constants to indicate whether a scan should start immediately or 96 // it should comply to the minimum scan interval rule. 97 private static final boolean SCAN_IMMEDIATELY = true; 98 private static final boolean SCAN_ON_SCHEDULE = false; 99 100 // PNO scan interval in milli-seconds. This is the scan 101 // performed when screen is off and connected. 102 private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds 103 // Maximum number of retries when starting a scan failed 104 @VisibleForTesting 105 public static final int MAX_SCAN_RESTART_ALLOWED = 5; 106 // Number of milli-seconds to delay before retry starting 107 // a previously failed scan 108 private static final int RESTART_SCAN_DELAY_MS = 2 * 1000; // 2 seconds 109 // Restricted channel list age out value. 110 private static final long CHANNEL_LIST_AGE_MS = 60 * 60 * 1000; // 1 hour 111 // This is the time interval for the connection attempt rate calculation. Connection attempt 112 // timestamps beyond this interval is evicted from the list. 113 public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins 114 // Max number of connection attempts in the above time interval. 115 public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6; 116 private static final int TEMP_BSSID_BLOCK_DURATION = 10 * 1000; // 10 seconds 117 // Maximum age of frequencies last seen to be included in pno scans. (30 days) 118 private static final long MAX_PNO_SCAN_FREQUENCY_AGE_MS = (long) 1000 * 3600 * 24 * 30; 119 private static final int POWER_SAVE_SCAN_INTERVAL_MULTIPLIER = 2; 120 // ClientModeManager has a bunch of states. From the 121 // WifiConnectivityManager's perspective it only cares 122 // if it is in Connected state, Disconnected state or in 123 // transition between these two states. 124 public static final int WIFI_STATE_UNKNOWN = 0; 125 public static final int WIFI_STATE_CONNECTED = 1; 126 public static final int WIFI_STATE_DISCONNECTED = 2; 127 public static final int WIFI_STATE_TRANSITIONING = 3; 128 129 // Initial scan state, used to manage performing partial scans in initial scans 130 // Initial scans are the first scan after enabling Wifi or turning on screen when disconnected 131 @VisibleForTesting 132 public static final int INITIAL_SCAN_STATE_START = 0; 133 public static final int INITIAL_SCAN_STATE_AWAITING_RESPONSE = 1; 134 public static final int INITIAL_SCAN_STATE_COMPLETE = 2; 135 136 // Log tag for this class 137 private static final String TAG = "WifiConnectivityManager"; 138 private static final String ALL_SINGLE_SCAN_LISTENER = "AllSingleScanListener"; 139 private static final String PNO_SCAN_LISTENER = "PnoScanListener"; 140 141 private final WifiContext mContext; 142 private final WifiConfigManager mConfigManager; 143 private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager; 144 private final WifiConnectivityHelper mConnectivityHelper; 145 private final WifiNetworkSelector mNetworkSelector; 146 private final WifiLastResortWatchdog mWifiLastResortWatchdog; 147 private final OpenNetworkNotifier mOpenNetworkNotifier; 148 private final WifiMetrics mWifiMetrics; 149 private final AlarmManager mAlarmManager; 150 private final Handler mEventHandler; 151 private final Clock mClock; 152 private final ScoringParams mScoringParams; 153 private final LocalLog mLocalLog; 154 private final WifiGlobals mWifiGlobals; 155 /** 156 * Keeps connection attempts within the last {@link #MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS} 157 * milliseconds. 158 */ 159 private final LinkedList<Long> mConnectionAttemptTimeStamps = new LinkedList<>(); 160 private final WifiBlocklistMonitor mWifiBlocklistMonitor; 161 private final PasspointManager mPasspointManager; 162 private final WifiScoreCard mWifiScoreCard; 163 private final WifiChannelUtilization mWifiChannelUtilization; 164 private final PowerManager mPowerManager; 165 private final DeviceConfigFacade mDeviceConfigFacade; 166 private final ActiveModeWarden mActiveModeWarden; 167 168 private WifiScanner mScanner; 169 private boolean mDbg = false; 170 private boolean mVerboseLoggingEnabled = false; 171 private boolean mWifiEnabled = false; 172 private boolean mAutoJoinEnabled = false; // disabled by default, enabled by external triggers 173 private boolean mRunning = false; 174 private boolean mScreenOn = false; 175 private int mWifiState = WIFI_STATE_UNKNOWN; 176 private int mInitialScanState = INITIAL_SCAN_STATE_COMPLETE; 177 private boolean mAutoJoinEnabledExternal = true; // enabled by default 178 private boolean mUntrustedConnectionAllowed = false; 179 private boolean mOemPaidConnectionAllowed = false; 180 private boolean mOemPrivateConnectionAllowed = false; 181 private WorkSource mOemPaidConnectionRequestorWs = null; 182 private WorkSource mOemPrivateConnectionRequestorWs = null; 183 private boolean mTrustedConnectionAllowed = false; 184 private boolean mSpecificNetworkRequestInProgress = false; 185 private int mScanRestartCount = 0; 186 private int mSingleScanRestartCount = 0; 187 private int mTotalConnectivityAttemptsRateLimited = 0; 188 private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 189 private long mLastNetworkSelectionTimeStamp = RESET_TIME_STAMP; 190 private boolean mPnoScanStarted = false; 191 private boolean mPeriodicScanTimerSet = false; 192 private boolean mDelayedPartialScanTimerSet = false; 193 private boolean mWatchdogScanTimerSet = false; 194 195 // Used for Initial Scan metrics 196 private boolean mFailedInitialPartialScan = false; 197 private int mInitialPartialScanChannelCount; 198 199 // Device configs 200 private boolean mWaitForFullBandScanResults = false; 201 202 // Scanning Schedules 203 // Default schedule used in case of invalid configuration 204 private static final int[] DEFAULT_SCANNING_SCHEDULE_SEC = {20, 40, 80, 160}; 205 private int[] mConnectedSingleScanScheduleSec; 206 private int[] mDisconnectedSingleScanScheduleSec; 207 private int[] mConnectedSingleSavedNetworkSingleScanScheduleSec; 208 private List<WifiCandidates.Candidate> mLatestCandidates = null; 209 private long mLatestCandidatesTimestampMs = 0; 210 211 private final Object mLock = new Object(); 212 213 @GuardedBy("mLock") 214 private int[] mCurrentSingleScanScheduleSec; 215 216 private int mCurrentSingleScanScheduleIndex; 217 // Cached WifiCandidates used in high mobility state to avoid connecting to APs that are 218 // moving relative to the user. 219 private CachedWifiCandidates mCachedWifiCandidates = null; 220 private @DeviceMobilityState int mDeviceMobilityState = 221 WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN; 222 223 // A helper to log debugging information in the local log buffer, which can 224 // be retrieved in bugreport. localLog(String log)225 private void localLog(String log) { 226 mLocalLog.log(log); 227 if (mVerboseLoggingEnabled) Log.v(TAG, log); 228 } 229 230 /** 231 * Enable verbose logging for WifiConnectivityManager. 232 */ enableVerboseLogging(boolean verbose)233 public void enableVerboseLogging(boolean verbose) { 234 mVerboseLoggingEnabled = verbose; 235 } 236 237 // A periodic/PNO scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 238 // if the start scan command failed. A timer is used here to make it a deferred retry. 239 private final AlarmManager.OnAlarmListener mRestartScanListener = 240 new AlarmManager.OnAlarmListener() { 241 public void onAlarm() { 242 startConnectivityScan(SCAN_IMMEDIATELY); 243 } 244 }; 245 246 // A single scan will be rescheduled up to MAX_SCAN_RESTART_ALLOWED times 247 // if the start scan command failed. An timer is used here to make it a deferred retry. 248 private class RestartSingleScanListener implements AlarmManager.OnAlarmListener { 249 private final boolean mIsFullBandScan; 250 RestartSingleScanListener(boolean isFullBandScan)251 RestartSingleScanListener(boolean isFullBandScan) { 252 mIsFullBandScan = isFullBandScan; 253 } 254 255 @Override onAlarm()256 public void onAlarm() { 257 startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE); 258 } 259 } 260 261 // As a watchdog mechanism, a single scan will be scheduled every 262 // config_wifiPnoWatchdogIntervalMinutes if it is in the WIFI_STATE_DISCONNECTED state. 263 private final AlarmManager.OnAlarmListener mWatchdogListener = 264 new AlarmManager.OnAlarmListener() { 265 public void onAlarm() { 266 watchdogHandler(); 267 } 268 }; 269 270 // Due to b/28020168, timer based single scan will be scheduled 271 // to provide periodic scan in an exponential backoff fashion. 272 private final AlarmManager.OnAlarmListener mPeriodicScanTimerListener = 273 new AlarmManager.OnAlarmListener() { 274 public void onAlarm() { 275 periodicScanTimerHandler(); 276 } 277 }; 278 279 private final AlarmManager.OnAlarmListener mDelayedPartialScanTimerListener = 280 new AlarmManager.OnAlarmListener() { 281 public void onAlarm() { 282 if (mCachedWifiCandidates == null 283 || mCachedWifiCandidates.frequencies == null 284 || mCachedWifiCandidates.frequencies.size() == 0) { 285 return; 286 } 287 ScanSettings settings = new ScanSettings(); 288 settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; 289 settings.band = getScanBand(false); 290 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 291 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 292 settings.numBssidsPerScan = 0; 293 int index = 0; 294 settings.channels = 295 new WifiScanner.ChannelSpec[mCachedWifiCandidates.frequencies.size()]; 296 for (Integer freq : mCachedWifiCandidates.frequencies) { 297 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 298 } 299 SingleScanListener singleScanListener = new SingleScanListener(false); 300 mScanner.startScan(settings, new HandlerExecutor(mEventHandler), 301 singleScanListener, WIFI_WORK_SOURCE); 302 mWifiMetrics.incrementConnectivityOneshotScanCount(); 303 } 304 }; 305 306 /** 307 * Interface for callback from handling scan results. 308 */ 309 private interface HandleScanResultsListener { 310 /** 311 * @param wasCandidateSelected true - if a candidate is selected by WifiNetworkSelector 312 * false - if no candidate is selected by WifiNetworkSelector 313 */ onHandled(boolean wasCandidateSelected)314 void onHandled(boolean wasCandidateSelected); 315 } 316 317 /** 318 * Helper method to consolidate handling of scan results when no candidate is selected. 319 */ handleScanResultsWithNoCandidate( @onNull HandleScanResultsListener handleScanResultsListener)320 private void handleScanResultsWithNoCandidate( 321 @NonNull HandleScanResultsListener handleScanResultsListener) { 322 if (mWifiState == WIFI_STATE_DISCONNECTED) { 323 mOpenNetworkNotifier.handleScanResults( 324 mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks()); 325 } 326 mWifiMetrics.noteFirstNetworkSelectionAfterBoot(false); 327 handleScanResultsListener.onHandled(false); 328 } 329 330 /** 331 * Helper method to consolidate handling of scan results when a candidate is selected. 332 */ handleScanResultsWithCandidate( @onNull HandleScanResultsListener handleScanResultsListener)333 private void handleScanResultsWithCandidate( 334 @NonNull HandleScanResultsListener handleScanResultsListener) { 335 mWifiMetrics.noteFirstNetworkSelectionAfterBoot(true); 336 handleScanResultsListener.onHandled(true); 337 } 338 339 /** 340 * Handles 'onResult' callbacks for the Periodic, Single & Pno ScanListener. 341 * Executes selection of potential network candidates, initiation of connection attempt to that 342 * network. 343 */ handleScanResults(@onNull List<ScanDetail> scanDetails, @NonNull String listenerName, boolean isFullScan, @NonNull HandleScanResultsListener handleScanResultsListener)344 private void handleScanResults(@NonNull List<ScanDetail> scanDetails, 345 @NonNull String listenerName, 346 boolean isFullScan, 347 @NonNull HandleScanResultsListener handleScanResultsListener) { 348 List<WifiNetworkSelector.ClientModeManagerState> cmmStates = new ArrayList<>(); 349 Set<String> connectedSsids = new HashSet<>(); 350 boolean hasExistingSecondaryCmm = false; 351 for (ClientModeManager clientModeManager : 352 mActiveModeWarden.getInternetConnectivityClientModeManagers()) { 353 if (clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED) { 354 hasExistingSecondaryCmm = true; 355 } 356 mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization( 357 clientModeManager.getWifiLinkLayerStats(), 358 WifiChannelUtilization.UNKNOWN_FREQ); 359 WifiInfo wifiInfo = clientModeManager.syncRequestConnectionInfo(); 360 if (clientModeManager.isConnected()) { 361 connectedSsids.add(wifiInfo.getSSID()); 362 } 363 cmmStates.add(new WifiNetworkSelector.ClientModeManagerState(clientModeManager)); 364 } 365 // We don't have any existing secondary CMM, but are we allowed to create a secondary CMM 366 // and do we have a request for OEM_PAID/OEM_PRIVATE request? If yes, we need to perform 367 // network selection to check if we have any potential candidate for the secondary CMM 368 // creation. 369 if (!hasExistingSecondaryCmm 370 && (mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed)) { 371 // prefer OEM PAID requestor if it exists. 372 WorkSource oemPaidOrOemPrivateRequestorWs = 373 mOemPaidConnectionRequestorWs != null 374 ? mOemPaidConnectionRequestorWs 375 : mOemPrivateConnectionRequestorWs; 376 if (oemPaidOrOemPrivateRequestorWs == null) { 377 Log.e(TAG, "Both mOemPaidConnectionRequestorWs & mOemPrivateConnectionRequestorWs " 378 + "are null!"); 379 } 380 if (oemPaidOrOemPrivateRequestorWs != null 381 && mActiveModeWarden.canRequestMoreClientModeManagersInRole( 382 oemPaidOrOemPrivateRequestorWs, 383 ROLE_CLIENT_SECONDARY_LONG_LIVED)) { 384 // Add a placeholder CMM state to ensure network selection is performed for a 385 // potential second STA creation. 386 cmmStates.add(new WifiNetworkSelector.ClientModeManagerState()); 387 } 388 } 389 // Check if any blocklisted BSSIDs can be freed. 390 mWifiBlocklistMonitor.tryEnablingBlockedBssids(scanDetails); 391 Set<String> bssidBlocklist = mWifiBlocklistMonitor.updateAndGetBssidBlocklistForSsids( 392 connectedSsids); 393 updateUserDisabledList(scanDetails); 394 // Clear expired recent failure statuses 395 mConfigManager.cleanupExpiredRecentFailureReasons(); 396 397 localLog(listenerName + " onResults: start network selection"); 398 399 List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan( 400 scanDetails, bssidBlocklist, cmmStates, mUntrustedConnectionAllowed, 401 mOemPaidConnectionAllowed, mOemPrivateConnectionAllowed); 402 mLatestCandidates = candidates; 403 mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis(); 404 405 if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT 406 && mContext.getResources().getBoolean( 407 R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) { 408 candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan); 409 } 410 411 mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis(); 412 mWifiLastResortWatchdog.updateAvailableNetworks( 413 mNetworkSelector.getConnectableScanDetails()); 414 mWifiMetrics.countScanResults(scanDetails); 415 // No candidates, return early. 416 if (candidates == null || candidates.size() == 0) { 417 localLog(listenerName + ": No candidates"); 418 handleScanResultsWithNoCandidate(handleScanResultsListener); 419 return; 420 } 421 // We have an oem paid/private network request and device supports STA + STA, check if there 422 // are oem paid/private suggestions. 423 if ((mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed) 424 && mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) { 425 // Split the candidates based on whether they are oem paid/oem private or not. 426 Map<Boolean, List<WifiCandidates.Candidate>> candidatesPartitioned = 427 candidates.stream() 428 .collect(Collectors.groupingBy(c -> c.isOemPaid() || c.isOemPrivate())); 429 List<WifiCandidates.Candidate> primaryCmmCandidates = 430 candidatesPartitioned.getOrDefault(false, Collections.emptyList()); 431 List<WifiCandidates.Candidate> secondaryCmmCandidates = 432 candidatesPartitioned.getOrDefault(true, Collections.emptyList()); 433 // Some oem paid/private suggestions found, use secondary cmm flow. 434 if (!secondaryCmmCandidates.isEmpty()) { 435 handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable( 436 listenerName, primaryCmmCandidates, secondaryCmmCandidates, 437 handleScanResultsListener); 438 return; 439 } 440 // intentional fallthrough: No oem paid/private suggestions, fallback to legacy flow. 441 } 442 handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( 443 listenerName, candidates, handleScanResultsListener); 444 } 445 446 /** 447 * Executes selection of best network for 2 concurrent STA's from the candidates provided, 448 * initiation of connection attempt to a network on both the STA's (if found). 449 */ handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable( @onNull String listenerName, @NonNull List<WifiCandidates.Candidate> primaryCmmCandidates, @NonNull List<WifiCandidates.Candidate> secondaryCmmCandidates, @NonNull HandleScanResultsListener handleScanResultsListener)450 private void handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable( 451 @NonNull String listenerName, 452 @NonNull List<WifiCandidates.Candidate> primaryCmmCandidates, 453 @NonNull List<WifiCandidates.Candidate> secondaryCmmCandidates, 454 @NonNull HandleScanResultsListener handleScanResultsListener) { 455 // Perform network selection among secondary candidates. 456 WifiConfiguration secondaryCmmCandidate = 457 mNetworkSelector.selectNetwork(secondaryCmmCandidates); 458 // No oem paid/private selected, fallback to legacy flow (should never happen!). 459 if (secondaryCmmCandidate == null 460 || secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate() == null) { 461 localLog(listenerName + ": No secondary candidate"); 462 handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( 463 listenerName, 464 Stream.concat(primaryCmmCandidates.stream(), secondaryCmmCandidates.stream()) 465 .collect(Collectors.toList()), 466 handleScanResultsListener); 467 return; 468 } 469 String secondaryCmmCandidateBssid = 470 secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate().BSSID; 471 WorkSource secondaryRequestorWs = null; 472 // OEM_PAID takes precedence over OEM_PRIVATE, so attribute to OEM_PAID requesting app. 473 if (secondaryCmmCandidate.oemPaid 474 && mActiveModeWarden.canRequestMoreClientModeManagersInRole( 475 mOemPaidConnectionRequestorWs, ROLE_CLIENT_SECONDARY_LONG_LIVED)) { 476 secondaryRequestorWs = mOemPaidConnectionRequestorWs; 477 } else if (secondaryCmmCandidate.oemPrivate 478 && mActiveModeWarden.canRequestMoreClientModeManagersInRole( 479 mOemPrivateConnectionRequestorWs, ROLE_CLIENT_SECONDARY_LONG_LIVED)) { 480 secondaryRequestorWs = mOemPrivateConnectionRequestorWs; 481 } 482 // Secondary STA not available, fallback to legacy flow. 483 if (secondaryRequestorWs == null) { 484 localLog(listenerName + ": No secondary STA available"); 485 handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( 486 listenerName, 487 Stream.concat(primaryCmmCandidates.stream(), secondaryCmmCandidates.stream()) 488 .collect(Collectors.toList()), 489 handleScanResultsListener); 490 return; 491 } 492 WifiConfiguration primaryCmmCandidate = 493 mNetworkSelector.selectNetwork(primaryCmmCandidates); 494 // Request for a new client mode manager to spin up concurrent connection 495 mActiveModeWarden.requestSecondaryLongLivedClientModeManager( 496 (cm) -> { 497 if (cm == null) { 498 localLog(listenerName + ": Secondary client mode manager request returned " 499 + "null, aborting (wifi off?)"); 500 handleScanResultsWithNoCandidate(handleScanResultsListener); 501 return; 502 } 503 // We did not end up getting the secondary client mode manager for some reason 504 // after we checked above! Fallback to legacy flow. 505 if (cm.getRole() == ROLE_CLIENT_PRIMARY) { 506 localLog(listenerName + ": Secondary client mode manager request returned" 507 + " primary, falling back to single client mode manager flow."); 508 handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( 509 listenerName, 510 Stream.concat(primaryCmmCandidates.stream(), 511 secondaryCmmCandidates.stream()) 512 .collect(Collectors.toList()), 513 handleScanResultsListener); 514 return; 515 } 516 // Don't use make before break for these connection requests. 517 518 // If we also selected a primary candidate trigger connection. 519 if (primaryCmmCandidate != null) { 520 localLog(listenerName + ": WNS candidate(primary)-" 521 + primaryCmmCandidate.SSID); 522 connectToNetworkUsingCmmWithoutMbb( 523 getPrimaryClientModeManager(), primaryCmmCandidate); 524 } 525 526 localLog(listenerName + ": WNS candidate(secondary)-" 527 + secondaryCmmCandidate.SSID); 528 // Secndary candidate cannot be null (otherwise we would have switched to legacy 529 // flow above) 530 connectToNetworkUsingCmmWithoutMbb(cm, secondaryCmmCandidate); 531 532 handleScanResultsWithCandidate(handleScanResultsListener); 533 }, secondaryRequestorWs, 534 secondaryCmmCandidate.SSID, 535 mConnectivityHelper.isFirmwareRoamingSupported() 536 ? null : secondaryCmmCandidateBssid); 537 } 538 539 /** 540 * Executes selection of best network from the candidates provided, initiation of connection 541 * attempt to that network. 542 */ handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( @onNull String listenerName, @NonNull List<WifiCandidates.Candidate> candidates, @NonNull HandleScanResultsListener handleScanResultsListener)543 private void handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable( 544 @NonNull String listenerName, @NonNull List<WifiCandidates.Candidate> candidates, 545 @NonNull HandleScanResultsListener handleScanResultsListener) { 546 WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates); 547 if (candidate != null) { 548 localLog(listenerName + ": WNS candidate-" + candidate.SSID); 549 connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate); 550 handleScanResultsWithCandidate(handleScanResultsListener); 551 } else { 552 localLog(listenerName + ": No candidate"); 553 handleScanResultsWithNoCandidate(handleScanResultsListener); 554 } 555 } 556 filterCandidatesHighMovement( List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan)557 private List<WifiCandidates.Candidate> filterCandidatesHighMovement( 558 List<WifiCandidates.Candidate> candidates, String listenerName, boolean isFullScan) { 559 boolean isNotPartialScan = isFullScan || listenerName.equals(PNO_SCAN_LISTENER); 560 if (candidates == null || candidates.isEmpty()) { 561 // No connectable networks nearby or network selection is unnecessary 562 if (isNotPartialScan) { 563 mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 564 null); 565 } 566 return null; 567 } 568 569 long minimumTimeBetweenScansMs = mContext.getResources().getInteger( 570 R.integer.config_wifiHighMovementNetworkSelectionOptimizationScanDelayMs); 571 if (mCachedWifiCandidates != null && mCachedWifiCandidates.candidateRssiMap != null) { 572 // cached candidates are too recent, wait for next scan 573 if (mClock.getElapsedSinceBootMillis() - mCachedWifiCandidates.timeSinceBootMs 574 < minimumTimeBetweenScansMs) { 575 mWifiMetrics.incrementNumHighMovementConnectionSkipped(); 576 return null; 577 } 578 579 int rssiDelta = mContext.getResources().getInteger(R.integer 580 .config_wifiHighMovementNetworkSelectionOptimizationRssiDelta); 581 List<WifiCandidates.Candidate> filteredCandidates = candidates.stream().filter( 582 item -> mCachedWifiCandidates.candidateRssiMap.containsKey(item.getKey()) 583 && Math.abs(mCachedWifiCandidates.candidateRssiMap.get(item.getKey()) 584 - item.getScanRssi()) < rssiDelta) 585 .collect(Collectors.toList()); 586 587 if (!filteredCandidates.isEmpty()) { 588 if (isNotPartialScan) { 589 mCachedWifiCandidates = 590 new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 591 candidates); 592 } 593 mWifiMetrics.incrementNumHighMovementConnectionStarted(); 594 return filteredCandidates; 595 } 596 } 597 598 // Either no cached candidates, or all candidates got filtered out. 599 // Update the cached candidates here and schedule a delayed partial scan. 600 if (isNotPartialScan) { 601 mCachedWifiCandidates = new CachedWifiCandidates(mClock.getElapsedSinceBootMillis(), 602 candidates); 603 localLog("Found " + candidates.size() + " candidates at high mobility state. " 604 + "Re-doing scan to confirm network quality."); 605 scheduleDelayedPartialScan(minimumTimeBetweenScansMs); 606 } 607 mWifiMetrics.incrementNumHighMovementConnectionSkipped(); 608 return null; 609 } 610 updateUserDisabledList(List<ScanDetail> scanDetails)611 private void updateUserDisabledList(List<ScanDetail> scanDetails) { 612 List<String> results = new ArrayList<>(); 613 List<ScanResult> passpointAp = new ArrayList<>(); 614 for (ScanDetail scanDetail : scanDetails) { 615 results.add(ScanResultUtil.createQuotedSSID(scanDetail.getScanResult().SSID)); 616 if (!scanDetail.getScanResult().isPasspointNetwork()) { 617 continue; 618 } 619 passpointAp.add(scanDetail.getScanResult()); 620 } 621 if (!passpointAp.isEmpty()) { 622 results.addAll(mPasspointManager 623 .getAllMatchingPasspointProfilesForScanResults(passpointAp).keySet()); 624 } 625 mConfigManager.updateUserDisabledList(results); 626 } 627 628 private class CachedWifiCandidates { 629 public final long timeSinceBootMs; 630 public final Map<WifiCandidates.Key, Integer> candidateRssiMap; 631 public final Set<Integer> frequencies; 632 CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates)633 CachedWifiCandidates(long timeSinceBootMs, List<WifiCandidates.Candidate> candidates) { 634 this.timeSinceBootMs = timeSinceBootMs; 635 if (candidates == null) { 636 this.candidateRssiMap = null; 637 this.frequencies = null; 638 } else { 639 this.candidateRssiMap = new ArrayMap<WifiCandidates.Key, Integer>(); 640 this.frequencies = new HashSet<Integer>(); 641 for (WifiCandidates.Candidate c : candidates) { 642 candidateRssiMap.put(c.getKey(), c.getScanRssi()); 643 frequencies.add(c.getFrequency()); 644 } 645 } 646 } 647 } 648 649 // All single scan results listener. 650 // 651 // Note: This is the listener for all the available single scan results, 652 // including the ones initiated by WifiConnectivityManager and 653 // other modules. 654 private class AllSingleScanListener implements WifiScanner.ScanListener { 655 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 656 private int mNumScanResultsIgnoredDueToSingleRadioChain = 0; 657 clearScanDetails()658 public void clearScanDetails() { 659 mScanDetails.clear(); 660 mNumScanResultsIgnoredDueToSingleRadioChain = 0; 661 } 662 663 @Override onSuccess()664 public void onSuccess() { 665 } 666 667 @Override onFailure(int reason, String description)668 public void onFailure(int reason, String description) { 669 localLog("registerScanListener onFailure:" 670 + " reason: " + reason + " description: " + description); 671 } 672 673 @Override onPeriodChanged(int periodInMs)674 public void onPeriodChanged(int periodInMs) { 675 } 676 677 @Override onResults(WifiScanner.ScanData[] results)678 public void onResults(WifiScanner.ScanData[] results) { 679 if (!mWifiEnabled || !mAutoJoinEnabled) { 680 clearScanDetails(); 681 mWaitForFullBandScanResults = false; 682 return; 683 } 684 685 // We treat any full band scans (with DFS or not) as "full". 686 boolean isFullBandScanResults = false; 687 if (results != null && results.length > 0) { 688 isFullBandScanResults = 689 WifiScanner.isFullBandScan(results[0].getScannedBandsInternal(), true); 690 } 691 // Full band scan results only. 692 if (mWaitForFullBandScanResults) { 693 if (!isFullBandScanResults) { 694 localLog("AllSingleScanListener waiting for full band scan results."); 695 clearScanDetails(); 696 return; 697 } else { 698 mWaitForFullBandScanResults = false; 699 } 700 } 701 702 // Create a new list to avoid looping call trigger concurrent exception. 703 List<ScanDetail> scanDetailList = new ArrayList<>(mScanDetails); 704 clearScanDetails(); 705 706 if (results != null && results.length > 0) { 707 mWifiMetrics.incrementAvailableNetworksHistograms(scanDetailList, 708 isFullBandScanResults); 709 } 710 if (mNumScanResultsIgnoredDueToSingleRadioChain > 0) { 711 Log.i(TAG, "Number of scan results ignored due to single radio chain scan: " 712 + mNumScanResultsIgnoredDueToSingleRadioChain); 713 } 714 handleScanResults(scanDetailList, 715 ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults, 716 wasCandidateSelected -> { 717 // Update metrics to see if a single scan detected a valid network 718 // while PNO scan didn't. 719 // Note: We don't update the background scan metrics any more as it is 720 // not in use. 721 if (mPnoScanStarted) { 722 if (wasCandidateSelected) { 723 mWifiMetrics.incrementNumConnectivityWatchdogPnoBad(); 724 } else { 725 mWifiMetrics.incrementNumConnectivityWatchdogPnoGood(); 726 } 727 } 728 729 // Check if we are in the middle of initial partial scan 730 if (mInitialScanState == INITIAL_SCAN_STATE_AWAITING_RESPONSE) { 731 // Done with initial scan 732 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE); 733 734 if (wasCandidateSelected) { 735 Log.i(TAG, "Connection attempted with the reduced initial scans"); 736 mWifiMetrics.reportInitialPartialScan( 737 mInitialPartialScanChannelCount, true); 738 mInitialPartialScanChannelCount = 0; 739 } else { 740 Log.i(TAG, "Connection was not attempted, issuing a full scan"); 741 startConnectivityScan(SCAN_IMMEDIATELY); 742 mFailedInitialPartialScan = true; 743 } 744 } else if (mInitialScanState == INITIAL_SCAN_STATE_COMPLETE) { 745 if (mFailedInitialPartialScan && wasCandidateSelected) { 746 // Initial scan failed, but following full scan succeeded 747 mWifiMetrics.reportInitialPartialScan( 748 mInitialPartialScanChannelCount, false); 749 } 750 mFailedInitialPartialScan = false; 751 mInitialPartialScanChannelCount = 0; 752 } 753 }); 754 } 755 756 @Override onFullResult(ScanResult fullScanResult)757 public void onFullResult(ScanResult fullScanResult) { 758 if (!mWifiEnabled || !mAutoJoinEnabled) { 759 return; 760 } 761 762 if (mDbg) { 763 localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID 764 + " capabilities " + fullScanResult.capabilities); 765 } 766 767 // When the scan result has radio chain info, ensure we throw away scan results 768 // not received with both radio chains (if |mUseSingleRadioChainScanResults| is false). 769 if (!mContext.getResources().getBoolean( 770 R.bool.config_wifi_framework_use_single_radio_chain_scan_results_network_selection) 771 && fullScanResult.radioChainInfos != null 772 && fullScanResult.radioChainInfos.length == 1) { 773 // Keep track of the number of dropped scan results for logging. 774 mNumScanResultsIgnoredDueToSingleRadioChain++; 775 return; 776 } 777 778 mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult)); 779 } 780 } 781 782 private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener(); 783 784 // Single scan results listener. A single scan is initiated when 785 // DisconnectedPNO scan found a valid network and woke up 786 // the system, or by the watchdog timer, or to form the timer based 787 // periodic scan. 788 // 789 // Note: This is the listener for the single scans initiated by the 790 // WifiConnectivityManager. 791 private class SingleScanListener implements WifiScanner.ScanListener { 792 private final boolean mIsFullBandScan; 793 SingleScanListener(boolean isFullBandScan)794 SingleScanListener(boolean isFullBandScan) { 795 mIsFullBandScan = isFullBandScan; 796 } 797 798 @Override onSuccess()799 public void onSuccess() { 800 } 801 802 @Override onFailure(int reason, String description)803 public void onFailure(int reason, String description) { 804 localLog("SingleScanListener onFailure:" 805 + " reason: " + reason + " description: " + description); 806 807 // reschedule the scan 808 if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 809 scheduleDelayedSingleScan(mIsFullBandScan); 810 } else { 811 mSingleScanRestartCount = 0; 812 localLog("Failed to successfully start single scan for " 813 + MAX_SCAN_RESTART_ALLOWED + " times"); 814 } 815 } 816 817 @Override onPeriodChanged(int periodInMs)818 public void onPeriodChanged(int periodInMs) { 819 localLog("SingleScanListener onPeriodChanged: " 820 + "actual scan period " + periodInMs + "ms"); 821 } 822 823 @Override onResults(WifiScanner.ScanData[] results)824 public void onResults(WifiScanner.ScanData[] results) { 825 mSingleScanRestartCount = 0; 826 } 827 828 @Override onFullResult(ScanResult fullScanResult)829 public void onFullResult(ScanResult fullScanResult) { 830 } 831 } 832 833 // PNO scan results listener for both disconnected and connected PNO scanning. 834 // A PNO scan is initiated when screen is off. 835 private class PnoScanListener implements WifiScanner.PnoScanListener { 836 private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>(); 837 private int mLowRssiNetworkRetryDelayMs; 838 limitLowRssiNetworkRetryDelay()839 private void limitLowRssiNetworkRetryDelay() { 840 mLowRssiNetworkRetryDelayMs = Math.min(mLowRssiNetworkRetryDelayMs, 841 mContext.getResources().getInteger(R.integer 842 .config_wifiPnoScanLowRssiNetworkRetryMaxDelaySec) * 1000); 843 } 844 clearScanDetails()845 public void clearScanDetails() { 846 mScanDetails.clear(); 847 } 848 849 // Reset to the start value when either a non-PNO scan is started or 850 // WifiNetworkSelector selects a candidate from the PNO scan results. resetLowRssiNetworkRetryDelay()851 public void resetLowRssiNetworkRetryDelay() { 852 mLowRssiNetworkRetryDelayMs = mContext.getResources().getInteger(R.integer 853 .config_wifiPnoScanLowRssiNetworkRetryStartDelaySec) * 1000; 854 } 855 856 @VisibleForTesting getLowRssiNetworkRetryDelay()857 public int getLowRssiNetworkRetryDelay() { 858 return mLowRssiNetworkRetryDelayMs; 859 } 860 861 @Override onSuccess()862 public void onSuccess() { 863 } 864 865 @Override onFailure(int reason, String description)866 public void onFailure(int reason, String description) { 867 localLog("PnoScanListener onFailure:" 868 + " reason: " + reason + " description: " + description); 869 870 // reschedule the scan 871 if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) { 872 scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS); 873 } else { 874 mScanRestartCount = 0; 875 localLog("Failed to successfully start PNO scan for " 876 + MAX_SCAN_RESTART_ALLOWED + " times"); 877 } 878 } 879 880 @Override onPeriodChanged(int periodInMs)881 public void onPeriodChanged(int periodInMs) { 882 localLog("PnoScanListener onPeriodChanged: " 883 + "actual scan period " + periodInMs + "ms"); 884 } 885 886 // Currently the PNO scan results doesn't include IE, 887 // which contains information required by WifiNetworkSelector. Ignore them 888 // for now. 889 @Override onResults(WifiScanner.ScanData[] results)890 public void onResults(WifiScanner.ScanData[] results) { 891 } 892 893 @Override onFullResult(ScanResult fullScanResult)894 public void onFullResult(ScanResult fullScanResult) { 895 } 896 897 @Override onPnoNetworkFound(ScanResult[] results)898 public void onPnoNetworkFound(ScanResult[] results) { 899 for (ScanResult result: results) { 900 if (result.informationElements == null) { 901 localLog("Skipping scan result with null information elements"); 902 continue; 903 } 904 mScanDetails.add(ScanResultUtil.toScanDetail(result)); 905 } 906 907 // Create a new list to avoid looping call trigger concurrent exception. 908 List<ScanDetail> scanDetailList = new ArrayList<>(mScanDetails); 909 clearScanDetails(); 910 mScanRestartCount = 0; 911 912 handleScanResults(scanDetailList, PNO_SCAN_LISTENER, false, 913 wasCandidateSelected -> { 914 if (!wasCandidateSelected) { 915 // The scan results were rejected by WifiNetworkSelector due to low 916 // RSSI values 917 // Lazy initialization 918 if (mLowRssiNetworkRetryDelayMs == 0) { 919 resetLowRssiNetworkRetryDelay(); 920 } 921 scheduleDelayedConnectivityScan(mLowRssiNetworkRetryDelayMs); 922 923 // Set up the delay value for next retry. 924 mLowRssiNetworkRetryDelayMs *= 2; 925 limitLowRssiNetworkRetryDelay(); 926 } else { 927 resetLowRssiNetworkRetryDelay(); 928 } 929 }); 930 } 931 } 932 933 private final PnoScanListener mPnoScanListener = new PnoScanListener(); 934 935 private class OnNetworkUpdateListener implements 936 WifiConfigManager.OnNetworkUpdateListener { 937 @Override onNetworkAdded(WifiConfiguration config)938 public void onNetworkAdded(WifiConfiguration config) { 939 triggerScanOnNetworkChanges(); 940 } 941 @Override onNetworkEnabled(WifiConfiguration config)942 public void onNetworkEnabled(WifiConfiguration config) { 943 triggerScanOnNetworkChanges(); 944 } 945 @Override onNetworkRemoved(WifiConfiguration config)946 public void onNetworkRemoved(WifiConfiguration config) { 947 triggerScanOnNetworkChanges(); 948 } 949 @Override onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig)950 public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig) { 951 triggerScanOnNetworkChanges(); 952 } 953 954 @Override onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason)955 public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) { 956 triggerScanOnNetworkChanges(); 957 } 958 } 959 960 private class OnSuggestionUpdateListener implements 961 WifiNetworkSuggestionsManager.OnSuggestionUpdateListener { 962 @Override onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions)963 public void onSuggestionsAddedOrUpdated(List<WifiNetworkSuggestion> suggestions) { 964 triggerScanOnNetworkChanges(); 965 } 966 967 @Override onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions)968 public void onSuggestionsRemoved(List<WifiNetworkSuggestion> suggestions) { 969 triggerScanOnNetworkChanges(); 970 } 971 } 972 973 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 974 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)975 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 976 update(); 977 } 978 979 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)980 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 981 update(); 982 } 983 984 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)985 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 986 update(); 987 } 988 update()989 private void update() { 990 List<ClientModeManager> primaryManagers = 991 mActiveModeWarden.getInternetConnectivityClientModeManagers(); 992 setWifiEnabled(!primaryManagers.isEmpty()); 993 } 994 } 995 996 /** 997 * WifiConnectivityManager constructor 998 */ WifiConnectivityManager( WifiContext context, ScoringParams scoringParams, WifiConfigManager configManager, WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper, WifiLastResortWatchdog wifiLastResortWatchdog, OpenNetworkNotifier openNetworkNotifier, WifiMetrics wifiMetrics, Handler handler, Clock clock, LocalLog localLog, WifiScoreCard scoreCard, WifiBlocklistMonitor wifiBlocklistMonitor, WifiChannelUtilization wifiChannelUtilization, PasspointManager passpointManager, DeviceConfigFacade deviceConfigFacade, ActiveModeWarden activeModeWarden, WifiGlobals wifiGlobals)999 WifiConnectivityManager( 1000 WifiContext context, 1001 ScoringParams scoringParams, 1002 WifiConfigManager configManager, 1003 WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager, 1004 WifiNetworkSelector networkSelector, 1005 WifiConnectivityHelper connectivityHelper, 1006 WifiLastResortWatchdog wifiLastResortWatchdog, 1007 OpenNetworkNotifier openNetworkNotifier, 1008 WifiMetrics wifiMetrics, 1009 Handler handler, 1010 Clock clock, 1011 LocalLog localLog, 1012 WifiScoreCard scoreCard, 1013 WifiBlocklistMonitor wifiBlocklistMonitor, 1014 WifiChannelUtilization wifiChannelUtilization, 1015 PasspointManager passpointManager, 1016 DeviceConfigFacade deviceConfigFacade, 1017 ActiveModeWarden activeModeWarden, 1018 WifiGlobals wifiGlobals) { 1019 mContext = context; 1020 mScoringParams = scoringParams; 1021 mConfigManager = configManager; 1022 mWifiNetworkSuggestionsManager = wifiNetworkSuggestionsManager; 1023 mNetworkSelector = networkSelector; 1024 mConnectivityHelper = connectivityHelper; 1025 mWifiLastResortWatchdog = wifiLastResortWatchdog; 1026 mOpenNetworkNotifier = openNetworkNotifier; 1027 mWifiMetrics = wifiMetrics; 1028 mEventHandler = handler; 1029 mClock = clock; 1030 mLocalLog = localLog; 1031 mWifiScoreCard = scoreCard; 1032 mWifiBlocklistMonitor = wifiBlocklistMonitor; 1033 mWifiChannelUtilization = wifiChannelUtilization; 1034 mPasspointManager = passpointManager; 1035 mDeviceConfigFacade = deviceConfigFacade; 1036 mActiveModeWarden = activeModeWarden; 1037 mWifiGlobals = wifiGlobals; 1038 1039 mAlarmManager = context.getSystemService(AlarmManager.class); 1040 mPowerManager = mContext.getSystemService(PowerManager.class); 1041 1042 // Listen for screen state change events. 1043 // TODO: We should probably add a shared broadcast receiver in the wifi stack which 1044 // can used by various modules to listen to common system events. Creating multiple 1045 // broadcast receivers in each class within the wifi stack is *somewhat* expensive. 1046 IntentFilter filter = new IntentFilter(); 1047 filter.addAction(Intent.ACTION_SCREEN_ON); 1048 filter.addAction(Intent.ACTION_SCREEN_OFF); 1049 mContext.registerReceiver( 1050 new BroadcastReceiver() { 1051 @Override 1052 public void onReceive(Context context, Intent intent) { 1053 String action = intent.getAction(); 1054 if (action.equals(Intent.ACTION_SCREEN_ON)) { 1055 handleScreenStateChanged(true); 1056 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 1057 handleScreenStateChanged(false); 1058 } 1059 } 1060 }, filter, null, mEventHandler); 1061 handleScreenStateChanged(mPowerManager.isInteractive()); 1062 1063 // Listen to WifiConfigManager network update events 1064 mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()); 1065 // Listen to WifiNetworkSuggestionsManager suggestion update events 1066 mWifiNetworkSuggestionsManager.addOnSuggestionUpdateListener( 1067 new OnSuggestionUpdateListener()); 1068 mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 1069 } 1070 1071 @NonNull getPrimaryWifiInfo()1072 private WifiInfo getPrimaryWifiInfo() { 1073 return getPrimaryClientModeManager().syncRequestConnectionInfo(); 1074 } 1075 getPrimaryClientModeManager()1076 private ClientModeManager getPrimaryClientModeManager() { 1077 // There should only be 1 primary client mode manager at any point of time. 1078 return mActiveModeWarden.getPrimaryClientModeManager(); 1079 } 1080 1081 /** Initialize single scanning schedules, and validate them */ initializeScanningSchedule(int state)1082 private int[] initializeScanningSchedule(int state) { 1083 int[] scheduleSec; 1084 1085 if (state == WIFI_STATE_CONNECTED) { 1086 scheduleSec = mContext.getResources().getIntArray( 1087 R.array.config_wifiConnectedScanIntervalScheduleSec); 1088 } else if (state == WIFI_STATE_DISCONNECTED) { 1089 scheduleSec = mContext.getResources().getIntArray( 1090 R.array.config_wifiDisconnectedScanIntervalScheduleSec); 1091 } else { 1092 scheduleSec = null; 1093 } 1094 1095 boolean invalidConfig = false; 1096 if (scheduleSec == null || scheduleSec.length == 0) { 1097 invalidConfig = true; 1098 } else { 1099 for (int val : scheduleSec) { 1100 if (val <= 0) { 1101 invalidConfig = true; 1102 break; 1103 } 1104 } 1105 } 1106 if (!invalidConfig) { 1107 return scheduleSec; 1108 } 1109 1110 Log.e(TAG, "Configuration for wifi scanning schedule is mis-configured," 1111 + "using default schedule"); 1112 return DEFAULT_SCANNING_SCHEDULE_SEC; 1113 } 1114 1115 /** 1116 * This checks the connection attempt rate and recommends whether the connection attempt 1117 * should be skipped or not. This attempts to rate limit the rate of connections to 1118 * prevent us from flapping between networks and draining battery rapidly. 1119 */ shouldSkipConnectionAttempt(long timeMillis)1120 private boolean shouldSkipConnectionAttempt(long timeMillis) { 1121 Iterator<Long> attemptIter = mConnectionAttemptTimeStamps.iterator(); 1122 // First evict old entries from the queue. 1123 while (attemptIter.hasNext()) { 1124 Long connectionAttemptTimeMillis = attemptIter.next(); 1125 if ((timeMillis - connectionAttemptTimeMillis) 1126 > MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS) { 1127 attemptIter.remove(); 1128 } else { 1129 // This list is sorted by timestamps, so we can skip any more checks 1130 break; 1131 } 1132 } 1133 // If we've reached the max connection attempt rate, skip this connection attempt 1134 return (mConnectionAttemptTimeStamps.size() >= MAX_CONNECTION_ATTEMPTS_RATE); 1135 } 1136 1137 /** 1138 * Add the current connection attempt timestamp to our queue of connection attempts. 1139 */ noteConnectionAttempt(long timeMillis)1140 private void noteConnectionAttempt(long timeMillis) { 1141 localLog("noteConnectionAttempt: timeMillis=" + timeMillis); 1142 mConnectionAttemptTimeStamps.addLast(timeMillis); 1143 } 1144 1145 /** 1146 * This is used to clear the connection attempt rate limiter. This is done when the user 1147 * explicitly tries to connect to a specified network. 1148 */ clearConnectionAttemptTimeStamps()1149 private void clearConnectionAttemptTimeStamps() { 1150 mConnectionAttemptTimeStamps.clear(); 1151 } 1152 coalesce(T a, T b)1153 private static <T> T coalesce(T a, T b) { 1154 return a != null ? a : b; 1155 } 1156 isClientModeManagerConnectedOrConnectingToCandidate( ClientModeManager clientModeManager, WifiConfiguration candidate)1157 private boolean isClientModeManagerConnectedOrConnectingToCandidate( 1158 ClientModeManager clientModeManager, WifiConfiguration candidate) { 1159 int targetNetworkId = candidate.networkId; 1160 WifiConfiguration connectedOrConnectingWifiConfiguration = coalesce( 1161 clientModeManager.getConnectingWifiConfiguration(), 1162 clientModeManager.getConnectedWifiConfiguration()); 1163 boolean connectingOrConnectedToTarget = 1164 connectedOrConnectingWifiConfiguration != null 1165 && (targetNetworkId == connectedOrConnectingWifiConfiguration.networkId 1166 || (mContext.getResources().getBoolean( 1167 R.bool.config_wifiEnableLinkedNetworkRoaming) 1168 && connectedOrConnectingWifiConfiguration.isLinked(candidate))); 1169 1170 // Is Firmware roaming control is supported? 1171 // - Yes, framework does nothing, firmware will roam if necessary. 1172 // - No, framework initiates roaming. 1173 if (mConnectivityHelper.isFirmwareRoamingSupported()) { 1174 // just check for networkID. 1175 return connectingOrConnectedToTarget; 1176 } 1177 1178 // check for networkID and BSSID. 1179 String connectedOrConnectingBssid = coalesce( 1180 clientModeManager.getConnectingBssid(), 1181 clientModeManager.getConnectedBssid()); 1182 ScanResult scanResultCandidate = 1183 candidate.getNetworkSelectionStatus().getCandidate(); 1184 String targetBssid = scanResultCandidate.BSSID; 1185 return connectingOrConnectedToTarget 1186 && Objects.equals(targetBssid, connectedOrConnectingBssid); 1187 } 1188 1189 /** 1190 * Trigger network connection for primary client mode manager using make before break. 1191 * 1192 * Note: This may trigger make before break on a secondary STA if available which will 1193 * eventually become primary after validation or torn down if it does not become primary. 1194 */ connectToNetworkForPrimaryCmmUsingMbbIfAvailable( @onNull WifiConfiguration candidate)1195 private void connectToNetworkForPrimaryCmmUsingMbbIfAvailable( 1196 @NonNull WifiConfiguration candidate) { 1197 ClientModeManager primaryManager = mActiveModeWarden.getPrimaryClientModeManager(); 1198 connectToNetworkUsingCmm( 1199 primaryManager, candidate, 1200 new ConnectHandler() { 1201 @Override 1202 public void triggerConnectWhenDisconnected( 1203 WifiConfiguration targetNetwork, 1204 String targetBssid) { 1205 triggerConnectToNetworkUsingCmm(primaryManager, targetNetwork, targetBssid); 1206 // since using primary manager to connect, stop any existing managers in the 1207 // secondary transient role since they are no longer needed. 1208 mActiveModeWarden.stopAllClientModeManagersInRole( 1209 ROLE_CLIENT_SECONDARY_TRANSIENT); 1210 } 1211 1212 @Override 1213 public void triggerConnectWhenConnected( 1214 WifiConfiguration currentNetwork, 1215 WifiConfiguration targetNetwork, 1216 String targetBssid) { 1217 mWifiMetrics.incrementWifiToWifiSwitchTriggerCount(); 1218 // If both the current & target networks have MAC randomization disabled, 1219 // we cannot use MBB because then both ifaces would need to use the exact 1220 // same MAC address (the "designated" factory MAC for the device), which is 1221 // illegal. Fallback to single STA behavior. 1222 1223 // TODO(b/172086124): Possibly move this logic to 1224 // ActiveModeWarden.handleAdditionalClientModeManagerRequest() to 1225 // ensure that all fallback logic in 1 central place (all the necessary 1226 // info is already included in the secondary STA creation request). 1227 if (currentNetwork.macRandomizationSetting == RANDOMIZATION_NONE 1228 && targetNetwork.macRandomizationSetting == RANDOMIZATION_NONE) { 1229 triggerConnectToNetworkUsingCmm( 1230 primaryManager, targetNetwork, targetBssid); 1231 // since using primary manager to connect, stop any existing managers in 1232 // the secondary transient role since they are no longer needed. 1233 mActiveModeWarden.stopAllClientModeManagersInRole( 1234 ROLE_CLIENT_SECONDARY_TRANSIENT); 1235 return; 1236 } 1237 // Else, use MBB if available. 1238 triggerConnectToNetworkUsingMbbIfAvailable(targetNetwork, targetBssid); 1239 } 1240 1241 @Override 1242 public void triggerRoamWhenConnected( 1243 WifiConfiguration currentNetwork, 1244 WifiConfiguration targetNetwork, 1245 String targetBssid) { 1246 triggerRoamToNetworkUsingCmm( 1247 primaryManager, targetNetwork, targetBssid); 1248 // since using primary manager to connect, stop any existing managers in the 1249 // secondary transient role since they are no longer needed. 1250 mActiveModeWarden.stopAllClientModeManagersInRole( 1251 ROLE_CLIENT_SECONDARY_TRANSIENT); 1252 } 1253 }); 1254 1255 } 1256 1257 /** 1258 * Trigger network connection for provided client mode manager without using make before break. 1259 */ connectToNetworkUsingCmmWithoutMbb( @onNull ClientModeManager clientModeManager, @NonNull WifiConfiguration candidate)1260 private void connectToNetworkUsingCmmWithoutMbb( 1261 @NonNull ClientModeManager clientModeManager, @NonNull WifiConfiguration candidate) { 1262 connectToNetworkUsingCmm(clientModeManager, candidate, 1263 new ConnectHandler() { 1264 @Override 1265 public void triggerConnectWhenDisconnected( 1266 WifiConfiguration targetNetwork, 1267 String targetBssid) { 1268 triggerConnectToNetworkUsingCmm( 1269 clientModeManager, targetNetwork, targetBssid); 1270 } 1271 1272 @Override 1273 public void triggerConnectWhenConnected( 1274 WifiConfiguration currentNetwork, 1275 WifiConfiguration targetNetwork, 1276 String targetBssid) { 1277 triggerConnectToNetworkUsingCmm( 1278 clientModeManager, targetNetwork, targetBssid); 1279 } 1280 1281 @Override 1282 public void triggerRoamWhenConnected( 1283 WifiConfiguration currentNetwork, 1284 WifiConfiguration targetNetwork, 1285 String targetBssid) { 1286 triggerRoamToNetworkUsingCmm( 1287 clientModeManager, targetNetwork, targetBssid); 1288 } 1289 }); 1290 } 1291 1292 /** 1293 * Interface to use for trigger connection in various scenarios. 1294 */ 1295 private interface ConnectHandler { 1296 /** 1297 * Invoked to trigger connection to a network when disconnected. 1298 */ triggerConnectWhenDisconnected( @onNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1299 void triggerConnectWhenDisconnected( 1300 @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid); 1301 /** 1302 * Invoked to trigger connection to a network when connected to a different network. 1303 */ triggerConnectWhenConnected( @onNull WifiConfiguration currentNetwork, @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1304 void triggerConnectWhenConnected( 1305 @NonNull WifiConfiguration currentNetwork, @NonNull WifiConfiguration targetNetwork, 1306 @NonNull String targetBssid); 1307 /** 1308 * Invoked to trigger roam to a specific bssid network when connected to a network. 1309 */ triggerRoamWhenConnected( @onNull WifiConfiguration currentNetwork, @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1310 void triggerRoamWhenConnected( 1311 @NonNull WifiConfiguration currentNetwork, @NonNull WifiConfiguration targetNetwork, 1312 @NonNull String targetBssid); 1313 } 1314 getAssociationId(@ullable WifiConfiguration config, @Nullable String bssid)1315 private String getAssociationId(@Nullable WifiConfiguration config, @Nullable String bssid) { 1316 return config == null ? "Disconnected" : config.SSID + " : " + bssid; 1317 } 1318 1319 /** 1320 * Attempt to connect to a network candidate. 1321 * 1322 * Based on the currently connected network, this method determines whether we should 1323 * connect or roam to the network candidate recommended by WifiNetworkSelector. 1324 */ connectToNetworkUsingCmm(@onNull ClientModeManager clientModeManager, @NonNull WifiConfiguration targetNetwork, @NonNull ConnectHandler connectHandler)1325 private void connectToNetworkUsingCmm(@NonNull ClientModeManager clientModeManager, 1326 @NonNull WifiConfiguration targetNetwork, 1327 @NonNull ConnectHandler connectHandler) { 1328 if (targetNetwork.getNetworkSelectionStatus().getCandidate() == null) { 1329 localLog("connectToNetwork(" + clientModeManager + "): bad candidate - " 1330 + targetNetwork + " scanResult is null!"); 1331 return; 1332 } 1333 String targetBssid = targetNetwork.getNetworkSelectionStatus().getCandidate().BSSID; 1334 String targetAssociationId = getAssociationId(targetNetwork, targetBssid); 1335 1336 if (isClientModeManagerConnectedOrConnectingToCandidate(clientModeManager, targetNetwork)) { 1337 localLog("connectToNetwork(" + clientModeManager + "): either already connected or is " 1338 + "connecting to " + targetAssociationId); 1339 return; 1340 } 1341 1342 if (targetNetwork.BSSID != null 1343 && !targetNetwork.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY) 1344 && !targetNetwork.BSSID.equals(targetBssid)) { 1345 localLog("connectToNetwork(" + clientModeManager + "): target BSSID " + targetBssid 1346 + " does not match the config specified BSSID " + targetNetwork.BSSID 1347 + ". Drop it!"); 1348 return; 1349 } 1350 1351 WifiConfiguration currentNetwork = coalesce( 1352 clientModeManager.getConnectedWifiConfiguration(), 1353 clientModeManager.getConnectingWifiConfiguration()); 1354 String currentBssid = coalesce( 1355 clientModeManager.getConnectedBssid(), clientModeManager.getConnectingBssid()); 1356 String currentAssociationId = getAssociationId(currentNetwork, currentBssid); 1357 1358 // Already on desired network id, we need to trigger roam since the device does not 1359 // support firmware roaming (already checked in 1360 // isClientModeManagerConnectedOrConnectingToCandidate()). 1361 if (currentNetwork != null 1362 && (currentNetwork.networkId == targetNetwork.networkId 1363 || (mContext.getResources().getBoolean(R.bool.config_wifiEnableLinkedNetworkRoaming) 1364 && currentNetwork.isLinked(targetNetwork)))) { 1365 localLog("connectToNetwork(" + clientModeManager + "): Roam to " + targetAssociationId 1366 + " from " + currentAssociationId); 1367 connectHandler.triggerRoamWhenConnected(currentNetwork, targetNetwork, targetBssid); 1368 return; 1369 } 1370 1371 // Need to connect to a different network id 1372 // Framework specifies the connection target BSSID if firmware doesn't support 1373 // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the 1374 // candidate configuration contains a specified BSSID. 1375 if (mConnectivityHelper.isFirmwareRoamingSupported() 1376 && (targetNetwork.BSSID == null 1377 || targetNetwork.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY))) { 1378 targetBssid = ClientModeImpl.SUPPLICANT_BSSID_ANY; 1379 } 1380 localLog("connectToNetwork(" + clientModeManager + "): Connect to " 1381 + getAssociationId(targetNetwork, targetBssid) + " from " 1382 + currentAssociationId); 1383 if (currentNetwork == null) { 1384 connectHandler.triggerConnectWhenDisconnected(targetNetwork, targetBssid); 1385 return; 1386 } 1387 connectHandler.triggerConnectWhenConnected(currentNetwork, targetNetwork, targetBssid); 1388 } 1389 shouldConnect()1390 private boolean shouldConnect() { 1391 long elapsedTimeMillis = mClock.getElapsedSinceBootMillis(); 1392 if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) { 1393 localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!"); 1394 mTotalConnectivityAttemptsRateLimited++; 1395 return false; 1396 } 1397 noteConnectionAttempt(elapsedTimeMillis); 1398 return true; 1399 } 1400 1401 /** 1402 * Trigger roaming to a new bssid while being connected to a different bssid in same network. 1403 */ triggerRoamToNetworkUsingCmm( @onNull ClientModeManager clientModeManager, @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1404 private void triggerRoamToNetworkUsingCmm( 1405 @NonNull ClientModeManager clientModeManager, 1406 @NonNull WifiConfiguration targetNetwork, 1407 @NonNull String targetBssid) { 1408 if (!shouldConnect()) { 1409 return; 1410 } 1411 clientModeManager.startRoamToNetwork(targetNetwork.networkId, targetBssid); 1412 } 1413 1414 /** 1415 * Trigger connection to a new wifi network while being disconnected. 1416 */ triggerConnectToNetworkUsingCmm( @onNull ClientModeManager clientModeManager, @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1417 private void triggerConnectToNetworkUsingCmm( 1418 @NonNull ClientModeManager clientModeManager, 1419 @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid) { 1420 if (!shouldConnect()) { 1421 return; 1422 } 1423 clientModeManager.startConnectToNetwork( 1424 targetNetwork.networkId, Process.WIFI_UID, targetBssid); 1425 } 1426 1427 /** 1428 * Trigger connection to a new wifi network while being connected to another network. 1429 * Depending on device configuration, this uses 1430 * - MBB make before break (Dual STA), or 1431 * - BBM break before make (Single STA) 1432 */ triggerConnectToNetworkUsingMbbIfAvailable( @onNull WifiConfiguration targetNetwork, @NonNull String targetBssid)1433 private void triggerConnectToNetworkUsingMbbIfAvailable( 1434 @NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid) { 1435 // Request a ClientModeManager from ActiveModeWarden to connect with - may be an existing 1436 // CMM or a newly created one (potentially switching networks using Make-Before-Break) 1437 mActiveModeWarden.requestSecondaryTransientClientModeManager( 1438 (@Nullable ClientModeManager clientModeManager) -> { 1439 localLog("connectToNetwork: received requested ClientModeManager " 1440 + clientModeManager); 1441 if (clientModeManager == null) { 1442 localLog("connectToNetwork: Wifi has been toggled off, aborting"); 1443 return; 1444 } 1445 // we don't know which ClientModeManager will be allocated to us. Thus, double 1446 // check if we're already connected before connecting. 1447 if (isClientModeManagerConnectedOrConnectingToCandidate( 1448 clientModeManager, targetNetwork)) { 1449 localLog("connectToNetwork: already connected or connecting to candidate=" 1450 + targetNetwork + " on " + clientModeManager); 1451 return; 1452 } 1453 if (clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_TRANSIENT) { 1454 mWifiMetrics.incrementMakeBeforeBreakTriggerCount(); 1455 } 1456 triggerConnectToNetworkUsingCmm(clientModeManager, targetNetwork, targetBssid); 1457 }, 1458 ActiveModeWarden.INTERNAL_REQUESTOR_WS, 1459 targetNetwork.SSID, 1460 mConnectivityHelper.isFirmwareRoamingSupported() ? null : targetBssid); 1461 } 1462 1463 // Helper for selecting the band for connectivity scan getScanBand()1464 private int getScanBand() { 1465 return getScanBand(true); 1466 } 1467 getScanBand(boolean isFullBandScan)1468 private int getScanBand(boolean isFullBandScan) { 1469 if (isFullBandScan) { 1470 if (SdkLevel.isAtLeastS()) { 1471 if (mContext.getResources().getBoolean(R.bool.config_wifiEnable6ghzPscScanning)) { 1472 return WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ; 1473 } 1474 return WifiScanner.WIFI_BAND_BOTH_WITH_DFS; 1475 } 1476 return WifiScanner.WIFI_BAND_ALL; 1477 } else { 1478 // Use channel list instead. 1479 return WifiScanner.WIFI_BAND_UNSPECIFIED; 1480 } 1481 } 1482 1483 // Helper for setting the channels for connectivity scan when band is unspecified. Returns 1484 // false if we can't retrieve the info. 1485 // If connected, return channels used for the connected network 1486 // If disconnected, return channels used for any network. setScanChannels(ScanSettings settings)1487 private boolean setScanChannels(ScanSettings settings) { 1488 Set<Integer> freqs; 1489 1490 WifiConfiguration config = getPrimaryClientModeManager().getConnectedWifiConfiguration(); 1491 if (config == null) { 1492 long ageInMillis = 1000 * 60 * mContext.getResources().getInteger( 1493 R.integer.config_wifiInitialPartialScanChannelCacheAgeMins); 1494 int maxCount = mContext.getResources().getInteger( 1495 R.integer.config_wifiInitialPartialScanChannelMaxCount); 1496 freqs = fetchChannelSetForPartialScan(maxCount, ageInMillis); 1497 } else { 1498 freqs = fetchChannelSetForNetworkForPartialScan(config.networkId); 1499 } 1500 1501 if (freqs != null && freqs.size() != 0) { 1502 int index = 0; 1503 settings.channels = new WifiScanner.ChannelSpec[freqs.size()]; 1504 for (Integer freq : freqs) { 1505 settings.channels[index++] = new WifiScanner.ChannelSpec(freq); 1506 } 1507 return true; 1508 } else { 1509 localLog("No history scan channels found, Perform full band scan"); 1510 return false; 1511 } 1512 } 1513 1514 /** 1515 * Add the channels into the channel set with a size limit. 1516 * If maxCount equals to 0, will add all available channels into the set. 1517 * @param channelSet Target set for adding channel to. 1518 * @param config Network for query channel from WifiScoreCard 1519 * @param maxCount Size limit of the set. If equals to 0, means no limit. 1520 * @param ageInMillis Only consider channel info whose timestamps are younger than this value. 1521 * @return True if all available channels for this network are added, otherwise false. 1522 */ addChannelFromWifiScoreCard(@onNull Set<Integer> channelSet, @NonNull WifiConfiguration config, int maxCount, long ageInMillis)1523 private boolean addChannelFromWifiScoreCard(@NonNull Set<Integer> channelSet, 1524 @NonNull WifiConfiguration config, int maxCount, long ageInMillis) { 1525 WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(config.SSID); 1526 for (Integer channel : network.getFrequencies(ageInMillis)) { 1527 if (maxCount > 0 && channelSet.size() >= maxCount) { 1528 localLog("addChannelFromWifiScoreCard: size limit reached for network:" 1529 + config.SSID); 1530 return false; 1531 } 1532 channelSet.add(channel); 1533 } 1534 return true; 1535 } 1536 1537 /** 1538 * Fetch channel set for target network. 1539 */ 1540 @VisibleForTesting fetchChannelSetForNetworkForPartialScan(int networkId)1541 public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId) { 1542 WifiConfiguration config = mConfigManager.getConfiguredNetwork(networkId); 1543 if (config == null) { 1544 return null; 1545 } 1546 final int maxNumActiveChannelsForPartialScans = mContext.getResources().getInteger( 1547 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels); 1548 Set<Integer> channelSet = new HashSet<>(); 1549 WifiInfo wifiInfo = getPrimaryWifiInfo(); 1550 // First add the currently connected network channel. 1551 if (wifiInfo.getFrequency() > 0) { 1552 channelSet.add(wifiInfo.getFrequency()); 1553 } 1554 // Then get channels for the network. 1555 addChannelFromWifiScoreCard(channelSet, config, maxNumActiveChannelsForPartialScans, 1556 CHANNEL_LIST_AGE_MS); 1557 return channelSet; 1558 } 1559 1560 /** 1561 * Fetch channel set for all saved and suggestion non-passpoint network for partial scan. 1562 */ 1563 @VisibleForTesting fetchChannelSetForPartialScan(int maxCount, long ageInMillis)1564 public Set<Integer> fetchChannelSetForPartialScan(int maxCount, long ageInMillis) { 1565 List<WifiConfiguration> networks = getAllScanOptimizationNetworks(); 1566 if (networks.isEmpty()) { 1567 return null; 1568 } 1569 1570 // Sort the networks with the most frequent ones at the front of the network list. 1571 Collections.sort(networks, mConfigManager.getScanListComparator()); 1572 1573 Set<Integer> channelSet = new HashSet<>(); 1574 1575 for (WifiConfiguration config : networks) { 1576 if (!addChannelFromWifiScoreCard(channelSet, config, maxCount, ageInMillis)) { 1577 return channelSet; 1578 } 1579 } 1580 1581 return channelSet; 1582 } 1583 1584 // Watchdog timer handler watchdogHandler()1585 private void watchdogHandler() { 1586 // Schedule the next timer and start a single scan if we are in disconnected state. 1587 // Otherwise, the watchdog timer will be scheduled when entering disconnected 1588 // state. 1589 if (mWifiState == WIFI_STATE_DISCONNECTED) { 1590 localLog("start a single scan from watchdogHandler"); 1591 1592 scheduleWatchdogTimer(); 1593 startSingleScan(true, WIFI_WORK_SOURCE); 1594 } 1595 } 1596 triggerScanOnNetworkChanges()1597 private void triggerScanOnNetworkChanges() { 1598 if (mScreenOn) { 1599 // Update scanning schedule if needed 1600 if (updateSingleScanningSchedule()) { 1601 localLog("Saved networks / suggestions updated impacting single scan schedule"); 1602 startConnectivityScan(false); 1603 } 1604 } else { 1605 // Update the PNO scan network list when screen is off. Here we 1606 // rely on startConnectivityScan() to perform all the checks and clean up. 1607 localLog("Saved networks / suggestions updated impacting pno scan"); 1608 startConnectivityScan(false); 1609 } 1610 } 1611 1612 // Start a single scan and set up the interval for next single scan. startPeriodicSingleScan()1613 private void startPeriodicSingleScan() { 1614 // Reaching here with scanning schedule is null means this is a false timer alarm 1615 if (getSingleScanningSchedule() == null) { 1616 return; 1617 } 1618 1619 long currentTimeStamp = mClock.getElapsedSinceBootMillis(); 1620 1621 if (mLastPeriodicSingleScanTimeStamp != RESET_TIME_STAMP) { 1622 long msSinceLastScan = currentTimeStamp - mLastPeriodicSingleScanTimeStamp; 1623 if (msSinceLastScan < getScheduledSingleScanIntervalMs(0)) { 1624 localLog("Last periodic single scan started " + msSinceLastScan 1625 + "ms ago, defer this new scan request."); 1626 schedulePeriodicScanTimer( 1627 getScheduledSingleScanIntervalMs(0) - (int) msSinceLastScan); 1628 return; 1629 } 1630 } 1631 1632 boolean isScanNeeded = true; 1633 boolean isFullBandScan = true; 1634 1635 boolean isShortTimeSinceLastNetworkSelection = 1636 ((currentTimeStamp - mLastNetworkSelectionTimeStamp) 1637 <= 1000 * mContext.getResources().getInteger( 1638 R.integer.config_wifiConnectedHighRssiScanMinimumWindowSizeSec)); 1639 1640 WifiInfo wifiInfo = getPrimaryWifiInfo(); 1641 boolean isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection = 1642 mNetworkSelector.hasSufficientLinkQuality(wifiInfo) 1643 && mNetworkSelector.hasInternetOrExpectNoInternet(wifiInfo) 1644 && isShortTimeSinceLastNetworkSelection; 1645 // Check it is one of following conditions to skip scan (with firmware roaming) 1646 // or do partial scan only (without firmware roaming). 1647 // 1) Network is sufficient 1648 // 2) link is good, internet status is acceptable 1649 // and it is a short time since last network selection 1650 // 3) There is active stream such that scan will be likely disruptive 1651 if (mWifiState == WIFI_STATE_CONNECTED 1652 && (mNetworkSelector.isNetworkSufficient(wifiInfo) 1653 || isGoodLinkAndAcceptableInternetAndShortTimeSinceLastNetworkSelection 1654 || mNetworkSelector.hasActiveStream(wifiInfo))) { 1655 // If only partial scan is proposed and firmware roaming control is supported, 1656 // we will not issue any scan because firmware roaming will take care of 1657 // intra-SSID roam. 1658 if (mConnectivityHelper.isFirmwareRoamingSupported()) { 1659 localLog("No partial scan because firmware roaming is supported."); 1660 isScanNeeded = false; 1661 } else { 1662 localLog("No full band scan because current network is sufficient"); 1663 isFullBandScan = false; 1664 } 1665 } 1666 1667 if (isScanNeeded) { 1668 mLastPeriodicSingleScanTimeStamp = currentTimeStamp; 1669 1670 if (mWifiState == WIFI_STATE_DISCONNECTED 1671 && mInitialScanState == INITIAL_SCAN_STATE_START) { 1672 startSingleScan(false, WIFI_WORK_SOURCE); 1673 1674 // Note, initial partial scan may fail due to lack of channel history 1675 // Hence, we verify state before changing to AWIATING_RESPONSE 1676 if (mInitialScanState == INITIAL_SCAN_STATE_START) { 1677 setInitialScanState(INITIAL_SCAN_STATE_AWAITING_RESPONSE); 1678 mWifiMetrics.incrementInitialPartialScanCount(); 1679 } 1680 } else { 1681 startSingleScan(isFullBandScan, WIFI_WORK_SOURCE); 1682 } 1683 schedulePeriodicScanTimer( 1684 getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex)); 1685 1686 // Set up the next scan interval in an exponential backoff fashion. 1687 mCurrentSingleScanScheduleIndex++; 1688 } else { 1689 // Since we already skipped this scan, keep the same scan interval for next scan. 1690 schedulePeriodicScanTimer( 1691 getScheduledSingleScanIntervalMs(mCurrentSingleScanScheduleIndex)); 1692 } 1693 } 1694 1695 // Retrieve a value from single scanning schedule in ms getScheduledSingleScanIntervalMs(int index)1696 private int getScheduledSingleScanIntervalMs(int index) { 1697 synchronized (mLock) { 1698 if (mCurrentSingleScanScheduleSec == null) { 1699 Log.e(TAG, "Invalid attempt to get schedule interval, Schedule array is null "); 1700 1701 // Use a default value 1702 return DEFAULT_SCANNING_SCHEDULE_SEC[0] * 1000; 1703 } 1704 1705 if (index >= mCurrentSingleScanScheduleSec.length) { 1706 index = mCurrentSingleScanScheduleSec.length - 1; 1707 } 1708 return getScanIntervalWithPowerSaveMultiplier( 1709 mCurrentSingleScanScheduleSec[index] * 1000); 1710 } 1711 } 1712 getScanIntervalWithPowerSaveMultiplier(int interval)1713 private int getScanIntervalWithPowerSaveMultiplier(int interval) { 1714 if (!mDeviceConfigFacade.isWifiBatterySaverEnabled()) { 1715 return interval; 1716 } 1717 return mPowerManager.isPowerSaveMode() 1718 ? POWER_SAVE_SCAN_INTERVAL_MULTIPLIER * interval : interval; 1719 } 1720 1721 // Set the single scanning schedule setSingleScanningSchedule(int[] scheduleSec)1722 private void setSingleScanningSchedule(int[] scheduleSec) { 1723 synchronized (mLock) { 1724 mCurrentSingleScanScheduleSec = scheduleSec; 1725 } 1726 } 1727 1728 // Get the single scanning schedule getSingleScanningSchedule()1729 private int[] getSingleScanningSchedule() { 1730 synchronized (mLock) { 1731 return mCurrentSingleScanScheduleSec; 1732 } 1733 } 1734 1735 // Update the single scanning schedule if needed, and return true if update occurs updateSingleScanningSchedule()1736 private boolean updateSingleScanningSchedule() { 1737 if (!mWifiEnabled || !mAutoJoinEnabled) { 1738 return false; 1739 } 1740 if (mWifiState != WIFI_STATE_CONNECTED) { 1741 // No need to update the scanning schedule 1742 return false; 1743 } 1744 1745 boolean shouldUseSingleSavedNetworkSchedule = useSingleSavedNetworkSchedule(); 1746 1747 if (mCurrentSingleScanScheduleSec == mConnectedSingleScanScheduleSec 1748 && shouldUseSingleSavedNetworkSchedule) { 1749 mCurrentSingleScanScheduleSec = mConnectedSingleSavedNetworkSingleScanScheduleSec; 1750 return true; 1751 } 1752 if (mCurrentSingleScanScheduleSec == mConnectedSingleSavedNetworkSingleScanScheduleSec 1753 && !shouldUseSingleSavedNetworkSchedule) { 1754 mCurrentSingleScanScheduleSec = mConnectedSingleScanScheduleSec; 1755 return true; 1756 } 1757 return false; 1758 } 1759 1760 // Set initial scan state setInitialScanState(int state)1761 private void setInitialScanState(int state) { 1762 Log.i(TAG, "SetInitialScanState to : " + state); 1763 mInitialScanState = state; 1764 } 1765 1766 @VisibleForTesting getInitialScanState()1767 public int getInitialScanState() { 1768 return mInitialScanState; 1769 } 1770 1771 // Reset the last periodic single scan time stamp so that the next periodic single 1772 // scan can start immediately. resetLastPeriodicSingleScanTimeStamp()1773 private void resetLastPeriodicSingleScanTimeStamp() { 1774 mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP; 1775 } 1776 1777 // Periodic scan timer handler periodicScanTimerHandler()1778 private void periodicScanTimerHandler() { 1779 localLog("periodicScanTimerHandler"); 1780 1781 // Schedule the next timer and start a single scan if screen is on. 1782 if (mScreenOn) { 1783 startPeriodicSingleScan(); 1784 } 1785 } 1786 1787 // Start a single scan startForcedSingleScan(boolean isFullBandScan, WorkSource workSource)1788 private void startForcedSingleScan(boolean isFullBandScan, WorkSource workSource) { 1789 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 1790 1791 ScanSettings settings = new ScanSettings(); 1792 if (!isFullBandScan) { 1793 if (!setScanChannels(settings)) { 1794 isFullBandScan = true; 1795 // Skip the initial scan since no channel history available 1796 setInitialScanState(INITIAL_SCAN_STATE_COMPLETE); 1797 } else { 1798 mInitialPartialScanChannelCount = settings.channels.length; 1799 } 1800 } 1801 settings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY; // always do high accuracy scans. 1802 settings.band = getScanBand(isFullBandScan); 1803 // Only enable RNR for full scans since we already have a known channel list for 1804 // partial scan. We do not want to enable RNR for partial scan since it could end up 1805 // wasting time scanning for 6Ghz APs that the device doesn't have credential to. 1806 if (SdkLevel.isAtLeastS()) { 1807 settings.setRnrSetting(isFullBandScan ? WifiScanner.WIFI_RNR_ENABLED 1808 : WifiScanner.WIFI_RNR_NOT_NEEDED); 1809 settings.set6GhzPscOnlyEnabled(isFullBandScan 1810 ? mContext.getResources().getBoolean(R.bool.config_wifiEnable6ghzPscScanning) 1811 : false); 1812 } 1813 settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT 1814 | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; 1815 settings.numBssidsPerScan = 0; 1816 settings.hiddenNetworks.clear(); 1817 // retrieve the list of hidden network SSIDs from saved network to scan for 1818 settings.hiddenNetworks.addAll(mConfigManager.retrieveHiddenNetworkList()); 1819 // retrieve the list of hidden network SSIDs from Network suggestion to scan for 1820 settings.hiddenNetworks.addAll(mWifiNetworkSuggestionsManager.retrieveHiddenNetworkList()); 1821 1822 SingleScanListener singleScanListener = 1823 new SingleScanListener(isFullBandScan); 1824 mScanner.startScan( 1825 settings, new HandlerExecutor(mEventHandler), singleScanListener, workSource); 1826 mWifiMetrics.incrementConnectivityOneshotScanCount(); 1827 } 1828 startSingleScan(boolean isFullBandScan, WorkSource workSource)1829 private void startSingleScan(boolean isFullBandScan, WorkSource workSource) { 1830 if (!mWifiEnabled || !mAutoJoinEnabled) { 1831 return; 1832 } 1833 startForcedSingleScan(isFullBandScan, workSource); 1834 } 1835 1836 // Start a periodic scan when screen is on startPeriodicScan(boolean scanImmediately)1837 private void startPeriodicScan(boolean scanImmediately) { 1838 mPnoScanListener.resetLowRssiNetworkRetryDelay(); 1839 1840 // No connectivity scan if auto roaming is disabled. 1841 if (mWifiState == WIFI_STATE_CONNECTED && !mContext.getResources().getBoolean( 1842 R.bool.config_wifi_framework_enable_associated_network_selection)) { 1843 return; 1844 } 1845 1846 // Due to b/28020168, timer based single scan will be scheduled 1847 // to provide periodic scan in an exponential backoff fashion. 1848 if (scanImmediately) { 1849 resetLastPeriodicSingleScanTimeStamp(); 1850 } 1851 mCurrentSingleScanScheduleIndex = 0; 1852 startPeriodicSingleScan(); 1853 } 1854 deviceMobilityStateToPnoScanIntervalMs(@eviceMobilityState int state)1855 private int deviceMobilityStateToPnoScanIntervalMs(@DeviceMobilityState int state) { 1856 switch (state) { 1857 case WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN: 1858 case WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT: 1859 case WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT: 1860 return getScanIntervalWithPowerSaveMultiplier(mContext.getResources() 1861 .getInteger(R.integer.config_wifiMovingPnoScanIntervalMillis)); 1862 case WifiManager.DEVICE_MOBILITY_STATE_STATIONARY: 1863 return getScanIntervalWithPowerSaveMultiplier(mContext.getResources() 1864 .getInteger(R.integer.config_wifiStationaryPnoScanIntervalMillis)); 1865 default: 1866 return -1; 1867 } 1868 } 1869 1870 /** 1871 * Pass device mobility state to WifiChannelUtilization and 1872 * alter the PNO scan interval based on the current device mobility state. 1873 * If the device is stationary, it will likely not find many new Wifi networks. Thus, increase 1874 * the interval between scans. Decrease the interval between scans if the device begins to move 1875 * again. 1876 * @param newState the new device mobility state 1877 */ setDeviceMobilityState(@eviceMobilityState int newState)1878 public void setDeviceMobilityState(@DeviceMobilityState int newState) { 1879 int oldDeviceMobilityState = mDeviceMobilityState; 1880 localLog("Device mobility state changed. state=" + newState); 1881 int newPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(newState); 1882 if (newPnoScanIntervalMs < 0) { 1883 Log.e(TAG, "Invalid device mobility state: " + newState); 1884 return; 1885 } 1886 mDeviceMobilityState = newState; 1887 mWifiChannelUtilization.setDeviceMobilityState(newState); 1888 1889 int oldPnoScanIntervalMs = deviceMobilityStateToPnoScanIntervalMs(oldDeviceMobilityState); 1890 if (newPnoScanIntervalMs == oldPnoScanIntervalMs) { 1891 if (mPnoScanStarted) { 1892 mWifiMetrics.logPnoScanStop(); 1893 mWifiMetrics.enterDeviceMobilityState(newState); 1894 mWifiMetrics.logPnoScanStart(); 1895 } else { 1896 mWifiMetrics.enterDeviceMobilityState(newState); 1897 } 1898 } else { 1899 Log.d(TAG, "PNO Scan Interval changed to " + newPnoScanIntervalMs + " ms."); 1900 1901 if (mPnoScanStarted) { 1902 Log.d(TAG, "Restarting PNO Scan with new scan interval"); 1903 stopPnoScan(); 1904 mWifiMetrics.enterDeviceMobilityState(newState); 1905 startDisconnectedPnoScan(); 1906 } else { 1907 mWifiMetrics.enterDeviceMobilityState(newState); 1908 } 1909 } 1910 } 1911 1912 // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected startDisconnectedPnoScan()1913 private void startDisconnectedPnoScan() { 1914 // Initialize PNO settings 1915 PnoSettings pnoSettings = new PnoSettings(); 1916 List<PnoSettings.PnoNetwork> pnoNetworkList = retrievePnoNetworkList(); 1917 int listSize = pnoNetworkList.size(); 1918 1919 if (listSize == 0) { 1920 // No saved network 1921 localLog("No saved network for starting disconnected PNO."); 1922 return; 1923 } 1924 1925 pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize]; 1926 pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList); 1927 pnoSettings.min6GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_6_GHZ_START_FREQ_MHZ); 1928 pnoSettings.min5GHzRssi = mScoringParams.getEntryRssi(ScanResult.BAND_5_GHZ_START_FREQ_MHZ); 1929 pnoSettings.min24GHzRssi = mScoringParams.getEntryRssi( 1930 ScanResult.BAND_24_GHZ_START_FREQ_MHZ); 1931 1932 // Initialize scan settings 1933 ScanSettings scanSettings = new ScanSettings(); 1934 scanSettings.band = getScanBand(); 1935 scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; 1936 scanSettings.numBssidsPerScan = 0; 1937 scanSettings.periodInMs = deviceMobilityStateToPnoScanIntervalMs(mDeviceMobilityState); 1938 1939 mScanner.startDisconnectedPnoScan( 1940 scanSettings, pnoSettings, new HandlerExecutor(mEventHandler), mPnoScanListener); 1941 mPnoScanStarted = true; 1942 mWifiMetrics.logPnoScanStart(); 1943 } 1944 getAllScanOptimizationNetworks()1945 private @NonNull List<WifiConfiguration> getAllScanOptimizationNetworks() { 1946 List<WifiConfiguration> networks = mConfigManager.getSavedNetworks(-1); 1947 networks.addAll(mWifiNetworkSuggestionsManager.getAllScanOptimizationSuggestionNetworks()); 1948 // remove all auto-join disabled or network selection disabled network. 1949 networks.removeIf(config -> !config.allowAutojoin 1950 || !config.getNetworkSelectionStatus().isNetworkEnabled()); 1951 return networks; 1952 } 1953 1954 /** 1955 * Retrieve the PnoNetworks from Saved and suggestion non-passpoint network. 1956 */ 1957 @VisibleForTesting retrievePnoNetworkList()1958 public List<PnoSettings.PnoNetwork> retrievePnoNetworkList() { 1959 List<WifiConfiguration> networks = getAllScanOptimizationNetworks(); 1960 1961 if (networks.isEmpty()) { 1962 return Collections.EMPTY_LIST; 1963 } 1964 Collections.sort(networks, mConfigManager.getScanListComparator()); 1965 boolean pnoFrequencyCullingEnabled = mContext.getResources() 1966 .getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled); 1967 1968 List<PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); 1969 Set<WifiScanner.PnoSettings.PnoNetwork> pnoSet = new HashSet<>(); 1970 for (WifiConfiguration config : networks) { 1971 WifiScanner.PnoSettings.PnoNetwork pnoNetwork = 1972 WifiConfigurationUtil.createPnoNetwork(config); 1973 if (pnoSet.contains(pnoNetwork)) { 1974 continue; 1975 } 1976 pnoList.add(pnoNetwork); 1977 pnoSet.add(pnoNetwork); 1978 if (!pnoFrequencyCullingEnabled) { 1979 continue; 1980 } 1981 Set<Integer> channelList = new HashSet<>(); 1982 addChannelFromWifiScoreCard(channelList, config, 0, 1983 MAX_PNO_SCAN_FREQUENCY_AGE_MS); 1984 pnoNetwork.frequencies = channelList.stream().mapToInt(Integer::intValue).toArray(); 1985 } 1986 return pnoList; 1987 } 1988 1989 // Stop PNO scan. stopPnoScan()1990 private void stopPnoScan() { 1991 if (!mPnoScanStarted) return; 1992 1993 mScanner.stopPnoScan(mPnoScanListener); 1994 mPnoScanStarted = false; 1995 mWifiMetrics.logPnoScanStop(); 1996 } 1997 1998 // Set up watchdog timer scheduleWatchdogTimer()1999 private void scheduleWatchdogTimer() { 2000 localLog("scheduleWatchdogTimer"); 2001 2002 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2003 mClock.getElapsedSinceBootMillis() + mContext.getResources().getInteger( 2004 R.integer.config_wifiPnoWatchdogIntervalMs), 2005 WATCHDOG_TIMER_TAG, 2006 mWatchdogListener, mEventHandler); 2007 mWatchdogScanTimerSet = true; 2008 } 2009 2010 // Cancel the watchdog scan timer. cancelWatchdogScan()2011 private void cancelWatchdogScan() { 2012 if (mWatchdogScanTimerSet) { 2013 mAlarmManager.cancel(mWatchdogListener); 2014 mWatchdogScanTimerSet = false; 2015 } 2016 } 2017 2018 // Schedules a delayed partial scan, which will scan the frequencies in mCachedWifiCandidates. scheduleDelayedPartialScan(long delayMillis)2019 private void scheduleDelayedPartialScan(long delayMillis) { 2020 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2021 mClock.getElapsedSinceBootMillis() + delayMillis, DELAYED_PARTIAL_SCAN_TIMER_TAG, 2022 mDelayedPartialScanTimerListener, mEventHandler); 2023 mDelayedPartialScanTimerSet = true; 2024 } 2025 2026 // Cancel the delayed partial scan timer. cancelDelayedPartialScan()2027 private void cancelDelayedPartialScan() { 2028 if (mDelayedPartialScanTimerSet) { 2029 mAlarmManager.cancel(mDelayedPartialScanTimerListener); 2030 mDelayedPartialScanTimerSet = false; 2031 } 2032 } 2033 2034 // Set up periodic scan timer schedulePeriodicScanTimer(int intervalMs)2035 private void schedulePeriodicScanTimer(int intervalMs) { 2036 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2037 mClock.getElapsedSinceBootMillis() + intervalMs, 2038 PERIODIC_SCAN_TIMER_TAG, 2039 mPeriodicScanTimerListener, mEventHandler); 2040 mPeriodicScanTimerSet = true; 2041 } 2042 2043 // Cancel periodic scan timer cancelPeriodicScanTimer()2044 private void cancelPeriodicScanTimer() { 2045 if (mPeriodicScanTimerSet) { 2046 mAlarmManager.cancel(mPeriodicScanTimerListener); 2047 mPeriodicScanTimerSet = false; 2048 } 2049 } 2050 2051 // Set up timer to start a delayed single scan after RESTART_SCAN_DELAY_MS scheduleDelayedSingleScan(boolean isFullBandScan)2052 private void scheduleDelayedSingleScan(boolean isFullBandScan) { 2053 localLog("scheduleDelayedSingleScan"); 2054 2055 RestartSingleScanListener restartSingleScanListener = 2056 new RestartSingleScanListener(isFullBandScan); 2057 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2058 mClock.getElapsedSinceBootMillis() + RESTART_SCAN_DELAY_MS, 2059 RESTART_SINGLE_SCAN_TIMER_TAG, 2060 restartSingleScanListener, mEventHandler); 2061 } 2062 2063 // Set up timer to start a delayed scan after msFromNow milli-seconds scheduleDelayedConnectivityScan(int msFromNow)2064 private void scheduleDelayedConnectivityScan(int msFromNow) { 2065 localLog("scheduleDelayedConnectivityScan"); 2066 2067 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2068 mClock.getElapsedSinceBootMillis() + msFromNow, 2069 RESTART_CONNECTIVITY_SCAN_TIMER_TAG, 2070 mRestartScanListener, mEventHandler); 2071 2072 } 2073 2074 // Start a connectivity scan. The scan method is chosen according to 2075 // the current screen state and WiFi state. startConnectivityScan(boolean scanImmediately)2076 private void startConnectivityScan(boolean scanImmediately) { 2077 localLog("startConnectivityScan: screenOn=" + mScreenOn 2078 + " wifiState=" + stateToString(mWifiState) 2079 + " scanImmediately=" + scanImmediately 2080 + " wifiEnabled=" + mWifiEnabled 2081 + " mAutoJoinEnabled=" + mAutoJoinEnabled); 2082 2083 if (!mWifiEnabled || !mAutoJoinEnabled) { 2084 return; 2085 } 2086 2087 // Always stop outstanding connectivity scan if there is any 2088 stopConnectivityScan(); 2089 2090 // Don't start a connectivity scan while Wifi is in the transition 2091 // between connected and disconnected states. 2092 if ((mWifiState != WIFI_STATE_CONNECTED && mWifiState != WIFI_STATE_DISCONNECTED) 2093 || (getSingleScanningSchedule() == null)) { 2094 return; 2095 } 2096 2097 if (mScreenOn) { 2098 startPeriodicScan(scanImmediately); 2099 } else { 2100 if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) { 2101 startDisconnectedPnoScan(); 2102 } 2103 } 2104 } 2105 2106 // Stop connectivity scan if there is any. stopConnectivityScan()2107 private void stopConnectivityScan() { 2108 // Due to b/28020168, timer based single scan will be scheduled 2109 // to provide periodic scan in an exponential backoff fashion. 2110 cancelPeriodicScanTimer(); 2111 cancelDelayedPartialScan(); 2112 stopPnoScan(); 2113 } 2114 2115 /** 2116 * Handler for screen state (on/off) changes 2117 */ handleScreenStateChanged(boolean screenOn)2118 private void handleScreenStateChanged(boolean screenOn) { 2119 localLog("handleScreenStateChanged: screenOn=" + screenOn); 2120 2121 mScreenOn = screenOn; 2122 2123 if (mWifiState == WIFI_STATE_DISCONNECTED 2124 && mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) { 2125 setInitialScanState(INITIAL_SCAN_STATE_START); 2126 } 2127 2128 mOpenNetworkNotifier.handleScreenStateChanged(screenOn); 2129 2130 startConnectivityScan(SCAN_ON_SCHEDULE); 2131 } 2132 2133 /** 2134 * Helper function that converts the WIFI_STATE_XXX constants to string 2135 */ stateToString(int state)2136 private static String stateToString(int state) { 2137 switch (state) { 2138 case WIFI_STATE_CONNECTED: 2139 return "connected"; 2140 case WIFI_STATE_DISCONNECTED: 2141 return "disconnected"; 2142 case WIFI_STATE_TRANSITIONING: 2143 return "transitioning"; 2144 default: 2145 return "unknown"; 2146 } 2147 } 2148 2149 /** 2150 * Check if Single saved network schedule should be used 2151 * This is true if the one of the following is satisfied: 2152 * 1. Device has a total of 1 network whether saved, passpoint, or suggestion. 2153 * 2. The device is connected to that network. 2154 */ useSingleSavedNetworkSchedule()2155 private boolean useSingleSavedNetworkSchedule() { 2156 WifiConfiguration currentNetwork = 2157 getPrimaryClientModeManager().getConnectedWifiConfiguration(); 2158 if (currentNetwork == null) { 2159 localLog("Current network is missing, may caused by remove network and disconnecting"); 2160 return false; 2161 } 2162 List<WifiConfiguration> savedNetworks = 2163 mConfigManager.getSavedNetworks(Process.WIFI_UID); 2164 // If we have multiple saved networks, then no need to proceed 2165 if (savedNetworks.size() > 1) { 2166 return false; 2167 } 2168 2169 List<PasspointConfiguration> passpointNetworks = 2170 mPasspointManager.getProviderConfigs(Process.WIFI_UID, true); 2171 // If we have multiple networks (saved + passpoint), then no need to proceed 2172 if (passpointNetworks.size() + savedNetworks.size() > 1) { 2173 return false; 2174 } 2175 2176 Set<WifiNetworkSuggestion> suggestionsNetworks = 2177 mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions(); 2178 // If total size not equal to 1, then no need to proceed 2179 if (passpointNetworks.size() + savedNetworks.size() + suggestionsNetworks.size() != 1) { 2180 return false; 2181 } 2182 2183 // Next verify that this network is the one device is connected to 2184 int currentNetworkId = currentNetwork.networkId; 2185 2186 // If we have a single saved network, and we are connected to it, return true. 2187 if (savedNetworks.size() == 1) { 2188 return (savedNetworks.get(0).networkId == currentNetworkId); 2189 } 2190 2191 // If we have a single passpoint network, and we are connected to it, return true. 2192 if (passpointNetworks.size() == 1) { 2193 String passpointKey = passpointNetworks.get(0).getUniqueId(); 2194 WifiConfiguration config = mConfigManager.getConfiguredNetwork(passpointKey); 2195 return (config != null && config.networkId == currentNetworkId); 2196 } 2197 2198 // If we have a single suggestion network, and we are connected to it, return true. 2199 WifiNetworkSuggestion network = suggestionsNetworks.iterator().next(); 2200 String suggestionKey = network.getWifiConfiguration().getProfileKey(); 2201 WifiConfiguration config = mConfigManager.getConfiguredNetwork(suggestionKey); 2202 return (config != null && config.networkId == currentNetworkId); 2203 } 2204 initSingleSavedNetworkSchedule()2205 private int[] initSingleSavedNetworkSchedule() { 2206 int[] schedule = mContext.getResources().getIntArray( 2207 R.array.config_wifiSingleSavedNetworkConnectedScanIntervalScheduleSec); 2208 if (schedule == null || schedule.length == 0) { 2209 return null; 2210 } 2211 2212 for (int val : schedule) { 2213 if (val <= 0) { 2214 return null; 2215 } 2216 } 2217 return schedule; 2218 } 2219 2220 /** 2221 * Handler for WiFi state (connected/disconnected) changes 2222 */ handleConnectionStateChanged( ConcreteClientModeManager clientModeManager, int state)2223 public void handleConnectionStateChanged( 2224 ConcreteClientModeManager clientModeManager, int state) { 2225 List<ClientModeManager> internetConnectivityCmms = 2226 mActiveModeWarden.getInternetConnectivityClientModeManagers(); 2227 if (!(internetConnectivityCmms.contains(clientModeManager))) { 2228 Log.w(TAG, "Ignoring call from non primary Mode Manager " + clientModeManager, 2229 new Throwable()); 2230 return; 2231 } 2232 localLog("handleConnectionStateChanged: state=" + stateToString(state)); 2233 2234 if (mConnectedSingleScanScheduleSec == null) { 2235 mConnectedSingleScanScheduleSec = initializeScanningSchedule(WIFI_STATE_CONNECTED); 2236 } 2237 if (mDisconnectedSingleScanScheduleSec == null) { 2238 mDisconnectedSingleScanScheduleSec = 2239 initializeScanningSchedule(WIFI_STATE_DISCONNECTED); 2240 } 2241 if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) { 2242 mConnectedSingleSavedNetworkSingleScanScheduleSec = 2243 initSingleSavedNetworkSchedule(); 2244 if (mConnectedSingleSavedNetworkSingleScanScheduleSec == null) { 2245 mConnectedSingleSavedNetworkSingleScanScheduleSec = mConnectedSingleScanScheduleSec; 2246 } 2247 } 2248 2249 mWifiState = state; 2250 2251 // Reset BSSID of last connection attempt and kick off 2252 // the watchdog timer if entering disconnected state. 2253 if (mWifiState == WIFI_STATE_DISCONNECTED) { 2254 scheduleWatchdogTimer(); 2255 // Switch to the disconnected scanning schedule 2256 setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec); 2257 startConnectivityScan(SCAN_IMMEDIATELY); 2258 } else if (mWifiState == WIFI_STATE_CONNECTED) { 2259 if (useSingleSavedNetworkSchedule()) { 2260 // Switch to Single-Saved-Network connected schedule 2261 setSingleScanningSchedule(mConnectedSingleSavedNetworkSingleScanScheduleSec); 2262 } else { 2263 // Switch to connected single scanning schedule 2264 setSingleScanningSchedule(mConnectedSingleScanScheduleSec); 2265 } 2266 startConnectivityScan(SCAN_ON_SCHEDULE); 2267 } else { 2268 // Intermediate state, no applicable single scanning schedule 2269 setSingleScanningSchedule(null); 2270 startConnectivityScan(SCAN_ON_SCHEDULE); 2271 } 2272 } 2273 2274 /** 2275 * Handler when a WiFi connection attempt ended. 2276 * 2277 * @param failureCode {@link WifiMetrics.ConnectionEvent} failure code. 2278 * @param bssid the failed network. 2279 * @param ssid identifies the failed network. 2280 */ handleConnectionAttemptEnded(@onNull ActiveModeManager activeModeManager, int failureCode, @NonNull String bssid, @NonNull String ssid)2281 public void handleConnectionAttemptEnded(@NonNull ActiveModeManager activeModeManager, 2282 int failureCode, @NonNull String bssid, @NonNull String ssid) { 2283 List<ClientModeManager> internetConnectivityCmms = 2284 mActiveModeWarden.getInternetConnectivityClientModeManagers(); 2285 if (!(internetConnectivityCmms.contains(activeModeManager))) { 2286 Log.w(TAG, "Ignoring call from non primary Mode Manager " + activeModeManager, 2287 new Throwable()); 2288 return; 2289 } 2290 WifiInfo wifiInfo = getPrimaryWifiInfo(); 2291 if (failureCode == WifiMetrics.ConnectionEvent.FAILURE_NONE) { 2292 String ssidUnquoted = (wifiInfo.getWifiSsid() == null) 2293 ? null 2294 : wifiInfo.getWifiSsid().toString(); 2295 mOpenNetworkNotifier.handleWifiConnected(ssidUnquoted); 2296 } else { 2297 mOpenNetworkNotifier.handleConnectionFailure(); 2298 retryConnectionOnLatestCandidates(bssid, ssid); 2299 } 2300 } 2301 retryConnectionOnLatestCandidates(String bssid, String ssid)2302 private void retryConnectionOnLatestCandidates(String bssid, String ssid) { 2303 try { 2304 if (mLatestCandidates == null || mLatestCandidates.size() == 0 2305 || mClock.getElapsedSinceBootMillis() - mLatestCandidatesTimestampMs 2306 > TEMP_BSSID_BLOCK_DURATION) { 2307 mLatestCandidates = null; 2308 return; 2309 } 2310 MacAddress macAddress = MacAddress.fromString(bssid); 2311 int prevNumCandidates = mLatestCandidates.size(); 2312 mLatestCandidates = mLatestCandidates.stream() 2313 .filter(candidate -> { 2314 // filter out the candidate with the BSSID that just failed 2315 if (macAddress.equals(candidate.getKey().bssid)) { 2316 return false; 2317 } 2318 // filter out candidates that are disabled. 2319 WifiConfiguration config = 2320 mConfigManager.getConfiguredNetwork(candidate.getNetworkConfigId()); 2321 return config != null 2322 && config.getNetworkSelectionStatus().isNetworkEnabled(); 2323 }) 2324 .collect(Collectors.toList()); 2325 if (prevNumCandidates == mLatestCandidates.size()) { 2326 return; 2327 } 2328 WifiConfiguration candidate = mNetworkSelector.selectNetwork(mLatestCandidates); 2329 if (candidate != null) { 2330 localLog("Automatic retry on the next best WNS candidate-" + candidate.SSID); 2331 // Make sure that the failed BSSID is blocked for at least TEMP_BSSID_BLOCK_DURATION 2332 // to prevent the supplicant from trying it again. 2333 mWifiBlocklistMonitor.blockBssidForDurationMs(bssid, ssid, 2334 TEMP_BSSID_BLOCK_DURATION, 2335 WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, 0); 2336 connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate); 2337 } 2338 } catch (IllegalArgumentException e) { 2339 localLog("retryConnectionOnLatestCandidates: failed to create MacAddress from bssid=" 2340 + bssid); 2341 mLatestCandidates = null; 2342 } 2343 } 2344 2345 // Enable auto-join if WifiConnectivityManager is enabled & we have any pending generic network 2346 // request (trusted or untrusted) and no specific network request in progress. checkAllStatesAndEnableAutoJoin()2347 private void checkAllStatesAndEnableAutoJoin() { 2348 // if auto-join was disabled externally, don't re-enable for any triggers. 2349 // External triggers to disable always trumps any internal state. 2350 setAutoJoinEnabled(mAutoJoinEnabledExternal 2351 && (mUntrustedConnectionAllowed || mOemPaidConnectionAllowed 2352 || mOemPrivateConnectionAllowed || mTrustedConnectionAllowed) 2353 && !mSpecificNetworkRequestInProgress); 2354 startConnectivityScan(SCAN_IMMEDIATELY); 2355 } 2356 2357 /** 2358 * Triggered when {@link WifiNetworkFactory} has a pending general network request. 2359 */ setTrustedConnectionAllowed(boolean allowed)2360 public void setTrustedConnectionAllowed(boolean allowed) { 2361 localLog("setTrustedConnectionAllowed: allowed=" + allowed); 2362 2363 if (mTrustedConnectionAllowed != allowed) { 2364 mTrustedConnectionAllowed = allowed; 2365 checkAllStatesAndEnableAutoJoin(); 2366 } 2367 } 2368 2369 /** 2370 * Triggered when {@link UntrustedWifiNetworkFactory} has a pending ephemeral network request. 2371 */ setUntrustedConnectionAllowed(boolean allowed)2372 public void setUntrustedConnectionAllowed(boolean allowed) { 2373 localLog("setUntrustedConnectionAllowed: allowed=" + allowed); 2374 2375 if (mUntrustedConnectionAllowed != allowed) { 2376 mUntrustedConnectionAllowed = allowed; 2377 checkAllStatesAndEnableAutoJoin(); 2378 } 2379 } 2380 2381 /** 2382 * Triggered when {@link OemPaidWifiNetworkFactory} has a pending network request. 2383 */ setOemPaidConnectionAllowed(boolean allowed, WorkSource requestorWs)2384 public void setOemPaidConnectionAllowed(boolean allowed, WorkSource requestorWs) { 2385 localLog("setOemPaidConnectionAllowed: allowed=" + allowed + ", requestorWs=" 2386 + requestorWs); 2387 2388 if (mOemPaidConnectionAllowed != allowed) { 2389 mOemPaidConnectionAllowed = allowed; 2390 mOemPaidConnectionRequestorWs = requestorWs; 2391 checkAllStatesAndEnableAutoJoin(); 2392 } 2393 } 2394 2395 /** 2396 * Triggered when {@link OemPrivateWifiNetworkFactory} has a pending network request. 2397 */ setOemPrivateConnectionAllowed(boolean allowed, WorkSource requestorWs)2398 public void setOemPrivateConnectionAllowed(boolean allowed, WorkSource requestorWs) { 2399 localLog("setOemPrivateConnectionAllowed: allowed=" + allowed + ", requestorWs=" 2400 + requestorWs); 2401 2402 if (mOemPrivateConnectionAllowed != allowed) { 2403 mOemPrivateConnectionAllowed = allowed; 2404 mOemPrivateConnectionRequestorWs = requestorWs; 2405 checkAllStatesAndEnableAutoJoin(); 2406 } 2407 } 2408 2409 /** 2410 * Triggered when {@link WifiNetworkFactory} is processing a specific network request. 2411 */ setSpecificNetworkRequestInProgress(boolean inProgress)2412 public void setSpecificNetworkRequestInProgress(boolean inProgress) { 2413 localLog("setSpecificNetworkRequestInProgress : inProgress=" + inProgress); 2414 2415 if (mSpecificNetworkRequestInProgress != inProgress) { 2416 mSpecificNetworkRequestInProgress = inProgress; 2417 checkAllStatesAndEnableAutoJoin(); 2418 } 2419 } 2420 2421 /** 2422 * Handler to prepare for connection to a user or app specified network 2423 */ prepareForForcedConnection(int netId)2424 public void prepareForForcedConnection(int netId) { 2425 WifiConfiguration config = mConfigManager.getConfiguredNetwork(netId); 2426 if (config == null) { 2427 return; 2428 } 2429 localLog("prepareForForcedConnection: SSID=" + config.SSID); 2430 2431 clearConnectionAttemptTimeStamps(); 2432 mWifiBlocklistMonitor.clearBssidBlocklistForSsid(config.SSID); 2433 } 2434 2435 /** 2436 * Handler for on-demand connectivity scan 2437 */ forceConnectivityScan(WorkSource workSource)2438 public void forceConnectivityScan(WorkSource workSource) { 2439 if (!mWifiEnabled || !mRunning) return; 2440 localLog("forceConnectivityScan in request of " + workSource); 2441 2442 clearConnectionAttemptTimeStamps(); 2443 mWaitForFullBandScanResults = true; 2444 startForcedSingleScan(true, workSource); 2445 } 2446 2447 /** 2448 * Helper method to populate WifiScanner handle. This is done lazily because 2449 * WifiScanningService is started after WifiService. 2450 */ retrieveWifiScanner()2451 private void retrieveWifiScanner() { 2452 if (mScanner != null) return; 2453 mScanner = Objects.requireNonNull(mContext.getSystemService(WifiScanner.class), 2454 "Got a null instance of WifiScanner!"); 2455 // Register for all single scan results 2456 mScanner.registerScanListener(new HandlerExecutor(mEventHandler), mAllSingleScanListener); 2457 } 2458 2459 /** 2460 * Start WifiConnectivityManager 2461 */ start()2462 private void start() { 2463 if (mRunning) return; 2464 retrieveWifiScanner(); 2465 mConnectivityHelper.getFirmwareRoamingInfo(); 2466 mWifiChannelUtilization.init(getPrimaryClientModeManager().getWifiLinkLayerStats()); 2467 clearConnectionAttemptTimeStamps(); // clear connection attempts. 2468 2469 if (mContext.getResources().getBoolean(R.bool.config_wifiEnablePartialInitialScan)) { 2470 setInitialScanState(INITIAL_SCAN_STATE_START); 2471 } 2472 2473 mRunning = true; 2474 mLatestCandidates = null; 2475 mLatestCandidatesTimestampMs = 0; 2476 } 2477 2478 /** 2479 * Stop and reset WifiConnectivityManager 2480 */ stop()2481 private void stop() { 2482 if (!mRunning) return; 2483 mRunning = false; 2484 stopConnectivityScan(); 2485 cancelWatchdogScan(); 2486 resetLastPeriodicSingleScanTimeStamp(); 2487 mOpenNetworkNotifier.clearPendingNotification(true /* resetRepeatDelay */); 2488 mWaitForFullBandScanResults = false; 2489 mLatestCandidates = null; 2490 mLatestCandidatesTimestampMs = 0; 2491 mScanRestartCount = 0; 2492 } 2493 2494 /** 2495 * Update WifiConnectivityManager running state 2496 * 2497 * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager 2498 * are enabled, otherwise stop it. 2499 */ updateRunningState()2500 private void updateRunningState() { 2501 if (mWifiEnabled && mAutoJoinEnabled) { 2502 localLog("Starting up WifiConnectivityManager"); 2503 start(); 2504 } else { 2505 localLog("Stopping WifiConnectivityManager"); 2506 stop(); 2507 } 2508 } 2509 2510 /** 2511 * Inform WiFi is enabled for connection or not 2512 */ setWifiEnabled(boolean enable)2513 private void setWifiEnabled(boolean enable) { 2514 if (mWifiEnabled == enable) return; 2515 2516 localLog("Set WiFi " + (enable ? "enabled" : "disabled")); 2517 2518 if (!enable) { 2519 mNetworkSelector.resetOnDisable(); 2520 mConfigManager.enableTemporaryDisabledNetworks(); 2521 mConfigManager.stopRestrictingAutoJoinToSubscriptionId(); 2522 mConfigManager.clearUserTemporarilyDisabledList(); 2523 mConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks(); 2524 // Flush ANQP cache if configured to do so 2525 if (mWifiGlobals.flushAnqpCacheOnWifiToggleOffEvent()) { 2526 mPasspointManager.clearAnqpRequestsAndFlushCache(); 2527 } 2528 } 2529 mWifiEnabled = enable; 2530 updateRunningState(); 2531 } 2532 2533 /** 2534 * Turn on/off the WifiConnectivityManager at runtime 2535 */ setAutoJoinEnabled(boolean enable)2536 private void setAutoJoinEnabled(boolean enable) { 2537 mAutoJoinEnabled = enable; 2538 updateRunningState(); 2539 } 2540 2541 /** 2542 * Turn on/off the auto join at runtime 2543 */ setAutoJoinEnabledExternal(boolean enable)2544 public void setAutoJoinEnabledExternal(boolean enable) { 2545 localLog("Set auto join " + (enable ? "enabled" : "disabled")); 2546 2547 if (mAutoJoinEnabledExternal != enable) { 2548 mAutoJoinEnabledExternal = enable; 2549 checkAllStatesAndEnableAutoJoin(); 2550 } 2551 } 2552 2553 @VisibleForTesting getLowRssiNetworkRetryDelay()2554 int getLowRssiNetworkRetryDelay() { 2555 return mPnoScanListener.getLowRssiNetworkRetryDelay(); 2556 } 2557 2558 @VisibleForTesting getLastPeriodicSingleScanTimeStamp()2559 long getLastPeriodicSingleScanTimeStamp() { 2560 return mLastPeriodicSingleScanTimeStamp; 2561 } 2562 2563 /** 2564 * Dump the local logs. 2565 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)2566 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2567 pw.println("Dump of WifiConnectivityManager"); 2568 pw.println("WifiConnectivityManager - Log Begin ----"); 2569 mLocalLog.dump(fd, pw, args); 2570 pw.println("WifiConnectivityManager - Log End ----"); 2571 mOpenNetworkNotifier.dump(fd, pw, args); 2572 mWifiBlocklistMonitor.dump(fd, pw, args); 2573 } 2574 } 2575