Android4.0 WIFI分析

来源:互联网 发布:ios专业拍照软件 编辑:程序博客网 时间:2024/05/16 05:24
wifi相关的文件位置 


WIFI Settings应用程序位于 


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


涉及到的文件 


WifiSettings.java 


 


frameworks部分 


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


frameworks/base/wifi/java/android/net/wifi/ 


涉及到的文件有 


SystemServer.java 


WifiManager.java 


WifiService.java 


WifiStateMachine.java 


Wifinative.java 


 


 


JNI部分 


frameworks/base/core/jni/android_net_wifi_Wifi.cpp 


 


wifi管理库。 


hardware/libhardware_legary/wifi/ 


涉及到的文件 


Wifi.c 


 


wifi用户空间的程序和库: 


external/wpa_supplicant_8/ 


生成守护进程wpa_supplicant 


 


 


一Framesworks层相应的类以及处理流程的介绍 


1注册service 


     Android系统起来后SystemServer进程的名字实际上是system_server是由Zygote


进程创建的SystemServer进程创建后会调用SystemServer.java中的main函数main函数


回调SystemServer中的init2函数启动android services,在init2函数中启动一个ServerThread


线程在该线程的run函数中会创建一系列的Android Services,将与网络以及wifi相关的


service在systemserver.java里面的线程systemthread里面注册也就是把网络与wifi相关的


service的实例用ServiceManager.addService(String name, IBinder service)进行注册与wifi相


关的就是wifiservice类相关代码如下 


           try { 


                Slog.i(TAG, "Wi-Fi Service"); 


                wifi = new WifiService(context); 


                ServiceManager.addService(Context.WIFI_SERVICE, wifi); 


            } catch (Throwable e) {                 reportWtf("starting Wi-Fi Service", e); 


            } 


 


            try { 


                Slog.i(TAG, "Connectivity Service"); 


                connectivity = new ConnectivityService( 


                        context, networkManagement, networkStats, networkPolicy); 


                ServiceManager.addService(Context.CONNECTIVITY_SERVICE, 


connectivity); 


                networkStats.bindConnectivityManager(connectivity); 


                networkPolicy.bindConnectivityManager(connectivity); 


                wifi.checkAndStartWifi(); 


                wifiP2p.connectivityServiceReady(); 


            } catch (Throwable e) { 


                reportWtf("starting Connectivity Service", e); 


            } 


上面的代码告诉我们将与网络相关的service实例networkStats与networkPolicy与


connectivityservice相关联所谓相关联我的理解是在networkStats与networkPolicy这


两个实例对应的service类中定义一个ConnectivityService类的父类的实例 


 


2 Wifiservice简介 


     wifiservice是整个框架的核心部分application层触发事件后会调用这个service里


面的方法来处理事件包括加载驱动开启wpa_supplicant扫描AP都是调用这个service


里面的方法实现的实际上这些方法最终调用的是wifistatemachine里面的方法去发送消息。


由wifistatemachine里面的描述wifi事件的内部类去处理这些消息。 


在这个类的构造函数中主要就是实例化了一个描述wifi状态机的类wifistatumachine 重写


了广播事件的onReceive函数。 


 


3Wifistatusmachine简介以及android wifi信息传递机制与流程简介。 


上面已经提到wifiservice中的方法最终是调用这个service里面的方法来实现的主要


就是对sendmessage()的封装。在这个service中需要传递到wpa_supplicant来实现wifi功


能的命令都在这个service中做了定义。针对每一个wifi状态都声明了一个内部类去描


述比如描述加载驱动开启wpa_supplicant等每一个状态类都有三个函数构成enter


statue().processmessage(),只不过不同类的这三个函数里面的程序不同而已。


Wifistatusmachine的构造函数里面主要的工作是实例化了wifi的监视器mWifiMonitor = 


new WifiMonitor(this)这个监视器的作用下面再做解释简单地讲就是通过这个监视器


阻塞监听wpa_supplicant传递上来的信息对信息进行处理后通知上层采取相应的形式去实


现。在这个构造函数中对每一个描述wifi状态的内部类的实例都使用add()函数把这些


