1. Wifi扫盲
Access point: 也叫hotspot(热点), 家里的无线路由就是ap。
SoftAp:软ap, 用无线网卡模拟ap的功能。
Wifi网络有两种模式:
- Infrastructure mode, in which wireless clients are connected to an access point. This is generally the default mode for 802.11b cards.
- Ad hoc mode, in which clients are connected to one another without any access point.
请参考http://en.kioskea.net/contents/wifi/wifimodes.php3
我们既可以通过Ad hoc也可以通过SoftAp方式来实现共享网络(例如手机可以通过笔记本访问internet), 但是原理不同。
这里只是简单的概括, 详细的解释请google或百度。
2. Android Wifi框架的结构图
上图只是wifi工作于station模式时的图, 当工作于soft ap模式时最基本的不同是不通过wpa_supplicant而是Framework层直接通过netd daemon来控制驱动。
虽然本文不关心JNI层以下的部分, 但wpa_supplient是什么需要解释一下。 WPA是WiFi Protected Access的缩写, wpa_supplicant是“WiFi网络安全存取认证”的意思。Android是用的wpa_supplicant的修改版本。
在Android wifi框架里, wpa_supplcant起着承上启下的作用。 它向上提供netlink socket接口, 向下依赖wifi驱动的wireless extention标准接口。 也就是说驱动必须支持wireless extention才可以。wireless extention其实是一个将要过时的标准, Android也将向nl80211标准过度, 采用未修改的wpa_supplicant。
请参见http://wireless.kernel.org/en/developers/Documentation/Android。
本节以下部分摘自《Android平台上无线网卡自动扫描并关联AP的实现》:http://hi.baidu.com/xyp86/blog/item/d371a1d78d4162d7a144dfd3.html。
Android平台使用的WiFi控制框架是基于大名鼎鼎的wpa_supplicant,它是一个安全中间件,为各种无线网卡提供统一的安全机制,如下图所示:
客户端程序,包括wpa_cli命令行或java图形界面程序,通过unix本地socket与
wpa_supplicant daemon服务通信,发送命令并接收结果;wpa_supplicant daemon服务,对应上述中间部分,功能是“上传下达”。所有客户端通过它控制硬件网卡,通过发送字符串命令控制是否扫描AP,提取扫描结果和是否关联AP等操作,同时将驱动的执行状态发送给用户。该服务是设计支持多种无线网卡芯片,因此各个厂商共同提供了一个通用接口给wpa_supplicant调用
Netd是android的一个守护进程, 是专门为Android开发的。 它功能很广, 包括设置nat, usb tethering, wifi tethering, soft ap设置, 还有网络接口add, remove, change事件的通知。 在FrameWork有NetworkManagementService负责和netd通信,framework代码可以通过 NetworkManagementService利用netd的全部功能。Netd的代码位于“android sources”/system/netd。
5. FrameWork层架构
当前系统正连接了以太网, Wifi是未使能的, 有无密码的Wifi信号存在, Wifi的优先级高于以太网。 此时在Settings使能Wifi, 系统就会连接到Wifi网络, 那么整个过程是怎样的呢?
下面是整个过程的UML序列图。
当在settings使能Wifi后, 首先会调用WifiService.setApEnabled(false), 也就是禁止Wifi模块的作为Ap(Access Point, 即一个接入点)的功能。需要了解的是, 现在的硬件Wifi模块即支持去连接附近的Ap, 也可以作为Ap供其它设备接入。那么给其它设备接入有什么用处呢? 那就是tethering, 即网络共享。 假设你的设备当前可以通过以太网访问因特网, 当在Wifi上使能Ap功能时, 其它连入的设备就可以通过这台设备访问因特网。 当然在同一时刻, 只能有一种身份。
接着会调用WifiService.setWifiEnabled(true), 去加载wifi适配层, 启动supplient守护进程, 启动MonitorThread开始监听supplient的事件。
首先 MonitorThread会收到DRIVER_STATE事件, 意味着硬件启动成功。 接着会调用到WifiNative.scanCommand()向supplient发送搜索Ap的命令。然后MonitorThread又进入等待事件的状态。
当supplient搜索Ap结束时, MonitorThread会收到SCAN_RESULTS事件。 接下来会调用WifiNative. setScanResultHandlingCommand(NormalMode)来让supplient自行决定去连接到哪个Ap, supplient会挑选信号最强并且没有密码的Ap进行连接。同时WifiSettings会收到WifiManager.SCAN_RESULTS_AVAILABLE_ACTION广播,调用mWifiManager.getScanResults()得到扫描到得ap列表。 ScanResult里包含如下信息:
public String SSID;
public String BSSID;
public String capabilities;
public int level;
public int frequency; |
下面是几个ScanResult的例子: SSID=”Exotica” FEATURES=”[WPA2-PSK-CCMP]” FREQ=2422 LEVEL=-40 BS=”01:02:03:04:05:06”
SSID=”Exotica” FEATURES=”[WEP]” FREQ=2432 LEVEL=-50 BS=”02:03:04:05:06:07” |
当supplient连接到一个Ap时, MonitorThread会收到CONNECTED事件。 接下来就会尝试配置IP地址, 如果IP地址配置成功就会发送EVENT_STATE_CHANGED给ConnectivityService,ConnectivityService就会根据网络优先级决定关掉以太网而通过Wifi联网的决定。这里要区分网络接口的disconnect和disable的不同之处, disconnected的接口依然可能enable, disable是指硬件上的。 在pc上当切换到其他网络只是disconnected以前的网络, 而Android为了省电, 是disable以前网络的。
系统启动时, Wifi和Ethernet都处于使能状态, 并且附近有没有密码的Wifi信号, Ethernet也是通的。系统启动后就会自动连接到Wifi网络, 那么系统启动时如何决定连接到哪个网络呢?
在上一篇《Android的网络支持和管理(一)---以太网》中, 我们分析了从网线插上到以太网通畅的全过程。 这过程中驱动层一共向上发出了两个信号, 第一个代表硬件连接上了, 第二个代表网络连接上了(即IP地址配置成功)。
刚才我们又分析了当使能Wifi到Wifi通畅的全过程。 这过程中驱动层一共向上发了三个信号, 第一个代表硬件使能成功,第二个代表搜索Ap完毕, 第三个代表网络连接上了(即IP地址配置成功)。
在IP地址配置成功以前, 以太网和Wifi都是默默做着自己的工作, 丝毫没有参与网络管理。 当即IP地址配置成功后, 能够确保各自网络通畅时, 才通知ConnectivityManager。
当启动时,如果以太网使能的话, 不管有没有网线连接都会去尝试根据保存的设置配置IP地址, 如果IP地址配置成功就会通知ConnectivityManager。同样, 如果Wifi使能的话, 就会加载Wifi驱动和启动wpa_supplient, 然后搜索AP并尝试连接, 接下来配置IP地址, 如果成果的话就会通知 ConnectivityManager。(注:WifiService.startWifi函数会在ConnectivityManager的构造函数里面调用去判断是不是要使能Wifi)无论ConnectivityManager先收到Wifi发出的信号还是以太网发出的信号, 最后的结果都是一样的, 当收到第二个信号时就会禁掉优先级小的以太网。
系统中只有Wifi, 附近有好几个有密码的Ap, 当在Settings里使能Wifi后, 会搜索到这些Ap并显示出来。 我们选择一个Ap后会弹出WifiDialog要求输入密码, 输入密码后点确定。 下面是随后的过程。
我们在WifiDialog中配置的信息可以用WifiConfigure来表示, 它包含的信息如下:
public int networkId;
public int status;
public String SSID;
public String BSSID;
public String preSharedKey;
public String[] wepKeys;
public int wepTxKeyIndex;
public int priority;
public boolean hiddenSSID;
public BitSet allowedKeyManagement;
public BitSet allowedProtocols;
public BitSet allowedAuthAlgorithms;
public BitSet allowedPairwiseCiphers;
public BitSet allowedGroupCiphers; |
ScanResult是Ap广播的信息,而WifiConfigure是wpa_supplicant用来尝试和Ap进行连接的信息。ScanResult说“窗前明月光”, WifiConfigure只有说“疑是地上霜”才能匹配。 WifiConfigure和wpa_supplicant配置文件里保存的信息格式是对应的, 关于wpa_supplicant更详细的介绍请看源码下的readme 。 可以说wpa_supplicant实现了所有我们需要的功能, 包括ap搜索, 连接,甚至设置的保存。Settings界面通过WifiManager来和wpa_supplicant通信。