WifiMonitor实现

概述

    WifiMonitor的主要任务是与wpa_supplicatn守护进程通信,从而监听来自底层驱动的消息。
而这些消息又会反馈给状态机WifiStateMachine,并触发状态转移。
    与wpa_supplicant交互是通过JNI调用C++代码实现,而WifiNative则是对C++代码的包装类。

内部类MonitorThread

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的交互

连接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";
/**
 * 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";
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 &quot;&#8195;&#8212;&#8195;&quot;
 */
void handleEvent(int event, String remainder) {
    switch (event) {
        // ......
        case SCAN_RESULTS:
            mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
            break;

        case UNKNOWN:
            break;
    }
}