8

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区-51CTO.COM

 2 years ago
source link: https://ost.51cto.com/posts/14673
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文正在参加星光计划3.0–夏日挑战赛

作者:巴延兴

1. WiFi简介

1.1 WiFi介绍

WiFi是一种无线通信技术,可以将个人电脑、手持设备(如pad、手机)等终端以无线方式互相连接。WiFi网络是使用无线通信技术在一定的局部范围内建立的网络,是计算机网络与无线通信技术相结合的产物,它以无线多址信道作为媒介,提供传统局域网的功能,使用户真正实现随时随地随意的宽带网络接入。

WiFi主要遵循IEEE802.11系列协议标准,该通信协议于1996年由澳洲的研究机构CSIRO提出,WiFi凭借其独特的技术优势,被公认为是目前最为主流的WLAN(无线局域网)技术标准。随着WiFi无线通信技术的不断优化和发展,目前主要的通信协议标准有802.11a、802.11b、802.11g、802.11n和802.11ac、802.11ax,根据不同的协议标准主要有两个工作频段,分别为2.4GHz和5.0GHz。

下表简单介绍了各个标准的发布时间和特点。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

1.2 基本概念

1.2.1 WiFi网络结构

WiFi无线网络中包含了一些基本的组成元素如工作站,接入点等,以下介绍WiFi网络中一些基础概念。

(1)工作站(Station)

工作站是指配备无线网络接口的终端设备(计算机、手机等),构建网络的目的就是为了在工作站间传送数据。

(2)接入点(Access Point)

802.11网络所使用的帧必须经过转换,方能被传递至其他不同类型的网络。具备无线至有线(wireless-to-wired)的桥接功能的设备称为接入点,简称AP。

(3)无线媒介(Wireless medium)

802.11标准以无线媒介在工作站之间传递帧。

(4)分布式系统(Distribution system)

几个接入点串联起来可以覆盖一块比较大的区域,接入点之间相互通信可以掌握移动式工作站的行踪,这就组成了一个分布式系统。分布式系统属于802.11的逻辑组件,负责将帧(frame)传送至目的地,分布式系统是接入点间转发帧的骨干网络,因此通常称为骨干网络(backbone network),基本都是以太网(Ethernet)。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

​ 图1 WiFi分布式系统组成

如图,在分布式网络拓扑结构中,有几个基本概念:

1)基本服务集(BSS)

由一组彼此通信的工作站组成,这里只讨论基础型BSS,一个热点覆盖的范围称为一个BSS。

2)扩展服务集(ESS)

多个BSS可以构成一个扩展网络,称为扩展服务集(ESS)网络,一个ESS网络内部的STA可以互相通信,是采用相同的SSID的多个BSS形成的更大规模的虚拟BSS。

连接BSS的组件称为分布式系统(Distribution System,DS)。

3)SSID

Service Set ID,服务集标识。

SSID是让网管人员为服务集合(SS)指定的识别码,组成ESS的所有BSS都会使用相同的SSID。

4)BSSID

Basic Service Set ID,基本服务集标识。

在基础网络里,BSSID就是接入点(AP)使用的MAC地址。

5)ESSID

Extended Service Set ID,扩展服务集标识。

因为ESS中所有BSS使用同一标识,所以ESSID就是SSID。

1.2.2 WiFi网络安全技术

IEEE802.11技术从出现开始,就一直为安全问题困扰。继因安全性问题被指责的WEP后,Wifi联盟先后推出了WPA和WPA2安全标准以及最新的WPA3标准。下面对常见的这几种安全标准做简单说明。

l WEP(Wired Equivalent Privacy,有线等效加密),是IEEE802.11最初提出的基于RC4流加密算法的安全协议,存在加密流重用、密钥管理等问题,已基本被弃用。

l WPA(WiFi Protected Access,WiFi保护访问),WiFi联盟在IEEE802.11i草案基础上制定的一项无线网络安全技术,目的在于替代传统WEP安全技术,分为WPA Personal(pre-shared key身份验证)和WPA Enterprise。WPA使用临时密钥完整性协议(Temporal Key Integrity Protocol,TKIP),提高了无线网络的安全性。

l WPA2是WPA的加强版,支持高级加密协议(Advanced Encryption Standard,AES),使用计数器模式密码块链消息完整码协议(CCMP),安全性比WPA有进一步提升。