内部类的实例添加到状态机中这两项主要工作对应的代码如下 


mWifiMonitor = new WifiMonitor(this); 


addState(mDefaultState); 


            addState(mInitialState, mDefaultState); 


            addState(mDriverUnloadingState, mDefaultState); 


            addState(mDriverUnloadedState, mDefaultState);             addState(mDriverFailedState, mDriverUnloadedState); 


            addState(mDriverLoadingState, mDefaultState); 


            addState(mDriverLoadedState, mDefaultState); 


            addState(mSupplicantStartingState, mDefaultState); 


            addState(mSupplicantStartedState, mDefaultState); 


            addState(mDriverStartingState, mSupplicantStartedState); 


            addState(mDriverStartedState, mSupplicantStartedState); 


            addState(mScanModeState, mDriverStartedState); 


            addState(mConnectModeState, mDriverStartedState); 


            addState(mConnectingState, mConnectModeState); 


            addState(mConnectedState, mConnectModeState); 


            addState(mDisconnectingState, mConnectModeState); 


                        addState(mDisconnectedState, mConnectModeState); 


                        addState(mWaitForWpsCompletionState, 


mConnectModeState); 


                addState(mDriverStoppingState, mSupplicantStartedState); 


                addState(mDriverStoppedState, mSupplicantStartedState); 


            addState(mSupplicantStoppingState, mDefaultState); 


            addState(mSoftApStartingState, mDefaultState); 


            addState(mSoftApStartedState, mDefaultState); 


                addState(mTetheringState, mSoftApStartedState); 


                addState(mTetheredState, mSoftApStartedState); 


            addState(mSoftApStoppingState, mDefaultState); 


            addState(mWaitForP2pDisableState, mDefaultState); 


之后使用setInitialState(mInitialState)使wifi状态机进入初始化状态并通过函数start()开


启状态机。通过把各种wifi状态类的实例添加到wifi状态机以后通过sendmessage(Message 


message)方法发送消息参数message会写进消息队列汇总wifi状态机会根据相应的


message调用相应的wifi状态类里面的函数对消息进行处理。例如wifi驱动的加载。


sendmessage(CMD_LOAD_DRIVER)发送加载驱动的消息到消息队列后会触发wifi状态机


的里面的状态类DriverLoadingState调用里面的方法去加载驱动。 


 


4Wifimonitor 


前面已经简单介绍过这个类主要功能就是阻塞等待wpa_supplicant传上来的命令后根


据命令来进行相应的处理。那么这个监视器是什么时候打开的呢我们来分析它的打开流程。 


在系统起来之后把wifiservice 和connectivity的实例添加到android的service管理器这


前面已经介绍过之后调用wifiservice的方法wifi.checkAndStartWifi();实际上调用了


wifimachine的方法setWifiEnabled(wifiEnabled)在这个方法里面加载驱动开启


wpa_supplicant,对应的代码如下 


            if (enable) { 


            /* Argument is the state that is entered prior to load */ 


            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 


0)); 


            sendMessage(CMD_START_SUPPLICANT); 


        } else {             sendMessage(CMD_STOP_SUPPLICANT); 


            /* Argument is the state that is entered upon success */ 


            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, 


WIFI_STATE_DISABLED, 0)); 


        } 





之后wifi状态机就会进入DriverLoadedState 状态通过native层调用wifi.c开启


wpa_supplicant的函数后就开启wifimonitor,也就是说监视器的开启必须是在驱动加载成功


以及开启wpa_supplicant后才开启相应的代码如下 


    if(WifiNative.startSupplicant()) { 


                        if (DBG) log("Supplicant start successful"); 


                        mWifiMonitor.startMonitoring(); 


                        transitionTo(mSupplicantStartingState); 


                    } else { 


                        loge("Failed to start supplicant!"); 


                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, 


WIFI_STATE_UNKNOWN, 0)); 


                    } 


介绍下wifimonitor是如何阻塞监听wpasupplicant传过来的信息的在监视器开启之后


在监视线层的循环体里面调用wifi.c里面的waitForEvent()函数来监听接收wpa_supplicant


