一 前言
上一篇分析了连接AP的过程,当底层完成wifi连接,便会事件通知WifiMonitor,WifiMonitor监听到该事件后发送消息NETWORK_CONNECTION_EVENT。
二 代码具体流程
1 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java
底层完成wifi连接,便会事件通知WifiMonitor,WifiMonitor监听到该事件后发送消息NETWORK_CONNECTION_EVENT。
/**
* Broadcast the network connection event to all the handlers registered for this event.
*
* @param iface Name of iface on which this occurred.
* @param networkId ID of the network in wpa_supplicant.
* @param bssid BSSID of the access point.
*/
public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
}
2 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java
WifiStateMachine此时处于DisconnectedState,该状态未能处理该消息,抛给它的父状态ConnectModeState处理。
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (mVerboseLoggingEnabled) log("Network connection established");
mLastNetworkId = message.arg1;
mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
mLastBssid = (String) message.obj;
reasonCode = message.arg2;
// TODO: This check should not be needed after WifiStateMachinePrime refactor.
// Currently, the last connected network configuration is left in
// wpa_supplicant, this may result in wpa_supplicant initiating connection
// to it after a config store reload. Hence the old network Id lookups may not
// work, so disconnect the network and let network selector reselect a new
// network.
config = getCurrentWifiConfiguration();
if (config != null) {
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
ScanDetailCache scanDetailCache =
mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache != null && mLastBssid != null) {
ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
if (scanResult != null) {
mWifiInfo.setFrequency(scanResult.frequency);
}
}
mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
// We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
if (config.enterpriseConfig != null
&& TelephonyUtil.isSimEapMethod(
config.enterpriseConfig.getEapMethod())) {
String anonymousIdentity =
mWifiNative.getEapAnonymousIdentity(mInterfaceName);
if (anonymousIdentity != null) {
config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
} else {
Log.d(TAG, "Failed to get updated anonymous identity"
+ " from supplicant, reset it in WifiConfiguration.");
config.enterpriseConfig.setAnonymousIdentity(null);
}
mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
}
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
} else {
logw("Connected to unknown networkId " + mLastNetworkId
+ ", disconnecting...");
sendMessage(CMD_DISCONNECT);
}
break;
可以看到一些操作,将ap的相关属性值赋给mWifiInfo。
mWifiInfo.setBSSID(mLastBssid)
mWifiInfo.setNetworkId(mLastNetworkId)
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName))
更新Wifi的状态信息NetworkInfo,mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID)
并调用sendNetworkStateChangeBroadcast(mLastBssid)。
private void sendNetworkStateChangeBroadcast(String bssid) {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
networkInfo.setExtraInfo(null);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
//TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
调用mContext.sendStickyBroadcastAsUser发送广播WifiManager.NETWORK_STATE_CHANGED_ACTION。
然后转到获取ip的状态ObtainingIpState,transitionTo(mObtainingIpState),首先会进入它的父状态L2ConnectedState。
@Override
public void enter() {
mRssiPollToken++;
if (mEnableRssiPolling) {
sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
}
if (mNetworkAgent != null) {
loge("Have NetworkAgent when entering L2Connected");
setNetworkDetailedState(DetailedState.DISCONNECTED);
}
setNetworkDetailedState(DetailedState.CONNECTING);
final NetworkCapabilities nc;
if (mWifiInfo != null && !mWifiInfo.getSSID().equals(WifiSsid.NONE)) {
nc = new NetworkCapabilities(mNetworkCapabilitiesFilter);
nc.setSSID(mWifiInfo.getSSID());
} else {
nc = mNetworkCapabilitiesFilter;
}
mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
"WifiNetworkAgent", mNetworkInfo, nc, mLinkProperties, 60, mNetworkMisc);
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to faile and the device to disconnect
clearTargetBssid("L2ConnectedState");
mCountryCode.setReadyForChange(false);
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
}
将mNetworkInfo状态设为CONNECTING,即调用setNetworkDetailedState(DetailedState.CONNECTING);创建了一个WifiNetworkAgent对象,关于WifiNetworkAgent的注册过程这次略过。
case WifiMonitor.NETWORK_CONNECTION_EVENT:
mWifiInfo.setBSSID((String) message.obj);
mLastNetworkId = message.arg1;
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
if(!mLastBssid.equals(message.obj)) {
mLastBssid = (String) message.obj;
sendNetworkStateChangeBroadcast(mLastBssid);
}
break;
看sendNetworkStateChangeBroadcast(mLastBssid)。
private void sendNetworkStateChangeBroadcast(String bssid) {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
networkInfo.setExtraInfo(null);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
//TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
调用mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL)发送广播WifiManager.NETWORK_STATE_CHANGED_ACTION。
4 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
接收到广播WifiManager.NETWORK_STATE_CHANGED_ACTION。
/**
* Receiver for handling broadcasts.
*
* This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
updateWifiState(
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
mStaleScanResults = false;
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
|| WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
// TODO(sghuman): Refactor these methods so they cannot result in duplicate
// onAccessPointsChanged updates being called from this intent.
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
updateNetworkInfo(info);
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
NetworkInfo info =
mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
updateNetworkInfo(info);
}
}
};
WifiTracker接收到广播开始刷新fetchScansAndConfigsAndUpdateAccessPoints()。
5 packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
handleWifiStateChanged(mWifiManager.getWifiState());
} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
if (!mConnected.get()) {
handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
}
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
mConnected.set(info.isConnected());
handleStateChanged(info.getDetailedState());
}
}
};
应用层Settings模块接收到广播进行状态的相关处理handleWifiStateChanged(mWifiManager.getWifiState())。
接着WifiStateMachine进入ObtainingIpState状态,在该状态下完成ip配置,分为静态ip和动态ip。
@Override
public void enter() {
final WifiConfiguration currentConfig = getCurrentWifiConfiguration();
final boolean isUsingStaticIp =
(currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
if (mVerboseLoggingEnabled) {
final String key = currentConfig.configKey();
log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
+ " " + key + " "
+ " roam=" + mIsAutoRoaming
+ " static=" + isUsingStaticIp);
}
// Send event to CM & network change broadcast
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to fail and the device to disconnect.
clearTargetBssid("ObtainingIpAddress");
// Stop IpClient in case we're switching from DHCP to static
// configuration or vice versa.
//
// TODO: Only ever enter this state the first time we connect to a
// network, never on switching between static configuration and
// DHCP. When we transition from static configuration to DHCP in
// particular, we must tell ConnectivityService that we're
// disconnected, because DHCP might take a long time during which
// connectivity APIs such as getActiveNetworkInfo should not return
// CONNECTED.
stopIpClient();
mIpClient.setHttpProxy(currentConfig.getHttpProxy());
if (!TextUtils.isEmpty(mTcpBufferSizes)) {
mIpClient.setTcpBufferSizes(mTcpBufferSizes);
}
final IpClient.ProvisioningConfiguration prov;
if (!isUsingStaticIp) {
prov = IpClient.buildProvisioningConfiguration()
.withPreDhcpAction()
.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
.withNetwork(getCurrentNetwork())
.withDisplayName(currentConfig.SSID)
.withRandomMacAddress()
.build();
} else {
StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
prov = IpClient.buildProvisioningConfiguration()
.withStaticConfiguration(staticIpConfig)
.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
.withNetwork(getCurrentNetwork())
.withDisplayName(currentConfig.SSID)
.build();
}
mIpClient.startProvisioning(prov);
// Get Link layer stats so as we get fresh tx packet counters
getWifiLinkLayerStats();
}
回调IpClient.Callback.onProvisioningSuccess接口。
class IpClientCallback extends IpClient.Callback {
@Override
public void onPreDhcpAction() {
sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION);
}
@Override
public void onPostDhcpAction() {
sendMessage(DhcpClient.CMD_POST_DHCP_ACTION);
}
@Override
public void onNewDhcpResults(DhcpResults dhcpResults) {
if (dhcpResults != null) {
sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
} else {
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
getTargetSsid(), mTargetRoamBSSID,
WifiLastResortWatchdog.FAILURE_CODE_DHCP);
}
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
...
}
在onProvisioningSuccess函数中发送消息CMD_IP_CONFIGURATION_SUCCESSFUL。
在L2ConnectedState处理消息CMD_IP_CONFIGURATION_SUCCESSFUL。
case CMD_IP_CONFIGURATION_SUCCESSFUL:
handleSuccessfulIpConfiguration();
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NONE,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
if (getCurrentWifiConfiguration() == null) {
// The current config may have been removed while we were connecting,
// trigger a disconnect to clear up state.
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
} else {
sendConnectedState();
transitionTo(mConnectedState);
}
break;
看sendConnectedState。
private void sendConnectedState() {
// If this network was explicitly selected by the user, evaluate whether to call
// explicitlySelected() so the system can treat it appropriately.
WifiConfiguration config = getCurrentWifiConfiguration();
if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
boolean prompt =
mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
if (mVerboseLoggingEnabled) {
log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
}
if (prompt) {
// Selected by the user via Settings or QuickSettings. If this network has Internet
// access, switch to it. Otherwise, switch to it only if the user confirms that they
// really want to switch, or has already confirmed and selected "Don't ask again".
if (mVerboseLoggingEnabled) {
log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected);
}
if (mNetworkAgent != null) {
mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
}
}
}
setNetworkDetailedState(DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
}
将NetworkInfo状态置为CONNECTED状态setNetworkDetailedState(DetailedState.CONNECTED);
并调用sendNetworkStateChangeBroadcast(mLastBssid)
private void sendNetworkStateChangeBroadcast(String bssid) {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
networkInfo.setExtraInfo(null);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
//TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
6 frameworks/base/frameworks/base/services/net/java/android/net/ip/IpClient.java
下面就是前面回调的接口。
/**
* Callbacks for handling IpClient events.
*
* These methods are called by IpClient on its own thread. Implementations
* of this class MUST NOT carry out long-running computations or hold locks
* for which there might be contention with other code calling public
* methods of the same IpClient instance.
*/
public static class Callback {
// In order to receive onPreDhcpAction(), call #withPreDhcpAction()
// when constructing a ProvisioningConfiguration.
//
// Implementations of onPreDhcpAction() must call
// IpClient#completedPreDhcpAction() to indicate that DHCP is clear
// to proceed.
public void onPreDhcpAction() {}
public void onPostDhcpAction() {}
// This is purely advisory and not an indication of provisioning
// success or failure. This is only here for callers that want to
// expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
// DHCPv4 or static IPv4 configuration failure or success can be
// determined by whether or not the passed-in DhcpResults object is
// null or not.
public void onNewDhcpResults(DhcpResults dhcpResults) {}
public void onProvisioningSuccess(LinkProperties newLp) {}
public void onProvisioningFailure(LinkProperties newLp) {}
// Invoked on LinkProperties changes.
public void onLinkPropertiesChange(LinkProperties newLp) {}
// Called when the internal IpReachabilityMonitor (if enabled) has
// detected the loss of a critical number of required neighbors.
public void onReachabilityLost(String logMsg) {}
// Called when the IpClient state machine terminates.
public void onQuit() {}
// Install an APF program to filter incoming packets.
public void installPacketFilter(byte[] filter) {}
// Asynchronously read back the APF program & data buffer from the wifi driver.
// Due to Wifi HAL limitations, the current implementation only supports dumping the entire
// buffer. In response to this request, the driver returns the data buffer asynchronously
// by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
public void startReadPacketFilter() {}
// If multicast filtering cannot be accomplished with APF, this function will be called to
// actuate multicast filtering using another means.
public void setFallbackMulticastFilter(boolean enabled) {}
// Enabled/disable Neighbor Discover offload functionality. This is
// called, for example, whenever 464xlat is being started or stopped.
public void setNeighborDiscoveryOffload(boolean enable) {}
}