WiFi Direct详解(p2p使能,扫描,连接流程)基于Android8.1.0

2020-07-15 10:16:15 浏览数 (1)

简介: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命令,这样就回到与之前主动连接一样的流程当中了。

0 人点赞