传过来的消息相应的代码如下 


public void run() { 


 


            if (connectToSupplicant()) { 


                // Send a message indicating that it is now possible to send commands 


                // to the supplicant 


                mStateMachine.sendMessage(SUP_CONNECTION_EVENT); 


            } else { 


                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); 


                return; 


            } 


 


            //noinspection InfiniteLoopStatement 


            for (;;) { 


                String eventStr = WifiNative.waitForEvent(); 


根据字符串eventStr的不同值调用相应的处理流程来对事件进行不同的处理上层做出相应


的回应。 


 


5Wifinative 


这一个类里面是调用native层里面的对应函数的接口的集合当我们在frameswork层希望


与下层进行交互调用下层的函数的时候都是先调用这个类里面相应的接口比如开启


wpa_supplicant就是通过WifiNative.startSupplicant()这个方式来调用native层的


android_net_wifi_startSupplicant最终调用wifi.c里面的wifi_start_supplicant()来开启


wpa_supplicanat。  


二JNI层 


   对应的文件是android_net_wifi_WiFi.cpp。这个文件里面是调用wifi.c里面对应函数的接


口的集合提供给frameswork层的类Wifinative里面的函数调用。 


 


三wifi.c文件 


    简单地说这个文件主要是实现驱动的加载和卸载wpa_supplicant的开启。我们公司


为配合skyworth的平台对多个dongle的支持对wifi.c做了部分修改修改部分主要是驱


动的加载部分。 


在本文件中重新定义了一个结构体dongle_info,结构体的成员是三个函数指针针对不


同 的wifi设 备  对每 一 个wifi设 备 都重 写 一 个文 件 文件 路 径 是


hardware\amlogic\wifi\dongle_info 在文件中主要是实 现三个函数分别 是


xxx_loaddriver,xxx_unloaddriver,xxx_search,三个函数同时定义一个存储vid和pid号的数


组这个数组的作用是通过判断插入的usb wifi设备的vid_pid号是否与该表中的某一组


vid_pid号相等来决定是否加载对应的驱动。在wifi.c中定义一个dongle_info 类型的数组


把各个wifi设备的xxx_loaddriver,xxx_unloaddriver,xxx_search赋值给数组成员。本文件主要


有以下几个函数比较重要重点介绍下分为三个部分 


驱动的加载卸载 


wifi_load_driver()驱动的加载。我们的wifi驱动都是编译成ko文件wifi.h开头定义


了ko文件储存的位置#define WIFI_DRIVER_MODULE_PATH         "/system/lib" 。这个


函数会被上层调用驱动加载的原理就是通过判断插入usb wifi设备的vid_pid号是否与相


对应的vid_pid表的某一组vid_pid号相等来决定是否加载对应的驱动如果有一组相等


则加载驱动成功。 


wpa_unload_driver() 


Wpa_supplicant相关 


wifi_start_supplicant()上层要开启wpa_supplicant直接调用的是这个函数这个函数


实际上是调用wifi_start_supplicant_common(const char *config_file) 


wifi_start_supplicant_common(const char *config_file):这个函数实际上为wifi.c进程与


wpa_supplicant进程相连做一些准备工作。 


wifi_connect_to_supplicant()wifi.c进程与wpa_supplicant进程进行通信他们之间采


用socket的方式来实现进程间通信计算机网络的客户端与服务器的用socket通信的实现


方法相信每一个人都很熟悉进程间用socket来进行通信的实现方法也是类似包括创建


套接口绑定连接具体就不详细介绍了。函数调用的是wpa_ctrl.c里面的


wpa_ctrl_open(const char *ctrl_path)函数来实现。 


 


数据的传输 


wifi_command(const char *command, char *reply, size_t *reply_len)这个函数被jni调用


主要是上层发送命令通过这个函数传递给wpa_supplicant。这个函数实际上是调用


wifi_send_command(ctrl_conn, command, reply, reply_len)函数。 


wifi_send_command(struct wpa_ctrl *ctrl, const char *cmd, char *reply, size_t *reply_len)


发送上层传递过来的命令给wpa_supplicant 


wifi_wait_for_event(char *buf, size_t buflen)上层调用这个函数阻塞等待wpa_supplicant


传递上来的信息主要是调用wifi_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)


