基于rk3288平台android5.1系统的wifi流程分析 ---- 连接热点

上一篇 http://blog.csdn.net/ballack_linux/article/details/78095182 讲了打开wifi 和 扫描热点的流程, 这一篇来了解下连接热点的流程 :

// ---------- packages/apps/settings/src/com/android/settings/wifi/WifiSettings.java ------------ //
public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case MENU_ID_CONNECT: {
              if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
                  connect(mSelectedAccessPoint.networkId);
              } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
                  /** Bypass dialog for unsecured networks */
                  mSelectedAccessPoint.generateOpenNetworkConfig();
                  connect(mSelectedAccessPoint.getConfig());
              } else {
                  showDialog(mSelectedAccessPoint, true);
              }
              return true;
          }
    }
}

protected void connect(final WifiConfiguration config) {
    mWifiManager.connect(config, mConnectListener);
}

protected void connect(final int networkId) {
    mWifiManager.connect(networkId, mConnectListener);
}
// -------------- frameworks/base/wifi/java/android/net/wifi/WifiManager.java ----------------- //
// 其中第一个参数WifiConfiguration是当前需要连接的AP的配置信息,包括SSID、BSSID、密码以及加密方式等信息;
// ActionListener作为callback来通知客户程序connect方法是否调用成功,这里的调用成功只是指参数是否正确,并不表示AP是否连接成功
public void connect(WifiConfiguration config, ActionListener listener) {
    sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
            putListener(listener), config);
}
由sAsyncChannel的知识可以知道, 这时候是WifiService来处理这个CONNECT_NETWORK消息:

// ---------- frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java ------------ //
private class ClientHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case WifiManager.CONNECT_NETWORK:
                if (DBG) Slog.d(TAG, "Connect with config" + config);
                mWifiStateMachine.sendMessage(Message.obtain(msg));
        }
    }
}

上一篇中scan的流程可以知道, scan之后WifiStateMachine状态机是处在ConnectModeState状态的, 那么该状态没有处理CONNECT_NETWORK事件,由其父类ConnectModeState处理:

class ConnectModeState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case WifiManager.CONNECT_NETWORK:
                // WifiConfigStore.saveNetwork(config)将AP的配置信息写入到wpa_supplicant.conf中;
                // WifiConfigStore.selectNetwork(netId)用于enable即将要连接的AP,而disable掉其它的AP;
                // WifiNative.reconnect()发起重新连接的请求给wpa_supplicant。接着transition到DisconnectingState
                
                mWifiConfigStore.saveNetwork(config, message.sendingUid);
                if (mWifiConfigStore.selectNetwork(netId) &&
                        mWifiNative.reconnect()) {
                    transitionTo(mDisconnectingState);
                }
        }
    }
}
当执行完WifiNative.reconnect(),wpa_supplicant会不断的往WifiMonitor发送包括CTRL-EVENT-STATE-CHANGE、ASSOCIATING、ASSOCIATED、FOUR_WAY_HANDSHARK、GROUP_HANDSHARK等event,WifiMonitor会不断的去parse这些event, 并向WifiStatemachine发送消息,其中一个比较重要的消息就是当wpa_supplicant的状态改变是会发送WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,上面的DisconnectiongState 收到这个消息后,会transition到DisconnectedState:
class DisconnectingState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                deferMessage(message);
                handleNetworkDisconnect();
                transitionTo(mDisconnectedState);
        }
    }
}
当Wifi和AP之间已经连接成功后,就会收到wpa_supplicant发送上来的CTRL-EVENT-CONNECTED这个event,
WifiMonitor收到这个消息后,会向WifiStateMachine发送NETWORK_CONNECTION_EVENT表示已经和AP之间成功的连线,
由于WifiStateMachine的DisconnectedState不处理这个消息, 故其父类ConnectModeState会来处理这个消息:

class ConnectModeState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case WifiMonitor.NETWORK_CONNECTION_EVENT:
                transitionTo(mObtainingIpState);
        }
    }
}
class ObtainingIpState extends State {
    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();                                                // 动态获取ip地址
        }
    } else {
        stopDhcp();
        StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(mLastNetworkId);
        InterfaceConfiguration ifcg = new InterfaceConfiguration();
        ifcg.setLinkAddress(config.ipAddress);                          // 从配置里面读取固定ip地址
        ifcg.setInterfaceUp();

        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
        if (DBG) log("Static IP configuration succeeded");
        DhcpResults dhcpResults = new DhcpResults(config);
        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);                // 发送设置成功的标志
    }
}
void startDhcp() {
    if (mDhcpStateMachine == null) {
        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
                mContext, WifiStateMachine.this, mInterfaceName);

    }
    mDhcpStateMachine.registerForPreDhcpNotification();                 // mRegisteredForPreDhcpNotification = true
    mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);     // 发送CMD_START_DHCP消息
}
// ------------- frameworks/base/core/java/android/net/DhcpStateMachine.java ---------------- //
public class DhcpStateMachine extends StateMachine {
    public static DhcpStateMachine makeDhcpStateMachine(Context context, StateMachine controller, String intf) {
        DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
        dsm.start();
        return dsm;
    }

