android中,wifi的核心是WPAS(wpa_supplicant),它管理和控制Android平臺中的Wi-Fi功能。在整個wifi模塊中,其更像是一個一個服務端,實現了連接,認證等工作流程。
而客戶端有wpa_cli,和wpa_supplicant之間的通信依靠wpa ctrl接口來實現,而在wifi的framework層,其實主要工作就是向下實現wpa_cli的功能和wpa_supplicant通信,向上實現WiFiManager的簡易接口供上層app調用。
wifi的framework分析起來相對於wpa_supplicant比較簡單,先記錄這幾天看代碼的一些點(閱讀《深入理解Android:WiFi模塊 NFC和GPS卷 - 鄧凡平》和android4.2代碼的一些筆記)。
首先必須瞭解兩個類:StateMachine和AsyncChannel
1、StateMachine
在wifi模塊framework層的東西中,有很多地方都是用了狀態機的機制:WifiStateMachine,SupplicantStateTracker,DhcpStateMachine,WifiWatchdogStateMachine等。瞭解狀態機的運行方式,是瞭解wifi模塊在framework層運作的基礎。
StateMachine是具有層級關係的狀態機類,記錄幾個重要的點:
(1)addState函數可以增加一個狀態,同時,該函數可以指定父子層級關係。
setInitialState函數指定初始狀態。調用start函數時,狀態機開始運行。
(2)從一個狀態跳轉到另一個狀態時調用transitionTo函數。進入另一個狀態時會先調用到狀態的enter函數,退出時調用exit函數,同時得考慮他們的層級關係,而不是直接跳轉。以WifiWatchdogStateMachine的狀態機map爲例,從Disabled 狀態轉到Verifying ,會先調用Disabled的exit,再依次調用Enabled和Verifying的enter。
下面是WifiWatchdogStateMachine的狀態機map:
/** * STATE MAP * Default * / \ * Disabled Enabled * / \ \ * NotConnected Verifying Connected * /---------\ * (all other states) */
(3)消息會在當前的狀態中處理,對應的函數爲processMessage。如果當前狀態不能處理,會返回NOT_HANDLED。轉到父狀態處理,如果父狀態依舊處理不了,則依次上去,如果都不能處理,最後unhandledMessage函數會被調用。
已經處理則返回HANDLED,如果一個消息想保留到下個狀態再處理,可以調用deferMessage函數。
(4)由於StateMachine內部是圍繞一個Handler來工作的, 所以外界只能調用StateMachine的obtainMessage函數以獲取一個Message。發想送消息給StateMachine,調用sendMessage函數,StateMachine中的Handler會處理它。
2、AsyncChannel
這個類在wifi模塊中也是被大量使用,是瞭解wifi的framework避不開的類。該類用於在兩個handler之間傳遞消息。
有兩種工作模式:一種是單通道模式,只有客戶端連接了AsyncChannel,能向客戶端發送請求,服務端只要迴應即可。另一種是雙通道模式,客戶端服務端都連接AsyncChannel,雙方均可以向對方發送請求。
單通道使用步驟:
(1)客戶端拿到自己的handler;
(2)拿到服務端的Messenger(使用服務端handler構建),或者是服務端的handler;
(3)創建AsyncChannel對象,調用其connect函數,請求連接客戶端自己的handler和服務端的Messenger;
(4)連接成功後,服務端會迴應CMD_CHANNEL_HALF_CONNECTED消息。
雙通道使用步驟:
其實雙通道是在單通道的基礎上完成的,
(1)客戶端在接收到CMD_CHANNEL_HALF_CONNECTED消息後,
發送sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
(2)服務端在handler中會受到該消息,接着服務端也創建AsyncChannel對象,調用其connect函數connect(mContext, this, msg.replyTo)。
(3)另外還有一種比較方便的方法創建雙通道,即調用該函數即可:
public int fullyConnectSync(Context srcContext, Handler srcHandler, Handler dstHandler)
需要傳入客戶端的context,客戶端的handler和服務端的handler。返回值STATUS_SUCCESSFUL即代表建立成功。
一些例子:
(1)在wifi的framework層,WifiManager和WifiService之間建立起了雙通道:
WifiManager作爲客戶端:
申請單通道連接:
private void init() {
synchronized (sThreadRefLock) {
if (++sThreadRefCount == 1) {
Messenger messenger = getWifiServiceMessenger();
if (messenger == null) {
sAsyncChannel = null;
return;
}
sHandlerThread = new HandlerThread("WifiManager");
sAsyncChannel = new AsyncChannel();
sConnected = new CountDownLatch(1);
sHandlerThread.start();
Handler handler = new ServiceHandler(sHandlerThread.getLooper());
sAsyncChannel.connect(mContext, handler, messenger);
......
}
}
}
在單通道連接基礎上,請求雙通道連接
sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION):
private static class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
Object listener = removeListener(message.arg2);
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
......
WifiService作爲服務端,在接收到CMD_CHANNEL_FULL_CONNECTION消息後,請求connect:
private class ClientHandler extends Handler {
ClientHandler(android.os.Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
// We track the clients by the Messenger
// since it is expected to be always available
mTrafficPoller.addClient(msg.replyTo);
} else {
Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
}
break;
}
......
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, this, msg.replyTo);
break;
}
(2)WifiService和WifiStateMachine之間連接方式爲單通道,WifiStateMachine作爲服務端,WifiService作爲客戶端,連接後,使用sendMessageSynchronously來進行請求,而WifiStateMachine會使用replyToMessage來進行回覆(WifiStateMachine會用到mReplyChannel這個對象):
private class WifiStateMachineHandler extends Handler {
private AsyncChannel mWsmChannel;
WifiStateMachineHandler(android.os.Looper looper) {
super(looper);
mWsmChannel = new AsyncChannel();
mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
mWifiStateMachineChannel = mWsmChannel;
} else {
Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
mWifiStateMachineChannel = null;
}
break;
}