- 浏览: 86052 次
- 性别:
- 来自: 北京
最新评论
-
chenmaostyle:
再通过rr.mResult.sendToTarget(),把返 ...
Android网络相关---上网流程 -
SCUTLHN:
请问下要怎么运行peerdroid呢
PeerDroid -
hesihua:
不是说classpath无效么?
cmd下运行Java程序(带有第三方jar包) -
guangfeng8023:
你好!我最近在弄peerdroid,有很多不清楚的地方,能帮帮 ...
PeerDroid
转自:http://blog.csdn.net/ylyuanlu/article/details/6622943
最近研究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
该类数据部分主要定义了下面几个类的变量:
{
private final IntentFilter mFilter;
//广播接收器,用来接收消息并做响应的处理工作
privatefinal BroadcastReceiver mReceiver;
//这是一个扫描类,会在用户手动扫描 AP时被调用
privatefinal Scanner mScanner;
private WifiInfo mLastInfo;
//服务代理端,作为WifiService对外的接口类呈现
privateWifiManager mWifiManager;
//这个类主要实现Wifi的开闭工作
privateWifiEnabler mWifiEnabler;
//AP
private AccessPoint mSelected;
private WifiDialog mDialog;
……
}
wifiSettings类的构造函数的主要工作:定义了一个IntentFilter(Intent过滤器)变量,并添加了六个动作,(了解 Android的intent机制的同学都知道什么意思,不明白的同学参考Intent机制的资料)接着定义一个广播接收器,并有相应的消息处理函数,下 面是该构造函数的定义:
public WifiSettings() {
mFilter = new IntentFilter();
//intent机制中的intent消息过滤器,下面添加可以处理的动作
mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
//注册了广播接收器,用来处理接收到的消息事件
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
handleEvent(intent); //事件处理函数
}
};
mScanner= new Scanner(); //手动扫描类
}
在广播接收器中的相应函数onReceive函数中有个handleEvent函数,它就是用来处理广播接收器接受到的intent消息的,它的功能是根 据intent消息中的动作类型,来执行相应的操作,每一种动作对应了activity的一项消息处理能力。
在oncreate函数中实例化了mWifiManager和mWifiEnabler 两个类,这两个类对wifiSettings来说至关重要,它后面的定义的一系列函数都是通过调用这两个类的相应接口来实现的。
……
mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
mWifiEnabler = new WifiEnabler(this,
(CheckBoxPreference) findPreference("enable_wifi"));
……
WifiSettings中还定义了显示菜单和响应菜单键的函数,即onCreateOptionsMenu()和 onOptionsItemSelected();还有响应配置对话框中按键的onClick()函数;最后定义了Scanner类,它是一个 handler的继承类,实现了消息处理函数,用于处理手动扫描的动作。
2) WifiEnabler.java:
packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
private final Context mContext;
private final CheckBoxPreference mCheckBox;
//两个重要成员
private final WifiManager mWifiManager;
private final IntentFilter mIntentFilter;
wifienabler类中定义了四个成员变量很重要,mContext,mCheckBox,mWifiManager和mReceiver,其中 mContext用于获取mwifiManager实例,mReceiver用来接收底层发来的消息,mCheckBox用来改变UI的状态。
该 类中定义了几个重要的函数onPreferenceChange,handleWifiStateChanged和 handleStateChanged,onPreferenceChange用来处理按下的Enbler键,它会调用 mWifiManager.setWifiEnabled(enable),另外两个用来处理接受的消息事件。
在类的构造函数中,主要做了一下工作:初始化了mContext,mCheckBox,mWifimanager,并且初始化了一个 mIntentFilter变量,添加了三个动作,在构造函数的上面定义了一个广播接收器,用来接收下层传来的消息,并根据intent动作的类型调用相 应的处理函数,这个广播接收器在onResum函数中被注册。
public WifiEnabler(Context context, CheckBoxPreferencecheckBox) {
mContext= context;
mCheckBox = checkBox;
mOriginalSummary = checkBox.getSummary();
checkBox.setPersistent(false);
mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
mIntentFilter= new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
// Theorder matters! We really should not depend on this. :(
mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
}
这 里可以总结为:如果上层需要监听或收到下层的消息,那么就要通过定义一个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的函数。
该类的构造函数很简单:
public WifiManager(IWifiManager service,Handler handler) {
mService = service;
mHandler = handler;
}
该 类中还定义了一个WifiLock类,这个类用来保证在有应用程序使用Wifi无线电传输数据时,wifiradio可用,即当一个应用程序使用wifi 的radio进行无线电数据传输时,就要先获得这个锁,如果该锁已被其他程序占有,就要等到该锁被释放后才能获得,只用当所有持有该锁的程序都释放该锁 后,才能关闭radio功能。
4)WifiService:
frameworks/base/services/java/com/android/server/WifiService.java
private final WifiStateTrackermWifiStateTracker;
private Context mContext;
private WifiWatchdogServicemWifiWatchdogService = null;
private final WifiHandler mWifiHandler;
这是WifiService中的几个重要的数据成员。
在 接下来的构造函数中初始化了mWifiStateTracker,mContext,然后动态生成mWifiThread子线程并启动,在主线程里用 mWifiThread调用getLooper()函数获得线程的looper,来初始化创建一个mWifiHandler对象,这个 WifiHandler在WifiService类的后面有定义,并重载了Handler类的handlermessage()函数,这样消息就可以在主 线程里被处理了,这是android的handlerthread消息处理机制,可参考相关资料,这里不予详述。在构造函数的最后,注册了两个广播接收 器,分别用来ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED这两个动作,这里是 android的intent消息通知机制,请参考相关资料,代码如下:
mContext = context;
mWifiStateTracker = tracker;
mWifiStateTracker.enableRssiPolling(true);
……
HandlerThread wifiThread = newHandlerThread("WifiService");
wifiThread.start();
mWifiHandler = newWifiHandler(wifiThread.getLooper());
……
随 后定义了一系列的函数,其中有服务器要发送的命令的系列函数,它通过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类首先定义了事件日志和事件码(这里包含了所有可能的事件类型),还定义了如下的重要成员数据:
//几个重要的数据成员
private WifiMonitor mWifiMonitor; //被启动用来监听supplicant传来的消息
private WifiInfo mWifiInfo;
private WifiManager mWM; //服务代理
private DhcpHandler mDhcpTarget; //IP地址获取线程
private DhcpInfo mDhcpInfo; //Dhcp的相关信息都在这里
类的构造函数中,初始化了系列成员变量,包括生成了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
WifiWatchdogService(Context context,WifiStateTracker wifiStateTracker) {
mContext = context;
mContentResolver = context.getContentResolver();
mWifiStateTracker =wifiStateTracker;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
createThread();
// The content observer to listen needs a handler, which createThreadcreates
registerForSettingsChanges();
if (isWatchdogEnabled()) {
registerForWifiBroadcasts();
}
if (V) {
myLogV("WifiWatchdogService: Created");
}
}
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类型来定义通信实体:
struct wpa_ctrl {
#ifdefCONFIG_CTRL_IFACE_UDP
int s;
struct sockaddr_in local;
struct sockaddr_in dest;
char *cookie;
#endif /*CONFIG_CTRL_IFACE_UDP */
#ifdefCONFIG_CTRL_IFACE_UNIX
int s;
struct sockaddr_un local;
struct sockaddr_un dest;
#endif /*CONFIG_CTRL_IFACE_UNIX */
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
HANDLE pipe;
#endif /*CONFIG_CTRL_IFACE_NAMED_PIPE */
};
然后是根据上面三个类型分别实现了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
{
……
case ConnectivityManager.TYPE_WIFI:
if (DBG) Slog.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context, mHandler); //创建WifiStateTracker实例
WifiService wifiService = newWifiService(context, wst);//创建WifiService实例
ServiceManager.addService(Context.WIFI_SERVICE, wifiService); //向服务管理系统添加Wifi服务
wifiService.startWifi(); //启动Wifi
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
wst.startMonitoring(); //启动WifiMonitor中的WifiThread线程
……
}
WifiService的主要工作:WifiMonitor和Wpa_supplicant的启动和关闭,向Wpa_supplicant发送命令。
WifiMonitor的主要工作:阻塞监听并接收来自Wpa_supplicant的消息,然后发送给WifiStateTracker。
上面两个线程通过AF_UNIX套接字和Wpa_supplicant通信,在通信过程中有两种连接方式:控制连接和监听连接。它们创建代码如下:
ctrl_conn =wpa_ctrl_open(ifname);
.. .. ..
monitor_conn = wpa_ctrl_open(ifname);
2)Wifi启动流程
(1)使能Wifi
要想使用Wifi模块,必须首先使能Wifi,当你第一次按下Wifi使能按钮时,WirelessSettings会实例化一个WifiEnabler对象,实例化代码如下:
packages/apps/settings/src/com/android/settings/WirelessSettings.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
CheckBoxPreferencewifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);
mWifiEnabler= new WifiEnabler(this, wifi);
……
}
WifiEnabler类的定义大致如下,它实现了一个监听接口,当WifiEnabler对象被初始化后,它监听到你按键的动作,会调用响应函数 onPreferenceChange(),这个函数会调用WifiManager的setWifiEnabled()函数。
public class WifiEnabler implementsPreference.OnPreferenceChangeListener {
……
public boolean onPreferenceChange(Preference preference,Object value) {
booleanenable = (Boolean) value;
……
if (mWifiManager.setWifiEnabled(enable) ) {
mCheckBox.setEnabled(false);
……
}
……
}
我们都知道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监听线程。代码如下:
……
if (enable) {
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed toload Wi-Fi driver.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
}
if (!mWifiStateTracker.startSupplicant()) {
mWifiStateTracker.unloadDriver();
Slog.e(TAG, "Failed tostart supplicant daemon.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
}
registerForBroadcasts();
mWifiStateTracker.startEventLoop();
……
至此,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
private void handleDriverEvent(Stringstate) {
if (state == null) {
return;
}
if (state.equals("STOPPED")) {
mWifiStateTracker.notifyDriverStopped();
} else if (state.equals("STARTED")) {
mWifiStateTracker.notifyDriverStarted();
} else if (state.equals("HANGED")) {
mWifiStateTracker.notifyDriverHung();
}
}
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
case EVENT_DRIVER_STATE_CHANGED:
switch(msg.arg1) {
case DRIVER_STARTED :
/**
*Set the number of allowed radio channels according
*to the system setting, since it gets reset by the
*driver upon changing to the STARTED state.
*/
setNumAllowedChannels();
synchronized (this) {
if (mRunState == RUN_STATE_STARTING) {
mRunState = RUN_STATE_RUNNING;
if (!mIsScanOnly) {
reconnectCommand();
} else {
// In somesituations, supplicant needs to be kickstarted to
// start thebackground scanning
scan(true);
}
}
}
break;
上面是启动Wifi时,自动进行的AP的扫描,用户当然也可以手动扫描AP,这部分实现在WifiService里面,WifiService通过startScan()接口函数发送扫描命令到supplicant。
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
public boolean startScan(booleanforceActive) {
enforceChangePermission();
switch (mWifiStateTracker.getSupplicantState()) {
case DISCONNECTED:
case INACTIVE:
case SCANNING:
case DORMANT:
break;
default:
mWifiStateTracker.setScanResultHandling(
WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
break;
}
return mWifiStateTracker.scan(forceActive);
}
然后下面的流程同上面的自动扫描,我们来分析一下手动扫描从哪里开始的。我们应该知道手动扫描是通过菜单键的扫描键来响应的,而响应该动作的应该是 WifiSettings类中Scanner类的handlerMessage()函数,它调用WifiManager的 startScanActive(),这才调用WifiService的startScan()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
.setIcon(R.drawable.ic_menu_scan_network);
menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
.setIcon(android.R.drawable.ic_menu_manage);
return super.onCreateOptionsMenu(menu);
}
当按下菜单键时,WifiSettings就会调用这个函数绘制菜单。如果选择扫描按钮,WifiSettings会调用onOptionsItemSelected()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
public booleanonOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ID_SCAN:
if(mWifiManager.isWifiEnabled()) {
mScanner.resume();
}
return true;
case MENU_ID_ADVANCED:
startActivity(new Intent(this,AdvancedSettings.class));
return true;
}
return super.onOptionsItemSelected(item);
}
private class Scanner extends Handler {
private int mRetry = 0;
void resume() {
if (!hasMessages(0) ) {
sendEmptyMessage(0);
}
}
void pause() {
mRetry = 0;
mAccessPoints.setProgress(false);
removeMessages(0);
}
@Override
public void handleMessage(Message message) {
if (mWifiManager.startScanActive() ){
mRetry = 0;
} else if (++mRetry >= 3) {
mRetry = 0;
Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,
Toast.LENGTH_LONG).show();
return;
}
mAccessPoints.setProgress(mRetry != 0);
sendEmptyMessageDelayed(0, 6000);
}
}
这里的mWifiManager.startScanActive()就会调用WifiService里的startScan()函数,下面的流程和上面的一样,这里不赘述。
当supplicant完成了这个扫描命令后,它会发送一个消息给上层,提醒他们扫描已经完成,WifiMonitor会接收到这消息,然后再发送给WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
void handleEvent(int event, String remainder) {
switch (event) {
caseDISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);
break;
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);
break;
case SCAN_RESULTS:
mWifiStateTracker.notifyScanResultsAvailable();
break;
case UNKNOWN:
break;
}
}
WifiStateTracker将会广播SCAN_RESULTS_AVAILABLE_ACTION消息:
Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
public voidhandleMessage(Message msg) {
Intent intent;
……
case EVENT_SCAN_RESULTS_AVAILABLE:
if(ActivityManagerNative.isSystemReady()) {
mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
sendScanResultsAvailable();
/**
* On receiving the first scanresults after connecting to
* the supplicant, switch scanmode over to passive.
*/
setScanMode(false);
break;
……
}
由于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
public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
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;
}
配置好以后,当按下“Connect Press”时,WifiSettings通过发送LIST_NETWORK命令到supplicant来检查该网络是否配置。如果没有该网络或没有配置 它,WifiService调用addorUpdateNetwork()函数来添加或更新网络,然后发送命令给supplicant,连接到这个网络。 下面是从响应连接按钮到WifiService发送连接命令的代码:
packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java
public void onClick(DialogInterfacedialogInterface, 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);
}
}
}
}
}
Frameworks\base\wifi\java\android\net\wifi\WifiManager.java
public intupdateNetwork(WifiConfiguration config) {
if(config == null || config.networkId < 0) {
return -1;
}
return addOrUpdateNetwork(config);
}
private intaddOrUpdateNetwork(WifiConfiguration config) {
try {
return mService.addOrUpdateNetwork(config) ;
} catch (RemoteException e) {
return -1;
}
}
WifiService.addOrUpdateNetwork()通过调用mWifiStateTracker.setNetworkVariable()将连接命令发送到Wpa_supplicant。
(4)获取IP地址
当连接到supplicant后,WifiMonitor就会通知WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
Public void Run(){
if (connectToSupplicant()) {
// Send a message indicatingthat it is now possible to send commands
// to the supplicant
mWifiStateTracker.notifySupplicantConnection();
} else {
mWifiStateTracker.notifySupplicantLost();
return;
}
……
}
WifiStateTracker发送EVENT_SUPPLICANT_CONNECTION消息到消息队列,这个消息有自己的handlermessage()函数处理,它会启动一个DHCP线程,而这个线程会一直等待一个消息事件,来启动DHCP协议分配IP地址。
frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
void notifySupplicantConnection() {
sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
}
public void handleMessage(Message msg) {
Intent intent;
switch (msg.what) {
case EVENT_SUPPLICANT_CONNECTION:
……
HandlerThread dhcpThread = newHandlerThread("DHCP Handler Thread");
dhcpThread.start();
mDhcpTarget = newDhcpHandler(dhcpThread.getLooper(), this);
……
……
}
当Wpa_supplicant连接到AP后,它会发送一个消息给上层来通知连接成功,WifiMonitor会接受到这个消息并上报给WifiStateTracker。
Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
void handleEvent(int event, String remainder) {
switch (event) {
case DISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);
break;
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);
break;
……
}
private voidhandleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
StringBSSID = null;
intnetworkId = -1;
if(newState == NetworkInfo.DetailedState.CONNECTED) {
Matcher match = mConnectedEventPattern.matcher(data);
if(!match.find()) {
if (Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTEDevent string");
}else {
BSSID = match.group(1);
try {
networkId = Integer.parseInt(match.group(2));
} catch (NumberFormatException e) {
networkId = -1;
}
}
}
mWifiStateTracker.notifyStateChange(newState,BSSID, networkId);
}
void notifyStateChange(DetailedState newState, StringBSSID, int networkId) {
Messagemsg = Message.obtain(
this, EVENT_NETWORK_STATE_CHANGED,
newNetworkStateChangeResult(newState, BSSID, networkId));
msg.sendToTarget();
}
caseEVENT_NETWORK_STATE_CHANGED:
……
configureInterface();
……
private void configureInterface() {
checkPollTimer();
mLastSignalLevel = -1;
if(!mUseStaticIp) { //使用DHCP线程动态IP
if(!mHaveIpAddress && !mObtainingIpAddress) {
mObtainingIpAddress = true;
//发送启动DHCP线程获取IP
mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
}
} else { //使用静态IP,IP信息从mDhcpInfo中获取
intevent;
if(NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
mHaveIpAddress = true;
event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configurationsucceeded");
}else {
mHaveIpAddress = false;
event = EVENT_INTERFACE_CONFIGURATION_FAILED;
if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
}
sendEmptyMessage(event); //发送IP获得成功消息事件
}
}
DhcpThread获取EVENT_DHCP_START消息事件后,调用handleMessage()函数,启动DHCP获取IP地址的服务。
public void handleMessage(Message msg) {
intevent;
switch (msg.what) {
case EVENT_DHCP_START:
……
Log.d(TAG, "DhcpHandler: DHCP requeststarted");
//启动一个DHCPclient的精灵进程,为mInterfaceName请求分配一个IP地//址
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo) ) {
event= EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if(LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
} else {
event= EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG,"DhcpHandler: DHCP request failed: " +
NetworkUtils.getDhcpError());
}
……
}
这 里调用了一个NetworkUtils.runDhcp()函数,NetworkUtils类是一个网络服务的辅助类,它主要定义了一些本地接口,这些接 口会通过他们的JNI层android_net_NetUtils.cpp文件和DHCP client通信,并获取IP地址。
至此,IP地址获取完毕,Wifi启动流程结束。
发表评论
-
JNI学习(转)
2013-05-27 10:30 10228JNI是在学习Android HAL时必须要面临一个知识点, ... -
(转)Android Animation学习笔记
2013-05-07 20:02 837转自:http://www.cnblogs.com/fei ... -
(转)onInterceptTouchEvent和onTouchEvent调用时序
2013-05-06 15:05 0转自:http://www.blogjava.net/TiG ... -
Android的service相关讲解
2011-12-27 10:42 1378转自:http://blog.sina.com.cn/s/ ... -
Android通信模块代码流程
2011-08-15 16:50 4262下面转载的这篇文章为开发android通信模块 ... -
Android网络相关---上网流程
2011-08-12 13:57 3384觉得写的不错,就转过来收藏了。 http://blog.cs ... -
发个比较全的android进程文件详解|后附精简版谷歌套件
2011-07-20 12:44 1888原贴:http://bbs.hiapk.com/viewthr ... -
Android通话和数据传输过程分析(转)
2011-07-19 09:25 6146第三部分、第四部分:猫相关的各种状态的监听和通知机制/通话相关 ...
相关推荐
Android__WIFI模块分析 Android__WIFI模块分析 Android__WIFI模块分析 Android__WIFI模块分析
Android WIFI 模块分析 Android WIFI 模块分析是 Android 操作系统中负责 WIFI 连接的模块。该模块主要由 WifiService、WifiMonitor、Wpa_supplicant、Wifi 驱动模块、Wifi 电源管理模块等组件组成。 Wifi 模块...
### WiFi模块分析 #### 一、WiFi模块启动流程 ##### 1. **系统启动初始化** - **触发入口**: 在系统启动过程中,通过`SystemServer`加载各种系统服务,其中包括WiFi服务。这一过程从获取`ConnectivityService`...
以下是对Android WIFI模块的深入分析: **WIFI模块框图** WIFI模块通常包括以下几个层次:应用程序层、框架层、JNI(Java Native Interface)层、硬件抽象层和内核驱动层。在框图中,我们可以看到WirelessSettings...
本文将深入探讨Android Wi-Fi源码分析,特别是如何连接到一个接入点(Access Point,简称AP)的过程。 首先,当用户在AcessPointDialog中选择加密方式并点击连接按钮后,Android系统会执行连接操作。关键代码在于`...
### WIFI模块应用说明 #### 一、WIFI模块的工作模式 WIFI模块是现代智能设备中不可或缺的一部分,它能够帮助设备实现无线网络连接,从而进行数据传输和远程控制。WIFI模块主要有两种工作模式:AP(Access Point)...
WiFi模块调试是一项涉及硬件和软件知识的技术活动,其中会用到多个技术领域的知识点。在本文中,我们将针对WiFi模块调试的各个方面展开详细讨论,包括WiFi的基础知识、Android WiFi软件架构、WCN-SS软件架构、WiFi...
新力维WIFI模块配置调试软件包是一款专为新力维WIFI模块设计的调试工具,主要用于帮助用户进行模块的配置、测试以及故障排查。在IT行业中,无线网络通信是不可或缺的一部分,而WIFI模块作为无线通信的重要组件,其...
本分析主要探讨了使用串口WiFi模块控制智能家电的几种方案,并从控制距离的不同角度,将其分为局域网控制和广域网控制。 局域网控制主要是指WiFi模块与智能终端(如智能手机、平板电脑)之间的短距离无线通信。在...
WIFI模块的解析和启动流程涉及框架分析和启动流程,主要包括WIFI适配层的解析和Wifi启动流程。在此过程中,WIFI驱动流程分析和网络设备注册流程都是关键步骤。 电源管理在WIFI模块中也是一个重要的组成部分。电源...
ESP8266-WIFI模块是物联网领域广泛应用的一款经济实惠且功能强大的无线模块,它集成了Wi-Fi功能,使得各种嵌入式设备能够轻松接入互联网。这个模块主要基于Espressif Systems公司的ESP8266芯片,该芯片具有内置TCP/...
在本文中,我们将深入探讨如何使用STM32微控制器连接WiFi模块,并着重讲解数据返回值的处理。STM32是一款广泛应用的32位微控制器,它具有强大的性能和丰富的外设接口,使得它成为实现无线通信功能的理想选择。在...
用户可以通过分析和修改这些文件,来适应自己的项目需求,例如更改继电器的数量或添加其他传感器。同时,它也提供了完整的硬件和软件解决方案,使得初学者可以快速理解Wi-Fi模块如何与硬件交互,以及如何编写控制...
ESP8266 WiFi模块是物联网领域广泛应用的一款低成本、高性能的Wi-Fi芯片解决方案。这款模块由乐鑫科技(Espressif Systems)开发,以其强大的功能和极高的性价比赢得了开发者们的青睐。下面将详细介绍ESP8266的主要...
《基于SP8266设计的WIFI模块AD09:硬件设计详解》 在现代物联网技术中,WIFI模块扮演着至关重要的角色,它们使得设备能够通过无线网络进行通信。本文将深入探讨一个基于SP8266的WIFI模块——AD09的设计,涵盖其硬件...
`Wifi模块分析_android.doc`可能会深入探讨WiFi模块的硬件特性、性能指标,以及如何在Android设备上进行模块集成和优化。 **Linux无线网络技术** Linux作为Android的基础,其无线网络技术对Android WiFi功能至关...
这个压缩包文件包含了ESP8266 5V版本的WIFI模块的硬件设计资源,包括AD设计的硬件原理图和4层PCB工程文件,这对于电子工程师或者爱好者来说是一份宝贵的学习资料。 首先,我们要理解ESP8266的特点。ESP8266是由...