l WPA3是Wi-Fi联盟组织于2018年1月8日发布的Wifi新加密协议,是WPA2技术的后续版本。WPA3支持SAE(对等同步认证)以及具有192位加密功能的WPA3-Enterprise,比WPA2更安全。

1.2.3 WiFi工作模式

鸿蒙系统的WiFi组件在驱动支持的前提下,可以支持三种工作模式:STATION模式、AP模式和P2P模式。

  • STATION模式,就是2.2.1中讲到的工作站,也就是无线局域网中的一个客户端,这是Wifi最基本的工作模式,通过连接其他接入点访问网络。

  • AP模式也就是接入点模式,即设备作为接入点,为无线局域网中的客户端提供网络接入功能,大多数终端设备称其为hotspot(热点)或者softap,通过wifi的AP模式,可以将设备的运营商数据网络共享给接入的客户端,实现随时随地的网络资源共享。

  • P2P模式是WFA(WiFi联盟)推出的一项与蓝牙类似的技术,允许设备间一对一直连,无需通过AP即可相互连接。P2P模式中的设备,称为P2P Device,P2P设备组成的网络叫P2P Group。在P2P网络中,P2P Device有两个角色,一个是GO(Group Owner),其作用类似于AP;另一个角色是GC(Group Client),类似于工作站(Station)。P2P设备完成协商组建为一个P2P网络的时候,有且只能有一个设备作为GO,其他设备做为GC。Wifi P2P模式传输速度和传输距离比蓝牙有大幅提升,但功耗也要比蓝牙高。

2. WiFi子系统介绍

本章主要讲解鸿蒙系统中WIFI子系统的架构组成以及部分关键模块的实现。

2.1 系统架构简介

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

图1 WiFi系统架构图

如图1所示,鸿蒙WiFi系统是典型的分层结构,自上而下包括:

主要包含鸿蒙提供的settings应用,该应用是典型的用户使用WiFi的方式,提供用户可见的设置界面,提供WiFi开关、WiFi扫描、连接断开等基本功能。

应用代码通过导入接口类,从而调用下层WIFI Native提供的JS接口,这部分实现的代码在以下目录中:

applications\standard\settings

  • Wifi Native JS

这部分应用了Node.js推出的用于开发C原生模块的接口N-API技术,对框架提供的C接口进行封装,为应用提供了调用WiFi功能的JS接口。

这部分代码在以下目录中:

foundation\communication\wifi\interfaces

  • WiFi框架

主要包含wifi服务的实现。其中,

wifi_manager_service负责管理STA、SCAN、AP、P2P等服务的加载和卸载。

wifi_ap_service实现AP模式的状态机管理和事件处理。

wifi_p2p_service实现P2P模式的状态机管理和事件处理。

wifi_scan_service实现SCAN时的状态机管理和事件处理。

wifi_sta_service实现STA模式的状态机管理和事件处理。

wifi_idl_client实现了与Wifi HAL进行RPC通信的客户端。

dhcp_manager_service实现DHCP管理服务,启动DHCP客户端或者DHCP服务器。

dhcp_client_service是DHCP客户端的实现。

dhcp_server是DHCP服务器的实现。

这部分代码在以下目录中:

foundation\communication\wifi\services\wifi_standard\wifi_framework

  • Wifi HAL

Wifi HAL提供RPC服务端,响应WiFi框架的远程调用,HAL的主要功能是适配WPA Supplicant,负责启动wpa_supplicant或者hostapd并添加网络接口,向wpa_supplicant或hostapd发送控制命令完成WiFi相关的业务操作。Wifi HAL作为wpa_supplicant的适配层,依赖wpa_supplicant的libwpa_cli库。

这部分代码在如下目录中:

foundation\communication\wifi\services\wifi_standard\wifi_hal

  • WPA Supplicant

包含libwpa、libwpa_client库和wpa_cli、wpa_supplicant、hostapd可执行程序。

libwpa是一个包含了wpa_suppliant和hostapd具体实现的库,Wifi HAL启动WPAS就是通过加载libwpa库,去执行wpa_supplicant或hostapd的入口函数。WPAS是一个开源项目,其中,wpa_supplicant是wpa的认证客户端,负责完成认证相关的登录、加密等工作,hostapd包含了IEEE802.11接入点管理、IEEE802.1X/WPA/WPA2认证、EAP服务器以及Radius鉴权服务器功能。

libwpa_client是一个给客户端连接和调用的库,提供创建与wpa_supplicant或hostapd通信控制接口的能力。