来实现。 wifi_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)调用wpa_ctrl.c中的


wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)来接收wpa_supplicant传递过


来的信息。 


 


四wpasupplicant层 


     wpa_supplicant本是开源项目源码被谷歌修改后加入android平台它主要是用来支


持WEPWPA/WPA2和WAPI无线协议和加密认证的而实际上的工作内容是通过socket


不管是wpa_supplicant与上层还是wpa_supplicant与驱动都采用socket通讯与驱动交互


上报数据给用户而用户可以通过socket发送命令给wpa_supplicant调动驱动来对WiFi芯


片操作。 简单的说wpa_supplicant就是WiFi驱动和用户的中转站外加对协议和加密认证


的支持。 


 这里主要介绍的是wpa_supplicant与驱动接口的联系。在drivers.h中定义了一个


wpa_driver_ops结构体结构体成员是一个个函数指针这些指针在哪里赋值呢接着往下


看在driviers.c里面都是不同驱动操作接口的集合wpa_driver_XXX_ops变量然后就是定


义一个驱动操作接口集合的数组根据宏定义添加对应的驱动操作接口集合的变量。不同的


驱动接口采用不同的文件来实现如果wpa_supplicant使用的是wext接口与驱动进行通信


那么就在driver_wext.c文件里面对wpa_driver_ops结构体里面的成员赋值这些成员指


针指向的函数也在这个文件里面实现。代码如下 


const struct wpa_driver_ops wpa_driver_wext_ops = { 


 .name = "wext", 


 .desc = "Linux wireless extensions (generic)", 


 .get_bssid = wpa_driver_wext_get_bssid, 


 .get_ssid = wpa_driver_wext_get_ssid, 


 .set_key = wpa_driver_wext_set_key, 


 .set_countermeasures = wpa_driver_wext_set_countermeasures, 


 .scan2 = wpa_driver_wext_scan, 


 .get_scan_results2 = wpa_driver_wext_get_scan_results, 


 .deauthenticate = wpa_driver_wext_deauthenticate, 


 .disassociate = wpa_driver_wext_disassociate, 


 .associate = wpa_driver_wext_associate, 


 .init = wpa_driver_wext_init, 


 .deinit = wpa_driver_wext_deinit, 


 .add_pmkid = wpa_driver_wext_add_pmkid, 


 .remove_pmkid = wpa_driver_wext_remove_pmkid, 


 .flush_pmkid = wpa_driver_wext_flush_pmkid, 


 .get_capa = wpa_driver_wext_get_capa, 


 .set_operstate = wpa_driver_wext_set_operstate, 


 .get_radio_name = wext_get_radio_name, 


#ifdef ANDROID 


 .signal_poll = wpa_driver_signal_poll, 


 .driver_cmd = wpa_driver_wext_driver_cmd, 


#endif 


}; 


那么还有一个疑问wpa_supplicant怎么知道使用哪个驱动接口呢在wpa_supplicant中 的 在wpa_supplicant_init_iface(struct wpa_supplicant 


*wpa_s,struct wpa_interface *iface)函数里面 


driver = iface->driver; 


之后调用wpa_supplicant_set_driver(wpa_s, driver)来设置wpa_supplicant使用哪个接


口与驱动进行通信。 


     wpa_supplicant是为不同驱动和操作系统具有更好移植性而被设计的以便


在wpa_supplicant层不用实现驱动的具体接口就可以添加新的驱动程序在


wpa_supplicant结构中有一个wpa_drv_ops类型的drvier成员在wpa_supplicant


进程中经常通过Wpa_supplicant_XXX函数传递wpa_supplicant实例指针wpa_s


参数给wpa_drv_XXX函数来调用它在wpa_drv_XX中会通过


wpa_s->driver->XXX()的流程来调用通用驱动接口。 


 


 


 


 


 


    


 


 


 


 
原创粉丝点击