这里先说一下读wifi模块的一个步骤,我是先从理解Settings模块的设计开始的,刚开始以为settings模块只是一个listview进行显示,但android的设计让我出乎意料,特别是android7.0在Settings模块中还加入了抽屉,让实现流程更加复杂。之后大致看了下WifiSettings这个fragment,这里主要是界面的设计,当阅读到WifiService中时,发现到处都是StateMachine这个东西,没办法,只能先去学习什么是StateMachine,这里还要从java的State设计模式开始学起,然后再学习StateMachine对State模式的实现方式。这里看的差不多了,才开始读里面的逻辑。
除了看源码外,我结合博文和书籍一起理解,这里推荐几篇博文和一本书:
http://blog.youkuaiyun.com/u013467735/article/details/42487537
http://blog.youkuaiyun.com/u013467735/article/details/42493665
http://blog.youkuaiyun.com/u013467735/article/details/42525387
http://blog.youkuaiyun.com/eastmoon502136/article/details/8721510
书籍是邓凡平的深入理解Android(Wi-Fi、NFC和GPS卷)
这篇博文也引用了以上资料中的一些表述,引用之处没有一一指出,请原作者谅解。
Android在wifi模块的设计上,frameworks层以上涉及的类主要有WifiSettings、WifiMaganer、WifiService、WifiServiceImpl、WifiStateMachine、WifiController等。
他们的主要关系如下图:
上图中几个重要的类的作用及其在android中的代码位置如下:
WifiManager:WifiService的客户端,是App层与Framework层之间的桥梁。
(代码位置:frameworks/base/wifi/java/android/net/wifi/WifiManager.java)
WifiService:是Framework层负责wifi功能的核心服务,是系统进程。
(代码位置:frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java)
WifiStateMachine:wifi状态机,WifiService的核心。
(代码位置:frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java)
WifiMonitor:一个线程,用来接收来自WPAS的消息。
(代码位置:frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java)
WifiNative:用于和WPAS通信,内部定义许多native方法,对应的JNI模块是android_net_wifi_Wifi。
(代码位置:frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
分析方式:流程图+具体代码实现。
(一)Wifi开启与关闭流程分析
根据以上流程图,以开启wifi为例,分析代码中的实现:
用户在打开wifi开关后,WifiEnabler里的onSwitchChanged()将被调用,输入参数为true,并调用mWifiManager.setWifiEnabled(true)。
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
WifiService没有自己定义这些方法,而是通过WifiServiceImpl对象定义的。
public synchronized boolean setWifiEnabled(boolean enable) {
//代码省略
mWifiController.obtainMessage(CMD_WIFI_TOGGLED, mWifiIpoOff ? 1 : 0,
Binder.getCallingUid()).sendToTarget();
return true;
}
这里通过WifiController发送消息CMD_WIFI_TOGGLED,该消息在WifiController的ApStaDisabledState中处理并跳转到StaEnabledState,查看其enter方法:
@Override
public void enter() {
if (DBG) Slog.d(TAG, getName() + "\n");
mWifiStateMachine.setSupplicantRunning(true);
}
此处调用WifiStateMachine的setSupplicantRunning(true):
public void setSupplicantRunning(boolean enable) {
if (enable) {
sendMessage(CMD_START_SUPPLICANT);
} else {
sendMessage(CMD_STOP_SUPPLICANT);
}
}
setSupplicantRunning()中向WifiStateMachine发送CMD_START_SUPPLICANT消息,它由InitialState处理:
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch (message.what) {
case CMD_START_SUPPLICANT:
if (mWifiNative.loadDriver()) {
//代码省略
}
if (mWifiNative.startSupplicant(mP2pSupported)) {
//代码省略
transitionTo(mSupplicantStartingState);
}
return HANDLED;
}
}
这里主要调用WifiNative的loadDriver和startSupplicant两个函数去加载wifidriver和启动wpa_supplicant。至此,wifi开启流程基本结束。Wifi关闭流程类似。
(二)Wifi扫描流程
当wifi开启之后,会发送WIFI_STATE_CHANGED_ACTION广播,WifiTracker注册监听该广播,收到该广播后,调用mScanner.resume()启动扫描,该方法中调用mWifiManage.startScan()发起扫描。再通过WifiService的startScan( )触发WifiMachine的startScan()方法
public void startScan(int callingUid, int scanCounter,
ScanSettings settings, WorkSource workSource) {
Bundle bundle = new Bundle();
bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
}
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch(message.what) {
case CMD_START_SCAN:
handleScanRequest(message);
break;
//代码省略
return HANDLED;
}
}
private void handleScanRequest(Message message) {
//代码省略
// call wifi native to start the scan
if (startScanNative(freqs, hiddenNetworkIds, workSource)) {
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
if (workSource != null) {
// External worksource was passed along the scan request,
// hence always send a broadcast
mSendScanResultsBroadcast = true;
}
return;
}
//代码省略
}
handleScanRequest(message)调用startScanNative向wpa_supplicant发送SCAN的命令,当wpa_suppliant执行完SCAN并成功找到一些AP后,就会给WifiMonitor发送CTRL-EVENT-SCAN-RESULTS的event,WifiMonitor会parse出这个event,并向WifiStateMachine发送SCAN_RESULTS_EVENT消息,WifiStateMachine的SupplicantStartedState会处理这个消息,如下:
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch(message.what) {
case WifiMonitor.SCAN_RESULTS_EVENT:
case WifiMonitor.SCAN_FAILED_EVENT:
//代码省略
setScanResults();
if (mIsFullScanOngoing || mSendScanResultsBroadcast || mWifiOnScanCount < 2) {
loge("mWifiOnScanCount: " + mWifiOnScanCount);
/* Just updated results from full scan, let apps know about this */
boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT;
sendScanResultsAvailableBroadcast(scanSucceeded);
}
//代码省略
break;
//代码省略
return HANDLED;
}
这里主要做了两件事,一是去获取scanResults,保存到mScanResults和mScanResultCache中,另外会发送一个广播信息出去,WifiSettings监听到这个广播的后,就去获取scanResults并显示。
扫描流程至此结束。
(三).Wifi连接流程
连接的过程比较复杂,涉及的状态和消息比较多。
当用户点击已保存的AP或输入密码按确定后,会调用WifiManager的connect函数:
public void connect(WifiConfiguration config, ActionListener listener) {
Log.d(TAG, "connect, pid:" + Process.myPid() + ", tid:" + Process.myTid() + ", uid:" +
Process.myUid());
if (config == null)
throw new IllegalArgumentException("config cannot be null");
// Use INVALID_NETWORK_ID for arg1 when passing a config object
// arg1 is used to pass network id when the network already exists
getChannel().sendMessage(
CONNECT_NETWORK,WifiConfiguration.INVALID_NETWORK_ID,
putListener(listener), config);
}
这里通过getChannel()发送消息CONNECT_NETWORK到WifiService,在ClientHandler中处理:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//代码省略
case WifiManager.CONNECT_NETWORK:
case WifiManager.SAVE_NETWORK: {
//代码省略
else if (config == null && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
mWifiStateMachine.sendMessage(Message.obtain(msg));
}
//代码省略
break;
}
}
}
}
这里将CONNECT_NETWORK消息转发给WifiStateMachine,由ConnectModeState处理该消息,处理函数的code非常多,有三个重要函数:
mWifiConfigStore.saveNetwork()
mWifiConfigStore.selectNetwork()
mWifiNative.reconnect()
通过mWifiNative.reconnect()发起重新连接的请求给wpa_supplicant,当wpa_supplicant的状态改变时会发送WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT让WifiStateMachine 转换到DisconnectedState。
Wifi和AP之间已经连接成功后,就会收到wpa_supplicant发送上来的CTRL-EVENT-CONNECTED这个event,WifiMonitor收到这个消息后,会向WifiStateMachine发送NETWORK_CONNECTION_EVENT表示已经和AP之间成功的连接,WifiStateMachine的ConnectModeState会来处理这个消息
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch(message.what) {
//代码省略
case WifiMonitor.NETWORK_CONNECTION_EVENT:
//代码省略
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
mWifiQualifiedNetworkSelector
.enableBssidForQualityNetworkSelection(mLastBssid, true);
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mObtainingIpState);
break;
//代码省略
return HANDLED;
}
}
}
处理结束会跳转到ObtainingIpState,其enter函数如下:
@Override
public void enter() {
//代码省略
if (!mWifiConfigManager.isUsingStaticIp(mLastNetworkId)) {
//静态ip模式
//代码省略
} else {
StaticIpConfiguration config = mWifiConfigManager.getStaticIpConfiguration(mLastNetworkId);
if (config.ipAddress == null) {
logd("Static IP lacks address");
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
} else {
final IpManager.ProvisioningConfiguration prov;
///M: IpReachabilityMonitor Enhancement @{
if (enableIpReachabilityMonitor()) {
prov = mIpManager.buildProvisioningConfiguration()
.withStaticConfiguration(config)
.withApfCapabilities(mWifiNative.getApfCapabilities())
.build();
} else {
prov = mIpManager.buildProvisioningConfiguration()
.withStaticConfiguration(config)
.withApfCapabilities(mWifiNative.getApfCapabilities())
.withoutIpReachabilityMonitor()
.build();
}
try {
Thread.sleep(500);
} catch (Exception e) {
}
mIpManager.startProvisioning(prov);
}
}
}
}
这里涉及到另外一个状态机IpManager,它还会和另外一个状态机DhcpClient进行交互,通过DHCP服务去获取ip地址。在进行DHCP协议之前,DhcpClient会发送一个消息CMD_PRE_DHCP_ACTION,由WifiStateMachine的L2ConnectedState处理该消息, 调用handlePreDhcpSetup()进行一些初始化设置,处理结束会发送CMD_PRE_DHCP_ACTION_COMOLETE消息,处理该消息时候会调用mIpManager.completedPreDhcpAction()告诉IpManager可以开始DHCP协议了。DHCP发现结束后DhcpClien又会发送消息CMD_POST_DHCP_ACTION,L2ConnectedState调用handlePostDhcpSetup()恢复之前的设置
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch (message.what) {
case DhcpClient.CMD_PRE_DHCP_ACTION:
handlePreDhcpSetup();
break;
case DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE:
mIpManager.completedPreDhcpAction();
break;
case DhcpClient.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
break;
//代码省略
return HANDLED;
}
}
最后通过监听IpManager.CallBack的onProvisioningSuccess,转到ConnectedState,漫长的连接流程至此结束。