wpa_cli和wpa_supplicant是客户端和服务器的关系,通过wpa_cli可以向wpa_supplicant发送命令,进行扫描、连接等做操作,可用来进行Wifi功能的验证,Wifi HAL也是wpa_supplicant的客户端。

wpa_supplicant和hostapd可执行程序依赖libwpa,启动这两个可执行程序,可以运行WPAS提供的所有功能。

这部分所处的路径为:

third_party\wpa_supplicant\wpa_supplicant-2.9_standard

  • WiFi驱动框架

鸿蒙提供了HDF驱动框架的WiFi驱动模型,可实现跨操作系统迁移,自适应器件差异,模块化拼装编译等功能。各WiFi厂商驱动开发人员可根据WiFi模块提供的向下统一接口适配各自的驱动代码。

包含linux内核标准的Wifi驱动程序和协议。

2.2 关键模块实现

2.2.1 进程间通信

Native JS和Wifi框架通过IPC(Inter-Process Communication)进行通信,实现接口调用及事件传递。IPC通信采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取提供服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。

在鸿蒙系统中,首先服务端注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr),SAMgr负责管理这些SA并向客户端提供相关的接口。客户端要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和服务端通信,Proxy表示服务请求方,Stub表示服务提供方。

Wifi系统对不同模式各实现了一套Proxy-Stub类,分别是WifiDeviceProxy和WifiDeviceStub、WifiHotspotProxy和WifiHotspotStub、WifiP2pProxy和WifiP2pStub、WifiScanProxy和WifiScanStub,对于不同业务流程进行了分离。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

WifiDeviceProxy和WifiDeviceStub类图

以WifiDeviceProxy和WifiDeviceStub为例,分别从服务方和代理方说明实现过程。

WiFi框架提供服务方WifiDeviceStub,继承IRemoteStub,实现了IWifiDevice接口类中未实现的方法,并重写了OnRemoteRequest方法。Proxy请求方发来的请求就在OnRemoteRequest中处理。


1. int WifiDeviceStub::OnRemoteRequest(uint32\_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)

2. {

3. int exception = data.ReadInt32();

4. if (exception) {

5. return WIFI\_OPT\_FAILED;

6. }

7.

8. HandleFuncMap::iterator iter = handleFuncMap.find(code);

9. if (iter == handleFuncMap.end()) {

10. WIFI\_LOGI("not find function to deal, code %{public}u", code);

11. reply.WriteInt32(0);

12. reply.WriteInt32(WIFI\_OPT\_NOT\_SUPPORTED);

13. } else {

14. (this-\>\*(iter-\>second))(code, data, reply);

15. }

16.

17. return 0;

18. }

以开关wifi接口处理函数为例,WifiDeviceStub对proxy请求事件和相应处理函数进行了映射。


1. handleFuncMap[WIFI\_SVR\_CMD\_ENABLE\_WIFI] = &WifiDeviceStub::OnEnableWifi;

2. handleFuncMap[WIFI\_SVR\_CMD\_DISABLE\_WIFI] = &WifiDeviceStub::OnDisableWifi;

WifiDeviceServiceImpl继承WifiDeviceStub类和SystemAbility类,是IPC通信服务方的具体实现,如以下代码所示,WifiDeviceServiceImpl通过MakeAndRegisterAbility将WifiDeviceServiceImpl实例注册到SAMgr。接下来,服务请求方就可以通过从SAMgr获取代理来和服务提供方通信。


1. const bool REGISTER\_RESULT = SystemAbility::MakeAndRegisterAbility(WifiDeviceServiceImpl::GetInstance().GetRefPtr());

2.

3. sptr\<WifiDeviceServiceImpl\> WifiDeviceServiceImpl::GetInstance()

4. {

5. if (g\_instance == nullptr) {

6. std::lock\_guard\<std::mutex\> autoLock(g\_instanceLock);

7. if (g\_instance == nullptr) {

8. auto service = new (std::nothrow) WifiDeviceServiceImpl;

9. g\_instance = service;

10. }

11. }

12. return g\_instance;

13. }

14.

15. WifiDeviceServiceImpl::WifiDeviceServiceImpl()

16. : SystemAbility(WIFI\_DEVICE\_ABILITY\_ID, true), mPublishFlag(false), mState(ServiceRunningState::STATE\_NOT\_START)

17. {}

