Android Wi-Fi P2P原理與原始碼學習
一,Wi-Fi P2P相關知識
(一)P2P及其依賴的技術項
(二)P2P工作流程
包括1.裝置的發現、2.組協調、3.認證關聯、4.WPS以及4次握手。總體流程如下圖:
1. Device Discovery工作流程介紹
P2P Device Discovery的工作流程包含兩個狀態和兩個階段。先來看兩個狀態,它們分別是:
- Search State:在該狀態中,P2P Device將在2.4GHz的1,6,11頻段上分別傳送Probe Request幀。這幾個頻段被稱為Social Channels。為了區別非P2P的Probe Request幀,P2P Device Discovery要求必須在Probe Request幀中包含P2P IE。
- Listen State:在該狀態下,P2P Device將隨機選擇在1,6,11頻段中的一個頻段(被選中的頻段被稱為Listen Channel)監聽Probe Request幀並回復Probe Response幀。值得指出的是,Listen Channel一旦選擇好後,在整個P2P Discovery階段就不能更改。另外,在這個階段中,P2P Device只處理那些包含了P2P IE資訊的Probe Request幀。
再來看兩個階段,它們分別是:
- Scan Phase:掃描階段。這一階段和前面章節介紹的無線網路掃描一樣,P2P Device會在各個頻段上傳送Probe Request幀(主動掃描)。P2P Device在這一階段中不會處理來自其他裝置的Probe Request幀。這一階段過後,P2P Device將進入下一個階段,即Find Phase。
- Find Phase:雖然從中文翻譯來看,Scan和Find意思比較接近,但P2P的Find Phase卻和Scan Phase大不相同。在這一階段中,P2P Device將在Search State和Listen State之間來回切換。Search State中,P2P Device將傳送Probe Request幀,而Listen State中,它將接收其他裝置的Probe Request幀並回復Probe Response幀。
Discovery流程如下圖:
- Discovery啟動後,Device首先進入Scan Phase。在這一階段,P2P裝置在其支援的所有頻段上都會發送Probe Request幀。
- Scan Phase完成後,Device進入Find Phase。在這一階段中,Device將在Listen和Search State中切換。根據前面的介紹,每一個裝置的Listen Channel在Discovery開始前就已確定。例如,圖中Device 1的Listen Channel是1,而Device 2的Listen Channel是6。
- 在Find Phase中,P2P規範對Device處於Listen State的時間也有所規定,其時間是100TU的整數倍,倍數值是一個隨機數,位於minDiscoverableInterval和maxDiscoverableInterval之間。這兩個值預設為1和3,而廠商可以修改。選擇隨機倍數的原因是為了防止兩個Device進入所謂的Lock-Step怪圈,即兩個Device同時進入Listen State,等待相同的時間後又同時進入Search State。如此,雙方都無法處理對方的Probe Request資訊(Search State中,Device只發送Probe Request)。圖7-3中,Device 1第一次在Listen State中待了2個100TU,而第二次在Listen State中待了1個100TU。
- 當Device處於Find Phase中的Search State時,它將在1,6,11頻段上傳送Probe Request幀。注意,只有當兩個裝置處於同一頻段時,一方傳送的幀才能被對方接收到。
2.組協調
GO Negotiation涉及三次幀交換,包括GO NegotiationRequest、GO Negotiation Response、GO Negotiation Confirmation,GO NegotiationRequest幀中的GO Intent屬性代表傳送裝置扮演GO的渴望程度,通過一下游戲規則來決定誰扮演GO:
二,Android Wi-Fi P2P原始碼分析(一)
Android中Wi-Fi P2P的操作主要有WifiP2pSettings類實現,其位置在:
packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
這裡先分析該類如何呼叫實現P2P的搜尋與連線,先不考慮底層如何實現。
1.註冊廣播監聽,監聽的內容有:
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
2. 獲取WifiP2pManager和Channel
manager =(WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel =manager.initialize(MainActivity.this, getMainLooper(), null)
3.搜尋周圍可用裝置
1.
manager.discoverPeers(channel,new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() { }
@Override
public void onFailure(int reasonCode) {
}
});
4.監聽裝置變化並獲取裝置列表
mWifiP2pManager.requestPeers(mChannel,WifiP2pSettings.this);
WifiP2pSettings實現PeerListListener介面,重寫onPeersAvailable方法
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
mPeers= peers;
handlePeersChanged();
}
5.連線裝置
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
manager.connect(channel,config, new ActionListener() {
@Override
public void onSuccess() {}
@Override
public void onFailure(int reason) {}
});
相關許可權:
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />
<uses-permission android:name=”android.permission.CHANGE_WIFI_STATE” />
<uses-permission android:name=”android.permission.INTERNET” />
三,Android Wi-Fi P2P原始碼分析(二)
通過上述分析,瞭解到WifiP2pSettings主要通過呼叫WifiP2pManager的方法實現P2P的掃描與連線,下面分析WifiP2pManager及以下層次的實現。
WifiP2pService是處理WiFi P2P相關工作的核心模組,它有一個內部類P2pStateMachine,其互動方式與WiFiService和WifiStateMachine類似。P2pStateMachine有16種狀態,初始狀態為P2pDisabledState(若支援P2P)。
1.初始化
當使用者開啟WiFi功能,WifiStateMachine會向P2pStateMachine傳送CMD_ENABLE_P2P的訊息,由P2pDisabledState處理,主要通過啟動WifiMonitor連線spa_supplicant,之後將轉入InactiveState狀態。
除了初始化階段是由WifiStateMachine發起之外,其餘的操作基本是通過WifiP2pSettings發起的。WifiP2pSettings呼叫WifiP2pManager的方法,WifiP2pManager通過AsyncChannel與WiFiService通訊,而WiFiService則將從WifiP2pManager傳送來的訊息轉發給P2pStateMachine處理。
2.掃描流程
WifiP2pSettings呼叫WifiP2pManager的discoverPeers方法,將傳送DISCOVER_PEERS訊息給P2pStateMachine,該訊息由當前狀態的父狀態P2pEnabledState處理。
@Override
public boolean processMessage(Message message) {
switch (message.what) {
//程式碼省略
case WifiP2pManager.DISCOVER_PEERS:
//程式碼省略
boolean retP2pFind = false;
if (isWfdSinkEnabled()) {
//copy from HE dongle
p2pConfigWfdSink();
retP2pFind = mWifiNative.p2pFind();
} else if (timeout == WifiP2pManager.BEAM_DISCOVERY_TIMEOUT) {
retP2pFind = mWifiNative.p2pFind(
WifiP2pManager.BEAM_DISCOVERY_TIMEOUT);
} else {
retP2pFind = mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
}
//程式碼省略
break;
//程式碼省略
}
}
通過呼叫WifiNative的p2pFind()通知WPAS掃描周圍的P2P裝置,搜尋到裝置後將裝置資訊傳送給WifiMonitor,WifiMonitor然後傳送P2P_DEVICE_FOUND_EVENT給P2pStateMachine,同樣由P2pEnabledState處理。
@Override
public boolean processMessage(Message message) {
switch (message.what) {
//程式碼省略
case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
mPeers.updateSupplicantDetails(device);
sendPeersChangedBroadcast();
///M: force update peer information for invitation @{
if (mUpdatePeerForInvited) {
if (mSavedPeerConfig.deviceAddress
.equals(device.deviceAddress)) {
mUpdatePeerForInvited = false;
sendMessage(M_P2P_DEVICE_FOUND_INVITATION);
}
}
break;
//程式碼省略
break;
//程式碼省略
}
}
上述程式碼主要將搜尋到的裝置儲存到mPeers中,再發送WIFI_P2P_PEERS_CHANGED_ACTION的廣播,WifiP2pSettings收到該廣播後將通過WifiP2pManager的requestPeers獲取mPeers的內容。
3.連線流程
當用戶選擇某個裝置進行連線時,WifiP2pSettings呼叫WifiP2pManager的connect方法發起連線,WifiP2pManager將傳送CONNECT訊息給P2pStateMachine,該訊息有當前狀態InactiveState自己處理:
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiP2pManager.CONNECT:
//程式碼省略
mAutonomousGroup = false;
mConnectToPeer = true;
if (mMiracastMode == WifiP2pManager.MIRACAST_SOURCE
&& !WFD_DONGLE_USE_P2P_INVITE) {
// To support WFD dongle that doesn't support p2p_invite
transitionTo(mProvisionDiscoveryState);
} else {
// Normal connection case
if (reinvokePersistentGroup(config)) {
transitionTo(mGroupNegotiationState);
} else {
transitionTo(mProvisionDiscoveryState);
}
}
mSavedPeerConfig = config;
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendPeersChangedBroadcast();
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
break;
//程式碼省略
}
}
進入reinvokePersistentGroup(config)函式,若為發起端,則返回false,之後進入ProvisionDiscoveryState狀態,若為接受端則啟動Group Formation流程,然後進入GroupNegotiationState狀態。ProvisionDiscoveryState的enter()方法將向對端裝置傳送Provision Discovery Request幀:
mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig)
當對端裝置同意連線之後,將收到一個P2P_PROV_DISC_PBC_RSP_EVENT訊息,由ProvisionDiscoveryState處理:
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiP2pManager.CONNECT:
case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
provDisc = (WifiP2pProvDiscEvent) message.obj;
device = provDisc.device;
if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress))
break;
///M: for crossmount @{
updateCrossMountInfo(provDisc.device.deviceAddress);
///@}
if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
}
break;
//程式碼省略
}
}
p2pConnectWithPinDisplay(mSavedPeerConfig)函式將呼叫WifiNative的p2pConnect函式觸發WPAS傳送GON Request幀,接收端收到該幀,將彈出對話方塊詢問使用者是否接受請求。之後進入GroupNegotiationState。
當WPAS進行GroupFormation結束之後,P2pStateMachine將收到P2P_GROUP_STARTED_EVENT訊息,該訊息由GroupNegotiationState處理。
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case WifiMonitor.P2P_GROUP_STARTED_EVENT:
mGroup = (WifiP2pGroup) message.obj;
//程式碼省略
if (mGroup.isGroupOwner()) {
if (!mAutonomousGroup) {
mWifiNative.setP2pGroupIdle(mGroup.getInterface(),
GROUP_IDLE_TIME_S);
}
startDhcpServer(mGroup.getInterface());
} else {
///M: No DHCP, use static IP @{
if (mGcIgnoresDhcpReq) {
//程式碼省略
} else {
mWifiNative.setP2pGroupIdle(mGroup.getInterface(),
GROUP_IDLE_TIME_S);
startIpManager(mGroup.getInterface());
}
//程式碼省略
}
transitionTo(mGroupCreatedState);
break;//程式碼省略
}
}
若本機扮演GO,則通過startDhcpServer(mGroup.getInterface())啟動DhcpServer,若本機為GC,則通過startIpManager(mGroup.getInterface())去獲取IP地址,之後轉入GroupCreatedState。
當連線成功後,WifiMonitor還會發送一個AP_STA_CONNECTED_EVENT訊息,該訊息由GroupCreatedState處理:
其將呼叫mGroup.addClient(mPeers.get(deviceAddress))新增一個P2P裝置。
至此,連線流程結束。