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