技术转载:Android Wifi详解
最近研究Wifi模块,查了不少的相关资料,但发现基本上是基于android2.0版本的的分析,而现在研发的android移动平台基本上都是2.3的版本,跟2.0版本的差别,在Wifi模块上也是显而易见的。2.3版本Wifi模块没有了WifiLayer,之前的WifiLayer主要负责一些复杂的Wifi功能,如AP选择等以提供给用户自定义,而新的版本里面的这块内容基本上被WifiSettings所代替。
本文就是基于android2.3版本的Wifi分析,主要分为两部分来分别说明:
(1) Wifi模块相关文件的解析
(2) Wpa_supplicant解析
(3) Wifi的启动流程(有代码供参考分析)
一,Wifi模块相关文件解析
1) wifisettings.java
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
该类数据部分主要定义了下面几个类的变量:
wifiSettings类的构造函数的主要工作:定义了一个IntentFilter(Intent过滤器)变量,并添加了六个动作,(了解Android的intent机制的同学都知道什么意思,不明白的同学参考Intent机制的资料)接着定义一个广播接收器,并有相应的消息处理函数,下面是该构造函数的定义:
在广播接收器中的相应函数onReceive函数中有个handleEvent函数,它就是用来处理广播接收器接受到的intent消息的,它的功能是根据intent消息中的动作类型,来执行相应的操作,每一种动作对应了activity的一项消息处理能力。
在oncreate函数中实例化了mWifiManager和mWifiEnabler两个类,这两个类对wifiSettings来说至关重要,它后面的定义的一系列函数都是通过调用这两个类的相应接口来实现的。
WifiSettings中还定义了显示菜单和响应菜单键的函数,即onCreateOptionsMenu()和onOptionsItemSelected();还有响应配置对话框中按键的onClick()函数;最后定义了Scanner类,它是一个handler的继承类,实现了消息处理函数,用于处理手动扫描的动作。
2) WifiEnabler.java:
packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
wifienabler类中定义了四个成员变量很重要,mContext,mCheckBox,mWifiManager和mReceiver,其中mContext用于获取mwifiManager实例,mReceiver用来接收底层发来的消息,mCheckBox用来改变UI的状态。
该类中定义了几个重要的函数onPreferenceChange,handleWifiStateChanged和handleStateChanged,onPreferenceChange用来处理按下的Enbler键,它会调用mWifiManager.setWifiEnabled(enable),另外两个用来处理接受的消息事件。
在类的构造函数中,主要做了一下工作:初始化了mContext,mCheckBox,mWifimanager,并且初始化了一个mIntentFilter变量,添加了三个动作,在构造函数的上面定义了一个广播接收器,用来接收下层传来的消息,并根据intent动作的类型调用相应的处理函数,这个广播接收器在onResum函数中被注册。
这里可以总结为:如果上层需要监听或收到下层的消息,那么就要通过定义一个BroadcastReciever,并将它注册,当然在接受到消息后应该有处理消息的函数,然后在onReciever函数中根据消息调用相应的处理函数,这里的消息通知机制是Intent,在BroadcastReciever类的onReciever函数的参数中可以看出。
该类成员函数的也是通过调用mWifimanager的接口来实现的。
3) WifiManager:
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
两个重要的数据成员:
//WifiService
IWifiManager mService;
HandlermHandler;
IWifiManager mService和HandlermHandler,这个类拥有了一个WifiService实例,就可以通过它进行一系列的调用;WifiManager中定义了的wifi和ap的状态,这些状态会在其他很多类中有使用;然后定义了大量的函数,这些函数几乎都是对WifiService接口函数的封装,直接调用WifiService的函数。
该类的构造函数很简单:
该类中还定义了一个WifiLock类,这个类用来保证在有应用程序使用Wifi无线电传输数据时,wifiradio可用,即当一个应用程序使用wifi的radio进行无线电数据传输时,就要先获得这个锁,如果该锁已被其他程序占有,就要等到该锁被释放后才能获得,只用当所有持有该锁的程序都释放该锁后,才能关闭radio功能。
4)WifiService:
frameworks/base/services/java/com/android/server/WifiService.java
这是WifiService中的几个重要的数据成员。
在接下来的构造函数中初始化了mWifiStateTracker,mContext,然后动态生成mWifiThread子线程并启动,在主线程里用mWifiThread调用getLooper()函数获得线程的looper,来初始化创建一个mWifiHandler对象,这个WifiHandler在WifiService类的后面有定义,并重载了Handler类的handlermessage()函数,这样消息就可以在主线程里被处理了,这是android的handlerthread消息处理机制,可参考相关资料,这里不予详述。在构造函数的最后,注册了两个广播接收器,分别用来ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED这两个动作,这里是android的intent消息通知机制,请参考相关资料,代码如下:
随后定义了一系列的函数,其中有服务器要发送的命令的系列函数,它通过mWifiStateTracker成员类调用自己的的发送命令的接口(其实就是对本地接口的一个封装),最后通过适配层发送命令给wpa_supplicant,而事件处理只到WifiStateTracker层被处理。
要注意的是,在WifiService中,定义了一些函数来创建消息,并通过mWifiHandler将消息发送到消息队列上,然后在mHandlerThread线程体run()分发\处理消息,在主线程中被mWifiHandler的handlerMessage()函数处理,最后调用mWifiStateTracker的对应函数来实现的。这里我也不明白为什么WifiService不直接调用mWifiStateTracker对应的函数,还要通过消息处理机制,绕了一圈在调用,当然Google这么做肯定是有它道理的,忘高手指点。
5) WifiStateTracker类:
frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
NetworkStateTracker继承了handler类,而WifiStateTracker继承了NetworkStateTracker类,就是说WifiStateTracker间接继承了handler类,属于一个事件处理类。
WifiStateTracker类首先定义了事件日志和事件码(这里包含了所有可能的事件类型),还定义了如下的重要成员数据:
类的构造函数中,初始化了系列成员变量,包括生成了WifiMonitor的实例,在构造函数中,因为WifiStateTracker是一个handler间接子类,所以他会自动调用handler的无参构造函数,获得looper和Queue消息队列。
然后定义了一些设置supplicant和更新网络信息的辅助函数。
startEventLoop()函数很重要,用来启动WifiMonitor线程,进入消息循环检测。接着定义了系列通知函数,被WifiMonitor调用来向WifiStateTracker传递从wpa_supplicant接收到的消息,他会调用消息发送函数到消息队列,并被WifiStateTracker的handlermessage()函数处理。这个handlermessage()函数就是在随后被定义的,它主要是调用相应的辅助函数完成动作,并可能会将消息封装后,通过intent机制发送出去,被上层的UI活动接收处理。
这里也定义了很多的WfiNative接口函数,这是JNI的本地接口;类DhcpHandler extends Handler{}也是在该类中定义的,它也是一个handler的子类,用来处理DHCP相关的消息EVENT_DHCP_START,可以想到它和WifiStateTracker不是共用一个looper。
注意:handleMessage是在该文件中定义的,用来处理经WifiMonitor转换过的消息。
6) WifiMonitor
frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
声明了一个重要的成员变量:mWifiStateTracker,并在构造函数中由参数提供初始化,还定义了一系列的可能从wpa_supplicant层接收的事件类型及其名字,这些是消息处理机制的基础。
startMonitoring()函数,这是一个线程启动的封装函数,WifiStateTracker就是通过这个函数启动的WifiThread。
这个重要的类classMonitorThreadextends Thread{};它是一个监控进程类,里面有一系列的事件处理函数和一个重要的Run()函数,run函数主要流程:connectToSupplicant()连接精灵进程wpa_supplicant,这里有一个mWifiStateTracker.notifySupplicantXXX()的调用,通知上层是否连接成功,然后就是一个轮询过程,其中调用了WifiNative.waitForEvent()本地轮询函数接口,并从返回的事件字符串类型中提取事件的名称,最后通过事件的名称调用相应的事件处理函数,并将事件转换成mWifiStateTracker能识别的类型上报。
注意:这里的每个事件的捕获(由wifimonitor完成),最终都会调用相应的mWifiStateTracker的消息通知函数上报消息;轮询和事件处理都是在Monitor线程类中实现的。
7)WifiNative
frameworks/base/wifi/java/android/net/wifi/WifiNative.java
里面定义了一个类WifiNative:其中声明了许多本地接口,可由native的标志看出,这是Java代码和本地库之间的联系接口;
8) android_net_wifi_Wifi.java
frameworks/base/core/jni/
里面定义了许多的JNI的本地代码实现,每个实现中都会调用wpa_supplicant适配层的接口,通过包含适配层的头文件wifi.h获取适配层定义的接口;后面是一个JNINativeMethod数组,定义了每个本地接口和本地实现的对应关系;最后有一个注册函数regester_XXX_XX(),用以把上面的方法类数组注册到系统中。
该类实现了本地接口的相关功能,并通过调用wpa的适配层的相关函数和wpa_supplicant通信,所以JNI是连接Java框架层和wpa适配层的桥梁.
9)wpa_supplicant适配层,wifi.c:目录libhardware/wifi/
里面定义很多字符串变量和适配层的接口实现,是对wpa_supplicant程序通信的接口封装,用来完成上层和wpa_supplicant的通信,头文件在libhardware/include/hardware下,这里的函数用来向JNI的本地实现提供调用接口。
这里的函数,我把它们分为三类函数:
一类是命令相关的(控制)函数,就是在JNI层android_XXX_Command()函数所调用的::Wifi_Command()函数,调用流程:android_XXX_command()=>docommand()=>wifi_command()=>wifi_send_command()=>wpa_ctrl_require()。
二类是监听函数,即Wifi_wait_for_event()函数,调用流程:android_net_wifi_Waitforevent()=>wifi_wait_for_event()=>wpa_ctrl_recv()。
三类是剩下的函数。
10)wpa_supplicant与上层的接口,wpa_ctrl.c:external/wpa_supplicant
定义了三类套接字,并分别实现了和wpa_supplicant的通信,因此wpa_supplicant适配层和wpa_supplicant层是通过socket通讯的。
要是从wifi.c中真的很难看出它和wpa_supplicant有什么关系,和它联系密切的是wpa_ctrl.h文件,这里面定义了一个类wpa_ctrl,这个类中声明了两个Socket套接口,一个是本地一个是要连接的套接口,wpa_ctrl与wpa_supplicant的通信就需要socket来帮忙了,而wpa_supplicant就是通过调用wpa_ctrl.h中定义的函数和wpa_supplicant进行通讯的,wpa_ctrl类(其实是其中的两个socket)就是他们之间的桥梁。
11)wpa_supplicant和driver_wext驱动接口的联系:
driver.h:该文件定义了系列结构,首先是一个wpa_scan_result结构,这是一个扫描结果的通用格式,里面包含了扫描的所有信息(如BSSID,SSID,信号质量,噪音水平,支持的最大波特率等等信息),每个驱动接口实现负责将从驱动中上传的扫描信息的格式转换到该通用的扫描信息格式;然后是一些宏定义和枚举定义,最后也是最重要的是wpa_driver_ops结构,该结构是wpa driver的操作函数集合,里面有驱动接口的名称和很多的函数指针。
drviers.c:该文件很简单,首先是一些外部变量的引用声明,都是不同驱动操作接口的集合wpa_driver_XXX_ops变量;然后就是定义一个驱动操作接口集合的数组,根据宏定义添加对应的驱动操作接口集合的变量。
drvier_XXX.h:这是不同驱动接口头文件,主要声明了操作接口
drvier_XXX.c:实现操作接口,定义一个操作集合变量,并用上面定义的函数初始化其中的函数指针
注意:下面要搞清楚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()的流程来调用通用驱动接口,简单才是生活的真相,可android始终让我感到真相还是遥不可及。
12)WifiWatchdogService:
首先声明了两个主要变量mWifiStateTracker,mWifiManager,需要这两个类对象来完成具体的控制工作,在WifiWatchdogService的构造函数中,创建了这两个类,并通过regesterForWifiBroadcasts()注册了BroadcastReceiver,BroadcastReceiver是用来获取网络变化的,然后启动了一个WatchdogTread线程,用来处理从WifiStateTracker接收到的消息。
frameworks/base/services/java/com/android/server/WifiWatchdogService.java
WifiWatchdogHandler继承了handler类,成为一个消息处理类,定义handlemessage()函数,处理消息队列上的消息。
二,wpa_supplicant再解析
1)wpa_ctrl.h:
该文件中并没有定义structwpa_ctrl结构,因为在其他包含该文件的.c文件中不能直接使用该结构的成员,这里主要声明了几个用于使用socket通信的函数接口,包括:wpa_ctrl_open,wpa_ctrl_close,wpa_ctrl_attach,wpa_ctrl_detach,wpa_ctrl_cleanup,wpa_ctrl_recv,wpa_ctrl_request, wpa_ctrl_pending, wpa_ctrl_get_fd 等函数。
这些函数的功能从名字上能看出,open函数就是创建一个socket接口,并绑定连接wpa_supplicant,attach函数用于定义一个控制接口为监听接口,pending函数用于查询有无消息可读处理,request和recv分别用来发送和读取消息。
其实,这里就是一个使用socket通信的封装,具体内容可以参考socket编程。
2)wpa_ctrl.c:
这里首先定义了一个wpa_ctrl结构,里面根据UDP,UNIX和命名管道三种domain类型来定义通信实体:
然后是根据上面三个类型分别实现了wpa_ctrl.h中声明的接口函数,这里就不做介绍了。
3)wpa_supplicant.h:
首先,定义了一个枚举wpa_event_type,罗列了系列wpa的事件类型,然后就是wpa_event_data类型,随后是两个函数:wpa_supplicant_event和wpa_supplicant_rx_eapol。
wpa_supplicant.c:首先定义一个驱动操作数组externstruct wpa_driver_ops *wpa_supplicant_drivers[],然后是系列wpa_supplicant_XXX()函数,很多函数里面调用wpa_drv_XXX()函数,这些函数是wpa_supplicant_i.h中实现的函数。几乎每个函数都需要一个wpa_supplicant结构,对其进行所有的控制和通信操作。
4)wpa_supplicant_i.h:
开头定义了几个结构,如:wpa_blacklist,wpa_interface,wpa_params,wpa_global,wpa_client_mlme和wpa_supplicant等结构,其中wpa_supplicant最为重要,wpa_supplicant结构里有一个重要的driver成员,它是wpa_driver_ops类型,可以被用来调用抽象层的接口。
接下来是系列函数声明,这些函数声明在wpa_supplicant.c中实现,然后就是wpa_drv_XXX函数,这些函数就是在wpa_supplicant.c中被wpa_supplicant_xxx函数调用的,而这些wpa_drv_xxx函数也都有一个wpa_supplicant结构的变量指针,用来调用封装的抽象接口。
这里要注意的是:在wpa_suppliant.c文件中定义的很多函数是在该头文件中声明的,而不是在wpa_supplicant.h中声明的。
5)driver.h:
该文件中定义了一个重要的数据结构:wpa_scan_result,这是一个从驱动发来的数据被封装成的通用的扫描结果数据结构,每个驱动结构的实现都要遵循的扫描结果格式,比如driver_wext.c中的实现。后面还有定义了很多的数据结构,这里不具表。
文件中最重要的一个数据结构是:wpa_driver_ops,这是所有驱动接口层必须为之实现的API,是所有驱动类型的一个接口封装包,wpa_supplicant就是通过该接口来和驱动交互的。
6)driver_wext.h:
该文件很简单,就是声明了该驱动的一些对应驱动API接口的函数。
driver_wext.c:
此文件就是实现了上面的一些函数,最后初始化了一个wpa_drv_ops变量。
三,Wifi模块解析
1)框架分析
图示1:Wifi框架
首先,用户程序使用WifiManager类来管理Wifi模块,它能够获得Wifi模块的状态,配置和控制Wifi模块,而所有这些操作都要依赖Wifiservice类来实现。
WifiService和WifiMonitor类是Wifi框架的核心,如图所示。下面先来看看WifiService是什么时候,怎么被创建和初始化的。
在systemServer启动之后,它会创建一个ConnectivityServer对象,这个对象的构造函数会创建一个WifiService的实例,代码如下所示:
framework/base/services/java/com/android/server/ConnectivityService.java
WifiService的主要工作:WifiMonitor和Wpa_supplicant的启动和关闭,向Wpa_supplicant发送命令。
WifiMonitor的主要工作:阻塞监听并接收来自Wpa_supplicant的消息,然后发送给WifiStateTracker。
上面两个线程通过AF_UNIX套接字和Wpa_supplicant通信,在通信过程中有两种连接方式:控制连接和监听连接。它们创建代码如下:
2)Wifi启动流程
(1)使能Wifi
要想使用Wifi模块,必须首先使能Wifi,当你第一次按下Wifi使能按钮时,WirelessSettings会实例化一个WifiEnabler对象,实例化代码如下:
packages/apps/settings/src/com/android/settings/WirelessSettings.java
WifiEnabler类的定义大致如下,它实现了一个监听接口,当WifiEnabler对象被初始化后,它监听到你按键的动作,会调用响应函数onPreferenceChange(),这个函数会调用WifiManager的setWifiEnabled()函数。
我们都知道Wifimanager只是个服务代理,所以它会调用WifiService的setWifiEnabled()函数,而这个函数会调用sendEnableMessage()函数,了解android消息处理机制的都知道,这个函数最终会给自己发送一个MESSAGE_ENABLE_WIFI的消息,被WifiService里面定义的handlermessage()函数处理,会调用setWifiEnabledBlocking()函数。下面是调用流程:
mWifiEnabler.onpreferencechange()=>mWifiManage.setWifienabled()=>mWifiService.setWifiEnabled()=>mWifiService.sendEnableMessage()=>mWifiService.handleMessage()=>mWifiService.setWifiEnabledBlocking().
在setWifiEnabledBlocking()函数中主要做如下工作:加载Wifi驱动,启动wpa_supplicant,注册广播接收器,启动WifiThread监听线程。代码如下:
至此,Wifi使能结束,自动进入扫描阶段。
(2) 扫描AP
当驱动加载成功后,如果配置文件的AP_SCAN = 1,扫描会自动开始,WifiMonitor将会从supplicant收到一个消息EVENT_DRIVER_STATE_CHANGED,调用handleDriverEvent(),然后调用mWifiStateTracker.notifyDriverStarted(),该函数向消息队列添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函数处理消息时调用scan()函数,并通过WifiNative将扫描命令发送到wpa_supplicant。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
上面是启动Wifi时,自动进行的AP的扫描,用户当然也可以手动扫描AP,这部分实现在WifiService里面,WifiService通过startScan()接口函数发送扫描命令到supplicant。
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
然后下面的流程同上面的自动扫描,我们来分析一下手动扫描从哪里开始的。我们应该知道手动扫描是通过菜单键的扫描键来响应的,而响应该动作的应该是WifiSettings类中Scanner类的handlerMessage()函数,它调用WifiManager的startScanActive(),这才调用WifiService的startScan()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
当按下菜单键时,WifiSettings就会调用这个函数绘制菜单。如果选择扫描按钮,WifiSettings会调用onOptionsItemSelected()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
这里的mWifiManager.startScanActive()就会调用WifiService里的startScan()函数,下面的流程和上面的一样,这里不赘述。
当supplicant完成了这个扫描命令后,它会发送一个消息给上层,提醒他们扫描已经完成,WifiMonitor会接收到这消息,然后再发送给WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
WifiStateTracker将会广播SCAN_RESULTS_AVAILABLE_ACTION消息:
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
由于WifiSettings类注册了intent,能够处理SCAN_RESULTS_AVAILABLE_ACTION消息,它会调用handleEvent(),调用流程如下所示。
WifiSettings.handleEvent() =>WifiSettings.updateAccessPoints() => mWifiManager.getScanResults() => mService.getScanResults()=> mWifiStateTracker.scanResults() => WifiNative.scanResultsCommand()……
将获取AP列表的命令发送到supplicant,然后supplicant通过Socket发送扫描结果,由上层接收并显示。这和前面的消息获取流程基本相同。
(3)配置,连接AP
当用户选择一个活跃的AP时,WifiSettings响应打开一个对话框来配置AP,比如加密方法和连接AP的验证模式。配置好AP后,WifiService添加或更新网络连接到特定的AP。
packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java
配置好以后,当按下“Connect Press”时,WifiSettings通过发送LIST_NETWORK命令到supplicant来检查该网络是否配置。如果没有该网络或没有配置它,WifiService调用addorUpdateNetwork()函数来添加或更新网络,然后发送命令给supplicant,连接到这个网络。下面是从响应连接按钮到WifiService发送连接命令的代码:
packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java
Frameworks\base\wifi\java\android\net\wifi\WifiManager.java
WifiService.addOrUpdateNetwork()通过调用mWifiStateTracker.setNetworkVariable()将连接命令发送到Wpa_supplicant。
(4)获取IP地址
当连接到supplicant后,WifiMonitor就会通知WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
WifiStateTracker发送EVENT_SUPPLICANT_CONNECTION消息到消息队列,这个消息有自己的handlermessage()函数处理,它会启动一个DHCP线程,而这个线程会一直等待一个消息事件,来启动DHCP协议分配IP地址。
frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
当Wpa_supplicant连接到AP后,它会发送一个消息给上层来通知连接成功,WifiMonitor会接受到这个消息并上报给WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
DhcpThread获取EVENT_DHCP_START消息事件后,调用handleMessage()函数,启动DHCP获取IP地址的服务。
//启动一个DHCPclient的精灵进程,为mInterfaceName请求分配一个IP地//址
这里调用了一个NetworkUtils.runDhcp()函数,NetworkUtils类是一个网络服务的辅助类,它主要定义了一些本地接口,这些接口会通过他们的JNI层android_net_NetUtils.cpp文件和DHCP client通信,并获取IP地址。
至此,IP地址获取完毕,Wifi启动流程结束。
posted on 2012-05-03 09:48 duancanmeng 阅读(6029) 评论(0) 编辑 收藏 举报