WifiDeviceProxy继承自IRemoteProxy,封装WiFi Station模式相关业务函数,调用SendRequest将请求发到服务端Stub。

WiFi Native JS作为服务请求方构造代理WifiDeviceProxy,WifiDeviceImpl实例初始化时通过Init函数构造WifiDeviceProxy,步骤如下:

首先,获取SAMgr。

然后,通过SAMgr及相应的ability id获取到对应SA的代理IRemoteObject。

最后,使用IRemoteObject构造WifiDeviceProxy。


1. bool WifiDeviceImpl::Init()

2. {

3. sptr\<ISystemAbilityManager\> sa\_mgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

4. if (sa\_mgr == nullptr) {

5. WIFI\_LOGE("failed to get SystemAbilityManager");

6. return false;

7. }

8.

9. sptr\<IRemoteObject\> object = sa\_mgr-\>GetSystemAbility(systemAbilityId\_);

10. if (object == nullptr) {

11. WIFI\_LOGE("failed to get DEVICE\_SERVICE");

12. return false;

13. }

14.

15. client\_ = iface\_cast\<IWifiDevice\>(object);

16. if (client\_ == nullptr) {

17. client\_ = new (std::nothrow) WifiDeviceProxy(object);

18. }

19.

20. if (client\_ == nullptr) {

21. WIFI\_LOGE("wifi device init failed. %{public}d", systemAbilityId\_);

22. return false;

23. }

24.

25. return true;

}

WifiDeviceProxy通过Remote()->SendRequest()发送请求,服务方通过OnRemoteRequest进行处理。以EnableWifi为例:


1. ErrCode WifiDeviceProxy::EnableWifi()

2. {

3. if (mRemoteDied) {

4. WIFI\_LOGD("failed to `%{public}s`,remote service is died!", \_\_func\_\_);

5. return WIFI\_OPT\_FAILED;

6. }

7. MessageOption option;

8. MessageParcel data;

9. MessageParcel reply;

10. data.WriteInt32(0);

11.

12. int error = Remote()-\>SendRequest(WIFI\_SVR\_CMD\_ENABLE\_WIFI, data, reply, option);

13. if (error != ERR\_NONE) {

14. WIFI\_LOGE("Set Attr(%{public}d) failed,error code is %{public}d", WIFI\_SVR\_CMD\_ENABLE\_WIFI, error);

15. return WIFI\_OPT\_FAILED;

16. }

17.

18. int exception = reply.ReadInt32();

19. if (exception) {

20. return WIFI\_OPT\_FAILED;

21. }

22. return ErrCode(reply.ReadInt32());

23. }

2.2.2 状态机管理

Wifi框架维护了四个状态机,分别是sta statemachine、scan statemachine、p2p statemachine和ap statemachine。Wifi各个模式工作流程中会涉及到各个不同的阶段,需要对不同阶段的状态进行管理。对于Native JS通过代理发送到Wifi框架的请求以及HAL回送的WPA Supplicant的响应,需要在相应模式的相应状态下做合适的处理。

本章仅介绍Wifi基本模式sta和scan的状态机。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

Sta状态机树状图

Sta状态机维护了wifi打开、关闭、连接、获取IP及漫游的状态及切换。Wifi打开时,会启动staService,构造sta statemachine并初始化。如sta状态机树状图所示,sta statemachine在初始化时,会创建状态树,创建子状态必须保证相应的父状态被创建。当迁移到子状态,子状态激活,也就是执行GoInState后,其父节点会同时处于激活状态,不会调用GoOutState,子节点共同需要处理的事件或者不关心的事件由父状态处理,子状态只负责处理自己感兴趣的消息。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

Sta状态机迁移图

比如wifi打开时,状态机从InitState迁移到目标状态SeparatedState,这时处于激活状态的有WpaStartedState及LinkState。

当wifi关闭时,WpaStartedState处理WIFI_SVR_CMD_STA_DISABLE_WIFI事件,关闭wifi,回到InitState状态。

当用户连接网络时,LinkState处理CMD_START_CONNECT_SELECTED_NETWORK事件进行连接网络的动作,收到WIFI_SVR_CMD_STA_NETWORK_CONNECTION_EVENT后,状态迁移到GetIpState状态,同时ApLinkedState状态处于激活。在GetIpState状态,如果IP地址分配成功,则进入LinkedState。如果分配失败,则回到SeparatedState。

