一、P2P的模型

1.1 P2P的组成

P2P设备

能够作为P2P GO(Group Owner)或P2P GC(Group Client)角色。

能够协商成为GO或GC。

能够支持WSC(Wi-Fi Simple Configuration)Registrar和P2P discovery机制。

能够支持WLAN和P2P并发运行。

 

P2P GO角色:

类似于AP实体,对P2P GC提供BSS功能和服务。并提供WSC功能。

 

P2P GC角色:

实现STA的功能,提供WSC Enrollee功能。

 

DMG:Device Management Group

1.2 P2P的拓扑

P2P拓扑是1:n策略,多个GC可以连接同一个GO。这些连接的设备被称为一个P2P Group。

在DMG之外的运行每个client可能是P2P GC或传统的不具有P2P功能的Client。在DMG之内运行的每个client必须是具备P2P功能的GC。

 

图1 在DMG之外的P2P组成和拓扑

 

 

 

 

图2 在DMG中的P2P组成和拓扑

一个P2P Group有唯一的SSID。

 

 

图3 P2P Group 拓扑为1:1

 

1.3 p2p发展历史

第一代802.11,1997年制定,只运行于2.4GHz,最快2Mbit/s

第二代802.11b,只运行于2.4GHz,最快11Mbit/s,正逐渐淘汰

第三代802.11g/a,分别运行于2.4GHz和5GHz,最快54Mbit/s

第四代802.11n,可运行于2.4GHz或5GHz,20和40MHz带宽下最快72和150Mbit/s

第五代802.11ac,只运行于5GHz

1.4 功能和服务

1.4.1 基本功能和服务

在DMG之外运行的P2P,规范是假设以下STA功能和服务在设备中已经实现:

IEEE 802.11g或比2.4GHz更新的PHY

IEEE 802.11i(IES-CCMP)

WiFi Protected Setup

WiFi Multimedia

在DMG之内运行的P2P,规范是假设以下STA功能和服务在设备中已经实现。

 

1.4.2 P2P的特殊功能和服务

P2P设备还支持以下特殊功能:

P2P Discovery:让设备轻易快速简单地识别并连接周围其他P2P设备和服务。

P2P Group Operation:在DMG之外运行时类似于基础BSS操作,如IEEE802.11-2020所定义。在DMG内运行时类似于PBSS操作。

P2P Power Management:提供了一系列的功能去减少P2P设备的功率消耗。

1.4.3 P2P的两种地址

P2P Device Address

一个P2P设备在加入P2P Group之前都是使用的Device Addr开展Discovery等工作。对于一个P2P设备而言,Device Addr是唯一的,作用等同于MAC地址。

P2P Interface Address

而当P2P设备加入P2P Group之后,它与Group中的其他设备交互时采用的时Interface Addr。另外,由于一个设备可以加入多个P2P Group,所有在每个P2P Group中设备必须采用不同的Interface Addr。当某一个P2P Group结束之后,设备在该Group中使用的Interface Addr也随之消失。

1.4.4 WSC相关配置概述

WPS全称为Wi-Fi Protected Setup,用户只需输入PIN码(Personal Identification Number,一串数字),或者摁一下专门的按钮(WSC中,该按钮被称为Push Button)甚至用户只要拿着支持NFC的手机到目标AP(它必须也支持NFC)旁刷一下,这些安全设置就能被自动配置好。有了这些信息,手机就能连接上目标无线网络了。显然,相比让用户记住SSID、密码等信息,WSC要简单多了。WFA推出WPA后不久,WPS规范便被推出。随着WPA2的出现,WFA又制订了WPS的升级版,即WSC。

 

 

二、P2P discovery 

2.1 discovery概念

1、P2P discovery阶段让P2P设备快速查找周围的其他P2P设备,并建立连接。P2P discovery主要由以下几个部分组成:

2、Device Discovery,让两个设备到达相同的信道并交换彼此的设备信息(例如设备名称和设备类型)。在P2P协议规范中,Device Discovery在1,6,11信道进行。

