Android-wifi学习2---android4.0 wifi 框架

来源:互联网 发布:怎么算包皮正常知乎 编辑:程序博客网 时间:2024/06/14 17:51

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实例networkStatsnetworkPolicyconnectivityservice相关联,所谓相关联,我的理解是,在networkStatsnetworkPolicy这两个实例对应的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的实例添加到androidservice管理器,这前面已经介绍过,之后,调用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,三个函数,同时,定义一个存储vidpid号的数组,这个数组的作用是,通过判断插入的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平台,它主要是用来支持WEPWPA/WPA2WAPI无线协议和加密认证的,而实际上的工作内容是通过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()的流程来调用通用驱动接口。

   

原创粉丝点击