zoukankan      html  css  js  c++  java
  • wifi连接流程分析

    Wifi 连接部分

    http://blog.csdn.net/typename/article/details/5879121

    当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的 onPreferenceTreeClick会被调用          @Override

        public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

             //点击AP响应函数

            if (preference instanceof AccessPoint) {

                mSelected = (AccessPoint) preference;

                showDialog(mSelected, false);

            } else if (preference == mAddNetwork) {

                mSelected = null;

                showDialog(null, true);

            } else if (preference == mNotifyOpenNetworks) {

                Secure.putInt(getContentResolver(),

                        Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,

                        mNotifyOpenNetworks.isChecked() ? 1 : 0);

            } else {

                return super.onPreferenceTreeClick(screen, preference);

            }

            return true;

        }

    用户配置好之后点击连接按钮,onClick函数会被调用。

    public void onClick(DialogInterface dialogInterface, int button) {

              //点击连接按钮的响应函数

            if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {

                forget(mSelected.networkId);

            } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) {

                WifiConfiguration config = mDialog.getConfig();

                if (config == null) {

                    if (mSelected != null && !requireKeyStore(mSelected.getConfig())) {

                        connect(mSelected.networkId);

                    }

                } else if (config.networkId != -1) {

                    if (mSelected != null) {

                        mWifiManager.updateNetwork(config);

                        saveNetworks();

                    }

                } else {

                    int networkId = mWifiManager.addNetwork(config);

                    if (networkId != -1) {

                        mWifiManager.enableNetwork(networkId, false);

                        config.networkId = networkId;

                        if (mDialog.edit || requireKeyStore(config)) {

                            saveNetworks();

                        } else {

                            connect(networkId);

                        }

                    }

                }

            }

    连接请求部分

    一.Settings的connect函数响应连接,更新网络保存配置,更新设置当前选择的优先级最高,并

        保存。然后通过enableNetwork使得其他网络不可用来进行连接。最后调用WifiManager的

          reconnect函数连接当前选择的网络。

    二.WifiManager的reconnect函数通过AIDL的Binder机制,调用WifiService的reconnect函数

    三.然后会调用 WifiStateTracker的reconnectCommand函数,通过JNI(android_net_wifi_Wifi)的             

          android_net_wifi_reconnectCommand 函数向WPA_WPASUPPLICANT发送 RECONNECT命令。

    四. android_net_wifi_Wifi通过 doCommand(命令名,响应缓冲,响应缓存大小)调用wifi.c中的

          wifi_command函数来发送命令。

    五.最后通过 wpa_ctrl的wpa_ctrl_request函数向控制通道发送连接命令。

    返回请求部分

    六.当连接上之后WPA_SUPPLICANT会向控制通道发送连接成功命令。wifi.c的

          wifi_wait_for_event函数阻塞调用并返回这个命令的字符串(CONNECTED).

    七.而后WifiMonitor会被执行来处理这个事件,WifiMonitor 再调用 WifiStateTracker的

         notifyStateChange,WifiStateTracker 则接着会往自身发送 EVENT_DHCP_START 消息来启动

        DHCP 去获取 IP 地址,然后广播NETWORK_STATE_CHANGED_ACTION消息,最后由

        WifiSettings类来响应,改变状态和界面信息。

    关键函数功能介绍

    一.connect函数功能

      1.updateNetwork:updateNetwork(config)会将当前选择连接的AP配置信息

       信息传递进去,配置信息有(网络ID等)。如果网络ID为-1则重新添加网络配置,然后向

       wpa_supplicant 发送SET_NETWORK命令(即通过这个网络ID设置其他一些相关信息,设置

       SSID,密码等)如果网络配置不为-1则直接执行后面步骤即发送SET_NETWORK命令。

      2.saveNetwork:告诉supplicant保存当前网络配置并更新列表。SaveNetwork会调用WifiService的

         saveConfiguration向wpa_supplicant发送SAVE_CONFIG命令保存当前网络配置信息,

        如果返回false,则向wpa_supplicant重新发送RECONFIGURE命令获取配置信息,如果获取信

        息成功后,会Intent一个  NETWORK_IDS_CHANGED_ACTION事件WifiSettings会注册接受

        这个 时间并更新列表。

      3.enableNetwork函数,向系统获取接口名并使得该接口有效。由于之前传递的disableOthers

        为true则向wpa_supplicant发送SELECT_NETWORK(如果传递的为false则发送                

         ENABLE_NETWORK命令),

      4.reconnect函数:连接AP

    二.reconnect函数功能:connect函数会调用WifiManager的reconnect然后通过Binder机制调用

       WifiService的reconnect,再由WifiStateTracke调用WifiNative向wpa_supplicant发送

       RECONNECT命令去连接网络,当连接上wpa_supplicant之后会向控制通道发送连接成功的命  

       令,

       wifi_wait_for_event函数阻塞等待该事件的发生,并返回这个命令的字符串(CONNECTED)

      

    三.android_net_wifi_Wifi函数的doCommand函数会调用wifi.c的wifi_command函数将上层的命

       令向wpa_supplicant发送。

      

    四.wifi_wait_for_event函数以阻塞的方式,等待控制通道传递的事件。当有事件传递过来的时候

       该函数会通过wpa_ctrl的wpa_ctrl_recv函数读取该事件,并以字符串形式返回该事件名。

       int wifi_wait_for_event(char *buf, size_t buflen)

    {

                           .......

        result = wpa_ctrl_recv(monitor_conn, buf, &nread);

        if (result < 0) {

            LOGD("wpa_ctrl_recv failed: %s/n", strerror(errno));

            strncpy(buf, WPA_EVENT_TERMINATING " - recv error", buflen-1);

            buf[buflen-1] = '/0';

            return strlen(buf);

        }

        buf[nread] = '/0';

        /* LOGD("wait_for_event: result=%d nread=%d string=/"%s/"/n", result, nread, buf); */

        /* Check for EOF on the socket */

        if (result == 0 && nread == 0) {

            /* Fabricate an event to pass up */

            LOGD("Received EOF on supplicant socket/n");

            strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1);

            buf[buflen-1] = '/0';

            return strlen(buf);

        }

        /*

         * Events strings are in the format

         *

         *     <N>CTRL-EVENT-XXX

         *

         * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,

         * etc.) and XXX is the event name. The level information is not useful

         * to us, so strip it off.

         */

        if (buf[0] == '<') {

            char *match = strchr(buf, '>');

            if (match != NULL) {

                nread -= (match+1-buf);

                memmove(buf, match+1, nread+1);

            }

        }

        return nread;

    }

    五.wpa_ctrl_request,通过socket方式向wpa_supplicant发送命令,以select模式阻塞在

       wpa_supplicant发送和接收。

    int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,char *reply, size_t *reply_len,void (*msg_cb)(char *msg, size_t len))

    {

             .......

                       res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);

                       if (FD_ISSET(ctrl->s, &rfds)) {

                                res = recv(ctrl->s, reply, *reply_len, 0);

                                if (res < 0)

                                         return res;

                                if (res > 0 && reply[0] == '<') {

                                         /* This is an unsolicited message from

                                          * wpa_supplicant, not the reply to the

                                          * request. Use msg_cb to report this to the

                                          * caller. */

                                         if (msg_cb) {

                                                   /* Make sure the message is nul

                                                    * terminated. */

                                                   if ((size_t) res == *reply_len)

                                                            res = (*reply_len) - 1;

                                                   reply[res] = '/0';

                                                   msg_cb(reply, res);

                                         }

                                         continue;

                                }

                                *reply_len = res;

                                break;

                       } else {

                                return -2;

                       }

             }

             return 0;

    }

    六.WifiMonitor 维护一个监视线程分发处理底层返回上来的事件

     void handleEvent(int event, String remainder) {

                switch (event) {

                    case DISCONNECTED:

                        handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);

                        break;

                    case CONNECTED:

                        handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);

                        break;

                    case SCAN_RESULTS:

                        mWifiStateTracker.notifyScanResultsAvailable();

                        break;

                    case UNKNOWN:

                        break;

                }

            }

    此时返回的事件是CONNECTED因此 handleNetworkStateChange会被调用,验证一下BSSID,重新获得networkId

    ,然后调用WifiStateTracke的notifyStateChange通知状态改变了的消息(EVENT_NETWORK_STATE_CHANGED

    接着处理这个消息,会移除可用网络通告,然后通过 configureInterface()的动态获取IP地址。最后

    发送一个NETWORK_STATE_CHANGED_ACTION Intent,WifiSetings注册了此Intent因此会响应该它。由updateConnectionState函数响应。

    七.updateConnectionState 获取连接信息,更新列表状态,设置为Connected,然后设置当前网络为可用状态

      

       private void updateConnectionState(DetailedState state) {

            /* sticky broadcasts can call this when wifi is disabled */

            if (!mWifiManager.isWifiEnabled()) {

                mScanner.pause();

                return;

            }

            if (state == DetailedState.OBTAINING_IPADDR) {

                mScanner.pause();

            } else {

                mScanner.resume();

            }

            mLastInfo = mWifiManager.getConnectionInfo();

            if (state != null) {

                mLastState = state;

            }

            for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {

                ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState);

            }

            if (mResetNetworks && (state == DetailedState.CONNECTED ||

                    state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) {

                updateAccessPoints();

                enableNetworks();

            }

        }

     

     流程图对应的源代码路径为:

     WifiEnabler,WifiSettings对应的路径如下:

     froyo/packages/apps/Settings/src/com/android/settings/

    WifiManager,WifiMonitor,WifiStateTracker,WifiNative.对应的源代码路径如下:

    froyo/frameworrks/base/wifi/java/android/net/wifi/

    WifiService 对应代码的位置

    froyo/frameworks/base/services/java/com/android/server/

    android_net_wifi_Wifi源代码路径如下:

    froyo/frameworks/base/core/jni/

    wifi_command,wifi_wait_for_envent源代码路径如下:

    /hardware/libhardware_legacy/wifi/wifi.c

    wpa_ctrl_源代码路径如下:

    /external/wpa_supplicant/wpa_ctrl.c

    wpa_supplicant源代码路径如下:

    froyo/external/wpa_supplicant/


  • 相关阅读:
    杂谈-论时间成本
    java笔记:排错5:误删maven target:恢复不了,怎么再生成
    十大经典排序(转发帖)
    java笔记(Idea,Maven):误删maven项目的target的class,怎么再生成target
    JDK解压版制作
    日常报错记录4:ssh工程复制粘贴顺序。
    欧朋Opera 浏览器(打不开百度)提示“您的连接不是私密连接”,解决办法
    日常报错记录2: MyBatis:DEBUG [main]
    JavaWeb-SQL-Servlet-JSP学做购物系统——日志一
    git 命令
  • 原文地址:https://www.cnblogs.com/yuzaipiaofei/p/4124584.html
Copyright © 2011-2022 走看看