WifiP2pService的啓動以及P2P的連接

這一章主要總結從WifiP2pService的啓動到用戶通過四種連接方式連接P2P的過程,四種方式包括:主動連接、被動連接、主動invite和被動invite。首先來看WifiP2pService的啓動。


WifiP2pService的啓動

WifiP2pService的創建以及啓動是在SystemServer中,主要代碼如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. wifiP2p = new WifiP2pService(context);  
  2. ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);  
  3.   
  4. wifiP2p.connectivityServiceReady();  

進入到WifiP2pService的構造函數分析:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public WifiP2pService(Context context) {  
  2.     mContext = context;  
  3.   
  4.     //STOPSHIP: get this from native side  
  5.     mInterface = "p2p0";  
  6.     mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");  
  7.   
  8.     mP2pSupported = mContext.getPackageManager().hasSystemFeature(  
  9.             PackageManager.FEATURE_WIFI_DIRECT);  
  10.   
  11.     mThisDevice.primaryDeviceType = mContext.getResources().getString(  
  12.             com.android.internal.R.string.config_wifi_p2p_device_type);  
  13.   
  14.     mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);  
  15.     mP2pStateMachine.start();  
  16. }  

WifiP2pService的構造函數很簡單,主要是創建一個NetworkInfo的對象,然後通過PackageManagerService獲取到系統是否支持P2P,然後創建一個P2pStateMachine並啓動這個StateMachine,進入到P2pStateMachine中分析:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. P2pStateMachine(String name, boolean p2pSupported) {  
  2.     super(name);  
  3.   
  4.     addState(mDefaultState);  
  5.         addState(mP2pNotSupportedState, mDefaultState);  
  6.         addState(mP2pDisablingState, mDefaultState);  
  7.         addState(mP2pDisabledState, mDefaultState);  
  8.         addState(mP2pEnablingState, mDefaultState);  
  9.         addState(mP2pEnabledState, mDefaultState);  
  10.             addState(mInactiveState, mP2pEnabledState);  
  11.             addState(mGroupCreatingState, mP2pEnabledState);  
  12.                 addState(mUserAuthorizingInviteRequestState, mGroupCreatingState);  
  13.                 addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState);  
  14.                 addState(mProvisionDiscoveryState, mGroupCreatingState);  
  15.                 addState(mGroupNegotiationState, mGroupCreatingState);  
  16.                 addState(mFrequencyConflictState, mGroupCreatingState);  
  17.             addState(mGroupCreatedState, mP2pEnabledState);  
  18.                 addState(mUserAuthorizingJoinState, mGroupCreatedState);  
  19.                 addState(mOngoingGroupRemovalState, mGroupCreatedState);  
  20.   
  21.     if (p2pSupported) {  
  22.         setInitialState(mP2pDisabledState);  
  23.     } else {  
  24.         setInitialState(mP2pNotSupportedState);  
  25.     }  
  26.     setLogRecSize(50);  
  27.     setLogOnlyTransitions(true);  
  28. }  

通過上面的構造函數,我們可以看出P2pStateMachine的各個State關係如下。並且若在支持P2P的情況下,InitialState是P2pDisabledState;若在不支持P2P的情況下,InitialState是P2pNotSupportedState。


接着回到SystemServer中,會調用到WifiP2pService的connectivityServiceReady函數,這個函數比較簡單,首先通過ServiceManager獲取NETWORKMANAGEMENT_SERVICE,然後記錄到mNwService中。這樣WifiP2pService的啓動流程就介紹完畢了,下面再來看當Enable Wifi時,如何Enable P2p。

Enable P2p