3、Service Discovery,是一个可选特性,允许P2P设备在形成连接之前发现可用的上层服务。

4、Group Formation,用于确定哪台设备时GO,并形成一个新的P2P Group。

P2P Invitation,用于调用一个存在P2P Group或者邀请一个P2P设备加入这个Group。

2.2 P2P设备Discovery过程

基本原理:P2P设备进行主动扫描从而发现附近的其他设备,设备使用probe request和probe response帧去发现彼此。P2P Group形成之后,GO会发送beacon帧去发现设备。

 

P2P Device不会回复probe request帧,除非它是GO或者处于Listen State。

P2P Device不会发送beacon帧,除非它是GO。

 

Scan阶段:扫描阶段使用IEEE Std 802.11-2012中定义的扫描过程。P2P设备可以在这个阶段查找其他的P2P设备或者P2P Group,并选择最佳的信道去建立P2P Group。在Scan阶段,设备扫描自己支持的所有信道(可以是Channel 1,6,11,也可以是全新到扫描)去寻找周围的其他设备。在Scan阶段的P2P设备不会回复Probe Request帧。

Listen状态:不属于P2P Group中的设备如果处于Listen状态可以被其他的设备发现。Listen状态会给定一个信道,叫Listen Channel监听信道。这是从Social Channels中选择的一个信道,2.4GHz频段中Channel 1,6,11被作为Social Channels。

Listen Channel应在设备Discovery之初选择,并应保持不变,直到P2P Discovery阶段完成。

处于Listen状态的P2P设备只能对Probe Request探测请求帧进行应答,探测请求帧包含P2P IE、P2P通配符SSID元素、一个通配符BSSID和一个目的地址(广播地址或其P2P设备地址)。

每个P2P 设备都会选择一个随机数,随机数的上限和下限分别是maxDiscoverableInterval和minDiscoverableInterval,默认情况分别为3和1。如果随机数为1,那么设备会处于Listen状态的时间为100TU,如果随机数为3,那么设备处于Listen状态的时间为3*100TU。随机数的机制主要是为了避免两个设备Listen和Search的时间同步,导致互相搜索不到彼此。

Search状态:P2P device会在social channel上发送一个或多个Probe Request帧。这些Probe Request帧包含以下信息。

P2P IE(P2P Information Element),数据内容、数据标识符、网络地址、元数据、校验和和其他控制信息。

WSC IE,以及Device Name,Primary Device Type和Device Password ID等。

SSID,用来设置P2P Wildcard SSID

BSSID,用来设置Wildcard BSSID

总结:一个P2P device处于Scan阶段,它有可能会发现另外一个处于Search状态 Listen状态的P2P device。Find阶段主要用于保证两个P2P device能够处于相同的channel相互发现,并交换设备信息。

一个P2P device进行connect可能会出现下面三种情况:

1、进行Group Owner Negotiation,从而建立一个新的P2P Group。

2、发送P2P Invitation Request frame去调用一个先前建立好的Persistent P2P Group。

3、发送 P2P Invitation Request frame以请求目标P2P Device加入已经建立好的P2P Group。

2.3 Group Formation过程

这里主要有三种方式可以进行Group Formation。它们分别是:

P2P Negotiation Method (Standard Method)

P2P Autonomous Group Formation.

P2P Persistent Group Formation.

2.3.1 P2P Negotiation Method (Standard Method)

可以通过下图看出P2P GO Negotiation的过程包含几个阶段。

1、P2P Device discovery,P2P Device的discovery阶段。

2、Optional Service Discovery,可选的Service discovery阶段。

3、Group Owner Negotiation,GO协商阶段。

4、Phase1 and Phase 2 of WPS Provisioning,WPS配置阶段。

 

当两个P2P Device到达相同的channel,并开始进行GO协商,这两个P2P Device在GO协商完成前会一直保持这个相同的channel。GO协商共有三次帧交互从而达成一致谁做GO,谁做GC,以及Group的特性。

 

