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