還記得我們在Wifi toggle on這個章節有說過,在WifiStateMachine的DriverStartedState中,如平臺支持P2P,還將會給WifiP2pService發送CMD_ENABLE_P2P的消息,在Android 4.4的平臺上面,當打開Wifi時,會同時把P2p也enable,我們從這裏開始分析。首先由前面的知識,我們進入P2pDisabledState看如何處理CMD_ENABLE_P2P消息:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class P2pDisabledState extends State {  
  2.    @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.     }  
  6.   
  7.     @Override  
  8.     public boolean processMessage(Message message) {  
  9.         if (DBG) logd(getName() + message.toString());  
  10.         switch (message.what) {  
  11.             case WifiStateMachine.CMD_ENABLE_P2P:  
  12.                 try {  
  13.                     mNwService.setInterfaceUp(mInterface);  
  14.                 } catch (RemoteException re) {  
  15.                     loge("Unable to change interface settings: " + re);  
  16.                 } catch (IllegalStateException ie) {  
  17.                     loge("Unable to change interface settings: " + ie);  
  18.                 }  
  19.                 mWifiMonitor.startMonitoring();  
  20.                 transitionTo(mP2pEnablingState);  
  21.                 break;  

首先通過NetworkManagementService將P2P0設置爲up狀態,然後通過WifiMonitor去和wpa_supplicant建立socket連接,因爲在WifiStateMachine的DriverStartedState中,wpa_supplicant已經啓動成功了,所以我們這裏可以直接去和wpa_supplicant通信,最後轉到P2pEnablingState處理和wpa_supplicant連接成功與否的消息。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class P2pEnablingState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.     }  
  6.   
  7.     @Override  
  8.     public boolean processMessage(Message message) {  
  9.         if (DBG) logd(getName() + message.toString());  
  10.         switch (message.what) {  
  11.             case WifiMonitor.SUP_CONNECTION_EVENT:  
  12.                 if (DBG) logd("P2p socket connection successful");  
  13.                 transitionTo(mInactiveState);  
  14.                 break;  
  15.             case WifiMonitor.SUP_DISCONNECTION_EVENT:  
  16.                 loge("P2p socket connection failed");  
  17.                 transitionTo(mP2pDisabledState);  
  18.                 break;  
  19.             case WifiStateMachine.CMD_ENABLE_P2P:  
  20.             case WifiStateMachine.CMD_DISABLE_P2P_REQ:  
  21.                 deferMessage(message);  
  22.                 break;  
  23.             default:  
  24.                 return NOT_HANDLED;  
  25.         }  
  26.         return HANDLED;  
  27.     }  
  28. }  

由Wifi toggle on的知識我們可以知道,當WifiMonitor與wpa_suppliant建立socket成功後,會給P2pStateMachine發送SUP_CONNECT_EVENT消息,P2pEnablingState這時會跳轉到InactiveState中,由前面P2pStateMachine的State關係圖我們可以知道,P2pEnabledState是InactiveState父State,我們先到P2pEnabledState的enter函數去分析:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class P2pEnabledState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         sendP2pStateChangedBroadcast(true);  
  6.         mNetworkInfo.setIsAvailable(true);  
  7.         sendP2pConnectionChangedBroadcast();  
  8.         initializeP2pSettings();  
  9.     }  

這裏主要發送兩個broadcast,然後初始化一些P2p的設置,包括deviceName、DeviceType、ConfigMethods,並且獲取以前persistent的相關device信息。


P2p設備掃描

當用戶到Settings-->P2p中點擊掃描後,就開始啓動discoverPeers的流程了,首先來看一下WifiP2pManager提供給我們的文檔說明:
The API is asynchronous and responses to requests from an application are on listener callbacks provided by the application. The application needs to do an initialization with initialize(Context, Looper, WifiP2pManager.ChannelListener) before doing any p2p operation.

這段話告訴我們WifiP2pManager提供給application的函數都是異步的,並且在調用其它p2p的方法之前,需要先調用initialize方法,那我們首先來看一下initialize的實現:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {  
  2.     Messenger messenger = getMessenger();  
  3.     if (messenger == nullreturn null;  
  4.   
  5.     Channel c = new Channel(srcContext, srcLooper, listener);  
  6.     if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)  
  7.             == AsyncChannel.STATUS_SUCCESSFUL) {  
  8.         return c;  
  9.     } else {  
  10.         return null;  
  11.     }  
  12. }  

這段代碼主要的功能是創建一個Channel對象,Channel是將application和wifi p2p framework連接的對象,當然我們知道wifi p2p framework中最核心的還是WifiP2pService,所以Channel需要將application的消息發送給WifiP2pService,所以我們就看到函數開頭的getMessenger函數,通過這個函數會利用WifiP2pService中的P2pStateMachine的handler創建一個Messenger對象並返回;然後Channel對象中又會創建一個AsyncChannel對象,通過AsyncChanne將Channel自身的handler與P2pStateMachine聯繫起來。我們可以簡單的理解Channel是對AsyncChannel的封裝,用於實現applicantion與WifiP2pService之間的異步通信。流程圖文章最底下圖中的左上方。

