简介:Wi-Fi Direct技术的目的是在没有Wi-Fi AP的情况下由两个或者多个Wi-Fi设备互相之间进行高速的数据通信。通信完全基于TCP/IP 协议,因此对于开发基于Wi-Fi Direct的应用来说非常友好。 Wi-Fi Direct在刚提出时叫Wi-Fi Peer-to-Peer,所以也可以称作Wi-Fi P2P。它的主要竞争对手是Blue Tooth,在目前看来Wi-Fi Direct和BT各有优劣,BT在功耗上具有绝对的优势,而Wi-Fi Direct则在传输速度和传输距离上遥遥领先。 ndroid平台中,P2P操作比较简单,用户只需要执行如下三个步骤:
1)进入WifiP2pSettings界面
2)搜索周围的P2P设备。搜索到的设备将显示在WifiP2pSettings中
3)用户选择其中的某个设备发起连接或者接受某设备发起的连接
本文相关代码路径:http://androidxref.com/8.1.0_r33/xref/ 代码中相应的行数即为源码中的位置。
1.1WifiP2pService的启动
WifiP2pService的创建以及启动是在SystemServer中,主要代码如下:
代码语言:javascript复制171 private static final String WIFI_P2P_SERVICE_CLASS =
172 "com.android.server.wifi.p2p.WifiP2pService";
代码语言:javascript复制1094 if (context.getPackageManager().hasSystemFeature(
1095 PackageManager.FEATURE_WIFI_DIRECT)) {
1096 traceBeginAndSlog("StartWifiP2P");
1097 mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
1098 traceEnd();
1099 }
进入到WifiP2pService的构造函数分析
代码语言:javascript复制386 public WifiP2pServiceImpl(Context context) {
387 mContext = context;
388
389 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
390
391 mP2pSupported = mContext.getPackageManager().hasSystemFeature(
392 PackageManager.FEATURE_WIFI_DIRECT);
393
394 mThisDevice.primaryDeviceType = mContext.getResources().getString(
395 com.android.internal.R.string.config_wifi_p2p_device_type);
396
397 HandlerThread wifiP2pThread = new HandlerThread("WifiP2pService");
398 wifiP2pThread.start();
399 mClientHandler = new ClientHandler(TAG, wifiP2pThread.getLooper());
400 mP2pStateMachine = new P2pStateMachine(TAG, wifiP2pThread.getLooper(), mP2pSupported);
401 mP2pStateMachine.start();
402 }
WifiP2pService的构造函数很简单,主要是创建一个NetworkInfo的对象,然后通过PackageManagerService获取到系统是否支持P2P,然后创建一个P2pStateMachine并启动这个StateMachine,进入到P2pStateMachine中分析:
P2pStateMachine状态图
代码语言:javascript复制709 P2pStateMachine(String name, Looper looper, boolean p2pSupported) {
710 super(name, looper);
713 addState(mDefaultState);
714 addState(mP2pNotSupportedState, mDefaultState);
715 addState(mP2pDisablingState, mDefaultState);
716 addState(mP2pDisabledState, mDefaultState);
717 addState(mP2pEnablingState, mDefaultState);
718 addState(mP2pEnabledState, mDefaultState);
719 addState(mInactiveState, mP2pEnabledState);
720 addState(mGroupCreatingState, mP2pEnabledState);
721 addState(mUserAuthorizingInviteRequestState, mGroupCreatingState);
722 addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState);
723 addState(mProvisionDiscoveryState, mGroupCreatingState);
724 addState(mGroupNegotiationState, mGroupCreatingState);
725 addState(mFrequencyConflictState, mGroupCreatingState);
726 addState(mGroupCreatedState, mP2pEnabledState);
727 addState(mUserAuthorizingJoinState, mGroupCreatedState);
728 addState(mOngoingGroupRemovalState, mGroupCreatedState);
731 if (p2pSupported) {
732 setInitialState(mP2pDisabledState);
733 } else {
734 setInitialState(mP2pNotSupportedState);
735 }
通过上面的构造函数,我们可以看出P2pStateMachine的各个State关系如下。并且若在支持P2P的情况下,InitialState是P2pDisabledState;若在不支持P2P的情况下,InitialState是P2pNotSupportedState。
1.2Wifi Direct的使能
P2pStateMachine虽然属于WifiP2pService,但它也受WifiStateMachine影响。如果平台支持P2P,在WifiStateMachine中将创建一个名为mWifiP2pChannel的AsyncChannelde 的对象用于向P2pStateMachine发消息。
如果用户打开WiFI功能,P2pStateMachine就会收到来自WifiStateMachine发送的消息CMD_ ENABLE_P2P。
代码语言:javascript复制3256 class DefaultState extends State {
3271 if (mOperationalMode == CONNECT_MODE) {
3275 sendMessage(CMD_ENABLE_P2P);
CMD_ ENABLE_P2P消息的处理过程主要在wifip2pserviceinpl.java的P2pDisabledState函数中完成
代码语言:javascript复制1139 case WifiStateMachine.CMD_ENABLE_P2P:
..........
1147 mWifiMonitor.startMonitoring(mWifiNative.getInterfaceName());
1148 transitionTo(mP2pEnablingState);
1149 break;
接收到CMD_ ENABLE_P2P消息后,P2pStateMachine主要做了两件工作:
1)创建一个WifiMonitor用于接收来自wpa_supplicant的消息
2)同时将状态机转入到P2pEnablingState状态
WifiMonitor连接连接wpa_supplicant之后,WifiMonitor会发送一个SUP_CONNECT_EVENT
消息给P2pStateMachine,该消息由P2pEnablingState处理。
代码语言:javascript复制1164 public boolean processMessage(Message message) {
1165 if (DBG) logd(getName() message.toString());
1166 switch (message.what) {
1167 case WifiP2pMonitor.SUP_CONNECTION_EVENT:
1168 if (DBG) logd("P2p socket connection successful");
1169 transitionTo(mInactiveState);
1170 break;
1171 case WifiP2pMonitor.SUP_DISCONNECTION_EVENT:
1172 loge("P2p socket connection failed");
1173 transitionTo(mP2pDisabledState);
1174 break;
1175 case WifiStateMachine.CMD_ENABLE_P2P:
1176 case WifiStateMachine.CMD_DISABLE_P2P_REQ:
1177 deferMessage(message);
1178 break;
1179 default:
1180 return NOT_HANDLED;
1181 }
1182 return HANDLED;
1183 }
1184 }
P2pEnabledState()函数主要工作如下:
1)通过sendP2pStateChangedBroadcast()发送WIFI_P2P_STATE_CHANGED_ACTION广播,它将携带WifiP2pInfo和NetworkInfo信息,同时还将通过sendP2pConnectionChangedBroadcast()函数向WifiStateMachine发送WIFI_P2P_CONNECTION_CHANGED_ACTION广播。
调用函数initializeP2pSettings()初始化P2P的一些设置,包括deviceName、DeviceType、ConfigMethods,并且获取以前persistent的相关device信息
代码语言:javascript复制1186 class P2pEnabledState extends State {
1187 @Override
1188 public void enter() {
1189 if (DBG) logd(getName());
1190 sendP2pStateChangedBroadcast(true);
1191 mNetworkInfo.setIsAvailable(true);
1192 sendP2pConnectionChangedBroadcast();
1193 initializeP2pSettings();
1194 }
1.3.Wifi Direct的扫描
P2P的扫描还是从WifiP2psettings开始,当用户单击“SEARCH”按钮搜索P2P设备。该按钮对应的函数是WifiP2pSettings的startSearch()函数。 startSearch()函数调用WifiManager的discoverPeers搜索周围的P2P设备。discoverPeers()函数主要的工作是发布DISCOVER_PEERS消息.
代码语言:javascript复制586 private void startSearch() {
587 if (mWifiP2pManager != null && !mWifiP2pSearching) {
588 mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
589 public void onSuccess() {
590 }
591 public void onFailure(int reason) {
592 if (DBG) Log.d(TAG, " discover fail " reason);
593 }
594 });
首先来看一下WifiP2pManager提供给我们的文档说明:
代码语言:javascript复制56 * <p> The API is asynchronous and responses to requests from an application are on listener
57 * callbacks provided by the application. The application needs to do an initialization with
58 * {@link #initialize} before doing any p2p operation.
这段话告诉我们WifiP2pManager提供给application的函数都是异步的,并且在调用其它p2p的方法之前,需要先调用initialize方法,那我们首先来看一下initialize的实现:
代码语言:javascript复制938 public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
939 Binder binder = new Binder();
940 Channel channel = initalizeChannel(srcContext, srcLooper, listener, getMessenger(binder),
941 binder);
942 return channel;
943 }
这段代码主要的功能是创建一个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设备的扫描了,代码如下:
代码语言:javascript复制988 public void discoverPeers(Channel c, ActionListener listener) {
989 checkChannel(c);
990 c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
991 }
由WifiP2pService的P2pEnabledState函数处理。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处理。
代码语言:javascript复制1267 case WifiP2pManager.DISCOVER_PEERS:
1268 if (mDiscoveryBlocked) {
1269 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1270 WifiP2pManager.BUSY);
1271 break;
1272 }
1274 clearSupplicantServiceRequest();
1275 if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
1276 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
1277 sendP2pDiscoveryChangedBroadcast(true);
1278 } else {
1279 replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
1280 WifiP2pManager.ERROR);
1281 }
1282 break;
下面来看下WifiP2pService中P2P_DEVICE_FOUND_EVENT处理流程。
代码语言:javascript复制1313 case WifiP2pMonitor.P2P_DEVICE_FOUND_EVENT:
1314 if (message.obj == null) {
1315 Log.e(TAG, "Illegal argument(s)");
1316 break;
1317 }
1318 WifiP2pDevice device = (WifiP2pDevice) message.obj;
1319 if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
1320 mPeers.updateSupplicantDetails(device);
1321 sendPeersChangedBroadcast();
1322 break;
mPeers是一个WifiP2pDeviceList对象,它内部维护一个全局的HashMap来维护所有找到的P2P 设备信息,HashMpa的key是P2P设备的MAC地址,值即为上面的WifiP2pDevice对象。然后通过sendPeersChangedBroadcast发送一个 WIFI_P2P_PEERS_CHANGED_ACTION的broadcast,这样WifiP2pSettings收到这个broadcast后,就可以直接通过WifiP2pDeviceList对象来访问里面的HasmMap来显示所有的P2P设备了。
下面来看看WifiP2pSetting收到WIFI_P2P_PEERS_CHANGED_ACTION广播以及
WIFI_P2P_DISCOVERY_CHANGE_ACTION广播后的处理过程。
代码语言:javascript复制106 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
107 @Override
108 public void onReceive(Context context, Intent intent) {
109 String action = intent.getAction();
115 } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
116 mPeers = (WifiP2pDeviceList) intent.getParcelableExtra(
117 WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
118 handlePeersChanged();
..........................
(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
139 int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
140 WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
141 if (DBG) Log.d(TAG, "Discovery state changed: " discoveryState);
142 if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
143 updateSearchMenu(true);
144 } else {
145 updateSearchMenu(false);
146 }
WifiP2pSetting收到WIFI_P2P_PEERS_CHANGED_ACTION广播后,通过WifiManager的handlepeerschanged()函数获取P2P Device信息。收到WIFI_P2P_DISCOVERY_CHANGED_ACTION调用updateSearchMenu()函数显示所有的设备。至此,P2P扫描的全部过程已经完成。
1.4Wifi Direct的连接
这里开始介绍四种连接方式:主动连接、被动连接、主动invite和被动invite
1.4.1 主动连接
Wifi P2P扫描完成后,用户可以在界面中选择某个P2P设备并与之连接,Wifi P2P连接也是有WifiP2pSettings.java开始。先来看看WifiP2pSettings的onPreferenceTreeClick函数:
代码语言:javascript复制392 @Override
393 public boolean onPreferenceTreeClick(Preference preference) {
394 if (preference instanceof WifiP2pPeer) {
395 mSelectedWifiPeer = (WifiP2pPeer) preference;
396 if (mSelectedWifiPeer.device.status == WifiP2pDevice.CONNECTED) {
397 showDialog(DIALOG_DISCONNECT);
398 } else if (mSelectedWifiPeer.device.status == WifiP2pDevice.INVITED) {
399 showDialog(DIALOG_CANCEL_CONNECT);
400 } else {
401 WifiP2pConfig config = new WifiP2pConfig();
402 config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
403
404 int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
405
406 if (forceWps != -1) {
407 config.wps.setup = forceWps;
408 } else {
409 if (mSelectedWifiPeer.device.wpsPbcSupported()) {
410 config.wps.setup = WpsInfo.PBC;
411 } else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
412 config.wps.setup = WpsInfo.KEYPAD;
413 } else {
414 config.wps.setup = WpsInfo.DISPLAY;
415 }
416 }
417
418 mWifiP2pManager.connect(mChannel, config,
419 new WifiP2pManager.ActionListener() {
420 public void onSuccess() {
421 if (DBG) Log.d(TAG, " connect success");
422 }
423 public void onFailure(int reason) {
424 Log.e(TAG, " connect fail " reason);
425 Toast.makeText(getActivity(),
426 R.string.wifi_p2p_failed_connect_message,
427 Toast.LENGTH_SHORT).show();
428 }
429 });
430 }
431 } else if (preference instanceof WifiP2pPersistentGroup) {
432 mSelectedGroup = (WifiP2pPersistentGroup) preference;
433 showDialog(DIALOG_DELETE_GROUP);
434 }
435 return super.onPreferenceTreeClick(preference);
436 }
onPreferenceTreeClick()主要做了三件事:
①获取用户指定的WifiP2pPeer项。
②获取对端设备的WSC的配置方法。
③构造WifiP2pConfig对象存储对端设备信息并调用WifiP2pManager的connect函数向该对端设备发起连接。
connect函数的工作主要是通过Channel的AsyncChannel给P2pStatuesMachine发送CONNECT消息。
代码语言:javascript复制1462 public boolean processMessage(Message message) {
1463 if (DBG) logd(getName() message.toString());
1464 switch (message.what) {
1465 case WifiP2pManager.CONNECT:
1466 if (DBG) logd(getName() " sending connect");
1467 WifiP2pConfig config = (WifiP2pConfig) message.obj;
1468 if (isConfigInvalid(config)) {
1469 loge("Dropping connect requeset " config);
1470 replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
1471 break;
1472 }
1473
1474 mAutonomousGroup = false;
1475 mWifiNative.p2pStopFind();
1476 if (reinvokePersistentGroup(config)) {
1477 transitionTo(mGroupNegotiationState);
1478 } else {
1479 transitionTo(mProvisionDiscoveryState);
1480 }
1481 mSavedPeerConfig = config;
1482 mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1483 sendPeersChangedBroadcast();
1484 replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
1485 break;
这里分为persistent的连接方式和非persistent的连接,对于persistent连接,如果第一次连接成功wpa_supplicant会记录连接的记录,包括credential、GO MAC地址、ssid等信息。采用非persistent的连接(即negotiate方式)则跳转到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_EVENT。 下面来看看P2P_PROV_DISC_PBC_RSP_EVENT消息的处理过程,主要做两件事
①调用p2pConnectWithPinDisplay函数插法WPAS发送GON Request帧(调用Wifinative的P2pconnect函数)。 ②跳转到GroupNegotiationState,开始Group negotiate
代码语言:javascript复制1823 class ProvisionDiscoveryState extends State {
1830 @Override
1831 public boolean processMessage(Message message) {
1835 switch (message.what) {
1836 case WifiP2pMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
1841 provDisc = (WifiP2pProvDiscEvent) message.obj;
1842 device = provDisc.device;
1843 if (device != null
1844 && !device.deviceAddress.equals(mSavedPeerConfig.deviceAddress))
1845 break;
1846 }
1847 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
1848 if (DBG) logd("Found a match " mSavedPeerConfig);
1849 p2pConnectWithPinDisplay(mSavedPeerConfig);
1850 transitionTo(mGroupNegotiationState);
下面来看下Group negotiate过程:
代码语言:javascript复制1910 class GroupNegotiationState extends State {
1916 @Override
1917 public boolean processMessage(Message message) {
1922 case WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
1923 case WifiP2pMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
1924 if (DBG) logd(getName() " go success");
1925 break;
1926 case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
.....................
1958 startDhcpServer(mGroup.getInterface());
1978 transitionTo(mGroupCreatedState);
1979 break;
开始group negotiate后,wpa_supplicant会发多个event给WifiMonitor,WifiMonitor处理后也会发送多个event消息给P2pStateMachine。其中比较重要的是P2P_GROUP_STARTED_EVENT.
GroupNegotiationState()接收到P2P_GROUP_STARTED_EVENT主要做了四件事: ①更新和设置mgroup的信息。 ②调用DHCP为两端设备分配IP地址。 ③向wifiP2pSettings发送广播。 ④更新Group owner信息,跳转到GroupCreatedState连接完成。
收到这个消息后,GroupNegotiationState主要调用DHCP相关的StateMachine开始会两端分配IP,这里比较重要的一点是,P2pStateMachine不会调用DhcpStateMachine的registerForPreDhcpNotification,所以不会收到CMD_PRE_DHCP_ACTION等消息。然后更新group owner的相关信息,最后跳转至GroupCreatedState就表示连接完成了。
代码语言:javascript复制2221 case IPM_PRE_DHCP_ACTION:
2222 mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
2223 mIpManager.completedPreDhcpAction();
2224 break;
代码语言:javascript复制2137 class GroupCreatedState extends State {
2138 @Override
2139 public void enter() {
2140 if (DBG) logd(getName());
2142 mSavedPeerConfig.invalidate();
2143 mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
2145 updateThisDevice(WifiP2pDevice.CONNECTED);
2147 // DHCP server has already been started if I am a group owner
2148 if (mGroup.isGroupOwner()) {
2149 setWifiP2pInfoOnGroupFormation(
2150 NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
2151 }
2155 if (mAutonomousGroup) {
2156 sendP2pConnectionChangedBroadcast();
2157 }
2158 }
GroupCreatedState函数主要调用sendP2pConnectionChangedBroadcast()广播当前连接状态的改变,WifiP2pSettings会捕获这个WIFI_P2P_CONNECTION_CHANGED_ACTION这个消息并且更新UI为已连接状态。到此P2P的主动连接已经结束。
代码语言:javascript复制2518 private void sendP2pConnectionChangedBroadcast() {
2519 if (DBG) logd("sending p2p connection changed broadcast");
2520 Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
2521 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2522 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2523 intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
2524 intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
2525 intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
2526 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2527 if (mWifiChannel != null) {
2528 mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
2529 new NetworkInfo(mNetworkInfo));
2530 } else {
2531 loge("sendP2pConnectionChangedBroadcast(): WifiChannel is null");
2532 }
2533 }
1.4.2 被动连接
当在InactiveState中,收到对方发送的P2P-PROV-DISC-PBC-REQ消息后,WifiMonitor就会给P2pStateMachine发送P2P_PROV_DISC_PBC_REQ_EVENT,来看InactiveState对这个消息的解释:
代码语言:javascript复制1556 case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
1557 case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
1558 case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
1559 // We let the supplicant handle the provision discovery response
1560 // and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
1561 // Handling provision discovery and issuing a p2p_connect before
1562 // group negotiation comes through causes issues
1563 break;
InactiveState并不关系这些消息,而是让wpa_supplicant自行处理,等待收到GO_NEGOTIATION_REQUEST_EVENT时再来处理,再来看InactiveState如何处理GO_NEGOTIATION_REQUEST_EVENT消息:
代码语言:javascript复制1498 case WifiP2pMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
1499 config = (WifiP2pConfig) message.obj;
1500 if (isConfigInvalid(config)) {
1501 loge("Dropping GO neg request " config);
1502 break;
1503 }
1504 mSavedPeerConfig = config;
1505 mAutonomousGroup = false;
1506 mJoinExistingGroup = false;
1507 transitionTo(mUserAuthorizingNegotiationRequestState);
1508 break;
直接跳转至UserAuthorizingNegotiationRequsetState,到UserAuthorizingNegotiationRequsetState的enter函数去分析:
代码语言:javascript复制1750 class UserAuthorizingNegotiationRequestState extends State {
1751 @Override
1752 public void enter() {
1753 if (DBG) logd(getName());
1754 notifyInvitationReceived();
1755 }
notifyInvitationReceived作用是弹出对话框,供用户选择取消或者确认,如果WPS方式是keypad或者display,则还有输入pin码的输入框或者显示框。以PBC为例,当用户点击确认后,则会给自身发送PEER_CONNECTION_USER_ACCEPT消息,来看UserAuthorizingNegotiationRequsetState如何处理:
代码语言:javascript复制1762 case PEER_CONNECTION_USER_ACCEPT:
1763 mWifiNative.p2pStopFind();
1764 p2pConnectWithPinDisplay(mSavedPeerConfig);
1765 mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1766 sendPeersChangedBroadcast();
1767 transitionTo(mGroupNegotiationState);
1768 break;
这里也是调用p2pConnectWithPinDisplay,然后调用WifiNative的p2pConnect向wpa_supplicant发送P2P_CONNECT命令,后面transition到GroupNegoTiationState,与前面主动连接的方式一样了。
1.4.3 主动Invite
当设备已经形成Group时,则可以邀请其他设备加入到这个Group,形成两个及两个以上P2P设备形成的Group,从前面Group形成的知识我们知道,这时P2pStateMachine处于GroupCreatedState,WifiP2pSettings会给当前State发送CONNECT的消息,来看具体处理代码:
代码语言:javascript复制2326 case WifiP2pManager.CONNECT:
2327 WifiP2pConfig config = (WifiP2pConfig) message.obj;
2328 if (isConfigInvalid(config)) {
2329 loge("Dropping connect request " config);
2330 replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
2331 break;
2332 }
2333 logd("Inviting device : " config.deviceAddress);
2334 mSavedPeerConfig = config;
2335 if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
2336 mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
2337 sendPeersChangedBroadcast();
2338 replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
2339 } else {
2340 replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
2341 WifiP2pManager.ERROR);
2342 }
上面的实现中主要调用WifiNative的p2pInvite来实现邀请对方加入当前的Group中,如果成功执行并向对方发送invitate request后,WifiMonitor会收到P2P-INVITATION-RESULT,然后给P2pStateMachine发送P2P_INVITATION_RESULT_EVENT消息,来看一下处理代码:
代码语言:javascript复制2346 case WifiP2pMonitor.P2P_INVITATION_RESULT_EVENT:
2347 P2pStatus status = (P2pStatus) message.obj;
2348 if (status == P2pStatus.SUCCESS) {
2349 // invocation was succeeded.
2350 break;
2351 }
2352 loge("Invitation result " status);
2353 if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
2354 // target device has already removed the credential.
2355 // So, remove this credential accordingly.
2356 int netId = mGroup.getNetworkId();
2357 if (netId >= 0) {
2358 if (DBG) logd("Remove unknown client from the list");
2359 removeClientFromList(netId, mSavedPeerConfig.deviceAddress, false);
2360 // try invitation.
2361 sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
2362 }
2363 }
2364 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,来看相关处理代码:
代码语言:javascript复制2365 case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2366 case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2367 case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2368 WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
2369 mSavedPeerConfig = new WifiP2pConfig();
2370 if (provDisc != null && provDisc.device != null) {
2371 mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
2372 }
2373 if (message.what == WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
2374 mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
2375 } else if (message.what == WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
2376 mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
2377 mSavedPeerConfig.wps.pin = provDisc.pin;
2378 } else {
2379 mSavedPeerConfig.wps.setup = WpsInfo.PBC;
2380 }
2381 transitionTo(mUserAuthorizingJoinState);
2382 break;
以P2P_PROV_DISC_PBC_REQ_EVENT为例,这里主要通过provision discovery中的消息来设置mSavedPeerConfig,记录这次连接的配置信息,然后就transition到UserAuthorizingJoinState中:
代码语言:javascript复制2400 class UserAuthorizingJoinState extends State {
2401 @Override
2402 public void enter() {
2403 if (DBG) logd(getName());
2404 notifyInvitationReceived();
2405 }
2406
2407 @Override
2408 public boolean processMessage(Message message) {
2409 if (DBG) logd(getName() message.toString());
2410 switch (message.what) {
2411 case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
2412 case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
2413 case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
2414 // Ignore more client requests
2415 break;
2416 case PEER_CONNECTION_USER_ACCEPT:
2417 // Stop discovery to avoid failure due to channel switch
2418 mWifiNative.p2pStopFind();
2419 if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
2420 mWifiNative.startWpsPbc(mGroup.getInterface(), null);
2421 } else {
2422 mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
2423 mSavedPeerConfig.wps.pin);
2424 }
2425 transitionTo(mGroupCreatedState);
2426 break;
2427 case PEER_CONNECTION_USER_REJECT:
2428 if (DBG) logd("User rejected incoming request");
2429 transitionTo(mGroupCreatedState);
2430 break;
2431 default:
2432 return NOT_HANDLED;
2433 }
2434 return HANDLED;
2435 }
在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,来看这一段的处理代码:
代码语言:javascript复制2166 case WifiP2pMonitor.AP_STA_CONNECTED_EVENT:
2167 if (message.obj == null) {
2168 Log.e(TAG, "Illegal argument(s)");
2169 break;
2170 }
2171 device = (WifiP2pDevice) message.obj;
2172 deviceAddress = device.deviceAddress;
2173 // Clear timeout that was set when group was started.
2174 mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
2175 if (deviceAddress != null) {
2176 if (mPeers.get(deviceAddress) != null) {
2177 mGroup.addClient(mPeers.get(deviceAddress));
2178 } else {
2179 mGroup.addClient(deviceAddress);
2180 }
2181 mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
2182 if (DBG) logd(getName() " ap sta connected");
2183 sendPeersChangedBroadcast();
2184 } else {
2185 loge("Connect on null device address, ignore");
2186 }
2187 sendP2pConnectionChangedBroadcast();
2188 break;
1.4.4 被动Invite
当已经形成Group的设备点击连接我们设备后,就会给我们设备发送invitation request,由前面的知识我们知道,这个request会被WifiMonitor接收并转换为P2P_INVITATION_RECEIVED_EVENT发送给P2pStateMachine,由于当前设备处于InactiveState,来看这个消息的处理:
代码语言:javascript复制1509 case WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT:
1510 if (message.obj == null) {
1511 Log.e(TAG, "Invalid argument(s)");
1512 break;
1513 }
1514 WifiP2pGroup group = (WifiP2pGroup) message.obj;
1515 WifiP2pDevice owner = group.getOwner();
1516 if (owner == null) {
1517 int id = group.getNetworkId();
1518 if (id < 0) {
1519 loge("Ignored invitation from null owner");
1520 break;
1521 }
1522
1523 String addr = mGroups.getOwnerAddr(id);
1524 if (addr != null) {
1525 group.setOwner(new WifiP2pDevice(addr));
1526 owner = group.getOwner();
1527 } else {
1528 loge("Ignored invitation from null owner");
1529 break;
1530 }
1531 }
1532 config = new WifiP2pConfig();
1533 config.deviceAddress = group.getOwner().deviceAddress;
1534 if (isConfigInvalid(config)) {
1535 loge("Dropping invitation request " config);
1536 break;
1537 }
1538 mSavedPeerConfig = config;
1539
1540 // Check if we have the owner in peer list and use appropriate
1541 // wps method. Default is to use PBC.
1542 if (owner != null && ((owner = mPeers.get(owner.deviceAddress)) != null)) {
1543 if (owner.wpsPbcSupported()) {
1544 mSavedPeerConfig.wps.setup = WpsInfo.PBC;
1545 } else if (owner.wpsKeypadSupported()) {
1546 mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
1547 } else if (owner.wpsDisplaySupported()) {
1548 mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
1549 }
1550 }
1551
1552 mAutonomousGroup = false;
1553 mJoinExistingGroup = true;
1554 transitionTo(mUserAuthorizingInviteRequestState);
1555 break;
首先通过P2P_INVITATION_RECEIVED带的group owner的mac地址构造一个配置信息,并保存到mSavedPeerConfig中,然后根据group owner所有的WpsInfo会选择一个WSC方式,优选PBC方式,接着transition到UserAuthorizingInviteRequestState中:
代码语言:javascript复制1785 class UserAuthorizingInviteRequestState extends State {
1786 @Override
1787 public void enter() {
1788 if (DBG) logd(getName());
1789 notifyInvitationReceived();
1790 }
1791
1792 @Override
1793 public boolean processMessage(Message message) {
1794 if (DBG) logd(getName() message.toString());
1795 boolean ret = HANDLED;
1796 switch (message.what) {
1797 case PEER_CONNECTION_USER_ACCEPT:
1798 mWifiNative.p2pStopFind();
1799 if (!reinvokePersistentGroup(mSavedPeerConfig)) {
1800 // Do negotiation when persistence fails
1801 p2pConnectWithPinDisplay(mSavedPeerConfig);
1802 }
1803 mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
1804 sendPeersChangedBroadcast();
1805 transitionTo(mGroupNegotiationState);
1806 break;
1807 case PEER_CONNECTION_USER_REJECT:
1808 if (DBG) logd("User rejected invitation " mSavedPeerConfig);
1809 transitionTo(mInactiveState);
1810 break;
UserAuthorizingInviteRequestState和UserAuthorizingNegotiationRequsetState类似,首先弹出对话框给用户选择同意或者拒绝,若选择keypad或者display的方式,还需要显示pin码等。当用户点击同意后,就会给自身发送PEER_CONNECTION_USER_ACCEPT,看上面处理PEER_CONNECTION_USER_ACCEPT的代码,首先尝试用persistent的方式连接;如果失败,则调用p2pConnectWithPinDisplay,然后调用WifiNative的p2pConnect向wpa_supplicant发送P2P_CONNECT命令,这样就回到与之前主动连接一样的流程当中了。