device1发送GO Negotiation给device2,帧交换的主要目的是进行GO intent值的交换,intent值决定了谁作GO。intent值的范围是0到15。谁的intent值越大,谁就作GO。如果intent值相同,则通过tie breaker来判断,tie breaker为0或1,1表示作GO,tie breaker的值是0或1随机选择的。

比如,device1的intent为5,在GO Negotiation Request中tie breaker随机设置为0。不巧的是,device2的intent也设置为5,但是device2通过GO Negotiation Request得知device1的tie breaker为0,那么将自己tie breaker切换为1并发送GO Negoation Repsonse。如果device1的tie breaker随机设置为1,那么device2的tie breaker就会相应切换为0。这样从而能保证在intent相同的情况下GO协商也能成功。

为保证商显设备默认GO,可以将其intent默认为15,lphone 默认为0,注意:当两个device的intent值都为15时,会GO协商失败。

一旦GO协商成功之后,GO就会发送beacon帧。beacon帧中包含了P2P Group的属性信息。

2.3.2 P2P Autonomous Group Formation

在AUTO GO Method中,一个设备将声明自己作为GO,我们将不会看到Negotiation Phase协商阶段。

 

用红色方框标记的就是GO negotiation过程。这个过程将决定哪个设备作为GO。

在Autonomous GO (AUTO GO) 方法中,我们将看不到协商过程,因为其中一个设备已经声明它作为GO。

下图展示了P2P Auto GO的过程。

在P2P Auto GO方法中,设备声明自己作为GO,该设备就变成了GO,并选择一个channel开始发送beacon帧.

当任意一个设备想加入一个已经存在的P2P group时,我们能够看到P2P Provisional Discovery Request和Response帧的交互过程。Provision Discovery Request帧会在Config Methods属性中配置发送方设备想让接收方设备使用的方法。当设备接收到Provision Discovery Request帧时,会向对端设备响应Provision Discovery Response帧。Provision Discovery Response帧如果在Config Methods属性配置了相同的方法,这表示成功,如果配置为NULL,则表示失败。

Provision Discovery Request帧的结构,config method配置为**“Push Button”,因为我们采用了Push button method连接。

在这之后,将进行Group Formation中的Phase 1 and Phase 2阶段

2.3.3 P2P Persistent Group Formation

2.3.1节和2.3.2节分别介绍了Negotiation Method和Autonomous Method两种Group Formation方法。本节主要介绍使用Persistent Method方法去形成P2P Group Formation。在Persistent method方法中,设备会保存M8 Message中的凭据credentials,GO或GC将使用这些凭据credentials去邀请P2P device去形成P2P Group。

现在我们将了解P2P Persistent Group Formation方法。我们将了解到该方法与Negotiation和Autonomous Group方法的不同之处。

在Group Formation的开始阶段,我们将看到Phase-1和Phase-2阶段。Phase-1阶段主要用于GO分发credentials凭据给GC。P2P Persistent Group Formation没有Phase-1阶段,因为在形成Group的时候,凭据credentials已经保存起来了。任何设备想要连接到之前连接过的设备,它将使用存储起来的凭据进行连接,因此不会有Phase-1阶段,Group Formation过程进行得更快,因为在GO和GC之间少了许多帧交换。

在Persistent Method方法中,我们也不会看到Negotiation Phase协商阶段,因为GO已经在Group形成的时候就确定了。当一个P2P device获取到Persistent Group的凭据时,P2P device应该将该凭据保存起来。这样能够让GO重新创建P2P Group。甚至P2P GC也可以保存凭据,并用这些凭据邀请GO来形成P2P Group。

persistent group形成有两种方式:

GO发送P2P INVITATION REQUEST请求给GC,GC回应P2P INVITATION RESPONSE;

GC发送P2P INVITATION REQUEST请求给GO,GO回应P2P INVITATION RESPONSE。

三 openharmony的p2p架构

3.1 模块整体架构图

如下所示:

 

3.2 运用层

JS API:OpenHarmony中应用app采用ts语言开发,模块中提供JS API层用于ts调用框架层c/c++功能。