接着我們就可以調用discoverPeers來進行P2P設備的掃描了,代碼如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void discoverPeers(Channel c, ActionListener listener) {  
  2.     checkChannel(c);  
  3.     c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));  
  4. }  

由前面Enable P2p的知識,我們知道P2pEnabledState會處理DISCOVER_PEERS消息,代碼如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiP2pManager.DISCOVER_PEERS:  
  2.     if (mDiscoveryBlocked) {  
  3.         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,  
  4.                 WifiP2pManager.BUSY);  
  5.         break;  
  6.     }  
  7.     // do not send service discovery request while normal find operation.  
  8.     clearSupplicantServiceRequest();  
  9.     if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {  
  10.         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);  
  11.         sendP2pDiscoveryChangedBroadcast(true);  
  12.     } else {  
  13.         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,  
  14.                 WifiP2pManager.ERROR);  
  15.     }  
  16.     break;  

P2pEnabledState處理DISCOVER_PEERS流程中主要調用WifiNative的p2pFind函數,它會向wpa_supplicant發送P2P_FIND命令。若成功發送這個命令,就會通過WifiManager中discoverPeers的第二個參數ActionListener來告知Application執行成功;若執行失敗,也會通知Application,只是回調不同的方法,一個是onSuccess(),一個是onFailure()。

wpa_supplicant收到P2P_FIND後,就會開始搜索周邊的P2P設備,如果有找到,則會給WifiMonitor發送P2P-DEVICE-FOUND這樣的event,這個event會帶有對方設備的信息,包括MAC地址、device type、設備名字以及config methods等,例如P2P-DEVICE-FOUND 02:08:22:22:ec:fb p2p_dev_addr=02:08:22:22:ec:fb pri_dev_type=10-0050F204-5 name='Android_7a5f' config_methods=0x188 dev_capab=0x25 group_capab=0x0 wfd_dev_info=0x00000600101c440032。WifiMonitor收到這樣的event後,會將P2P-DEVICE-FOUND後面的data數據封裝成爲一個WifiP2pDevice對象,然後發送P2P_DEVICE_FOUND_EVENT消息給WIfiStateMachine處理。接着我們到P2pEnabledState看如何處理P2P_DEVICE_FOUND_EVENT:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_DEVICE_FOUND_EVENT:  
  2.     WifiP2pDevice device = (WifiP2pDevice) message.obj;  
  3.     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;  
  4.     mPeers.updateSupplicantDetails(device);  
  5.     sendPeersChangedBroadcast();  
  6.     break;  

mPeers是一個WifiP2pDeviceList對象,它內部維護一個全局的HashMap來維護所有找到的P2P 設備信息,HashMpa的key是P2P設備的MAC地址,值即爲上面的WifiP2pDevice對象。然後通過sendPeersChangedBroadcast發送一個 WIFI_P2P_PEERS_CHANGED_ACTION的broadcast,這樣WifiP2pSettings收到這個broadcast後,就可以直接通過WifiP2pDeviceList對象來訪問裏面的HasmMap來顯示所有的P2P設備了。

連接P2P設備

這裏開始介紹四種連接方式:主動連接、被動連接、主動invite和被動invite

主動連接

先來看一下這部分的總體流程圖:


當用戶選擇掃描到的設備列表中的一個設備後,就會開始連接流程,通過調用WifiManager.connect(Channel c, WifiP2pConfig config, ActionListener listener),其中第一個參數Channel即從initialize中返回的,第二個參數WifiP2pConfig包含對方設備的MAC地址、WPS等信息。進入到connect函數看這個方法的實現:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void connect(Channel c, WifiP2pConfig config, ActionListener listener) {  
  2.     checkChannel(c);  
  3.     checkP2pConfig(config);  
  4.     c.mAsyncChannel.sendMessage(CONNECT, 0, c.putListener(listener), config);  
  5. }  

