本章主要介绍用户手动的在Settings中点击Scan和Connect按钮,输入密码后的连接过程,先看整体流程图:
public boolean startScan(WorkSource workSource) {
try {
mService.startScan(workSource);
return true;
} catch (RemoteException e) {
return false;
}
}
WifiService.java
public void startScan(WorkSource workSource) {
enforceChangePermission();
if (workSource != null) {
enforceWorkSourcePermission();
// WifiManager currently doesn't use names, so need to clear names out of the
// supplied WorkSource to allow future WorkSource combining.
workSource.clearNames();
}
mWifiStateMachine.startScan(Binder.getCallingUid(), workSource);
}
public boolean processMessage(Message message) {
switch(message.what) {
case CMD_START_SCAN:
noteScanStart(message.arg1, (WorkSource) message.obj);
startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
break;
case WifiMonitor.SCAN_RESULTS_EVENT:
setScanResults();
sendScanResultsAvailableBroadcast();
mScanResultIsPending = false;
break;
private void setScanResults() {
while (true) {
tmpResults = mWifiNative.scanResults(sid);
if (TextUtils.isEmpty(tmpResults)) break;
scanResultsBuf.append(tmpResults);
scanResultsBuf.append("\n");
String[] lines = tmpResults.split("\n");
sid = -1;
for (int i=lines.length - 1; i >= 0; i--) {
if (lines[i].startsWith(END_STR)) {
break;
} else if (lines[i].startsWith(ID_STR)) {
try {
sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
} catch (NumberFormatException e) {
// Nothing to do
}
break;
}
}
if (sid == -1) break;
}
scanResults = scanResultsBuf.toString();
if (TextUtils.isEmpty(scanResults)) {
return;
}
synchronized(mScanResultCache) {
for (String line : lines) {
if (line.startsWith(BSSID_STR)) {
bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
} else if (line.startsWith(FREQ_STR)) {
try {
freq = Integer.parseInt(line.substring(FREQ_STR.length()));
} catch (NumberFormatException e) {
freq = 0;
}
}
else if (line.startsWith(SSID_STR)) {
wifiSsid = WifiSsid.createFromAsciiEncoded(
line.substring(SSID_STR.length()));
} else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
if (bssid != null) {
String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
String key = bssid + ssid;
ScanResult scanResult = mScanResultCache.get(key);
if (scanResult != null) {
scanResult.level = level;
scanResult.wifiSsid = wifiSsid;
// Keep existing API
scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
WifiSsid.NONE;
scanResult.capabilities = flags;
scanResult.frequency = freq;
scanResult.timestamp = tsf;
} else {
scanResult =
new ScanResult(
wifiSsid, bssid, flags, level, freq, tsf);
mScanResultCache.put(key, scanResult);
}
mScanResults.add(scanResult);
}
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);
}
case WifiManager.CONNECT_NETWORK:
case WifiManager.SAVE_NETWORK: {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
if (config != null && config.isValid()) {
// This is restricted because there is no UI for the user to
// monitor/control PAC.
if (config.proxySettings != ProxySettings.PAC) {
if (DBG) Slog.d(TAG, "Connect with config" + config);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} reak;
}
case WifiManager.CONNECT_NETWORK:
int netId = message.arg1;
config = (WifiConfiguration) message.obj;
/* Save the network config */
if (config != null) {
NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
netId = result.getNetworkId();
}
if (mWifiConfigStore.selectNetwork(netId) &&
mWifiNative.reconnect()) {
/* The state tracker handles enabling networks upon completion/failure */
mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
/* Expect a disconnection from the old connection */
transitionTo(mDisconnectingState);
}
break;
class DisconnectingState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 != CONNECT_MODE) {
deferMessage(message);
}
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
/* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
* we have missed the network disconnection, transition to mDisconnectedState
* and handle the rest of the events there
*/
deferMessage(message);
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (DBG) log("Network connection established");
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
/* send event to CM & network change broadcast */
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
break;
class ObtainingIpState extends State {
@Override
public void enter() {
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
// TODO: If we're switching between static IP configuration and DHCP, remove the
// static configuration first.
startDhcp();
} else {
// stop any running dhcp before assigning static IP
stopDhcp();
DhcpResults dhcpResults = new DhcpResults(
mWifiConfigStore.getLinkProperties(mLastNetworkId));
InterfaceConfiguration ifcg = new InterfaceConfiguration();
Iterator addrs =
dhcpResults.linkProperties.getLinkAddresses().iterator();
if (!addrs.hasNext()) {
loge("Static IP lacks address");
sendMessage(CMD_STATIC_IP_FAILURE);
} else {
ifcg.setLinkAddress(addrs.next());
ifcg.setInterfaceUp();
try {
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
if (DBG) log("Static IP configuration succeeded");
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);
}
}
}
}
void startDhcp() {
if (mDhcpStateMachine == null) {
mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
mContext, WifiStateMachine.this, mInterfaceName);
}
mDhcpStateMachine.registerForPreDhcpNotification();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
}
public boolean processMessage(Message message) {
switch (message.what) {
case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
handlePreDhcpSetup();
break;
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
if (DBG) log("DHCP successful");
handleSuccessfulIpConfiguration((DhcpResults) message.obj);
transitionTo(mVerifyingLinkState);
} else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
if (DBG) log("DHCP failed");
handleFailedIpConfiguration();
transitionTo(mDisconnectingState);
}
break;
class VerifyingLinkState extends State {
@Override
public void enter() {
log(getName() + " enter");
setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
sendNetworkStateChangeBroadcast(mLastBssid);
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
//stay here
log(getName() + " POOR_LINK_DETECTED: no transition");
break;
case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
transitionTo(mCaptivePortalCheckState);
break;
default:
if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
return NOT_HANDLED;
}
return HANDLED;
}
}
class CaptivePortalCheckState extends State {
@Override
public void enter() {
log(getName() + " enter");
setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
sendNetworkStateChangeBroadcast(mLastBssid);
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_CAPTIVE_CHECK_COMPLETE:
log(getName() + " CMD_CAPTIVE_CHECK_COMPLETE");
try {
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
loge("Failed to enable IPv6: " + re);
} catch (IllegalStateException e) {
loge("Failed to enable IPv6: " + e);
}
setNetworkDetailedState(DetailedState.CONNECTED);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectedState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}