    private DhcpStateMachine(Context context, StateMachine controller, String intf) {
        mController = controller;                                       // 可以知道mController指向WifiStateMachine类的对象
        setInitialState(mStoppedState);
    }

    class StoppedState extends State {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START_DHCP:
                    if (mRegisteredForPreDhcpNotification) {
                        /* Notify controller before starting DHCP */
                        mController.sendMessage(CMD_PRE_DHCP_ACTION);   // 向WifiStateMachine类发送CMD_PRE_DHCP_ACTION消息
                        transitionTo(mWaitBeforeStartState);            // 切换到mWaitBeforeStartState状态
                    } else {
                        if (runDhcp(DhcpAction.START)) {
                            transitionTo(mRunningState);
                        }
                    }
                    break;
            }
        }
    }
}
由上面可以知道WifiStateMachine的当前状态是ObtainingIpState, 而ObtainingIpState不处理CMD_PRE_DHCP_ACTION消息, 交由其父类mL2ConnectedState处理:
class L2ConnectedState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                handlePreDhcpSetup();
                break;
    }
}

void handlePreDhcpSetup() {
    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);
}
其中mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pServiceImpl.getP2pStateMachineMessenger());
故上面是向mWifiP2pServiceImpl发送WifiP2pServiceImpl.BLOCK_DISCOVERY消息,那当前WifiP2pServiceImpl处在什么状态呢?
还记得在打开wifi的时候,会执行以下命令:
mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);

由于P2pStateMachine初始状态是P2pDisabledState, 故由其来处理CMD_ENABLE_P2P: 

class P2pDisabledState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case WifiStateMachine.CMD_ENABLE_P2P:
                mNwService.setInterfaceUp(mInterface);
                mWifiMonitor.startMonitoring();
                transitionTo(mP2pEnablingState);
        }
    }
}
P2pStateMachine会切换到mP2pEnablingState状态, 由于mP2pEnablingState状态不处理BLOCK_DISCOVERY消息,故由其父类mDefaultState处理:

// ---------- frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java ------------ //
class DefaultState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case BLOCK_DISCOVERY:
                mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
                mDiscoveryPostponed = false;
                if (mDiscoveryBlocked) {
                    StateMachine m = (StateMachine)message.obj;     // 这里message.obj就是mDHCPStateMachine
                    m.sendMessage(message.arg2);                    // 这里message.arg2就是DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE
                }
        }
    }
}
从上面可以知道, handlePreDhcpSetup其实就是向DhcpStateMachine类发送CMD_PRE_DHCP_ACTION_COMPLETE消息:
当前DhcpStateMachine类处于mWaitBeforeStartState状态:
// ---------- frameworks/base/core/java/android/net/DhcpStateMachine.java ---------- //
class WaitBeforeStartState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case CMD_PRE_DHCP_ACTION_COMPLETE:
                if (runDhcp(DhcpAction.START)) {
                    transitionTo(mRunningState);
                } else {
                    transitionTo(mStoppedState);
                }
        }
    }
}

private boolean runDhcp(DhcpAction dhcpAction) {
    if (dhcpAction == DhcpAction.START) {
        /* Stop any existing DHCP daemon before starting new */
        NetworkUtils.stopDhcp(mInterfaceName);
        success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults);

        mDhcpResults = dhcpResults;

        // 向WifiStateMachine发送CMD_POST_DHCP_ACTION消息
        mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults).sendToTarget();   
    }
}
// ---------------- frameworks/base/core/jni/android_net_NetUtils.cpp ------------------ //
static JNINativeMethod gNetworkUtilMethods[] = {
    { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_runDhcp },
}

static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
{
    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
}

static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname, jobject dhcpResults, bool renew) {
    if (renew) {
        result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
                dns, server, &lease, vendorInfo, domains, mtu);
    } else {
        result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
                dns, server, &lease, vendorInfo, domains, mtu);
    }
}
// -------------------- system/core/libnetutils/Dhcp_utils.c ---------------------- //
static const char DAEMON_NAME[]        = "dhcpcd";
static const char DAEMON_PROP_NAME[]   = "init.svc.dhcpcd";
static const char HOSTNAME_PROP_NAME[] = "net.hostname";
static const char DHCP_PROP_NAME_PREFIX[]  = "dhcp";
static const char DHCP_CONFIG_PATH[]   = "/system/etc/dhcpcd/dhcpcd.conf";