目前支持的接口如下:

 

3.3、wifi_manager_service

3.3.1接口简介

wifi_manager_service层为WiFi模块业务逻辑层,如架构图中所示,里面包括WiFi模式(sta、ap、p2p)的业务实现,对WiFi模式的管理等功能,对应的代码路径为services/wifi_standard/wifi_framework。

wifi_manage/wifi_xxx_stub.cpp对应实现API接口,即IPC中的响应部分,wifi_manage/wifi_xxx_callback_proxy.cpp实现事件通知IPC请求部分。

wifi_manage/wifi_manager.cpp是整个wifi_manager_service中的管理模块,如架构图中,在业务模块中包括配置中心、认证管理、事件通知、WiFi模式管理等多个功能,都是在wifi_manager.cpp中进行初始化的。

wifi_manage/wifi_auth_center 认证、权限管理,对操作WiFi的应用、其他框架服务权限进行管控。

wifi_manage/wifi_config_center WifiSettings。配置中心,如保存已连接过的WiFi热点信息,保存上次设置WiFi热点时配置的热点名称、密码等信息。

WifiSettings类可以用在每个具体WiFi模式内部中进行访问,而WifiConfigCenter则在IPC stub等更上一层访问

wifi_mange/wifi_internel_event_dispatcher事件通知,开启关闭WiFi、连接成功或断开连接等当前WiFi状态通知给应用或其他框架服务。

wifi_manage/wifi_service_manager模式管理模块,在WiFi模块中为了减小运行中占用内存量,设计为动态加载模式相关动态库的方式。比如当打开WiFi sta模式时才加载STA模块动态库,当关闭WiFi sta模式时会卸载STA模块动态库。

wifi_manage/wifi_sta WiFi STA模式业务实现代码,在每个模式中都定义了不同的状态,比如初始状态、连接中、已连接、断开连接等。 实际业务就是在不同的状态中处理对应逻辑,以及状态的切换。有关状态机的实现在wifi_manage/common中。

wifi_manage/idl_client向下调用的client端

wifi_manager_service和wifi_hal_service,两个进程之间通信的方式也是ipc方式,通信框架实现代码路径为 services/wifi_standard/ipc_framework。

在WiFi模块中将调用wpa_supplicant与上层框架内部对WiFi状态的管理,对配置的管理等区分为两个不同的进程,即wifi_manager_service和wifi_hal_service。idl_client即为时的一个api层。

3.3.2 服务启动

OpenHarmony子系统启动文件都是编译在/etc/init目录下,由init进程读取配置文件并启动服务进程。wifi_manager_service的启动文件为wifi/services/wifi_standard/etc/init/wifi_standard.cfg

通过/system/bin/sa_main程序启动服务进程是OpenHarmony中通用的方式,sa_main读取参数xml文件中定义的动态库信息,加载相关动态库并最后将进程名更改为services/name中定义的名字,从而完成服务启动过程。

WifiDeviceServiceImpl继承SystemAbility与WifiDeviceStub。WifiDeviceStub如前所述,用来定义IPC的stub端;而SystemAbility类定义了OnStart和OnStop虚函数,在具体实现中重写这两个函数用于在sa_main加载动态库时执行OnStart函数。

在OnStart函数中首先判断是否已经启动,再执行Init函数,并设置启动状态,最后执行WifiManager::GetInstance();。

 

Init函数中执行Publish函数,用于注册IPC stub到系统服务中,从而初始化wifi_manager_service的IPC服务。

 

在WifiManager::Init中即可看到会继续初始化wifi_manager_service中的配置管理、认证管理、模块管理、事件通知等模块。如此,所有模块都初始化后,进程启动成功。

并根据上一次启动时wifi 状态初始化本次设备状态。(更改设备默认开发wifi ,热点可在此位置更改)

 

open harmony 的p2p开启在wifi开启成功的回调函数中执行,使用p2p功能需开启wifi sta。

void WifiStaManager::DealStaOpenRes(OperateResState state, int instId)