通過Channel中的AsyncChannel給P2pStateMachine發送一個CONNECT消息,由前面的掃描階段的知識,InactiveState會處理CONNECT消息:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiP2pManager.CONNECT:  
  2.     if (DBG) logd(getName() + " sending connect");  
  3.     WifiP2pConfig config = (WifiP2pConfig) message.obj;  
  4.     if (isConfigInvalid(config)) {  
  5.         loge("Dropping connect requeset " + config);  
  6.         replyToMessage(message, WifiP2pManager.CONNECT_FAILED);  
  7.         break;  
  8.     }  
  9.   
  10.     mAutonomousGroup = false;  
  11.     mWifiNative.p2pStopFind();  
  12.     if (reinvokePersistentGroup(config)) {  
  13.         transitionTo(mGroupNegotiationState);  
  14.     } else {  
  15.         transitionTo(mProvisionDiscoveryState);  
  16.     }  
  17.     mSavedPeerConfig = config;  
  18.     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);  
  19.     sendPeersChangedBroadcast();  
  20.     replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);  
  21.     break;  

這裏分爲persistent的連接方式和非persistent方式,對於persistent的連接,如果第一次連接成功後,wpa_supplicant會記錄這次連接的所有信息,包含credential、GO MAC地址、Ssid等信息,所以判斷對方是否採用persisten連接,就是去wpa_supplicant拿這些信息,並進行匹配。對於非persistent的連接方式,即採用negotiate方式,則跳轉到ProvisionDiscoveryState,用於發送Provision discovery封包,ProvisionDiscoveryState的父State是GroupCreatingState。首先到GroupCreartingState和ProvisionDiscoveryState的enter函數去分析:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class GroupCreatingState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,  
  6.                 ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);  
  7.     }  
  8.   
  9. class ProvisionDiscoveryState extends State {  
  10.     @Override  
  11.     public void enter() {  
  12.         if (DBG) logd(getName());  
  13.         mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);  
  14.     }  

在GroupCreartingState中會發送一個delay message,用於設置P2P連接的timeout時間,timeout時間爲120秒。在ProvisionDiscoveryState中調用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,來看ProvisionDiscoveryState如何處理這個消息:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.             case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:  
  2.                 provDisc = (WifiP2pProvDiscEvent) message.obj;  
  3.                 device = provDisc.device;  
  4.                 if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;  
  5.   
  6.                 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {  
  7.                     if (DBG) logd("Found a match " + mSavedPeerConfig);  
  8.                     p2pConnectWithPinDisplay(mSavedPeerConfig);  
  9.                     transitionTo(mGroupNegotiationState);  
  10.                 }  
  11.                 break;  
  12.   
  13. private void p2pConnectWithPinDisplay(WifiP2pConfig config) {  
  14.     WifiP2pDevice dev = fetchCurrentDeviceDetails(config);  
  15.   
  16.     String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());  
  17.     try {  
  18.         Integer.parseInt(pin);  
  19.         notifyInvitationSent(pin, config.deviceAddress);  
  20.     } catch (NumberFormatException ignore) {  
  21.         // do nothing if p2pConnect did not return a pin  
  22.     }  
  23. }  