int dhcp_do_request(const char *interface,
        char *ipaddr,
        char *gateway,
        uint32_t *prefixLength,
        char *dns[],
        char *server,
        uint32_t *lease,
        char *vendorInfo,
        char *domain,
        char *mtu)
{
    char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
    const char *ctrl_prop = "ctl.start";
    const char *desired_status = "running";
    /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
    char p2p_interface[MAX_INTERFACE_LENGTH];

    get_p2p_interface_replacement(interface, p2p_interface);                    // 获取p2p_interface

    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",        // 例:dhcp.wlan0.result
            DHCP_PROP_NAME_PREFIX,
            p2p_interface);

    snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",               // 例:init.svc.dhcpcd_wlan0
            DAEMON_PROP_NAME,
            p2p_interface);

    /* Erase any previous setting of the dhcp result property */
    property_set(result_prop_name, "");

    /* Start the daemon and wait until it's ready */
    if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '/0')) {

        // -----------------prop_value实例: [net.hostname]: [android-7a296df9ee18dade]
        // dhcpcd_wlan0:-f /system/etc/dhcpcd/dhcpcd.conf -h android-7a296df9ee18dade wlan0 -t 60

        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s -t 60", DAEMON_NAME,
                p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
    }
    else {
        // dhcpcd_wlan0:-f /system/etc/dhcpcd/dhcpcd.conf  wlan0 -t 60

        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s -t 60", DAEMON_NAME,
                p2p_interface, DHCP_CONFIG_PATH, interface);
    }
    memset(prop_value, '/0', PROPERTY_VALUE_MAX);
    property_set(ctrl_prop, daemon_cmd);                                    // ctl.start -----
    if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {      // wait for init.svc.dhcpcd_wlan0 changed to running
        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
        return -1;
    }

    /* Wait for the daemon to return a result */
    if (wait_for_property(result_prop_name, NULL, 60) < 0) {
        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
        return -1;
    }

    if (strcmp(prop_value, "ok") == 0) {
        char dns_prop_name[PROPERTY_KEY_MAX];

        // fill_ip_info函数用于填充传入的参数, 包括ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo, domain, mtu
        if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
                    server, lease, vendorInfo, domain, mtu) == -1) {
            return -1;
        }
        return 0;
    }
}
由上面可以知道WifiStateMachine的当前状态是ObtainingIpState, 而ObtainingIpState不处理CMD_POST_DHCP_ACTION消息,
交由其父类mL2ConnectedState处理:
// -------- frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java ----------- //
class L2ConnectedState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                handlePostDhcpSetup();
                if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
                    if (DBG) log("WifiStateMachine DHCP successful");
                    handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
                    // We advance to mVerifyingLinkState because handleIPv4Success will call
                    // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.      // 发送CMD_IP_CONFIGURATION_SUCCESSFUL
                }
                break;
            case CMD_IP_CONFIGURATION_SUCCESSFUL:
                handleSuccessfulIpConfiguration();
                sendConnectedState();
                transitionTo(mConnectedState);
                break;
        }
    }
}
class ConnectedState extends State {
    public boolean processMessage(Message message) {
        switch (message.what) {
            // 如果信号不好, 则跳转到mVerifyingLinkState状态
            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
                if (DBG) log("Watchdog reports poor link");
                transitionTo(mVerifyingLinkState);
                break;
        }
    }
}
class VerifyingLinkState extends State {
    public void enter() {
        setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
        sendNetworkStateChangeBroadcast(mLastBssid);
        // End roaming
        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
    }

    public boolean processMessage(Message message) {
        switch (message.what) {
            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
                log(getName() + " POOR_LINK_DETECTED: no transition");
                break;
            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
                log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
                sendConnectedState();
                transitionTo(mConnectedState);
                break;
        }
    }
}
在VerifyingLinkState主要是来验证当前连接状况的,主要方式是通过统计信号强度以及丢包率,
这些工作是交给WifiWatchdogStateMachine来做的,当WifiAP的信号强度增强或者变弱,会发送两种消息给WifiStateMachine,
一种是WifiWatchdogStateMachine.GOOD_LINK_DETECTED,另一种是WifiWatchdogStateMachine.POOR_LINK_DETECTED。
当收到GOOD_LINK_DETECTED消息后,就会跳转到mConnectedState中;当收到的是POOR_LINK_DETECTED,则维持原来的状态不变。




猜你喜欢

转载自blog.csdn.net/ballack_linux/article/details/78109789