这里我们选择Android R原生代码记录:http://aospxref.com/android-11.0.0_r21
主要流程在下面这个类里:
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
处理扫描结果:
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName,
boolean isFullScan) {
// 刷新频道统计信息和频道利用率
mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(
mStateMachine.getWifiLinkLayerStats(), WifiChannelUtilization.UNKNOWN_FREQ);
// 更新用户禁用列表
updateUserDisabledList(scanDetails);
// 检查是否可以释放任何被列入阻止列表的BSSID
mBssidBlocklistMonitor.tryEnablingBlockedBssids(scanDetails); ---> 移除之前由于RSSI过低的AP
Set<String> bssidBlocklist = mBssidBlocklistMonitor.updateAndGetBssidBlocklistForSsid( ---> 时间到期的AP可以移除
mWifiInfo.getSSID());
// 检查supplicant是否处于转换阶段的状态,如果是则返回false
if (mStateMachine.isSupplicantTransientState()) {
localLog(listenerName
+ " onResults: No network selection because supplicantTransientState is "
+ mStateMachine.isSupplicantTransientState());
return false;
}
localLog(listenerName + " onResults: start network selection");
// 从扫描结果中获取候选网络列表
List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan(
scanDetails, bssidBlocklist, mWifiInfo, mStateMachine.isConnected(),
mStateMachine.isDisconnected(), mUntrustedConnectionAllowed);
mLatestCandidates = candidates;
mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis();
// 在设备高移动性状态下进行候选网络筛选
if (mDeviceMobilityState == WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT
&& mContext.getResources().getBoolean(
R.bool.config_wifiHighMovementNetworkSelectionOptimizationEnabled)) {
candidates = filterCandidatesHighMovement(candidates, listenerName, isFullScan);
}
// 选择最佳的候选网络
WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
mWifiLastResortWatchdog.updateAvailableNetworks(
mNetworkSelector.getConnectableScanDetails());
mWifiMetrics.countScanResults(scanDetails);
// 如果有候选网络,连接到网络并返回true
if (candidate != null) {
localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate); ----> 发起连接
return true;
} else {
// 如果没有候选网络且Wi-Fi状态为断开状态,处理开放/未保存网络的扫描结果,并返回false
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mOpenNetworkNotifier.handleScanResults(
mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
}
return false;
}
}
总结下上面的代码做了这些事,最终目的就是选出候选网络 candidate,发起连接
1. 更新频道统计信息和频道利用率
2. 更新用户禁用列表
3. 尝试启用被阻止的BSSID
4. 获取当前网络的BSSID阻止列表
5. 检查supplicant是否处于临时状态(切状态),如果是,则返回false
6. 从扫描结果中获取候选网络列表
7. 更新最新的候选网络列表和时间戳
8. 如果设备处于高移动性状态且启用了高移动性网络选择优化,则对候选网络进行筛选
9. 选择最佳的候选网络
10. 更新网络选择的时间戳和可用网络列表
11. 记录扫描结果的统计信息
12. 如果有候选网络,则连接到网络并返回true
13. 如果没有候选网络且Wi-Fi状态为断开状态,则处理开放/未保存网络的扫描结果
接着看下对候选网络发起连接的流程:
private void connectToNetwork(WifiConfiguration candidate) {
ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
// 检查候选网络的扫描结果是否有效
if (scanResultCandidate == null) {
localLog("connectToNetwork: bad candidate - " + candidate
+ " scanResult: " + scanResultCandidate);
return;
}
String targetBssid = scanResultCandidate.BSSID;
String targetAssociationId = candidate.SSID + " : " + targetBssid;
// 检查是否已连接到目标BSSID或正在连接到目标BSSID,是就直接返回了
if (targetBssid != null
&& (targetBssid.equals(mLastConnectionAttemptBssid)
|| targetBssid.equals(mWifiInfo.getBSSID()))
&& SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
localLog("connectToNetwork: Either already connected "
+ "or is connecting to " + targetAssociationId);
return;
}
// 检查候选网络的BSSID是否与配置中指定的BSSID匹配
if (candidate.BSSID != null
&& !candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY)
&& !candidate.BSSID.equals(targetBssid)) {
localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the "
+ "config specified BSSID " + candidate.BSSID + ". Drop it!");
return;
}
long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
// 检查是否应该跳过连接尝试
if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
mTotalConnectivityAttemptsRateLimited++;
return;
}
noteConnectionAttempt(elapsedTimeMillis);
mLastConnectionAttemptBssid = targetBssid;
// 获取当前已连接的网络配置和关联信息
WifiConfiguration currentConnectedNetwork = mConfigManager
.getConfiguredNetwork(mWifiInfo.getNetworkId());
String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
(mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
// 如果当前已连接的网络与候选网络相同或存在关联,则进行漫游处理
if (currentConnectedNetwork != null
&& (currentConnectedNetwork.networkId == candidate.networkId)) {
// Framework仅在固件不支持漫游时启动漫游(例如,不支持{@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING})
if (mConnectivityHelper.isFirmwareRoamingSupported()) {
// 目前仅用于验证固件漫游行为,日志保留在这里
localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "."
+ " The actual roaming target is up to the firmware.");
} else {
localLog("connectToNetwork: Roaming to " + targetAssociationId + " from "
+ currentAssociationId);
mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate);
}
} else {
// 如果固件支持漫游或候选配置包含指定的BSSID,则指定连接目标BSSID
if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null
|| candidate.BSSID.equals(ClientModeImpl.SUPPLICANT_BSSID_ANY))) {
targetBssid = ClientModeImpl.SUPPLICANT_BSSID_ANY;
localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid
+ " from " + currentAssociationId);
} else {
localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
+ currentAssociationId);
}
mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid); ---> 继续看这个
}
}
总结下:
1. 获取候选网络的扫描结果。
2. 检查候选网络的有效性。
3. 获取目标BSSID和关联ID。
4. 检查是否已连接到目标BSSID或正在连接到目标BSSID,避免重复连接。
5. 检查候选网络的BSSID是否与配置中指定的BSSID匹配。
6. 检查是否应该跳过连接尝试,根据屏幕状态和连接尝试次数限制。
7. 记录连接尝试时间。
8. 更新最后一次连接尝试的BSSID。
9. 获取当前已连接的网络配置和关联信息。
10. 如果当前已连接的网络与候选网络相同或存在关联,则进行漫游处理(如果固件不支持漫游)。
11. 如果固件支持漫游或候选配置包含指定的BSSID,则指定连接目标BSSID。
12. 根据连接目标启动连接或漫游到网络。
http://aospxref.com/android-11.0.0_r21/xref/frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java?fi=startConnectToNetwork#startConnectToNetwork
这个就是走到我们熟悉的连接流程了:
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
自动连接流程,总结一句话就是从扫描结果中选取到符合的候选网络发起连接。