ProvisionDiscoveryState通過調用p2pConnectWithPinDisplay,然後調用WifiNative的p2pConnect向wpa_supplicant發送P2P_CONNECT命令,如果採用keyPad的連接方式,會彈出pin碼的對話框,供用戶在對方設備上填入pin碼;如果採用PBC的連接方式,WifiNative.p2pConnect返回”OK“,Integer.parseInt拋出異常,就不會彈出對話框了。然後ProvisionDiscoveryState跳轉到GroupNegotiationState:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class GroupNegotiationState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.     }  
  6.   
  7.     @Override  
  8.     public boolean processMessage(Message message) {  
  9.         if (DBG) logd(getName() + message.toString());  
  10.         switch (message.what) {  
  11.             case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:  
  12.             case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:  
  13.                 if (DBG) logd(getName() + " go success");  
  14.                 break;  
  15.             case WifiMonitor.P2P_GROUP_STARTED_EVENT:  
  16.                 mGroup = (WifiP2pGroup) message.obj;  
  17.                 if (DBG) logd(getName() + " group started");  
  18.   
  19.                 if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {  
  20.                     updatePersistentNetworks(NO_RELOAD);  
  21.                     String devAddr = mGroup.getOwner().deviceAddress;  
  22.                     mGroup.setNetworkId(mGroups.getNetworkId(devAddr,  
  23.                             mGroup.getNetworkName()));  
  24.                 }  
  25.   
  26.                 if (mGroup.isGroupOwner()) {  
  27.                     if (!mAutonomousGroup) {  
  28.                         mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);  
  29.                     }  
  30.                     startDhcpServer(mGroup.getInterface());  
  31.                 } else {  
  32.                     mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);  
  33.                     mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,  
  34.                             P2pStateMachine.this, mGroup.getInterface());  
  35.                     // TODO: We should use DHCP state machine PRE message like WifiStateMachine  
  36.                     mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);  
  37.                     mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);  
  38.                     WifiP2pDevice groupOwner = mGroup.getOwner();  
  39.                     WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);  
  40.                     if (peer != null) {  
  41.                         // update group owner details with peer details found at discovery  
  42.                         groupOwner.updateSupplicantDetails(peer);  
  43.                         mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);  
  44.                         sendPeersChangedBroadcast();  
  45.                     } else {  
  46.                         logw("Unknown group owner " + groupOwner);  
  47.                     }  
  48.                 }  
  49.                 transitionTo(mGroupCreatedState);  
  50.                 break;  

開始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,這裏比較重要的一點是,P2pStateMachine不會調用DhcpStateMachine的registerForPreDhcpNotification,所以不會收到CMD_PRE_DHCP_ACTION等消息。然後更新group owner的相關信息,最後跳轉至GroupCreatedState就表示連接完成了。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class GroupCreatedState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         // Once connected, peer config details are invalid  
  6.         mSavedPeerConfig.invalidate();  
  7.         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, nullnull);  
  8.   
  9.         updateThisDevice(WifiP2pDevice.CONNECTED);  
  10.   
  11.         //DHCP server has already been started if I am a group owner  
  12.         if (mGroup.isGroupOwner()) {  
  13.             setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS));  
  14.         }  
  15.   
  16.         // In case of a negotiation group, connection changed is sent  
  17.         // after a client joins. For autonomous, send now  
  18.         if (mAutonomousGroup) {  
  19.             sendP2pConnectionChangedBroadcast();  
  20.         }  
  21.     }  

在DhcpStateMachine獲取到IP地址以後,就會發送DhcpStateMachine.CMD_POST_DHCP_ACTION消息給P2pStateMachine,相關的處理代碼如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case DhcpStateMachine.CMD_POST_DHCP_ACTION:  
  2.     DhcpResults dhcpResults = (DhcpResults) message.obj;  
  3.     if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&  
  4.             dhcpResults != null) {  
  5.         if (DBG) logd("DhcpResults: " + dhcpResults);  
  6.         setWifiP2pInfoOnGroupFormation(dhcpResults.serverAddress);  
  7.         sendP2pConnectionChangedBroadcast();  
  8.         //Turn on power save on client  
  9.         mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);  
  10.     } else {  
  11.         loge("DHCP failed");  
  12.         mWifiNative.p2pGroupRemove(mGroup.getInterface());  
  13.     }  
  14.     break;  

這裏主要調用sendP2pConnectionChangedBroadcast用於廣播當前連接狀態的改變,WifiP2pSettings會捕獲這個WIFI_P2P_CONNECTION_CHANGED_ACTION並更新UI狀態爲已連接
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. private void sendP2pConnectionChangedBroadcast() {  
  2.     if (DBG) logd("sending p2p connection changed broadcast");  
  3.     Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);  
  4.     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT  
  5.             | Intent.FLAG_RECEIVER_REPLACE_PENDING);  
  6.     intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));  
  7.     intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));  
  8.     intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));  
  9.     mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);  
  10.     mWifiChannel.sendMessage(WifiP2pService.P2P_CONNECTION_CHANGED,  
  11.             new NetworkInfo(mNetworkInfo));  
  12. }  

被動連接

首先來看一下三種方式的連接流程圖:


當在InactiveState中,收到對方發送的P2P-PROV-DISC-PBC-REQ消息後,WifiMonitor就會給P2pStateMachine發送P2P_PROV_DISC_PBC_REQ_EVENT,來看InactiveState對這個消息的解釋:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:  
  2. case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:  
  3. case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:  
  4.     //We let the supplicant handle the provision discovery response  
  5.     //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.  
  6.     //Handling provision discovery and issuing a p2p_connect before  
  7.     //group negotiation comes through causes issues  
  8.     break;  

InactiveState並不關係這些消息,而是讓wpa_supplicant自行處理,等待收到GO_NEGOTIATION_REQUEST_EVENT時再來處理,再來看InactiveState如何處理GO_NEGOTIATION_REQUEST_EVENT消息:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:  
  2.     config = (WifiP2pConfig) message.obj;  
  3.     if (isConfigInvalid(config)) {  
  4.         loge("Dropping GO neg request " + config);  
  5.         break;  
  6.     }  
  7.     mSavedPeerConfig = config;  
  8.     mAutonomousGroup = false;  
  9.     mJoinExistingGroup = false;  
  10.     transitionTo(mUserAuthorizingNegotiationRequestState);  
  11.     break;  

直接跳轉至UserAuthorizingNegotiationRequsetState,到UserAuthorizingNegotiationRequsetState的enter函數去分析:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class UserAuthorizingNegotiationRequestState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         notifyInvitationReceived();  
  6.     }  

notifyInvitationReceived作用是彈出對話框,供用戶選擇取消或者確認,如果WPS方式是keypad或者display,則還有輸入pin碼的輸入框或者顯示框。以PBC爲例,當用戶點擊確認後,則會給自身發送PEER_CONNECTION_USER_ACCEPT消息,來看UserAuthorizingNegotiationRequsetState如何處理:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case PEER_CONNECTION_USER_ACCEPT:  
  2.     mWifiNative.p2pStopFind();  
  3.     p2pConnectWithPinDisplay(mSavedPeerConfig);  
  4.     mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);  
  5.     sendPeersChangedBroadcast();  
  6.     transitionTo(mGroupNegotiationState);  
  7.    break;  

這裏也是調用p2pConnectWithPinDisplay,然後調用WifiNative的p2pConnect向wpa_supplicant發送P2P_CONNECT命令,後面transition到GroupNegoTiationState,與前面主動連接的方式一樣了。

主動Invite

當設備已經形成Group時,則可以邀請其他設備加入到這個Group,形成兩個及兩個以上P2P設備形成的Group,從前面Group形成的知識我們知道,這時P2pStateMachine處於GroupCreatedState,WifiP2pSettings會給當前State發送CONNECT的消息,來看具體處理代碼:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiP2pManager.CONNECT:  
  2.     WifiP2pConfig config = (WifiP2pConfig) message.obj;  
  3.     if (isConfigInvalid(config)) {  
  4.         loge("Dropping connect requeset " + config);  
  5.         replyToMessage(message, WifiP2pManager.CONNECT_FAILED);  
  6.         break;  
  7.     }  
  8.     logd("Inviting device : " + config.deviceAddress);  
  9.     mSavedPeerConfig = config;  
  10.     if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {  
  11.         mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);  
  12.         sendPeersChangedBroadcast();  
  13.         replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);  
  14.     } else {  
  15.         replyToMessage(message, WifiP2pManager.CONNECT_FAILED,  
  16.                 WifiP2pManager.ERROR);  
  17.     }  
  18.     // TODO: figure out updating the status to declined when invitation is rejected  
  19.     break;  

上面的實現中主要調用WifiNative的p2pInvite來實現邀請對方加入當前的Group中,如果成功執行並向對方發送invitate request後,WifiMonitor會收到P2P-INVITATION-RESULT,然後給P2pStateMachine發送P2P_INVITATION_RESULT_EVENT消息,來看一下處理代碼:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_INVITATION_RESULT_EVENT:  
  2.     P2pStatus status = (P2pStatus)message.obj;  
  3.     if (status == P2pStatus.SUCCESS) {  
  4.         // invocation was succeeded.  
  5.         break;  
  6.     }  
  7.     loge("Invitation result " + status);  
  8.     if (status == P2pStatus.UNKNOWN_P2P_GROUP) {  
  9.         // target device has already removed the credential.  
  10.         // So, remove this credential accordingly.  
  11.         int netId = mGroup.getNetworkId();  
  12.         if (netId >= 0) {  
  13.             if (DBG) logd("Remove unknown client from the list");  
  14.             if (!removeClientFromList(netId,  
  15.                     mSavedPeerConfig.deviceAddress, false)) {  
  16.                 // not found the client on the list  
  17.                 loge("Already removed the client, ignore");  
  18.                 break;  
  19.             }  
  20.             // try invitation.  
  21.             sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);  
  22.         }  
  23.     }  
  24.     break;  