{ ...

    WIFI_LOGD("Enter DealStaOpenRes: %{public}d", static_cast<int>(state));

    if (p2pState == WifiOprMidState::CLOSED) {

        WifiManager::GetInstance().GetWifiP2pManager()->AutoStartP2pService();

    }

}

3.3.2 p2p状态机

p2p状态机维护了p2p打开、关闭、连接、协商,组网等的状态及切换。p2p打开时,会启动篇p2pService,构造p2p statemachine并初始化。如p2p状态机树状图所示.

p2p statemachine在初始化时,会创建状态树,创建子状态必须保证相应的父状态被创建。当迁移到子状态,子状态激活,也就是执行GoInState后,其父节点会同时处于激活状态,不会调用GoOutState,子节点共同需要处理的事件或者不关心的事件由父状态处理,子状态只负责处理自己感兴趣的消息。

 

 

3.3.3 消息机制,回调通知

WiFi系统中大部分操作都设计为异步的,比如连接一个热点,这是一个耗时操作,肯定需要做成异步方式。发起连接后,在WiFi真正进行连接操作后(成功或失败)在通知给发起者本次连接结果。

在事件通知模块中定义了一个消息队列并在初始化时启动了一个消息处理线程。当WiFi模式中的回调函数上报事件时,调用WifiInternalEventDispatcher::GetInstance().AddBroadCastMsg函数发送消息到消息队列中。事件模块中消息处理线程则继续根据消息类型,调用IPC callback proxy通知给sdk。


1、运用层注册回调函数:

Js   wifiManager.on("p2pDiscoveryChange", this.p2pDiscoveryChangeCb);注册回调函数:

napi_value On(napi_env env, napi_callback_info cbinfo) {

        napi_get_value_string_utf8(env, argv[0], type, sizeof(type), &typeLen);

    WIFI_LOGI("On Register:type %{public}s, argc:%{public}zu", type, argc);

}

==》napi注册通知函数

ErrCode EventRegister::RegisterP2PEvents(const std::vector<std::string> &event)

{

std::unique_ptr<WifiP2p> wifiP2pPtr = WifiP2p::GetInstance(WIFI_P2P_ABILITY_ID);

    ErrCode ret = wifiP2pPtr->RegisterCallBack(wifiP2pCallback, event);

}



class WifiNapiP2pEventCallback : public IWifiP2pCallback, public NapiEvent {

...

    void OnP2pDiscoveryChanged(bool isChange) override {

        WIFI_LOGI("received discovery state changed event");

        CheckAndNotify(EVENT_P2P_DISCOVERY_CHANGE, (int)isChange);

    }

};

回调的触发==>返回给运用层接收

void P2pStateMachine::BroadcastP2pStatusChanged(P2pState state) const