不管是在GetIpState还是在LinkedState,只要收到断开网络请求WIFI_SVR_CMD_STA_DISCONNECT,都由ApLinkedState处理,进入SeparatedState。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

Scan状态机维护了wifi普通扫描,pno扫描(硬件扫描和软件扫描)的状态及切换过程。

这里对PNO扫描稍加说明,PNO扫描即Preferred Network Offload,用于系统在休眠的时候连接WiFi,当手机休眠时,存在已经保存的网络并且没有连接时,进行PNO扫描,只扫描已保存的网络。PNO模式能让设备在熄屏时通过搜索最近连接过的Wifi网络,从而优先连接至Wifi网络,达到延长续航时间并且减少手机数据流量消耗的目的。

Wifi打开后,启动scanService同时构造scan statemachine并初始化,与sta statemachine相同,scan statemachine按照Scan状态机树状图所示创建状态机各个状态。

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

Scan状态机迁移

scan statemachine初始化时设置状态为InitState,随后发送CMD_SCAN_PREPARE给InitState,进入HardwareReady状态。

处于HardwareReady状态时Native JS调用scan接口进行扫描,HardwareReady会收到CMD_START_COMMON_SCAN消息进入CommonScanning状态,扫描成功、失败或者超时后进入CommonScanUnworked状态,如果这时候再次发起扫描则会回到CommonScanning状态。

处于HardwareReady状态时发起PNO扫描时,首先判断系统是否支持硬件PNO扫描,如果支持,则进入PnoScanHardware状态,向底层下发PNO扫描命令。

在PnoScanHardware状态,如果收到PNO扫描结果通知,并且NeedCommonScanAfterPno为true,则进入CommonScanAfterPno状态,等收到CMD_START_COMMON_SCAN进入CommonScanning,或者收到普通扫描命令,进入HardwareReady状态准备进行普通扫描,否则一直处于PNO硬件扫描状态。

当发起PNO扫描后,如果系统不支持硬件PNO扫描,则进入PnoScanSoftware状态,启动一次软件PNO扫描,进入PnoSwScanning状态,扫描成功或者失败或者超时后进入PnoSwScanFree状态。

在PnoSwScanFree状态,收到CMD_START_COMMON_SCAN命令,则回到HardwareReady状态准备进行一次普通扫描;如果收到PNO扫描命令则回到PnoSwScanning状态。

2.2.3 WPA Supplicant

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

wpa_supplicant框架图

wpa_supplicant是一个独立运行的守护进程,其核心是一个消息循环,在消息循环中处理WPA状态机、控制命令、驱动事件、配置信息等。

wpa_supplicant由启动,通过创建两个上行接口,通过这两个接口进行命令发送和事件监听;通过通信实现下行接口,与内核进行通信,下发命令和获取消息。

3. WiFi业务流程

3.1 WiFi Station流程

3.1.1 打开流程

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

wifi启动时序

上图为wifi启动时序图,其中WifiDeviceImpl是Native JS层WifiDevice的实现类。

WifiDeviceServiceImpl是Wifi框架层对Navtive JS端IPC通信服务的实现类。

StaService是Wifi框架层Station服务的主要实现,通过创建StaStateMachine和StaMonitor对Wifi Station命令和事件进行处理。如上图中所示,调用Native JS中的EnableWifi接口,首先获取WifiDevice实例,调用该实例提供的EnableWifi。 然后通过WifiDeviceProxy向Stub发送请求,Stub响应请求。

Stub服务端实现了EnableWifi的实现逻辑,首先,构造StaService并进行初始化,StaService初始化时构造StaStateMachine并初始化状态机,进入InitState状态,接下来构造StaMonitor并初始化,注册事件回调函数。StaMonitor主要对AP连接状态改变等事件进行处理。

这些准备工作做完后,就是真正执行EnableWifi的流程。

StaService向StaStateMachine发送WIFI_SVR_CMD_STA_ENABLE_WIFI消息,StaStateMachine转化为WIFI_SVR_CMD_STA_START_SUPPLICANT消息后通过wifi_idl_client向wifi hal发起RPC调用"Start"。

Wifi Hal作为RPC服务端,启动后调用InitRpcFunc初始化RPC函数,然后CreateRpcServer,最后调用RunRpcLoop循环读取远程调用信息,处理客户端请求。

InitRpcFunc中Map了"Start"消息的处理函数,PushRpcFunc("Start", RpcStart)。RpcStart实际操作实现在wifi_hal_sta_interface的Start函数,主要做了三步操作:

  1. start supplicant

