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