{

    WifiSettings::GetInstance().SetP2pState(static_cast<int>(state));

    if (p2pServiceCallbacks.OnP2pStateChangedEvent) {

        p2pServiceCallbacks.OnP2pStateChangedEvent(state);

        WifiBroadCastHelper::Send("P2pStatusChanged", static_cast<int>(state));

    }



void WifiManager::DealP2pStateChanged(P2pState state)

{

    WifiInternalEventDispatcher::GetInstance().AddBroadCastMsg(cbMsg);

    WifiCommonEventHelper::PublishP2pStateChangedEvent((int)state, "OnP2pStateChanged");

}

3.4、wifi_hal_service

3.4.1接口简介

wifi_hal_service层主要实现用于调用开源wpa_supplicant(WiFi sta、p2p模式)、hostapd(WiFi Ap模式)以及其他芯片厂商vendor功能,对应的代码路径为services/wifi_standard/wifi_hal。

main.c定义进程的入口,在入口中初始化ipc服务端并注册ipc响应函数接收并处理idl client中请求。

wifi_hal_xxx_interface.c文件实现具体某个模式的接入功能,比如STA模式下调用wifi_hal_sta_interface.c文件,里面实现打开WiFi,关闭WiFi等。

wifi_hal_module_manage.c 对开启wpa_supplicant 和hostapd进行管理。为啥需要管理呢?sta模式和p2p模式都是通过wpa_supplicant调用驱动, 所以只需要启动一次wpa_suppliant即可;另外OpenHarmony设计为支持多STA,多AP模式,也是需要对进程进行管理。

wifi_hal_adapter.c与wifi_hal_vendor_interface.c则是提供可扩展支持芯片厂商提供特定的功能。wifi_hal_vendor_interface.c中定义默认的可扩展函数与默认实现,wifi_hal_adapter中加载芯片厂商扩展库并覆盖需扩展函数。

wifi_hal_module这里才是最终调用的地方,我们说WiFi模块最终会调用开源的wpa_supplicant或hostapd,wifi_hal_module_manage中负责启动wpa_supplicant或hostapd,而wifi_hal_module中则是与wpa_supplicant或hostapd进行通信。

3.4.2 wpa_supplicant的调用过程

以扫描和 连接为例,见第四章

四 p2p接口调用过程分析

4.1 设备扫描流程

p2p设备发现时序图

WiFi框架调用DiscoverDevices启动WiFi P2P设备搜索,DiscoverDevices主要的工作是调用WIFI HAL的WpaP2pCliCmdP2pFound函数,向wpa_supplicant发送P2P_FIND命令。

wpa_supplicant收到P2P_FIND后,就会开始搜索周边的P2P设备,如果找到,给WIFI HAL HAL发送P2P_EVENT_DEVICE_FOUND事件,这个event会带有对方设备的信息,包括MAC地址、device type、设备名字以及config methods等。

WiFi HAL收到这样的event后,会将P2P_EVENT_DEVICE_FOUND事件携带的数据封装成HidlP2pDeviceInfo,通过RPC服务端回送给WiFi框架。

作为PRC客户端,WiFi框架收到HAL事件P2P_DEVICE_FOUND_EVENT对应的事件WIFI_IDL_CBK_CMD_P2P_DEVICE_FOUND_EVENT,读取HidlP2pDeviceInfo,发送P2P_EVENT_DEVICE_FOUND事件通知wifiP2pStateMachine。

wifiP2pStateMachine调用WifiP2pDeviceManager的UpdateDeviceSupplicantInf函数,更新并保存本地设备列表之后调用BroadcastP2pPeersChanged发送设备列表改变的通知。

注册了相关事件监听的应用,在收到通知后调用QueryP2pDevices获取设备列表。

QueryP2pDevices最终调用WifiP2pDeviceManager的GetDevicesList获取本地保存的设备列表。

4.2 设备连接流程

WiFi框架调用p2pConnect启动WiFi P2P连接,连接设备为前一步扫描到的设备列表中的一个。

1, 停止继续扫描。wpa_supplicant收到P2P_STOP_FIND后,就会停止搜索周边的P2P设备。

2,与目标设备进行协商交换连接信息并初始化连接

发现对等节点,获取对等设备信息:P2P_PEER 命令可能用于请求特定对等节点的详细信息,如设备名称、设备能力、支持的服务等。这有助于设备确定它是否要与该对等节点建立连接。wpa_supplicant收到P2P_PERR后开始。

3,Phase1 and Phase 2 WPS配置阶段,当wpa_supplicant收到P2P_PROV_DISC后进行,匹配完成后wpa_supplicant回复”P2P-PROV-DISC-PBC-RESP”。

4,第二次触发GetP2pPeer 的原因是P2P-PROV-DISC-PBC-RESP的回调函数执行时执行了P2pConnectByShowingPin方-->FetchNewerDeviceInfo--->GetP2pPeer引起。

5,注册了相关事件监听的应用,在收到通知后调用"P2P-GO-NEG-SUCCESS"

更新当前连接状态。

HAL层监听wpa_supplicant返回信息后,最终调用P2pStateMachine::BroadcastP2pConnectionChanged() ->OnP2pConnectionChangedEvent()将当前的状态信息更新到运用层。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