命令:wpa_supplicant -iglan0 -g/data/misc/wifi/sockets

  1. Add a new interface wlan0

命令:interface_add wlan0 /data/misc/wifi/wpa_supplicant/wpa_supplicant.conf

  1. 构造并初始化WifiWpaStaInterface,封装了wpa_supplicant关于STA的操作命令。

以上三步成功后,RPC调用返回WIFI_HAL_SUCCESS。

StaStateMachine在EnableWifi成功后,执行OnStaOpenRes回调,在此回调里,广播wifi状态改变消息,构造ScanService并初始化。初始化过程做了这几件事:

  1. 构造ScanStateMachine并初始化,调用EnrollScanStatusListener绑定Scan状态上报事件的处理函数。

  2. 构造ScanMonitor并初始化,在初始化函数中调用RegisterSupplicantEventCallback,注册supplicant事件回调。

3.1.2 扫描流程

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

​ Wifi扫描时序

上图为WiFi扫描时序图,其中WifiScanImpl是Native JS层WifiScan的实现类。

WifiScanServiceImpl是Wifi框架层对Navtive JS端IPC通信服务的实现类。

ScanService是Wifi框架层Scan服务的主要实现,通过创建ScanStateMachine和ScanMonitor对Wifi Station命令和事件进行处理。

当应用调用Native JSOL的Scan接口,与WiFi打开过程类似,会通过获取WifiScan实例,调用C++接口,最终通过WifiScanProxy向服务框架发送WIFI_SVR_CMD_FULL_SCAN请求。

收到请求,WifiScanServiceImpl调用ScanService,构造scanConfig,然后向ScanStateMachine发送CMD_START_COMMON_SCAN命令并携带scanConfig。

ScanStateMachine如果处于HardwareReady等可以发起扫描的激活状态下,则进行获取扫描参数的操作,并校验Scan类型是否合法,之后转换扫描参数,通过RPC调用HAL的scan操作,HAL得去scan配置参数后,向supplicant发送SCAN命令。

接着获取扫描结果,时序图如下:

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

​ 获取扫描结果

Supplicant执行扫描成功后,调用WifiHalCbNotifyScanEnd(WPA_CB_SCAN_OVER_OK)通知扫描成功。ScanMonitor执行回调,向ScanStateMachine发送SCAN_RESULT_EVENT事件:

pScanStateMachine->SendMessage(static_cast<int>(SCAN_RESULT_EVENT));

ScanStateMachine处理事件,远程调用wifi hal获取扫描结果。

WifiHal返回扫描结果给ScanStateMachine后,ScanStateMachine构造ScanStatusReport,包含scanInfoList和status,交给回调函数ScanService::HandleScanStatusReport处理。

ScanService拿到扫描结果,主要做的事是调用WifiSettings的SaveScanInfoList(filterScanInfo),将扫描结果保存。之后,调用native js的GetScanInfos接口,通过WifiScanServiceImpl调用WifiSettings的GetScanInfoList获取到保存的扫描结果。

扫描结果中包含的信息如下,一般常用到的信息有:

Bssid - 扫描到的AP的mac地址

Ssid - 扫描到的AP的标识名称

Band - 支持频段为2.4G还是5G

securityType - 安全类型:

OPEN/WEP/PSK/EAP/SAE/EAP_SUITE_B/OWE/WAPI_CERT/WAPI_PSK

3.1.3 连接流程

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

Wifi连接时序图

Native JS调用connectToDevice连接选择的wifi网络,通过IPC代理发送请求,调用到staService的ConnectToDevice函数。

StaService首先调用AddDeviceConfig,在这个函数中主要做了两件事:

调用GetNextNetworkId,通过HAL向supplicant发送ADD_NETWORK命令,得到netwrok id,保存在WifiDeviceConfig。

调用ConvertDeviceCfg,在StaStateMachine中将网络配置参数转换为idl参数,然后调用HAL的SetDeviceConfig函数,向supplicant发送SET_NETWORK命令。

StaService在调用AddDeviceConfig得到networkid并且设置配置参数到supplicant成功后,向StaStateMachine发送消息,向supplicant发送EnableNetwork、SELECT_NETWORK以及SAVE_CONFIG命令,supplicant根据收到的命令完成AP的连接管理。

Supplicant连接成功后,回送事件,经过,转换为由的回调函数处理,发送消息给,状态机进入,获取,静态或者获取成功后,继续调用检查网络连接状态。