這裏主要根據P2P-INVITATION-RESULT後面帶到status狀態來處理persistent連接的情況,比如device A和device B前面有連接過,這時再去連接的話,應該走invitation這條路,但如果在device B上面已經把和device A連接的credential給刪除了。關於persistent的連接以後再來分析。接着會收到wpa_supplicant發送的P2P_PROV_DISC_PBC_REQ或者P2P_PROV_DISC_ENTER_PIN或者P2P_PROV_DISC_SHOW_PIN的event,wifiMonitor分別會發送P2P_PROV_DISC_PBC_REQ_EVENT、P2P_PROV_DISC_ENTER_PIN_EVENT和P2P_PROV_DISC_SHOW_PIN_EVENT三個消息給P2pStateMachine,來看相關處理代碼:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:  
  2. case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:  
  3. case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:  
  4.     WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;  
  5.     mSavedPeerConfig = new WifiP2pConfig();  
  6.     mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;  
  7.     if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {  
  8.         mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;  
  9.     } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {  
  10.         mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;  
  11.         mSavedPeerConfig.wps.pin = provDisc.pin;  
  12.     } else {  
  13.         mSavedPeerConfig.wps.setup = WpsInfo.PBC;  
  14.     }  
  15.     transitionTo(mUserAuthorizingJoinState);  
  16.     break;  

以P2P_PROV_DISC_PBC_REQ_EVENT爲例,這裏主要通過provision discovery中的消息來設置mSavedPeerConfig,記錄這次連接的配置信息,然後就transition到UserAuthorizingJoinState中:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class UserAuthorizingJoinState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         notifyInvitationReceived();  
  6.     }  
  7.   
  8.     @Override  
  9.     public boolean processMessage(Message message) {  
  10.         if (DBG) logd(getName() + message.toString());  
  11.         switch (message.what) {  
  12.             case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:  
  13.             case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:  
  14.             case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:  
  15.                 //Ignore more client requests  
  16.                 break;  
  17.             case PEER_CONNECTION_USER_ACCEPT:  
  18.                 //Stop discovery to avoid failure due to channel switch  
  19.                 mWifiNative.p2pStopFind();  
  20.                 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {  
  21.                     mWifiNative.startWpsPbc(mGroup.getInterface(), null);  
  22.                 } else {  
  23.                     mWifiNative.startWpsPinKeypad(mGroup.getInterface(),  
  24.                             mSavedPeerConfig.wps.pin);  
  25.                 }  
  26.                 transitionTo(mGroupCreatedState);  
  27.                 break;  
  28.             case PEER_CONNECTION_USER_REJECT:  
  29.                 if (DBG) logd("User rejected incoming request");  
  30.                 transitionTo(mGroupCreatedState);  
  31.                 break;  
  32.             default:  
  33.                 return NOT_HANDLED;  
  34.         }  
  35.         return HANDLED;  
  36.     }  

在UserAuthorizingJoinState的enter函數中,通過notifyInvitationReceived來彈出對話框給用戶選擇確認或者取消,以及輸入pin碼等。當用戶點擊確認後,就會發送PEER_CONNECTION_USER_ACCEPT給它自身,接看看上面代碼中處理PEER_CONNECTION_USER_ACCEPT消息,主要通過調用WifiNative的startWpsPbc進行連接,然後transition到GroupCreatedState中。在GroupCreatedState會收到WPS-SUCCESS以及AP-STA-CONNECT表示連接是否成功,當WifiMonitor收到AP-STA-CONNECT後,就會給P2pStateMachine發送AP_STA_CONNECTED_EVENT,來看這一段的處理代碼:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.AP_STA_CONNECTED_EVENT:  
  2.     WifiP2pDevice device = (WifiP2pDevice) message.obj;  
  3.     String deviceAddress = device.deviceAddress;  
  4.     // Clear timeout that was set when group was started.  
  5.     mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);  
  6.     if (deviceAddress != null) {  
  7.         if (mPeers.get(deviceAddress) != null) {  
  8.             mGroup.addClient(mPeers.get(deviceAddress));  
  9.         } else {  
  10.             mGroup.addClient(deviceAddress);  
  11.         }  
  12.         mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);  
  13.         if (DBG) logd(getName() + " ap sta connected");  
  14.         sendPeersChangedBroadcast();  
  15.     } else {  
  16.         loge("Connect on null device address, ignore");  
  17.     }  
  18.     sendP2pConnectionChangedBroadcast();  
  19.     break;  

