android wifi p2p / wifi direct
版權宣告:本文為博主原創文章,未經博主允許不得轉載。https://blog.csdn.net/h784707460/article/details/81502574
一. wifi P2P協議相關
Wi-Fi Alliance(Wi-Fi聯盟)推出的一項重要技術規範Wi-Fi P2P。該規範的商品名為Wi-Fi Direct,它支援多個Wi-Fi裝置在沒有AP的情況下相互連線。 藉助P2P技術,Wi-Fi裝置之間的直接相連將極大拓展Wi-Fi技術的使用場景。如智慧終端裝置之間的多屏共享和互動功能。
1.1 P2P架構
p2p架構中定義了兩種角色,分別是:
- P2P Group Owner:簡稱GO,其作用類似於Infrastructure BSS中的AP;
- P2P Client:簡稱GC,其作用類似於Infrastructure BSS中的STA。
1.2 Device Discovery
Device Discovery使用Probe Request和Probe Response幀來交換裝置資訊。
P2P為Device Discovery定義了兩個狀態和兩個階段。
兩個狀態:
- Search State:在該狀態中,P2P Device將在2.4GHz的1,6,11頻段上分別傳送Probe Request幀。這幾個頻段被稱為Social Channels。
- Listen State:在該狀態下,P2P Device將隨機選擇在1,6,11頻段中的一個頻段(被選中的頻段被稱為Listen Channel)監聽Probe Request幀並回復Probe Response幀。Listen Channel一旦選擇好後,在整個P2P Discovery階段就不能更改。
兩個階段:
- Scan Phase:掃描階段。P2P Device會在其所支援的所有頻段上傳送Probe Request幀(主動掃描)。用於快速發現周圍的Group。P2P Device在這一階段中不會處理來自其他裝置的Probe Request幀。(全通道掃描)
- Find Phase:P2P Device將在Search State和Listen State之間來回切換。Search State中,P2P Device將傳送Probe Request幀,而Listen State中,它將接收其他裝置的Probe Request幀並回復Probe Response幀。
注:Operating Channel屬性表示組建的P2P Group將在哪個頻段上工作。並且其頻段不限於Social Channels中指定的那三個頻段。
1.3 GO Formation
GO Formation 包含兩個階段:
- GO Negotiation:在這一階段中,兩個Device要協商好由誰來做GO。
- Provisioning:GO和Client角色確定後,兩個Device要藉助WSC來交換安全配置資訊。此後,Client就可以利用安全配置資訊關聯上GO。(Provision Discovery(PD)流程是為了確定互動雙方使用的WSC方法,不屬於GF流程)
GO協商包含一個三路握手過程,用於協商誰是GO以及P2P組的一些特徵。 這三路握手過程主要主要交換如下一些資訊:
- Group Owner Intent Value
- Acceptable Operating Channel the Group may use
- Credentials for the P2P Group
- Group Duration: Temporary or Persistent
- Group support for other devices and optional capabilities
當一個P2P裝置傳送GO Negotiation Request後,100ms內沒有接收到確認幀,則認為此次協商失敗。GO Negotiation的一個主要目的就是交換GO Intent屬性,以決定誰當GO。如果一個P2P裝置只能當GO,則其GO Intent值必須設定為15。
第一個GO Negotiation Request幀的Tie Breaker位會被隨機設定為0或1。在接下來的GO Negotiation Request幀中(該位會被置反,重傳除外)。GO Negotiation Response幀的Tie breaker位將會根據相應的GO Negotiation Request幀的相應位置反。
wifi p2p工作流程
二. HSM和AsyncChannel介紹
HSM(對應的類是StateMachine)和AsyncChannel是Android Java Framework中兩個重要的類。目前還僅由Framework內部使用,SDK中並沒有包含它們。
2.1 HSM(Hierarchical State Machine,結構化狀態機)
HSM在傳統狀態機對所有狀態都一視同仁的基礎上做了一些改變,使得狀態和狀態之間有了層級關係。在父狀態中實現generic的功能,而在子狀態中實現一些特定的處理。
State中重要函式:
- enter, SM進入某狀態時將呼叫其EA(Entry Action);
- exit,退出某狀態時將呼叫其EXA(Exit Action);
- processMessage,在HSM中,外界和HSM互動的方式就是向其sendMessage。Message由當前State的processMessage函式來處理。如果當前State成功處理此message,則返回HANDLED。否則返回NOT_HANDLED。在Message處理中,如果子狀態返回NOT_HANDLED,則其父狀態的processMessage將被呼叫。如果當前狀態及祖先狀態都不能處理,則HSM的unhandledMessage將被呼叫。
HSM中一些重要的API:
- addState:新增一個狀態。同時還可指定父狀態。
- transitionTo:將狀態機切換到某個狀態。
- obtainMessage:由於HSM內部是圍繞一個Handler來工作的,所以外界只能呼叫HSM的obtainMessage以獲取一個Message。
- sendMessage:傳送訊息給HSM。HSM中的Handler會處理它。
- start:啟動狀態機。
- 停止狀態機可使用quit或quitNow函式。
HSM中狀態和狀態之間的層級關係體現:
- SM啟動後,初始狀態的EA將按派生順序執行。即其祖先狀態的EA先執行,子狀態的 EA後執行。
- 當State發生切換時,舊State的exit先執行,新State的enter後執行,並且新舊State派生樹上對應的State也需要執行exit或enter函式。但公共祖先狀態的EXA和EA不會執行。EA執行順序由祖先類開始直至子孫類,而EXA執行先從子孫類開始,直到祖先類。
- State處理Message時,如果子狀態不能處理(返回NOT_HANDLED),則交給父狀態去 處理。
2.2 AsyncChannel
AsyncChannel用於兩個Handler之間的通訊。具體的通訊方式為源Handler通過sendMessage向目標Handler傳送訊息,而目標Handler通過replyToMessage回覆源Handler處理結果。這兩個Handler可位於同一個程序,也可分屬兩個不同的程序.
用法包含兩種不同的應用模式(usage model)。
- 簡單的request/response模式(一對多的通訊方式)下,Server端無須維護Client的資訊,它只要處理來自Client的請求即可。通訊前雙方不需要顯示建立連線。客戶端(傳送方)將請求傳送給伺服器(接收方),伺服器則通過replayToMessage方法向客戶端傳送應答訊息。
- 一對一的通訊方式,這種應用模式就是Server端維護Client的資訊。這樣,Server可以向Client傳送自己的狀態或者其他一些有意義的資訊。
WifiService相關模組中,第二種應用模式使用得較多。
三. WifiP2pSettings和WifiP2pService介紹
WifiP2pSettings是Settings應用中負責處理P2P相關UI/UE邏輯的主要類,與之互動的則是位於SystemServer程序中的WifiP2pService。
3.1 涉及原始碼檔名及位置(Marshmallow - 6.0.1_r10)
WifiP2pManager /frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
WifiP2pSettings packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
WifiP2pServiceImpl /frameworks/opt/net/wifi/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
WifiNative /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
WifiMonitor /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java
3.2 WifiP2pSettings
載入介面元素
通過監聽廣播的方式來了解系統中Wi-Fi P2P相關的資訊及變化情況,進行相應的處理:
- WIFI_P2P_STATE_CHANGED_ACTION:用於通知系統中P2P功能的啟用情況,如該功能是enable還是disable。
- WIFI_P2P_PEERS_CHANGED_ACTION:系統內部將儲存搜尋到的其他P2P裝置資訊,如果這些資訊有變化,則系統將傳送該廣播。接收者需要通過WifiP2pManager的requestPeers函式重新獲取這些P2P裝置的資訊。
- WIFI_P2P_CONNECTION_CHANGED_ACTION:用於通知P2P連線情況,該廣播可攜帶WifiP2pInfo和NetworkInfo兩個物件。相關資訊可從這兩個物件中獲取。
- WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:用於通知本機P2P裝置資訊發生了變化。
- WIFI_P2P_DISCOVERY_CHANGED_ACTION:用於通知P2P Device Discovery的工作狀態,如啟動或停止。
- WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION:用於通知之persistent group資訊發生了變化。
WifiP2pSettings將藉助WifiP2pManager和系統中的WifiP2pService互動。WifiP2pSettings主要使用WifiP2pManager的幾個重要函式:
- initialize:初始化WifiP2pManager.Channel ,WifiP2pManager內部將和WifiP2pService通過AsyncChannel建立互動關係。
- discoverPeers:觸發WifiP2pService發起P2P Device掃描工作。
- requestPeers:獲取系統中儲存的P2P Device資訊。
- connect:和指定P2P Device發起連線,也就是相互協商以建立或加入一個P2P網路,即一個Group。
- requestGroupInfo:獲取當前連線上的Group資訊。
3.3 WifiP2pService
WifiP2pService是Android系統中P2P功能的Java層核心模組。其家族類圖如下。
- IWifiP2pManager、IWifiP2pManager.Stub和IWifiP2pManager.Stub.Proxy類均由IWifiP2pManager.aidl檔案在編譯時通過aidl工具轉換而來。
- WifiP2pService派生自IWifiP2pManager.Stub類,它是Binder服務端。
- WifiP2pManager是WifiP2pService的客戶端。它通過成員變數mService和WifiSP2pervice進行Binder互動。
P2pStateMachine是WifiP2pService定義的內部類,P2pStateMachine是WifiP2pService的核心。P2pStateMachine中定義的各個狀態及層級關係如圖:
3.4 WifiP2pService工作流程
P2pStateMachine初始狀態為P2pDisabledState,然後:
1)P2pStateMachine收到來自WifiStateMachine的CMD_ENABLE_P2P訊息,在該訊息的處理邏輯中,P2pStateMachine將建立一個WifiMonitor物件以和wpa_supplicant程序互動。P2pStateMachine轉入P2pEnablingState。
2)在P2pEnablingState中,處理SUP_CONNECT_EVENT訊息,它代表WifiMonitor成功連線上了wpa_supplicant。P2pStateMachine將轉入InactiveState。
3)InactiveState的父狀態是P2pEnabledState,P2pEnabledState的EA將初始化P2P設定,這部分程式碼邏輯在initializeP2pSettings函式中。另外,WifiP2pSettings將收到一些P2P廣播,此時P2P功能正常啟動。
4)搜尋裝置時,P2pStateMachine將收到DISCVOER_PEERS訊息。它在P2pEnabledState中被處理,wpas_supplicant將發起P2P DeviceDiscovery流程以搜尋周圍的P2P裝置。
5)若有P2P裝置被發現,P2pStateMachine將收到P2P_DEVICE_FOUND_EVENT訊息。該訊息由其父狀態P2pEnabledState來處理。同時,WifiP2pSettings也會相應收到資訊以更新UI。
6)連線時,WifiP2pSettings將傳送CONNECT訊息給P2pStateMachine。該訊息由InactiveState來處理。大部分情況下(除了Persistent Group或者對端裝置是GO的情況下),P2pStateMachine將轉入ProvisionDiscoveryState。
7)ProvisionDiscoveryState中,P2pStateMachine將通知WPAS以開展ProvisioningDiscovery流程。之後,P2pStateMachine將收到P2P_PROV_DISC_PBC_RSP_EVENT訊息。在該訊息的處理過程中,P2pStateMachine將通過p2pConnectWithPinDisplay函式通知WPAS和對端裝置啟動Group Formation流程。此後,P2pStateMachine轉入GroupNegotiationState。
8)Group Formation完成,一個Group也就建立成功,P2pStateMachine將收到P2P_GROUP_STARTED_EVENT訊息。該訊息由GroupNegotiationState處理。如果本機扮演GO的話,它將啟動一個Dhcp伺服器
9)當對端P2P Client(Group建立後,角色也就確定了)關聯上本機的GO後, AP_STA_CONNECTED_EVENT訊息將被髮送給P2pStateMachine處理。
四. P2P流程
相關檔案簡介:
1、WifiP2pSettings 屬於應用程式層,是對相關操作結果的一個視覺呈現。主要負責介面初始化、接收底層事件訊息進行介面更新、發出相應命令等。
2、WifiP2pManager:是Wi-Fi P2P部分對外的介面,通過它來訪問Wi-Fi P2p的核心功能。
3、WifiP2pService:是Wi-Fi P2p部分的核心,負責Wi-Fi整個流程的控制。
4、P2pStateMachine:繼承了StateMachine。下發了載入驅動和啟動supplicant命令,啟動了WifiMonitor。
5、WifiMonitor:開啟一個MonitorThread來實現事件的輪詢,而輪詢的關鍵函式是WifiNative.waitForEvent()。WifiMonitor將接收到的底層事件轉換成WifiStateMachine所能識別的訊息,然後將訊息傳送給WifiStateMachine。
6、WifiNative:封裝了一系列本地呼叫的介面函式,通過JNI呼叫C++程式碼。
4.1 P2P掃描流程
APP
WifiP2pManager manager;
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2pManager.Channel
channel; channel = manager.initialize(this, getMainLooper(), null);
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {;}
@Override
public void onFailure(int reasonCode) {;}
}
WifiP2pManager:
public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {return initalizeChannel(srcContext, srcLooper, listener, getMessenger());}
private Channel initalizeChannel(Context srcContext, Looper srcLooper, ChannelListener listener,Messenger messenger) {
if (messenger == null) return null;
Channel c = new Channel(srcContext, srcLooper, listener);
if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
== AsyncChannel.STATUS_SUCCESSFUL) {
return c;
} else {return null;}
}
public void discoverPeers(Channel c, ActionListener listener) {
checkChannel(c);//void,c是否為空,若空,丟擲IllegalArgumentException異常
c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
}
WifiP2pService:
P2pStateMachine當前處於InactiveState,不過DISCOVER_PEERS訊息卻是由其父狀態
P2pEnabledState來處理的.
case WifiP2pManager.DISCOVER_PEERS:
if (mDiscoveryBlocked) {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.BUSY);
break;
}
// do not send service discovery request while normal find operation.
clearSupplicantServiceRequest();
if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {//向WPAS傳送P2P_FIND命令
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
sendP2pDiscoveryChangedBroadcast(true);
} else {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.ERROR);
}
break;
WifiP2pService:
上述處理流程主要呼叫WifiNative的p2pFind函式,它會向wpa_supplicant傳送P2P_FIND命令。若成功傳送這個命令,就會通過WifiManager中discoverPeers的第二個引數ActionListener來告知Application執行成功;若執行失敗,也會通知Application,只是回撥不同的方法,一個是onSuccess(),一個是onFailure()。
wpa_supplicant收到P2P_FIND後,開始搜尋周邊的P2P裝置,若找到,給WifiMonitor傳送P2P-DEVICE-FOUND這樣的event,WifiMonitor收到後,會將P2P-DEVICE-FOUND後面的data資料封裝成為WifiP2pDevice物件,然後傳送P2P_DEVICE_FOUND_EVENT訊息給WIfiStateMachine處理。
P2P_DEVICE_FOUND_EVENT也由InactiveState的父狀態P2pEnabledState來處理:
case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
mPeers.updateSupplicantDetails(device);
sendPeersChangedBroadcast();
break;
APP:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
if (manager != null) {
manager.requestPeers(channel, (WifiP2pManager.PeerListListener) activity.getFragmentManager().findFragmentById(R.id.frag_list));
}
}
WifiP2pManager:
public void requestPeers(Channel c, PeerListListener listener) {
checkChannel(c);
c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
}
4.2 P2P連線流程
P2P裝置連線有四種情況:主動連線、被動連線、主動invite和被動invite 這裡以主動連線介紹P2P連線流程。
APP:
mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
// 設定對端P2P Device的WSC配置方法為PBC
config.wps.setup = WpsInfo.PBC;
((DeviceListFragment.DeviceActionListener) getActivity()).connect(config);
}
});
public void connect(WifiP2pConfig config) {
manager.connect(channel, config, new WifiP2pManager.ActionListener() {
public void onSuccess() {}
public void onFailure(int reason) {}
});
}
WifiP2pManager:
public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {
checkChannel(c);
checkP2pConfig(config);
c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config);
}
private static void checkP2pConfig(WifiP2pConfig c) {
if (c == null) throw new IllegalArgumentException("config cannot be null");
if (TextUtils.isEmpty(c.deviceAddress)) {
throw new IllegalArgumentException("deviceAddress cannot be empty");
}
}
WifiP2pManager的connect函式將傳送CONNECT訊息給P2pStateMachine,該訊息由
InactiveState狀態自己來處理
WifiP2pService:
case WifiP2pManager.CONNECT:
if (DBG) logd(getName() + " sending connect");
WifiP2pConfig config = (WifiP2pConfig) message.obj;
if (isConfigInvalid(config)) {
loge("Dropping connect requeset " + config);
replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
break;
}
mAutonomousGroup = false;
mWifiNative.p2pStopFind();
if (reinvokePersistentGroup(config)) {//判斷是否採用persisten連線
transitionTo(mGroupNegotiationState);
} else { transitionTo(mProvisionDiscoveryState);}
mSavedPeerConfig = config;
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendPeersChangedBroadcast();
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
break;
WifiP2pService:
Group分為persistent和Temporary兩種,對於persistent的連線,一次連線成功後,連線資訊不在變化,包含credential、GO MAC地址、Ssid等資訊。若採用persisten連線,就是去wpa_supplicant拿這些資訊,並進行匹配。對於temporary,採用negotiate方式,則跳轉到ProvisionDiscoveryState,其父狀態是GroupCreatingState。
class GroupCreatingState extends State {
public void enter() {
if (DBG) logd(getName());
sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
}
class ProvisionDiscoveryState extends State {
public void enter() {
if (DBG) logd(getName());
mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
}
WifiNative的p2pProvisionDiscovery向對方傳送Provision discovery封包。當對方收到Provision discovery封包後,會回覆provision response,wpa_supplicant處理後,會給WifiMonitor傳送P2P-PROV-DISC-PBC-RESP(當WPS方式為PBC時),WifiMonitor就會給P2pStateMachine傳送P2P_PROV_DISC_PBC_RSP_EVNET。
WifiP2pService:
case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
provDisc = (WifiP2pProvDiscEvent) message.obj;
device = provDisc.device;
if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
}
break;
private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());//向wpa_supplicant傳送P2P_CONNECT命令
try {
Integer.parseInt(pin);
notifyInvitationSent(pin, config.deviceAddress);
} catch (NumberFormatException ignore) {}
}
WifiP2pService:
開始Group negotiate後,wpa_supplicant會發送多個event給WifiMonitor,包括P2P-GO-NEG-SUCCESS、WPS-SUCCESS、P2P-GROUP-FORMATION-SUCCESS、P2P-GROUP-STARTED等.
其中比較重要的是P2P-GROUP-STARTED這個event,WifiMonitor收到這個event後會給P2pStateMachine傳送P2P_GROUP_STARTED_EVENT,收到這個訊息後,GroupNegotiationState主要呼叫DHCP相關的StateMachine開始會兩端分配IP,然後更新group owner的相關資訊,最後跳轉至GroupCreatedState就表示連線完成了。
在DhcpStateMachine獲取到IP地址以後,就會發送DhcpStateMachine.CMD_POST_DHCP_ACTION訊息給P2pStateMachine
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
DhcpResults dhcpResults = (DhcpResults) message.obj;
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS && dhcpResults != null) {
if (DBG) logd("DhcpResults: " + dhcpResults);
setWifiP2pInfoOnGroupFormation(dhcpResults.serverAddress);
sendP2pConnectionChangedBroadcast();
//Turn on power save on client
mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
try { String iface = mGroup.getInterface();
mNwService.addInterfaceToLocalNetwork(iface, dhcpResults.getRoutes(iface));
} catch (RemoteException e) { }
} else { loge("DHCP failed"); mWifiNative.p2pGroupRemove(mGroup.getInterface()); }
break;
其他三種連線
PS:
最近在測試wifi p2p相關. 發現一些流程已經不是很清楚了, 翻出來兩年前自己整理的資料, 發現遠超現在的水平.
逆水行舟, 不進則退.