3.2 WiFi P2P流程

3.2.1 设备发现流程

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

​ 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获取本地保存的设备列表。

3.3 WiFi 热点流程

#夏日挑战赛#OpenHarmony 短距子系统-WIFI源码分析-开源基础软件社区

热点启动时序图

上图为WiFi热点启动时序图,其中WifiHotspotImpl是Native JS层WifiHotspot的实现类。

WifiHotspotServiceImpl是Wifi框架层对Navtive JS端IPC通信服务的实现类。

ApService是Wifi框架层AP服务的主要实现,通过创建ApStateMachine和ApMonitor对Wifi热点命令和事件进行处理。

WifiHotspotImpl通过IPC调用框架WifiHotspotServiceImpl的EnableHotspot,首先检查是否处于飞行模式或者省电模式,在这两种模式中禁用热点,返回相应的错误码。然后加载APService并初始化,注册WifiManager的回调函数到APService并通过APService向APStateMachine发送CMD_START_HOTSPOT消息,APStateMachine切换到ApStartedState状态。

进入启动状态时:

启动APMonitor,RegisterApEvent到WifiApHalInterface处理工作站接入或离开事件以及热点状态变化事件。

调用WifiApHalInterface的StartAp启动hostapd,构造WifiHostapdHalDevice并创建于hostapd通信的控制接口ctrlConn和ctrlRecv,ctrlConn用于向hostapd发送命令,ctrlRecv用于接收从hostapd通知的事件。

调用WifiApHalInterface的SetSoftApConfig对softap进行配置,基本配置信息包括:ssid(wifi热点名称)、热点密码以及安全类型(可选的有无加密、WPA-PSK和WPA2-PSK)、最大连接数、支持频带及信道等。通过RPC调用,向hostapd下发以下一系列命令:

SET wpa_passphrase:设置密码

SET ssid:设置热点名称

SET wpa:设置加密类型 0为NONE,1为WPA-PSK,2位WPA2-PSK

SET hw_mode:设置频段,参数分别为"any"、"g"(2.4G)、"a"(5G)

SET channel:设置信道

SET max_num_sta:设置最大连接数

RELOAD:重新加载配置

DISABLE:关闭AP

ENABLE:使能AP

Hostapd处理完以上命令之后,会上报AP-ENABLED,HAL通知ApMonitor将其转化为CMD_UPDATE_HOTSPOTCONFIG_RESULT给到ApStartedState,ApStartedState启动DHCP服务器,负责为连接的工作站分配IP,最后通过之前注册在状态机的回调函数通知上层AP状态变为AP_STATE_STATED。

当有工作站连接热点,底层连接成功后,hostapd会向HAL发送AP-STA-CONNECTED消息,通知ApMonitor后发送命令CMD_STATION_JOIN到ApStateMachine,调用ApStationsManager通过WifiSettings添加连接设备的信息,之后广播COMMON_EVENT_WIFI_AP_STA_JOIN事件,应用通过注册该事件监听知道有设备连接。

4. WiFi接口说明

WiFi基础功能由@ohos.wifi类提供,其接口(JS接口)说明如下。

| 接口名 | 描述 |

| ------------------------------------------------------------ | :-------------------------------------------------------: |

| function enableWifi(): boolean | 打开WiFi |

| function disableWifi(): boolean | 关闭WiFi。 |

| function isWifiActive(): boolean | 查询WiFi是否处于打开状态。 |

| function scan(): boolean | 发起WiFi扫描。 |

| function getScanlnfos():Promise<Array<WifiScanlnfo>>function getScanlnfos(callback: AsyncCallback<Array<WifiScanlnfo>>): void | 获取WiFi扫描结果,接口可采用promise或callback 方式调用。 |

| function addDeviceConfig(config: WifiDeviceConfig):Promise<number>function addDeviceConfig(config: WifiDeviceConfig, callback: AsyncCallback<number>): void | 添加WiFi的配置信息,接口可采用promise或callback方式调用。 |

| function connectToNetwork(networkld: number): boolean | 连接到WiFi网络。 |

| function connectToDevice(config: WifiDeviceConfig):boolean | 连接到WiFi网络。 |

| function disconnect(): boolean | 断开WiFi连接。 |

| function getSignalLevel(rssi: number, band: number):number | 获取WiFi信号强度。 |

更多原创内容请关注:深开鸿技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK