HSM(對應的類是StateMachine)和AsyncChannel是Android Java Framework中兩個重要的類。不過,它們目前還僅由Framework內部使用,SDK中并沒有包含它們。這兩個類的作用如下。
* HSM在傳統狀態機對所有狀態都一視同仁的基礎上做了一些改變,使得狀態和狀態之間有了層級關系。HSM中的狀態層級關系與Java中父子類的派生和繼承關系類似,即在父狀態中實現generic的功能,而在子狀態中實現一些特定的處理。不過,和Java中類派生不同的是,HSM中父子狀態對應的是毫無派生關系的兩個類,所以使用時需要創建兩個對象。而Java中子類則從其父類派生,實際使用時創建一個子類對象即可,該子類對象就能完成父類的工作。
* AsyncChannel用于兩個Handler之間的通信。具體的通信方式為源Handler通過sendMessage向目標Handler發送消息,而目標Handler通過replyToMessage回復源Handler處理結果。注意,這兩個Handler可位于同一個進程,也可分屬兩個不同的進程。
本節先來介紹HSM。
>[info] 注意 由于HSM和AsyncChannel并非本書的主題,故本章僅介紹它們的用法。對實現原理感興趣的讀者不妨在了解它們用法的基礎上,自行研究相關代碼。
**1、HSM使用**
HSM對應的類叫StateMachine,下面通過一個例子來介紹其用法。
**HSM示例**
~~~
// 此例來源于StateMachine.java文件中的注釋
// StateMachineTest是StateMachine的子類
class StateMachineTest extends StateMachine {
StateMachineTest(String name) {
super(name);
// 為狀態機添加一個狀態。代碼中一般用縮進的方式表達層級關系
addState(mP0);
// 添加一個狀態mS0,其父狀態為mP0
addState(mS0, mP0);
addState(mP1, mP0);
addState(mS1,mP1); // 添加一個狀態mS1,其父狀態為mP1
addState(mS5,mS1)
addState(mS2, mP1);
addState(mS3, mS2);
addState(mS4, mS2);
// setInitialState函數用于設置狀態機的初始狀態,本例中該狀態是mS5
setInitialState(mS5);
}// StateMachineTest構造函數結束
}
~~~
上述代碼中StateMachineTest所涉及的狀態及層級關系如圖5-2所示。
:-: 
圖5-2 HSM示例中狀態關系
圖5-2中,mS5是初始狀態,由代碼中的setInitialState函數設置。接著來看StateMachineTest的代碼。
**HSM示例**
~~~
// 接上面的代碼。P0從State類派生。在HSM中,狀態由類State來表達
class P0 extends State {
/*
enter代表一個狀態的Entry Action,SM進入此狀態時將調用其EA。
exit代表一個狀態的Exit Action,SM退出某狀態時將調用其EXA。
*/
public void enter() {
......// do sth here
}
public void exit() {
......// do sth here
}
/*
除了EA和EXA外,每個State中最重要的函數就是processMessage了。
在HSM中,外界和HSM交互的方式就是向其sendMessage。Message由當前State的processMessage
函數來處理。如果當前State成功處理此message,則返回HANDLED。否則返回NOT_HANDLED。
在Message處理中,如果子狀態返回NOT_HANDLED,則其父狀態的processMessage將被調用。
如果當前狀態及祖先狀態都不能處理,則HSM的unhandledMessage將被調用。而HSM的派生類
可重載unhandledMessage函數以處理這個不能被當前狀態及祖先狀態處理的消息。
*/
public boolean processMessage(Message message) {
return HANDLED; // P0能處理任何Message
}
}
class P1 extends State {
......// 實現P1的enter,exit和processMessage函數
}
class S0 extends State {
......// 實現S0的enter,exit和processMessage函數
}
......// S1到S4的定義
class S5 extends State {
public void enter() {
......// 實現S5的enter函數
}
public void exit() {
......// 實現S5的exit函數
}
public boolean processMessage(Message message) {
switch(message.what){
case: TRANSITION_CMD:
transitionTo(mS4);// 切換狀態時,需要調用此函數
break;
case: TRANSITON_CMD_DEFER_MSG:
// deferMessage用于保留某個消息。而被保留的消息將留待到下一個狀態中去處理
deferMessage(message);
transitionTo(mS1);
break;
default:
break;
}
return HANDLED;
}
}
......// StateMachine其他一些可重載函數。以后碰到它們時再介紹
// 定義各個狀態對應的對象
private P0 mP0 = new P0();
private P1 mP1 = new P1();
private S0 mS0 = new S0();
private S1 mS1 = new S1();
private S2 mS2 = new S2();
private S3 mS3 = new S3();
private S4 mS4 = new S4();
private S5 mS5 = new S5();
// 定義消息
final static int TRANSITION_CMD = 0;
final static int TRANSITON_CMD_DEFER_MSG = 1;
// 主函數
public void main() throws Exception {
StateMachineTest smTest = new StateMachineTest("StateMachineTest");
smTest.start(); // 啟動狀態機
synchronized (sm5) {
// 外界只能通過obtainMessage以及sendMessage發送消息給SM去執行
smTest.sendMessage(obtainMessage(TRANSITION_CMD));
smTest.sendMessage(obtainMessage(TRANSITON_CMD_DEFER_MSG));
......
}
......
}
~~~
上面代碼介紹了HSM中一些重要的API。
* addState:添加一個狀態。同時還可指定父狀態。
* transitionTo:將狀態機切換到某個狀態。
* obtainMessage:由于HSM內部是圍繞一個Handler來工作的,所以外界只能調用HSM的obtainMessage以獲取一個Message[^①]。
* sendMessage:發送消息給HSM。HSM中的Handler會處理它。
* deferMessage:保留某個消息。該消息將留待下一個新狀態中去處理。其內部實現就是把這些被deferred的message保存到一個隊列中。當HSM切換到新狀態后,這些deferred消息將被移到HSM內部Handler所對應消息隊列的頭部,從而新狀態能首先處理這些deferred消息。
* start:啟動狀態機。
* 停止狀態機可使用quit或quitNow函數。這兩個函數均會發送SM_QUIT_CMD消息給HSM內部的Handler,不過效果略有區別。當使用quit時,SM_QUIT_CMD添加在消息隊列尾;而使用quitNow時,SM_QUIT_CMD被添加到消息隊列頭。
HSM中狀態和狀態之間的層級關系體現在哪些方面呢?以上述代碼為例:
* SM啟動后,初始狀態的EA將按派生順序執行。即其祖先狀態的EA先執行,子狀態的EA后執行。以示例代碼中的初始狀態mS5為例。當HSM的start調用完畢后,EA調用順序為mP0、mP1、mS1、mS5。
* 當State發生切換時,舊State的exit先執行,新State的enter后執行,并且新舊State派生樹上對應的State也需要執行exit或enter函數。以mS5切換到mS4為例,在此切換過程中,首先執行的是EXA,其順序是mS5,mS1。注意,EXA執行的終點是離mS4和mS5最近的一個公共(即同時是mS4和mS5的祖先)祖先State(此處是mP1),但公共祖先狀態的EXA不會執行。然后執行的是EA,其順序是mS2、mS4。同理,公共祖先的EA也不會執行。細心的讀者可以發現,HSM中EA和EXA執行順序和C++類構造/析構函數執行順序類似。EA執行順序由祖先類開始直至子孫類,而析構函數的執行先從子孫類開始,直到祖先類。
* State處理Message時,如果子狀態不能處理(返回NOT_HANDLED),則交給父狀態去處理。這一點也和C++中類的派生函數類似。
HSM的介紹就到此為止,感興趣的讀者可自行研究HSM的實現。
**2、AsyncChannel使用**
AsyncChannel用于兩個Handler之間的通信,其用法包含兩種不同的應用模式(usagemodel)。
* 簡單的request/response模式下,Server端無須維護Client的信息,它只要處理來自Client的請求即可。連接時,Client調用connectSync(同步連接)或connect(異步連接,連接成功后Client會收到CMD_CHANNEL_HALF_CONNECTED消息)即可連接到Server。
* 與request/response模式相反,AsyncChannel中另外一種應用模式就是Server端維護Client的信息。這樣,Server可以向Client發送自己的狀態或者其他一些有意義的信息。與這種模式類似的應用場景就是第4章介紹的wpa_cli和wpa_supplicant。wpa_cli可以發送命令給WPAS去執行。同時,WPAS也會將自己的狀態及其他一些消息通知給wpa_cli。
在WifiService相關模塊中,第二種應用模式使用得較多。另外,AsyncChannel中Client和Server端在最開始建立連接關系時,可以采用同步或異步的方式。以異步方式為例介紹第二種應用模式中AsyncChannel的使用步驟。
1. Client調用AsyncChannel的connect函數。Client的Handler會收到一個名為CMD_CHANNEL_HALF_CONNECTED消息。
2. Client在處理CMD_CHANNEL_HALF_CONNECTED消息時,需通過sendMessage函數向Server端發送一個名為CMD_CHANNEL_FULL_CONNECTION的消息。
3. Server端的Handler將收到此CMD_CHANNEL_FULL_CONNECTION消息。成功處理它后,Server端先調用AsyncChannel的connected函數,然后通過sendMessage函數向Client端發送CMD_CHANNEL_FULLY_CONNECTED消息(特別注意,詳情見下文)。
4. Client端收到CMD_CHANNEL_FULLY_CONNECTED消息。至此,Client和server端成功建立連接。
5. Client和Server端的兩個Handler可借助sendMessage和replyToMessge來完成請求消息及回復消息的傳遞。注意,只有針對那些需要回復的情況,Server端才需調用replyToMessage。
6. 最后,Client和Server的任意一端都可以調用disconnect函數以結束連接。該函數將導致Client和Server端都會收到CMD_CHANNEL_DISCONNECTED消息。
>[info] 特別說明 上述步驟的描述來自AsyncChannel.java文件中的注釋。但實際上Server端代碼在處理CMD_CHANNEL_FULL_CONNECTION消息時并不能按照上面的描述開展工作。因為AsyncChannel對象一般由客戶端創建,而CMD_CHANNEL_FULL_CONNECTION消息無法攜帶AsyncChannel對象(AsyncChannel對象無法通過Binder進行跨進程傳遞)。所以,Server端并不能獲取客戶端創建的這個AsyncChannel對象,它也就沒辦法調用AsyncChannel的connected函數。
那么,Server端的正確處理應該是什么樣子呢?接下來將通過代碼向讀者展示正確的做法。
下面結合WifiManager中的相關代碼來介紹AsyncChannel中第二種模式涉及的一些重要函數。
WifiManager的init函數中會創建一個AsyncChannel以和WifiService中的某個Handler建立連接關系,代碼如下所示。
**WifiManager.java::init**
~~~
private void init() {
/*
該函數內部通過Binder機制調用WifiService的getWifiServiceMessgener函數,返回值
是一個類型為Messagener的對象。Messgener從Parcelable派生,其內部有一個IMessenger
對象用于支持跨進程的Binder通信[^②]。
WifiService中,getWifiServiceMessgener的代碼如下。
public Messenger getWifiServiceMessenger() {
......// 權限檢查
return new Messenger(mAsyncServiceHandler);// 通過Messenger封裝了目標Handler
}
*/
mWifiServiceMessenger = getWifiServiceMessenger();
......
// 創建一個HandlerThread。對HandlerThread不熟悉的讀者請參考腳注
sHandlerThread = new HandlerThread("WifiManager");
sHandlerThread.start();
// Client中的Handler,它將運行在sHandlerThread線程中
// AsyncChannel對Client Handler運行在什么線程沒有要求
mHandler = new ServiceHandler(sHandlerThread.getLooper());
/*
connect是AsyncChannel的重要函數。此處使用的connect函數原型如下:
connect(Context srcContext, Handler srcHandler, Messenger dstMessenger)
srcContext:為Client端的Context對象,AsyncChannel內部將使用它。
srcHandler:為Client端的Handler。
dstMessgener:是Server端Handler在Client端的代表。
*/
mAsyncChannel.connect(mContext, mHandler, mWifiServiceMessenger);
......
}
~~~
connect函數將觸發Client端Handler收到一個CMD_CHANNEL_HALF_CONNECTED消息。馬上來看WIfiManager中ServiceHandler。
Messgener從Parcelable派生,其內部有一個IMessenger對象用于支持跨進程的Binder通信[^②]。
**WifiManager.java::ServiceHandler**
~~~
private class ServiceHandler extends Handler {
......// 此處只關注和AsyncChannel相關的內容
public void handleMessage(Message message) {
......
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:// 半連接成功
if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
// 向Server端發送CMD_CHANNEL_FULL_CONNECTION消息
mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
}......
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: // 連接成功
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:// 連接關閉
mAsyncChannel = null;
getLooper().quit();// 連接關閉,退出線程
break;
......
}
......
}
}
~~~
WifiService中的目標Handler是AsyncServiceHandler,其代碼如下所示。
**WifiService::AsyncServiceHandler**
~~~
private class AsyncServiceHandler extends Handler {
......
// 請讀者先看它是如何處理CMD_CHANNEL_FULL_CONNECTION消息的
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
// 處理因ac.connect調用而收到的CMD_CHANNEL_HALF_CONNECTED消息
// 該消息攜帶了一個AsyncChannel對象,即ac
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
// 保存這個AsyncChannel對象,用于向Client發送消息
mClients.add((AsyncChannel) msg.obj);
/*
注意,Server端可在此處向Client端發送CMD_CHANNEL_FULLY_CONNECTED消息。
例如:
AsyncChannel sample = (AsyncChannel) msg.obj;
sample.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
*/
}
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
mClients.remove((AsyncChannel) msg.obj);
break;
}
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {// Server端先收到此消息
/*
新創建一個AsyncChannel對象ac,然后調用它的connect函數。其中:
msg.replyTo代表Client端的Handler,也就是WifiManager中ServiceHandler。
connect函數將觸發CMD_CHANNEL_HALF_CONNECTED消息被發送,而且該消息
會攜帶對應的AsyncChannel對象,即此處的ac。
請讀者回到handleMessage的前面去看CMD_CHANNEL_HALF_CONNECTED的處理。
*/
AsyncChannel ac = new AsyncChannel();// 創建一個新的AsyncChannel對象
ac.connect(mContext, this, msg.replyTo);
break;
}
......
}
}
}
~~~
根據WifiService的代碼并結合上文“特別說明”,由于Server端無法得到Client端的AsyncChannel對象,所以它干脆自己又新創建了一個AsyncChannel,并connect到客戶端。這樣,Server和Client端實際上有兩個不同的AsyncChannel對象,并且都需要調用connect函數。
>[info] 提示 如果AsyncChannel支持跨進程傳遞,那么Server端只要獲取Client端傳遞過來的AsyncChannel對象,并調用其connected(注意,不是connect)函數即可。
介紹完HSM和AsyncChannel后,馬上來看WifiService的創建和相關的初始化工作。
[^①]:關于Android中Handler的實現原理,讀者可閱讀《深入理解Android:卷Ⅰ》5.4節。
[^②]:關于Java Binder的實現機制,讀者可參考《深入理解Android:卷Ⅱ》第2章。
- 前言
- 第1章 準備工作
- 1.1 Android系統架構
- 1.2 工具使用
- 1.2.1 Source Insight的使用
- 1.2.2 Eclipse的使用
- 1.2.3 BusyBox的使用
- 1.3 本書資源下載說明
- 第2章 深入理解Netd
- 2.1 概述
- 2.2 Netd工作流程
- 2.2.1 main函數分析
- 2.2.2 NetlinkManager分析
- 2.2.3 CommandListener分析
- 2.2.4 DnsProxyListener分析
- 2.2.5 MDnsSdListener分析
- 2.3 CommandListener中的命令
- 2.3.1 iptables、tc和ip命令
- 2.3.2 CommandListener構造函數和測試工具ndc
- 2.3.3 InterfaceCmd命令
- 2.3.4 IpFwd和FirewallCmd命令
- 2.3.5 ListTtysCmd和PppdCmd命令
- 2.3.6 BandwidthControlCmd和IdletimerControlCmd命令
- 2.3.7 NatCmd命令
- 2.3.8 TetherCmd和SoftapCmd命令
- 2.3.9 ResolverCmd命令
- 2.4 NetworkManagementService介紹
- 2.4.1 create函數詳解
- 2.4.2 systemReady函數詳解
- 2.5 本章總結和參考資料說明
- 2.5.1 本章總結
- 2.5.2 參考資料說明
- 第3章 Wi-Fi基礎知識
- 3.1 概述
- 3.2 無線電頻譜和802.11協議的發展歷程
- 3.2.1 無線電頻譜知識
- 3.2.2 IEEE 802.11發展歷程
- 3.3 802.11無線網絡技術
- 3.3.1 OSI基本參考模型及相關基本概念
- 3.3.2 802.11知識點導讀
- 3.3.3 802.11組件
- 3.3.4 802.11 Service介紹
- 3.3.5 802.11 MAC服務和幀
- 3.3.6 802.11 MAC管理實體
- 3.3.7 無線網絡安全技術知識點
- 3.4 Linux Wi-Fi編程API介紹
- 3.4.1 Linux Wireless Extensions介紹
- 3.4.2 nl80211介紹
- 3.5 本章總結和參考資料說明
- 3.5.1 本章總結
- 3.5.2 參考資料說明
- 第4章 深入理解wpa_supplicant
- 4.1 概述
- 4.2 初識wpa_supplicant
- 4.2.1 wpa_supplicant架構
- 4.2.2 wpa_supplicant編譯配置
- 4.2.3 wpa_supplicant命令和控制API
- 4.2.4 git的使用
- 4.3 wpa_supplicant初始化流程
- 4.3.1 main函數分析
- 4.3.2 wpa_supplicant_init函數分析
- 4.3.3 wpa_supplicant_add_iface函數分析
- 4.3.4 wpa_supplicant_init_iface函數分析
- 4.4 EAP和EAPOL模塊
- 4.4.1 EAP模塊分析
- 4.4.2 EAPOL模塊分析
- 4.5 wpa_supplicant連接無線網絡分析
- 4.5.1 ADD_NETWORK命令處理
- 4.5.2 SET_NETWORK命令處理
- 4.5.3 ENABLE_NETWORK命令處理
- 4.6 本章總結和參考資料說明
- 4.6.1 本章總結
- 4.6.2 參考資料說明
- 第5章 深入理解WifiService
- 5.1 概述
- 5.2 WifiService的創建及初始化
- 5.2.1 HSM和AsyncChannel介紹
- 5.2.2 WifiService構造函數分析
- 5.2.3 WifiStateMachine介紹
- 5.3 加入無線網絡分析
- 5.3.1 Settings操作Wi-Fi分析
- 5.3.2 WifiService操作Wi-Fi分析
- 5.4 WifiWatchdogStateMachine介紹
- 5.5 Captive Portal Check介紹
- 5.6 本章總結和參考資料說明
- 5.6.1 本章總結
- 5.6.2 參考資料說明
- 第6章 深入理解Wi-Fi Simple Configuration
- 6.1 概述
- 6.2 WSC基礎知識
- 6.2.1 WSC應用場景
- 6.2.2 WSC核心組件及接口
- 6.3 Registration Protocol詳解
- 6.3.1 WSC IE和Attribute介紹
- 6.3.2 802.11管理幀WSC IE設置
- 6.3.3 EAP-WSC介紹
- 6.4 WSC代碼分析
- 6.4.1 Settings中的WSC處理
- 6.4.2 WifiStateMachine的處理
- 6.4.3 wpa_supplicant中的WSC處理
- 6.4.4 EAP-WSC處理流程分析
- 6.5 本章總結和參考資料說明
- 6.5.1 本章總結
- 6.5.2 參考資料說明
- 第7章 深入理解Wi-Fi P2P
- 7.1 概述
- 7.2 P2P基礎知識
- 7.2.1 P2P架構
- 7.2.2 P2P Discovery技術
- 7.2.3 P2P工作流程
- 7.3 WifiP2pSettings和WifiP2pService介紹
- 7.3.1 WifiP2pSettings工作流程
- 7.3.2 WifiP2pService工作流程
- 7.4 wpa_supplicant中的P2P
- 7.4.1 P2P模塊初始化
- 7.4.2 P2P Device Discovery流程分析
- 7.4.3 Provision Discovery流程分析
- 7.4.4 GO Negotiation流程分析
- 7.5 本章總結和參考資料說明
- 7.5.1 本章總結
- 7.5.2 參考資料說明
- 第8章 深入理解NFC
- 8.1 概述
- 8.2 NFC基礎知識
- 8.2.1 NFC概述
- 8.2.2 NFC R/W運行模式
- 8.2.3 NFC P2P運行模式
- 8.2.4 NFC CE運行模式
- 8.2.5 NCI原理
- 8.2.6 NFC相關規范
- 8.3 Android中的NFC
- 8.3.1 NFC應用示例
- 8.3.2 NFC系統模塊
- 8.4 NFC HAL層討論
- 8.5 本章總結和參考資料說明
- 8.5.1 本章總結
- 8.5.2 參考資料說明
- 第9章 深入理解GPS
- 9.1 概述
- 9.2 GPS基礎知識
- 9.2.1 衛星導航基本原理
- 9.2.2 GPS系統組成及原理
- 9.2.3 OMA-SUPL協議
- 9.3 Android中的位置管理
- 9.3.1 LocationManager架構
- 9.3.2 LocationManager應用示例
- 9.3.3 LocationManager系統模塊
- 9.4 本章總結和參考資料說明
- 9.4.1 本章總結
- 9.4.2 參考資料說明
- 附錄