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