被動Invite

當已經形成Group的設備點擊連接我們設備後,就會給我們設備發送invitation request,由前面的知識我們知道,這個request會被WifiMonitor接收並轉換爲P2P_INVITATION_RECEIVED_EVENT發送給P2pStateMachine,由於當前設備處於InactiveState,來看這個消息的處理:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:  
  2.     WifiP2pGroup group = (WifiP2pGroup) message.obj;  
  3.     WifiP2pDevice owner = group.getOwner();  
  4.   
  5.     if (owner == null) {  
  6.         loge("Ignored invitation from null owner");  
  7.         break;  
  8.     }  
  9.   
  10.     config = new WifiP2pConfig();  
  11.     config.deviceAddress = group.getOwner().deviceAddress;  
  12.   
  13.     if (isConfigInvalid(config)) {  
  14.         loge("Dropping invitation request " + config);  
  15.         break;  
  16.     }  
  17.     mSavedPeerConfig = config;  
  18.   
  19.     if ((owner = mPeers.get(owner.deviceAddress)) != null) {  
  20.         if (owner.wpsPbcSupported()) {  
  21.             mSavedPeerConfig.wps.setup = WpsInfo.PBC;  
  22.         } else if (owner.wpsKeypadSupported()) {  
  23.             mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;  
  24.         } else if (owner.wpsDisplaySupported()) {  
  25.             mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;  
  26.         }  
  27.     }  
  28.   
  29.     mAutonomousGroup = false;  
  30.     mJoinExistingGroup = true;  
  31.     transitionTo(mUserAuthorizingInviteRequestState);  
  32.     break;  

首先通過P2P_INVITATION_RECEIVED帶的group owner的mac地址構造一個配置信息,並保存到mSavedPeerConfig中,然後根據group owner所有的WpsInfo會選擇一個WSC方式,優選PBC方式,接着transition到UserAuthorizingInviteRequestState中:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. class UserAuthorizingInviteRequestState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) logd(getName());  
  5.         notifyInvitationReceived();  
  6.     }  
  7.   
  8.     @Override  
  9.     public boolean processMessage(Message message) {  
  10.         if (DBG) logd(getName() + message.toString());  
  11.         boolean ret = HANDLED;  
  12.         switch (message.what) {  
  13.             case PEER_CONNECTION_USER_ACCEPT:  
  14.                 mWifiNative.p2pStopFind();  
  15.                 if (!reinvokePersistentGroup(mSavedPeerConfig)) {  
  16.                     // Do negotiation when persistence fails  
  17.                     p2pConnectWithPinDisplay(mSavedPeerConfig);  
  18.                 }  
  19.                 mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);  
  20.                 sendPeersChangedBroadcast();  
  21.                 transitionTo(mGroupNegotiationState);  
  22.                break;  

UserAuthorizingInviteRequestState和UserAuthorizingNegotiationRequsetState類似,首先彈出對話框給用戶選擇同意或者拒絕,若選擇keypad或者display的方式,還需要顯示pin碼等。當用戶點擊同意後,就會給自身發送PEER_CONNECTION_USER_ACCEPT,看上面處理PEER_CONNECTION_USER_ACCEPT的代碼,首先嚐試用persistent的方式連接;如果失敗,則調用p2pConnectWithPinDisplay,然後調用WifiNative的p2pConnect向wpa_supplicant發送P2P_CONNECT命令,這樣就回到與之前主動連接一樣的流程當中了。
發佈了7 篇原創文章 · 獲贊 23 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章