WifiMonitor实现
概述
WifiMonitor的主要任务是与wpa_supplicatn守护进程通信,从而监听来自底层驱动的消息。
而这些消息又会反馈给状态机WifiStateMachine,并触发状态转移。
与wpa_supplicant交互是通过JNI调用C++代码实现,而WifiNative则是对C++代码的包装类。
内部类MonitorThread
-
该线程开启时会连接wpa_supplicant服务,并向状态机发送连接的消息。
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
}
public void run() {
if (connectToSupplicant()) {
mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
} else {
mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
return;
}
// ......
}
}
与wpa_supplicant的交互
-
调用WifiNative.connectToSupplicant方法连接wpa_supplicant。
连接wpa_supplicant
private boolean connectToSupplicant() {
int connectTries = 0;
while (true) {
if (WifiNative.connectToSupplicant()) {
return true;
}
if (connectTries++ < 5) {
nap(1);
} else {
break;
}
}
return false;
}
处理安全密码认证失败
private static final String WPA_EVENT_PREFIX_STR = "WPA:";
private static final String PASSWORD_MAY_BE_INCORRECT_STR =
"pre-shared key may be incorrect";
if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
}
处理WPS-overlap
private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
if (eventStr.startsWith(WPS_OVERLAP_STR)) {
mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
}
处理P2P事件
// P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1
// name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0
private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND";
// P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27
private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
// P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4
private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
"P2P-GROUP-FORMATION-SUCCESS";
private static final String P2P_GROUP_FORMATION_FAILURE_STR =
"P2P-GROUP-FORMATION-FAILURE";
// P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
// [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
// go_dev_addr=fa:7b:7a:42:02:13
private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
// P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED";
// P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
// bssid=fa:7b:7a:42:82:13 unknown-network
private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED";
// P2P-INVITATION-RESULT status=1
private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT";
// P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
// pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
// group_capab=0x0
private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
// P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
// pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
// group_capab=0x0
private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN";
// P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
// pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
// group_capab=0x0
private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
private void handleP2pEvents(String dataString) {
if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT,
new WifiP2pDevice(dataString));
} else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT,
new WifiP2pDevice(dataString));
} else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
new WifiP2pConfig(dataString));
} else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
} else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT);
} else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
} else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT);
} else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT,
new WifiP2pGroup(dataString));
} else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT,
new WifiP2pGroup(dataString));
} else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
new WifiP2pGroup(dataString));
} else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
String[] tokens = dataString.split(" ");
if (tokens.length != 2) return;
String[] nameValue = tokens[1].split("=");
if (nameValue.length != 2) return;
mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
} else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
new WifiP2pDevice(dataString));
} else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT,
new WifiP2pDevice(dataString));
}
}
处理AP热点事件
// AP-STA-CONNECTED 42:fc:89:a8:96:09
private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
// AP-STA-DISCONNECTED 42:fc:89:a8:96:09
private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
private void handleHostApEvents(String dataString) {
String[] tokens = dataString.split(" ");
if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, tokens[1]);
} else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, tokens[1]);
}
}
处理supplicant状态变化
/**
* <pre>
* CTRL-EVENT-STATE-CHANGE x
* </pre>
* <code>x</code> is the numerical value of the new state.
*/
private static final String STATE_CHANGE_STR = "STATE-CHANGE";
-
handleSupplicantStateChange方法
/**
* Handle the supplicant STATE-CHANGE event
* @param dataString New supplicant state string in the format:
* id=network-id state=new-state
*/
private void handleSupplicantStateChange(String dataString) {
String[] dataTokens = dataString.split(" ");
String BSSID = null;
int networkId = -1;
int newState = -1;
for (String token : dataTokens) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) {
continue;
}
if (nameValue[0].equals("BSSID")) {
BSSID = nameValue[1];
continue;
}
int value;
try {
value = Integer.parseInt(nameValue[1]);
} catch (NumberFormatException e) {
continue;
}
if (nameValue[0].equals("id")) {
networkId = value;
} else if (nameValue[0].equals("state")) {
newState = value;
}
}
if (newState == -1) return;
SupplicantState newSupplicantState = SupplicantState.INVALID;
for (SupplicantState state : SupplicantState.values()) {
if (state.ordinal() == newState) {
newSupplicantState = state;
break;
}
}
if (newSupplicantState == SupplicantState.INVALID) {
}
notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
/**
* Send the state machine a notification that the state of the supplicant
* has changed.
* @param networkId the configured network on which the state change occurred
* @param newState the new {@code SupplicantState}
*/
void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
new StateChangeResult(networkId, BSSID, newState)));
}
处理驱动挂起
/**
* <pre>
* CTRL-EVENT-DRIVER-STATE state
* </pre>
* <code>state</code> can be HANGED
*/
private static final String DRIVER_STATE_STR = "DRIVER-STATE";
private void handleDriverEvent(String state) {
if (state == null) {
return;
}
if (state.equals("HANGED")) {
mStateMachine.sendMessage(DRIVER_HUNG_EVENT);
}
}
处理终止状态
/**
* <pre>
* CTRL-EVENT-TERMINATING - signal x
* </pre>
* <code>x</code> is the signal that caused termination.
*/
private static final String TERMINATING_STR = "TERMINATING";
// 接收安全验证错误
private static final String WPA_RECV_ERROR_STR = "recv error";
if (event == TERMINATING) {
// 如果socket连接关闭,则退出监听线程
if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
if (false) {
}
break;
}
if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
if (++mRecvErrors > MAX_RECV_ERRORS) {
if (false) {
}
} else {
continue;
}
}
mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
break;
}
处理EAP认证错误
/**
* <pre>
* CTRL-EVENT-EAP-FAILURE EAP authentication failed
* </pre>
*/
private static final String EAP_FAILURE_STR = "EAP-FAILURE";
if (event == EAP_FAILURE) {
if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
}
}
处理网络状态变化
/**
* <pre>
* CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed
* </pre>
* <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
*/
private static final String CONNECTED_STR = "CONNECTED";
/**
* <pre>
* CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
* </pre>
*/
private static final String DISCONNECTED_STR = "DISCONNECTED";
-
handleNetworkStateChange方法
private void handleNetworkStateChange(NetworkInfo.DetailedState newState,
String data) {
String BSSID = null;
int networkId = -1;
if (newState == NetworkInfo.DetailedState.CONNECTED) {
Matcher match = mConnectedEventPattern.matcher(data);
if (!match.find()) {
if (false) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
} else {
BSSID = match.group(1);
try {
networkId = Integer.parseInt(match.group(2));
} catch (NumberFormatException e) {
networkId = -1;
}
}
}
notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
* Send the state machine a notification that the state of Wifi connectivity
* has changed.
* @param networkId the configured network on which the state change occurred
* @param newState the new network state
* @param BSSID when the new state is {@link DetailedState#CONNECTED
* NetworkInfo.DetailedState.CONNECTED},
* this is the MAC address of the access point. Otherwise, it
* is {@code null}.
*/
void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
if (newState == NetworkInfo.DetailedState.CONNECTED) {
Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
netId, 0, BSSID);
mStateMachine.sendMessage(m);
} else {
Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
netId, 0, BSSID);
mStateMachine.sendMessage(m);
}
}
处理扫描结果
/**
* <pre>
* CTRL-EVENT-SCAN-RESULTS ready
* </pre>
*/
private static final String SCAN_RESULTS_STR = "SCAN-RESULTS";
/**
* 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) {
switch (event) {
// ......
case SCAN_RESULTS:
mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
break;
case UNKNOWN:
break;
}
}