Android -- Wifi连接流程分析
Android -- Wifi连接流程分析
当我们在Android手机上连接一个AP时,间接调用WifiManager的connect()方法:
/**
* Connect to a network with the given configuration. The network also
* gets added to the supplicant configuration.
*
* For a new network, this function is used instead of a
* sequence of addNetwork(), enableNetwork(), saveConfiguration() and
* reconnect()
*
* @param config the set of variables that describe the configuration,
* contained in a {@link WifiConfiguration} object.
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
*
* @hide
*/
public void connect(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
validateChannel();
// Use INVALID_NETWORK_ID for arg1 when passing a config object
// arg1 is used to pass network id when the network already exists
sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
putListener(listener), config);
}
/**
* Connect to a network with the given networkId.
*
* This function is used instead of a enableNetwork(), saveConfiguration() and
* reconnect()
*
* @param networkId the network id identifiying the network in the
* supplicant configuration list
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
* @hide
*/
public void connect(int networkId, ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
validateChannel();
sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
}connect()方法有两种形式,一种接受WifiConfiguration对象,一种接受某个AP的networkID。通过AsyncChannel机制,向WifiServiceImpl发送CONNECT_NETWORK消息,可知在WifiServiceImpl::ClientHandler中被处理:
/* Client commands are forwarded to state machine */
case WifiManager.CONNECT_NETWORK:
case WifiManager.SAVE_NETWORK: {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
if (msg.what == WifiManager.SAVE_NETWORK) {
Slog.e("WiFiServiceImpl ", "SAVE"
+ " nid=" + Integer.toString(networkId)
+ " uid=" + msg.sendingUid
+ " name="
+ mContext.getPackageManager().getNameForUid(msg.sendingUid));
}
if (msg.what == WifiManager.CONNECT_NETWORK) {
Slog.e("WiFiServiceImpl ", "CONNECT "
+ " nid=" + Integer.toString(networkId)
+ " uid=" + msg.sendingUid
+ " name="
+ mContext.getPackageManager().getNameForUid(msg.sendingUid));
}
if (config != null && isValid(config)) {
if (DBG) Slog.d(TAG, "Connect with config" + config);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else if (config == null
&& networkId != WifiConfiguration.INVALID_NETWORK_ID) {
if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else {
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
if (msg.what == WifiManager.CONNECT_NETWORK) {
replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.INVALID_ARGS);
} else {
replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
WifiManager.INVALID_ARGS);
}
}
break;
}这里CONNECT_NETWORK消息被转发到WifiStateMachine中,ConnectModeState处理: case WifiManager.CONNECT_NETWORK:
/**
* The connect message can contain a network id passed as arg1 on message or
* or a config passed as obj on message.
* For a new network, a config is passed to create and connect.
* For an existing network, a network id is passed
*/
netId = message.arg1;
config = (WifiConfiguration) message.obj;
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
boolean updatedExisting = false;
/* Save the network config */
if (config != null) {
// When connecting to an access point, WifiStateMachine wants to update the
// relevant config with administrative data. This update should not be
// considered a 'real' update, therefore lockdown by Device Owner must be
// disregarded.
if (!recordUidIfAuthorized(config, message.sendingUid,
/* onlyAnnotate */ true)) {
logw("Not authorized to update network "
+ " config=" + config.SSID
+ " cnid=" + config.networkId
+ " uid=" + message.sendingUid);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.NOT_AUTHORIZED);
break;
}
String configKey = config.configKey(true /* allowCached */);
WifiConfiguration savedConfig =
mWifiConfigStore.getWifiConfiguration(configKey);
if (savedConfig != null) {
// There is an existing config with this netId, but it wasn't exposed
// (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
// getConfiguredNetworks). Remove those bits and update the config.
config = savedConfig;
logd("CONNECT_NETWORK updating existing config with id=" +
config.networkId + " configKey=" + configKey);
config.ephemeral = false;
config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
updatedExisting = true;
}
result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
netId = result.getNetworkId();
}
config = mWifiConfigStore.getWifiConfiguration(netId);
if (config == null) {
logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
+ mSupplicantStateTracker.getSupplicantStateName() + " my state "
+ getCurrentState().getName());
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
break;
} else {
String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
logd("CONNECT_NETWORK id=" + Integer.toString(netId)
+ " config=" + config.SSID
+ " cnid=" + config.networkId
+ " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " uid = " + message.sendingUid
+ wasSkipped);
}
autoRoamSetBSSID(netId, "any");
if (message.sendingUid == Process.WIFI_UID
|| message.sendingUid == Process.SYSTEM_UID) {
// As a sanity measure, clear the BSSID in the supplicant network block.
// If system or Wifi Settings want to connect, they will not
// specify the BSSID.
// If an app however had added a BSSID to this configuration, and the BSSID
// was wrong, Then we would forever fail to connect until that BSSID
// is cleaned up.
clearConfigBSSID(config, "CONNECT_NETWORK");
}
if (deferForUserInput(message, netId, true)) {
break;
} else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
WifiConfiguration.USER_BANNED) {
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.NOT_AUTHORIZED);
break;
}
mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
/* Tell autojoin the user did try to connect to that network if from settings */
boolean persist =
mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);
mWifiConfigStore.setLastSelectedConfiguration(netId);
didDisconnect = false;
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
&& mLastNetworkId != netId) {
/** Supplicant will ignore the reconnect if we are currently associated,
* hence trigger a disconnect
*/
didDisconnect = true;
mWifiNative.disconnect();
}
// Make sure the network is enabled, since supplicant will not reenable it
mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
message.sendingUid) && mWifiNative.reconnect()) {
lastConnectAttemptTimestamp = System.currentTimeMillis();
targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
/* The state tracker handles enabling networks upon completion/failure */
mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
if (didDisconnect) {
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
} else if (updatedExisting && getCurrentState() == mConnectedState &&
getCurrentWifiConfiguration().networkId == netId) {
// Update the current set of network capabilities, but stay in the
// current state.
updateCapabilities(config);
} else {
/**
* Directly go to disconnected state where we
* process the connection events from supplicant
**/
transitionTo(mDisconnectedState);
}
} else {
loge("Failed to connect config: " + config + " netId: " + netId);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
break;
}
break;有这么几种处理:- 将connect()传过来的AP信息保存到WifiConfigStore中
- enable即将要连接的AP
- 连接选定的AP
连接选定的AP是通过调用WifiNative方法向wpa_supplicant发送connect指令,wpa_s通知驱动进行连接;当底层连接成功后,framework就能通过WifiMonitor接受到wpa_s上报的event消息:
/**
* Handle all supplicant events except STATE-CHANGE
* @param event the event type
* @param remainder the rest of the string following the
* event name and "?—?"
*/
void handleEvent(int event, String remainder) {
if (DBG) {
logDbg("handleEvent " + Integer.toString(event) + " " + remainder);
}
switch (event) {
case DISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
break;
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
break;
case SCAN_RESULTS:
mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
break;
case SCAN_FAILED:
mStateMachine.sendMessage(SCAN_FAILED_EVENT);
break;
case UNKNOWN:
if (DBG) {
logDbg("handleEvent unknown: " + Integer.toString(event) + " " + remainder);
}
break;
default:
break;
}
} private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
String BSSID = null;
int networkId = -1;
int reason = 0;
int ind = -1;
int local = 0;
Matcher match;
if (newState == NetworkInfo.DetailedState.CONNECTED) {
match = mConnectedEventPattern.matcher(data);
if (!match.find()) {
if (DBG) Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");
} else {
BSSID = match.group(1);
try {
networkId = Integer.parseInt(match.group(2));
} catch (NumberFormatException e) {
networkId = -1;
}
}
notifyNetworkStateChange(newState, BSSID, networkId, reason);
} else if (newState == NetworkInfo.DetailedState.DISCONNECTED) {
match = mDisconnectedEventPattern.matcher(data);
if (!match.find()) {
if (DBG) Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");
} else {
BSSID = match.group(1);
try {
reason = Integer.parseInt(match.group(2));
} catch (NumberFormatException e) {
reason = -1;
}
try {
local = Integer.parseInt(match.group(3));
} catch (NumberFormatException e) {
local = -1;
}
}
notifyNetworkStateChange(newState, BSSID, local, reason);
}
}
/**
* Send the state machine a notification that the state of Wifi connectivity
* has changed.
* @param newState the new network state
* @param BSSID when the new state is {@link NetworkInfo.DetailedState#CONNECTED},
* this is the MAC address of the access point. Otherwise, it
* is {@code null}.
* @param netId the configured network on which the state change occurred
*/
void notifyNetworkStateChange(NetworkInfo.DetailedState newState,
String BSSID, int netId, int reason) {
if (newState == NetworkInfo.DetailedState.CONNECTED) {
Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
netId, reason, BSSID);
mStateMachine.sendMessage(m);
} else {
Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
netId, reason, BSSID);
if (DBG) logDbg("WifiMonitor notify network disconnect: "
+ BSSID
+ " reason=" + Integer.toString(reason));
mStateMachine.sendMessage(m);
}
}向WifiStateMachine发送NETWORK_CONNECTION_EVENT消息,告知底层已经连接成功,可以获取IP了。ConnectModeState进行处理:
4.4之后Android加入了AP评分机制。这个机制根据某个AP的很多配置信息一通处理,最后得出一个score,来表明某个AP的可连接性。它评判的属性太多,后面在研究,现在本渣还是没有搞明白,囧....
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (DBG) log("Network connection established");
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
break;这里会将这次连接的AP的networkID保存下来,进入到ObtainingIpState状态,进入enter()方法获取IP地址: @Override
public void enter() {
if (DBG) {
String key = "";
if (getCurrentWifiConfiguration() != null) {
key = getCurrentWifiConfiguration().configKey();
}
log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
+ " " + key + " "
+ " roam=" + mAutoRoaming
+ " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
+ " watchdog= " + obtainingIpWatchdogCount);
}
// Reset link Debouncing, indicating we have successfully re-connected to the AP
// We might still be roaming
linkDebouncing = false;
// 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 faile and the device to disconnect
clearCurrentConfigBSSID("ObtainingIpAddress");
try {
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Failed to enable IPv6: " + re);
} catch (IllegalStateException e) {
loge("Failed to enable IPv6: " + e);
}
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
if (isRoaming()) {
renewDhcp();
} else {
// Remove any IP address on the interface in case we're switching from static
// IP configuration to DHCP. This is safe because if we get here when not
// roaming, we don't have a usable address.
clearIPv4Address(mInterfaceName);
startDhcp();
}
obtainingIpWatchdogCount++;
logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
// Get Link layer stats so as we get fresh tx packet counters
getWifiLinkLayerStats(true);
sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
} else {
// stop any running dhcp before assigning static IP
stopDhcp();
StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
mLastNetworkId);
if (config.ipAddress == null) {
logd("Static IP lacks address");
sendMessage(CMD_STATIC_IP_FAILURE);
} else {
InterfaceConfiguration ifcg = new InterfaceConfiguration();
ifcg.setLinkAddress(config.ipAddress);
ifcg.setInterfaceUp();
try {
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
if (DBG) log("Static IP configuration succeeded");
DhcpResults dhcpResults = new DhcpResults(config);
sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
} catch (RemoteException re) {
loge("Static IP configuration failed: " + re);
sendMessage(CMD_STATIC_IP_FAILURE);
} catch (IllegalStateException e) {
loge("Static IP configuration failed: " + e);
sendMessage(CMD_STATIC_IP_FAILURE);
}
}
}
}这里分了两种连接方式,Static IP和DHCP。这里区分静态和DHCP是通过WifiConfiguration对象来处理的。WifiConfiguration代表一个配置过的AP连接,主要包含IpAssignment(标识上层配置是静态还是DHCP)、AP的networkID、AP的名字等等。进入DHCP流程之前,会先清除当前的IP地址信息: void startDhcp() {
maybeInitDhcpStateMachine();
mDhcpStateMachine.registerForPreDhcpNotification();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
}DhcpStateMachine也是一个小状态机,它主要处理DHCP下的IP地址获取:case CMD_START_DHCP:
if (mRegisteredForPreDhcpNotification) {
/* Notify controller before starting DHCP */
mController.sendMessage(CMD_PRE_DHCP_ACTION);
transitionTo(mWaitBeforeStartState);
} else {
if (runDhcpStart()) {
transitionTo(mRunningState);
}
}
break;向WifiStateMachine发送CMD_PRE_DHCP_ACTION消息,告知状态机做一些DHCP之前的处理工作,L2ConnectedState处理该消息:case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
handlePreDhcpSetup();
break;
void handlePreDhcpSetup() {
mDhcpActive = true;
if (!mBluetoothConnectionActive) {
/*
* There are problems setting the Wi-Fi driver's power
* mode to active when bluetooth coexistence mode is
* enabled or sense.
* <p>
* We set Wi-Fi to active mode when
* obtaining an IP address because we've found
* compatibility issues with some routers with low power
* mode.
* <p>
* In order for this active power mode to properly be set,
* we disable coexistence mode until we're done with
* obtaining an IP address. One exception is if we
* are currently connected to a headset, since disabling
* coexistence would interrupt that connection.
*/
// Disable the coexistence mode
mWifiNative.setBluetoothCoexistenceMode(
mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
}
// Disable power save and suspend optimizations during DHCP
// Note: The order here is important for now. Brcm driver changes
// power settings when we control suspend mode optimizations.
// TODO: Remove this comment when the driver is fixed.
setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
mWifiNative.setPowerSave(false);
// Update link layer stats
getWifiLinkLayerStats(false);
/* P2p discovery breaks dhcp, shut it down in order to get through this */
Message msg = new Message();
msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
msg.arg1 = WifiP2pServiceImpl.ENABLED;
msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
msg.obj = mDhcpStateMachine;
mWifiP2pChannel.sendMessage(msg);
}从注释可知,为了保证Wifi
DHCP过程的顺利进行,对Bluetooth、Power和P2p都做了处理工作,CMD_PRE_DHCP_ACTION_COMPLETE消息会在WifiP2pServiceImpl被发送到DhcpStateMachine,告知预处理工作已经结束,可以进行DHCP了:case CMD_PRE_DHCP_ACTION_COMPLETE:
if (runDhcpStart()) {
transitionTo(mRunningState);
} else {
transitionTo(mPollingState);
}
break; private boolean runDhcpStart() {
/* Stop any existing DHCP daemon before starting new */
NetworkUtils.stopDhcp(mInterfaceName);
mDhcpResults = null;
if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
if (!NetworkUtils.startDhcp(mInterfaceName) || !dhcpSucceeded()) {
Log.e(TAG, "DHCP request failed on " + mInterfaceName + ": " +
NetworkUtils.getDhcpError());
mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
.sendToTarget();
return false;
}
return true;
}调用NetworkUtils.startDhcp()方法进行DHCP获取IP地址。如果成功:private boolean dhcpSucceeded() {
DhcpResults dhcpResults = new DhcpResults();
if (!NetworkUtils.getDhcpResults(mInterfaceName, dhcpResults)) {
return false;
}
if (DBG) Log.d(TAG, "DHCP results found for " + mInterfaceName);
long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
//Sanity check for renewal
if (leaseDuration >= 0) {
//TODO: would be good to notify the user that his network configuration is
//bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
leaseDuration = MIN_RENEWAL_TIME_SECS;
}
//Do it a bit earlier than half the lease duration time
//to beat the native DHCP client and avoid extra packets
//48% for one hour lease time = 29 minutes
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
leaseDuration * 480, //in milliseconds
mDhcpRenewalIntent);
} else {
//infinite lease time, no renewal needed
}
// Fill in any missing fields in dhcpResults from the previous results.
// If mDhcpResults is null (i.e. this is the first server response),
// this is a noop.
dhcpResults.updateFromDhcpRequest(mDhcpResults);
mDhcpResults = dhcpResults;
mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
.sendToTarget();
return true;
}就会发送CMD_POST_DHCP_ACTION消息给WifiStateMachine,并附加runDhcp()获取到的DhcpResult对象。L2ConnectedState处理:case DhcpStateMachine.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
if (DBG) log("DHCP successful");
handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
// We advance to mConnectedState because handleIPv4Success will call
// updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
} else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_DHCP_FAILURE);
if (DBG) {
int count = -1;
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
count = config.numConnectionFailures;
}
log("DHCP failure count=" + count);
}
handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
// As above, we transition to mDisconnectingState via updateLinkProperties.
}
break;首先调用handlePostDhcpSetup()重置之前做的预处理动作;再调用handleIPv4Success()更新网络的配置信息:private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
if (PDBG) {
logd("handleIPv4Success <" + dhcpResults.toString() + ">");
logd("link address " + dhcpResults.ipAddress);
}
Inet4Address addr;
synchronized (mDhcpResultsLock) {
mDhcpResults = dhcpResults;
addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
}
if (isRoaming()) {
int previousAddress = mWifiInfo.getIpAddress();
int newAddress = NetworkUtils.inetAddressToInt(addr);
if (previousAddress != newAddress) {
logd("handleIPv4Success, roaming and address changed" +
mWifiInfo + " got: " + addr);
}
}
mWifiInfo.setInetAddress(addr);
mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
updateLinkProperties(reason);
}
updateLinkProperties()更新当前的网络配置信息,并根据DHCP是否成功发送CMD_IP_CONFIGURATION_SUCCESSFUL消息,L2ConnectedState处理:case CMD_IP_CONFIGURATION_SUCCESSFUL:
handleSuccessfulIpConfiguration();
sendConnectedState();
transitionTo(mConnectedState);主要做一些网络状态的更新操作,通知网络状态已经变化,有网络已经连上了。切换到ConnectedState,至此AP已经连接、IP也已获取到,并且网络状态也以更新。后续的过程貌似没什么大用。。4.4之后Android加入了AP评分机制。这个机制根据某个AP的很多配置信息一通处理,最后得出一个score,来表明某个AP的可连接性。它评判的属性太多,后面在研究,现在本渣还是没有搞明白,囧....
在framework里面,其实屏蔽了很多操作,比如WifiNative里面的大多数方法都是要通过JNI调用wifi.c,再调到wpa_s里面去的。这些中间过程其实比framework的处理要复杂的多,代码也更难看些。还有runDhcp(),这些网络操作有个专门的守护进程进行处理,就是dhcpcd;它的实现个人感觉跟wpa_s一样,都是很复杂的。即使了解DHCP协议的工作流程,要看懂它的代码实现也很痛苦。Android里还有PPPoE拨号方式,它的守护进程pppd的实现也很复杂......
文章来自:http://blog.csdn.net/csdn_of_coder/article/details/51922801