社区编辑申请
注册/登录
OpenHarmony3.1-WIFI子系统之STA模式源码解析
系统 OpenHarmony
Wi-Fi是WLAN具体采用的技术,也是目前WLAN的主流技术。Wi-Fi采用的技术是IEEE80211系列协议,IEEE (Institute of Electrical and Electronics Engineers)是美国电气和电子工程师协会的简称。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

简介

Wi-Fi是WLAN具体采用的技术,也是目前WLAN的主流技术。Wi-Fi采用的技术是IEEE80211系列协议,IEEE (Institute of Electrical and Electronics Engineers)是美国电气和电子工程师协会的简称。

STA模式:Station,类似于无线终端,sta本身并不接受无线的接入,它可以连接到AP(AccessPoint),一般无线网卡即工作在该模式,这是Wifi最基本的工作模式。

Wifi子系统架构

<center>Wifi子系统架构图</center>

Wifi架构解析

Wi-Fi App:

主要是开发者自行开发Wi-Fi相关功能的应用。通过调用Wifi SDK对外提供的API实现对设备Wi-Fi的控制及其功能实现。这一层平台将会提供相关的API调用示例,以供参考。

Wi-Fi Native JS:

JS层使用NAPI机制开发,连接APP层与Framework层,将wifi功能封装成JS接口提供给应用调用,并同时支持Promise和Callback异步回调。

Wi-Fi Framework:

Wi-Fi核心功能实现。直接为上层应用提供服务。根据其工作模式的不同分为四大业务服务模块,分别是STA服务、AP服务、P2P服务、Aware服务,同时DHCP功能。

Wi-Fi Hal:

为FrameWork层操作Wi-Fi硬件提供统一的接口服务,实现应用框架与硬件操作的分离。主要包括Hal适配器及扩展Hal模块及Wi-Fi硬件厂家提供的二进制库模块。

WPA Supplicant:

包含wpa_supplicant和hosapd两个子模块,wpa_supplicant和hostapd实现了定义好的驱动API,对外提供控制接口,框架就能通过其控制接口来实现Wifi的各种操作。wpa_supplicant支持STA及P2P模式,hostapd则支持AP模式。

HDF:

HDF 驱动框架主要由驱动基础框架、驱动程序、驱动配置文件和驱动接口这四个部分组成,实现WIfi驱动功能,加载驱动,驱动接口部署等。

Wi-Fi Kernel:

包含Wi-Fi 驱动,包括了对设备的一些基本的读写操作由Wi-Fi驱动移植人员编译进内核。

代码结构:

/foundation/communication/wifi
├── figures # 图片资源目录
├── interfaces # 接口适配目录
├── innerkits # 内部接口适配目录
└── kits # WLAN组件接口的适配代码存放目录
├── services # service适配目录
└── wifi_standard # service实现目录
├── tests # 测试代码目录
└── wifi_standard # service实现模块测试代码
└── utils # 实用函数目录
├── inc # 实用函数头文件目录
└── src # 实用函数实现目录

关键模块实现

IPC通信

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类,STA模式分别是WifiDeviceProxy和WifiDeviceStub,WifiScanProxy和WifiScanStub,对扫描和其他STA流程进行了分离。

<center>IPC通信图</center>

由图所示,首先WifiDeviceImpl是Native JS层WifiDevice的实现类,是服务请求方,作为IPC服务客户端,WifiDeviceProxy作为代理类,通过它来向服务方发起请求;WifiDeviceStub作为服务方接收请求并处理;WifiDeviceServiceImpl继承WifiDeviceStub类和SystemAbility类,是IPC通信服务方的具体实现。

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

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

首先,获取SAMgr。

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

最后,使用IRemoteObject构造WifiDeviceProxy。

目录:foundation/communication/wifi/interfaces/innerkits/native_cpp/wifi_standard/src/wifi_device_impl.cpp.

bool WifiDeviceImpl::Init()
{
//获取SAMgr
sptr<ISystemAbilityManager> sa_mgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
if (sa_mgr == nullptr) {
WIFI_LOGE("failed to get SystemAbilityManager");
return false;
}
//通过SAMgr及相应的ability id获取到对应SA的代理IRemoteObject
sptr<IRemoteObject> object = sa_mgr->GetSystemAbility(systemAbilityId_);
if (object == nullptr) {
WIFI_LOGE("failed to get DEVICE_SERVICE");
return false;
}
//使用IRemoteObject构造WifiDeviceProxy
client_ = iface_cast<IWifiDevice>(object);
if (client_ == nullptr) {
client_ = new (std::nothrow) WifiDeviceProxy(object); //构造代理
}

if (client_ == nullptr) {
WIFI_LOGE("wifi device init failed. %{public}d", systemAbilityId_);
return false;
}

return true;
}

我们以打开WiFi为例看看其调用过程,客户端发起请求后,接口层提供WifiDeviceProxy作为代理发送打开WiFi请求:

目录:foundation/communication/wifi/interfaces/innerkits/native_cpp/wifi_standard/src/wifi_device_proxy.cpp。

ErrCode WifiDeviceProxy::EnableWifi()
{
if (mRemoteDied) {
WIFI_LOGD("failed to `%{public}s`,remote service is died!", __func__);
return WIFI_OPT_FAILED;
}
MessageOption option;
MessageParcel data;
MessageParcel reply;
data.WriteInt32(0);

int error = Remote()->SendRequest(WIFI_SVR_CMD_ENABLE_WIFI, data, reply, option); //向服务方发送WIFI_SVR_CMD_ENABLE_WIFI请求
if (error != ERR_NONE) {
WIFI_LOGE("Set Attr(%{public}d) failed,error code is %{public}d", WIFI_SVR_CMD_ENABLE_WIFI, error);
return WIFI_OPT_FAILED;
}

int exception = reply.ReadInt32();
if (exception) {
return WIFI_OPT_FAILED;
}
return ErrCode(reply.ReadInt32());
}

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

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

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_device_stub.cpp。

int WifiDeviceStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
int exception = data.ReadInt32();
if (exception) {
return WIFI_OPT_FAILED;
}
HandleFuncMap::iterator iter = handleFuncMap.find(code);
if (iter == handleFuncMap.end()) {
WIFI_LOGI("not find function to deal, code %{public}u", code);
reply.WriteInt32(0);
reply.WriteInt32(WIFI_OPT_NOT_SUPPORTED);
} else {
(this->*(iter->second))(code, data, reply);
}
return 0;
}

WifiDeviceStub对proxy请求事件和相应处理函数进行了映射。

void WifiDeviceStub::InitHandleMap()
{
handleFuncMap[WIFI_SVR_CMD_ENABLE_WIFI] = &WifiDeviceStub::OnEnableWifi; //打开wifi
handleFuncMap[WIFI_SVR_CMD_DISABLE_WIFI] = &WifiDeviceStub::OnDisableWifi;
......
return;
}

根据映射关系调用其OnEnableWifi方法:

void WifiDeviceStub::OnEnableWifi(uint32_t code, MessageParcel &data, MessageParcel &reply)
{
WIFI_LOGD("run %{public}s code %{public}u, datasize %{public}zu", __func__, code, data.GetRawDataSize());
ErrCode ret = EnableWifi();
reply.WriteInt32(0);
reply.WriteInt32(ret);

return;
}

这里调用EnableWifi(),其具体实现在子类WifiDeviceServiceImpl。

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

const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(WifiDeviceServiceImpl::GetInstance().GetRefPtr());
sptr<WifiDeviceServiceImpl> WifiDeviceServiceImpl::GetInstance()
{
if (g_instance == nullptr) {
std::lock_guard<std::mutex> autoLock(g_instanceLock);
if (g_instance == nullptr) {
auto service = new (std::nothrow) WifiDeviceServiceImpl;
g_instance = service;
}
}
return g_instance;
}
WifiDeviceServiceImpl::WifiDeviceServiceImpl()
: SystemAbility(WIFI_DEVICE_ABILITY_ID, true), mPublishFlag(false), mState(ServiceRunningState::STATE_NOT_START)
{}

状态机管理

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

本章仅介绍Wifi基本模式STA和Scan的状态机。

<center>STA状态机树状图</center>

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

<center>STA状态迁移图</center>

比如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。

<center>Scan状态机树状图</center>

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

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

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

<center>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状态。

STA业务代码流程

前面讲过,Native Js发起调用,经过IPC通信,调用到WifiDeviceServiceImpl作为服务端的实现。

Wifi打开流程

<center>Wifi打开流程时序图

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_device_service_impl.cpp。

ErrCode WifiDeviceServiceImpl::EnableWifi()
{
...
errCode = WIFI_OPT_FAILED;
do {
if (WifiServiceManager::GetInstance().CheckAndEnforceService(WIFI_SERVICE_STA) < 0) {//加载STA服务
break;
}
IStaService *pService = WifiServiceManager::GetInstance().GetStaServiceInst(); //构建StaService
if (pService == nullptr) {
WIFI_LOGE("Create %{public}s service failed!", WIFI_SERVICE_STA);
break;
}
errCode = pService->RegisterStaServiceCallback(WifiManager::GetInstance().GetStaCallback()); //注册回调函数
if (errCode != WIFI_OPT_SUCCESS) {
WIFI_LOGE("Register sta service callback failed!");
break;
}

errCode = pService->EnableWifi(); //打开wifi
if (errCode != WIFI_OPT_SUCCESS) {
WIFI_LOGE("service enable sta failed, ret %{public}d!", static_cast<int>(errCode));
break;
}
} while (false);
...
return WIFI_OPT_SUCCESS;
}

这里主要做了四件事情:

  1. 加载STA服务,在WifiServiceManager中根据服务名称WIFI_SERVICE_STA,调用LoadStaService方法加载STA服务。
  2. 构建StaService,获取接口IStaService实例。
  3. 注册回调函数。
  4. 调用StaService打开Wifi。

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

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_service.cpp。

StaService首先在调用之前,首先会初始化:

ErrCode StaService::InitStaService(const StaServiceCallback &callbacks)
{
WIFI_LOGI("Enter StaService::InitStaService.\n");
pStaStateMachine = new (std::nothrow) StaStateMachine(); //构造StaStateMachine状态机
...
RegisterStaServiceCallback(callbacks); //注册事件回调函数

pStaMonitor = new (std::nothrow) StaMonitor(); //构造StaMonitor并初始化
...
pStaMonitor->SetStateMachine(pStaStateMachine);

pStaAutoConnectService = new (std::nothrow) StaAutoConnectService(pStaStateMachine); //构造自动连接服务
...
WIFI_LOGI("Init staservice successfully.\n");

return WIFI_OPT_SUCCESS;
}

初始化主要会做四件事:

1.构造StaStateMachine状态机

2.注册回调函数

3.构造StaMonitor

4.构造自动连接服务

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

ErrCode StaService::EnableWifi() const
{
WIFI_LOGI("Enter StaService::EnableWifi.\n");
pStaStateMachine->SendMessage(WIFI_SVR_CMD_STA_ENABLE_WIFI, STA_CONNECT_MODE); //向StaStateMachine发送WIFI_SVR_CMD_STA_ENABLE_WIFI消息
return WIFI_OPT_SUCCESS;
}

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_state_machine.cpp。

StaStateMachine在初始化时设置初始状态为InitState ,”SetFirstState(pInitState)“,故接收到WIFI_SVR_CMD_STA_ENABLE_WIFI在InitState状态中处理:

bool StaStateMachine::InitState::ExecuteStateMsg(InternalMessage *msg)
{
if (msg == nullptr) {
return false;
}
bool ret = NOT_EXECUTED;
switch (msg->GetMessageName()) {
case WIFI_SVR_CMD_STA_ENABLE_WIFI: { //StastateMachine接收到WIFI_SVR_CMD_STA_ENABLE_WIFI消息
ret = EXECUTED;
pStaStateMachine->operationalMode = msg->GetParam1();
pStaStateMachine->StartWifiProcess(); //调用StartWifiProcess()方法
break;
}
case WIFI_SVR_CMD_STA_OPERATIONAL_MODE:
break;
default:
LOGI("InitState-msgCode=%d not handled.\n", msg->GetMessageName());
break;
}
return ret;
}
void StaStateMachine::StartWifiProcess()
{
WifiSettings::GetInstance().SetWifiState(static_cast<int>(WifiState::ENABLING));
staCallback.OnStaOpenRes(OperateResState::OPEN_WIFI_OPENING);
int res = WifiStaHalInterface::GetInstance().StartWifi(); //调用WifiStaHalInterface的StartWifi()
//下面是对返回结果的处理
...
}

WifiStaHalInterface是框架调用Wifi Hal的接口。

框架与Wifi Hal的通信主要是借助IDL Client组件,采用RPC服务实现其交互。

无论是框架调用Wifi Hal还是Wifi Hal回调消息到框架,都要经过IDL Clinet,从架构图来看,它属于Wifi框架的一部分,我们先来看看它。

IDL客户端初始化:

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_base_hal_interface.cpp。

bool WifiBaseHalInterface::InitIdlClient(void)
{
if (mIdlClient == nullptr) {
mIdlClient = new (std::nothrow) WifiIdlClient;
}
if (mIdlClient == nullptr) {
LOGE("Failed to create idl client");
return false;
}
if (mIdlClient->InitClient() != 0) {
LOGE("Failed to init idl client");
return false;
}
return true;
}

复制IDL客户端初始化主要做了两件事:

1.构造WifiIdlClient实例。

2.初始化RPC通信客户端。

int WifiIdlClient::InitClient(void)
{
const std::string idlSockPath = "/data/misc/wifi/unix_sock.sock";
pRpcClient = CreateRpcClient(idlSockPath.c_str());
if (pRpcClient == nullptr) {
LOGE("init rpc client failed!");
return -1;
}
return 0;
}

有了WifiIdlClient实例,调用其StartWifi()。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_sta_hal_interface.cpp。

WifiErrorNo WifiStaHalInterface::StartWifi(void)
{
return mIdlClient->StartWifi();
}

通过wifi_idl_client向wifi hal发起RPC调用“Start”。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/wifi_idl_client.cpp。

WifiErrorNo WifiIdlClient::StartWifi(void)
{
CHECK_CLIENT_NOT_NULL;
return Start();
}

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/idl_client/idl_interface/i_wifi.c。

WifiErrorNo Start(void)
{
RpcClient *client = GetStaRpcClient();
LockRpcClient(client);
Context *context = client->context;
WriteBegin(context, 0);
WriteFunc(context, "Start");
WriteEnd(context);
if (RpcClientCall(client, "Start") != WIFI_IDL_OPT_OK) {
return WIFI_IDL_OPT_FAILED;
}
int result = WIFI_IDL_OPT_FAILED;
ReadInt(context, &result);
ReadClientEnd(client);
UnlockRpcClient(client);
return result;
}

这里主要操作是获取RPC调用客户端,然后通过RemoteCall(client)方法发起远程调用。

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

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/main.c。

int main(void)
{
char rpcSockPath[] = "/data/misc/wifi/unix_sock.sock";
if (access(rpcSockPath, 0) == 0) {
unlink(rpcSockPath);
}
if (InitRpcFunc() < 0) { //
LOGE("Init Rpc Function failed!");
return -1;
}
RpcServer *server = CreateRpcServer(rpcSockPath); //framework中server.c
if (server == NULL) {
LOGE("Create RPC Server by %{public}s failed!", rpcSockPath);
return -1;
}
SetRpcServerInited(server);
setvbuf(stdout, NULL, _IOLBF, 0);
signal(SIGINT, SignalExit);
signal(SIGTERM, SignalExit);
signal(SIGPIPE, SIG_IGN);

RunRpcLoop(server);
/* stop wpa_supplicant, hostapd, and other resources */
ForceStop();
StopSoftAp();
P2pForceStop();
ReleaseWifiHalVendorInterface();
/* clear RPC Server */
SetRpcServerInited(NULL);
ReleaseRpcServer(server);
ReleaseRpcFunc();
LOGI("hal service exists!");
return 0;
}

InitRpcFunc中Map了“Start”消息的处理函数,PushRpcFunc(“Start”, RpcStart)。

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/wifi_hal_crpc_server.c。

static int InitRpcFuncMapSta(void)
{
int ret = 0;
ret += PushRpcFunc("Start", RpcStart);
ret += PushRpcFunc("Stop", RpcStop);
......
return ret;
}

根据配对关系调用到RpcStart。

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/wifi_hal_crpc_sta.c。

int RpcStart(RpcServer *server, Context *context)
{
if (server == NULL || context == NULL) {
return -1;
}
WifiErrorNo err = Start(); //调用Start方法
WriteBegin(context, 0);
WriteInt(context, err);
WriteEnd(context);
return 0;
}

调用Start()实际操作实现在wifi_hal_sta_interface的Start函数,调用完成。

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/wifi_hal_sta_interface.c。

WifiErrorNo Start(void)
{
LOGD("Ready to start wifi");
if (StartSupplicant() != WIFI_HAL_SUCCESS) { //start supplicant
LOGE("wpa_supplicant start failed!");
return WIFI_HAL_OPEN_SUPPLICANT_FAILED;
}
LOGD("wpa_supplicant start successfully!");

if (AddWpaIface(0) != WIFI_HAL_SUCCESS) { //Add a new interface wlan0
LOGE("Failed to add wpa interface!");
StopWpaAndWpaHal(0);
return WIFI_HAL_CONN_SUPPLICANT_FAILED;
}

if (ConnectSupplicant() != WIFI_HAL_SUCCESS) { //构造并初始化WifiWpaStaInterface
LOGE("SupplicantHal connect wpa_supplicant failed!");
StopWpaAndWpaHal(0);
return WIFI_HAL_CONN_SUPPLICANT_FAILED;
}
LOGD("SupplicantHal connect wpa_supplicant successfully!");
LOGD("Start wifi successfully");
return WIFI_HAL_SUCCESS;
}

主要做了三步操作:

start supplicant:

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

const char *wpaConf = "/data/misc/wifi/wpa_supplicant/wpa_supplicant.conf";

Add a new interface wlan0:

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

pWpaInterface->wpaCliAddIface(pWpaInterface, &argv)

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

WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);

以上三步成功后,RPC调用返回WIFI_HAL_SUCCESS,日志会打印"Start wifi successfully"。

StaStateMachine在EnableWifi成功后,设置wifistate为ENABLED并执行OnStaOpenRes回调:

WifiSettings::GetInstance().SetWifiState(static_cast<int>(WifiState::ENABLED));
staCallback.OnStaOpenRes(OperateResState::OPEN_WIFI_SUCCEED);

回调在WifiManager中处理,广播wifi状态改变消息,构造ScanService并初始化,初始化过程做了这几件事:

  1. 构造ScanStateMachine并初始化,调用EnrollScanStatusListener绑定Scan状态上报事件的处理函数。
  2. 构造ScanMonitor并初始化,在初始化函数中调用RegisterSupplicantEventCallback,注册supplicant事件回调。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_scan/scan_service.cpp。

bool ScanService::InitScanService(const IScanSerivceCallbacks &scanSerivceCallbacks)
{
WIFI_LOGI("Enter ScanService::InitScanService.\n");

mScanSerivceCallbacks = scanSerivceCallbacks;
//构造ScanStateMachine
pScanStateMachine = new (std::nothrow) ScanStateMachine();
if (pScanStateMachine == nullptr) {
WIFI_LOGE("Alloc pScanStateMachine failed.\n");
return false;
}
//初始化ScanStateMachine
if (!pScanStateMachine->InitScanStateMachine()) {
WIFI_LOGE("InitScanStateMachine failed.\n");
return false;
}
//绑定Scan状态上报事件的处理函数
if (!pScanStateMachine->EnrollScanStatusListener(
std::bind(&ScanService::HandleScanStatusReport, this, std::placeholders::_1))) {
WIFI_LOGE("ScanStateMachine_->EnrollScanStatusListener failed.\n");
return false;
}
//构造ScanMonitor
pScanMonitor = new (std::nothrow) ScanMonitor();
if (pScanMonitor == nullptr) {
WIFI_LOGE("Alloc pScanMonitor failed.\n");
return false;
}
//初始化ScanMonitor
if (!pScanMonitor->InitScanMonitor()) {
WIFI_LOGE("InitScanMonitor failed.\n");
return false;
}
//获取支持频段
if ((WifiStaHalInterface::GetInstance().GetSupportFrequencies(SCAN_BAND_24_GHZ, freqs2G) != WIFI_IDL_OPT_OK) ||
(WifiStaHalInterface::GetInstance().GetSupportFrequencies(SCAN_BAND_5_GHZ, freqs5G) != WIFI_IDL_OPT_OK) ||
(WifiStaHalInterface::GetInstance().GetSupportFrequencies(SCAN_BAND_5_GHZ_DFS_ONLY, freqsDfs) !=
WIFI_IDL_OPT_OK)) {
WIFI_LOGE("GetSupportFrequencies failed.\n");
}

pScanMonitor->SetScanStateMachine(pScanStateMachine);
//向ScanStateMachine发送CMD_SCAN_PREPARE
pScanStateMachine->SendMessage(static_cast<int>(CMD_SCAN_PREPARE));
GetScanControlInfo();

return true;
}

ScanStateMachine初始化状态为InitState,然后接收到CMD_SCAN_PREPARE消息,调用LoadDriver()。

bool ScanStateMachine::InitState::ExecuteStateMsg(InternalMessage *msg)
{
......
switch (msg->GetMessageName()) {
case CMD_SCAN_PREPARE:
LoadDriver();
return true;

case CMD_SCAN_FINISH:
UnLoadDriver();
return true;
......
default:
return false;
}
}
void ScanStateMachine::InitState::LoadDriver()
{
WIFI_LOGI("Enter ScanStateMachine::LoadDriver.\n");
pScanStateMachine->SwitchState(pScanStateMachine->hardwareReadyState);
pScanStateMachine->ReportStatusChange(SCAN_STARTED_STATUS);
WIFI_LOGI("Start Scan Service Success.\n");
}

LoadDriver()做了两件事情:

  1. ScanStateMAchine切换状态为为HardwareReadyState。
  2. 上报SCAN_STARTED_STATUS状态。

然后就可以发起扫描的流程了。

扫描流程

<center>Wifi扫描流程时序图

应用调用Native JS的Scan接口,会通过获取WifiScan实例,调用C++接口,发起IPC通信,通过WifiScanProxy向服务框架发送WIFI_SVR_CMD_FULL_SCAN请求。WifiScanStub作为服务端接收到请求,WifiScanServiceImpl作为服务端的主要实现。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_scan_service_impl.cpp。

ErrCode WifiScanServiceImpl::Scan()
{
WIFI_LOGI("Scan");
.....
//判断ScanService是否启动
if (!IsScanServiceRunning()) {
return WIFI_OPT_SCAN_NOT_OPENED;
}
//获取IScanService实例
IScanService *pService = WifiServiceManager::GetInstance().GetScanServiceInst();
if (pService == nullptr) {
return WIFI_OPT_SCAN_NOT_OPENED;
}
return pService->Scan(true);
}

构造ScanService,通过IScanService接口调用其Scan方法:

ErrCode ScanService::Scan(bool externFlag)
{
WIFI_LOGI("Enter ScanService::Scan.\n");
if (!scanStartedFlag) {
WIFI_LOGE("Scan service has not started.\n");
return WIFI_OPT_FAILED;
}
if (externFlag) {
ErrCode rlt = ApplyScanPolices(ScanType::SCAN_TYPE_EXTERN);
if (rlt != WIFI_OPT_SUCCESS) {
return rlt;
}
}
ScanConfig scanConfig;
/*
* Invoke the interface provided by the configuration center to obtain the
* hidden network list.
*/
if (!GetHiddenNetworkSsidList(scanConfig.hiddenNetworkSsid)) {
WIFI_LOGE("GetHiddenNetworkSsidList failed.\n");
}
//构造ScanConfig
scanConfig.scanBand = SCAN_BAND_BOTH_WITH_DFS;
scanConfig.fullScanFlag = true;
scanConfig.externFlag = externFlag;
scanConfig.scanStyle = SCAN_TYPE_HIGH_ACCURACY;
if (!SingleScan(scanConfig)) {
WIFI_LOGE("SingleScan failed.\n");
return WIFI_OPT_FAILED;
}
return WIFI_OPT_SUCCESS;
}

这里首先构造scanConfig,然后调用SingleScan向ScanStateMachine发送CMD_START_COMMON_SCAN命令并携带scanConfig。

//保存scanConfig
int requestIndex = StoreRequestScanConfig(scanConfig, interConfig);
//生成Message
InternalMessage *interMessage = pScanStateMachine->CreateMessage(static_cast<int>(CMD_START_COMMON_SCAN), requestIndex);
//发送消息
pScanStateMachine->SendMessage(interMessage);

ScanService初始化完成后,ScanStateMachine处于HardwareReady是可以发起扫描的激活状态,

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_scan/scan_state_machine.cpp。

bool ScanStateMachine::HardwareReady::ExecuteStateMsg(InternalMessage *msg)
{
WIFI_LOGI("ScanStateMachine::HardwareReady::ExecuteStateMsg.\n");
if (msg == nullptr) {
WIFI_LOGE("msg is null.\n");
return true;
}
switch (msg->GetMessageName()) {
//处理CMD_START_COMMON_SCAN消息
case CMD_START_COMMON_SCAN:
pScanStateMachine->CommonScanRequestProcess(msg);
return true;
case CMD_START_PNO_SCAN:
pScanStateMachine->PnoScanRequestProcess(msg);
return true;
default:
return false;
}
}

接收CMD_START_COMMON_SCAN并处理,进行获取扫描参数的操作,并校验Scan类型是否合法,之后转换扫描参数,通过RPC调用HAL的scan操作。

WIFI_LOGI("Begin call Scan.\n");
WifiErrorNo ret = WifiStaHalInterface::GetInstance().Scan(scanParam);
if ((ret != WIFI_IDL_OPT_OK) && (ret != WIFI_IDL_OPT_SCAN_BUSY)) {
WIFI_LOGE("WifiStaHalInterface::GetInstance().scan failed.");
return false;
}
WIFI_LOGI("End call Scan.\n");

HAL得到scan配置参数后,通过wpaCliCmdScan向supplicant发送SCAN命令。

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/wifi_hal_sta_interface.c。

WifiErrorNo StartScan(const ScanSettings *settings)
{
LOGD("Ready to start scan with param.");
WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);
if (pStaIfc == NULL) {
return WIFI_HAL_SUPPLICANT_NOT_INIT;
}
int ret = pStaIfc->wpaCliCmdScan(pStaIfc, settings); //向supplicant发送SCAN命令
if (ret < 0) {
LOGE("StartScan failed! ret=%{public}d", ret);
return WIFI_HAL_FAILED;
}
if (ret == WIFI_HAL_SCAN_BUSY) {
LOGD("StartScan return scan busy");
return WIFI_HAL_SCAN_BUSY;
}
LOGD("StartScan successfully!");
return WIFI_HAL_SUCCESS;
}

扫描成功,HAL返回WIFI_HAL_SUCCESS。

调用成功StaStateMachine切换状态为CommonScanningState。

扫描结果获取

<center>扫描结果获取流程时序图

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

void ScanMonitor::SendScanInfoEvent()
{
pScanStateMachine->SendMessage(static_cast<int>(SCAN_RESULT_EVENT));
return;
}

ScanStateMachine此时状态为CommonScanning,接收消息并处理,发起RPC远程调用wifi hal获取扫描结果。

case SCAN_RESULT_EVENT:
pScanStateMachine->CommonScanInfoProcess();
pScanStateMachine->SwitchState(pScanStateMachine->commonScanUnworkedState); //切换状态为CommonScanUnworkedState
return true;
void ScanStateMachine::CommonScanInfoProcess()
{
WIFI_LOGI("Enter ScanStateMachine::CommonScanInfoProcess.\n");
//构造ScanStatusReport
ScanStatusReport scanStatusReport;
//获取扫描结果
if (!GetScanInfos(scanStatusReport.scanInfoList)) {
WIFI_LOGE("GetScanInfos failed.");
ReportCommonScanFailedAndClear(true);
return;
}
GetRunningIndexList(scanStatusReport.requestIndexList);
//设置扫描状态为COMMON_SCAN_SUCCESS
scanStatusReport.status = COMMON_SCAN_SUCCESS;
if (scanStatusReportHandler) {
scanStatusReportHandler(scanStatusReport);
}
runningScans.clear();
return;
}
bool ScanStateMachine::GetScanInfos(std::vector<InterScanInfo> &scanInfos)
{
WIFI_LOGI("Enter ScanStateMachine::GetScanInfos.\n");
WIFI_LOGI("Begin: QueryScanInfos.");
//发起RPC远程调用wifi hal获取扫描结果
if (WifiStaHalInterface::GetInstance().QueryScanInfos(scanInfos) != WIFI_IDL_OPT_OK) {
WIFI_LOGE("WifiStaHalInterface::GetInstance().GetScanInfos failed.");
return false;
}
WIFI_LOGI("End: QueryScanInfos.");
GetSecurityTypeAndBand(scanInfos);

return true;
}

WifiHal返回扫描结果给ScanStateMachine后,ScanStateMachine构造ScanStatusReport,包含scanInfoList和status为COMMON_SCAN_SUCCESS,通过scanStatusReportHandler上报,在ScanService::HandleScanStatusReport中处理。

oid ScanService::HandleScanStatusReport(ScanStatusReport &scanStatusReport)
{
WIFI_LOGI("Enter ScanService::HandleScanStatusReport.\n");
switch (scanStatusReport.status) {
......
case COMMON_SCAN_SUCCESS: {
HandleCommonScanInfo(scanStatusReport.requestIndexList, scanStatusReport.scanInfoList);
break;
}
......
}
return;
}

ScanService拿到扫描结果,主要做的事是调用WifiSettings的SaveScanInfoList(filterScanInfo),将扫描结果保存。

之后,应用调用native js的GetScanInfos接口,通过WifiScanServiceImpl调用WifiSettings的GetScanInfoList获取到保存的扫描结果。

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

Bssid - 扫描到的AP的mac地址。

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

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

securityType - 安全类型: OPEN/WEP/PSK/SAE。

struct WifiScanInfo {
std::string bssid;
std::string ssid;
/**
* Network performance, including authentication,
* key management, and encryption mechanisms
* supported by the access point
*/
std::string capabilities;
int frequency;
int band; /* ap band: 1 - 2.4GHZ, 2 - 5GHZ */
WifiChannelWidth channelWidth;
int centerFrequency0;
int centerFrequency1;
int rssi; /* signal level */
WifiSecurity securityType;
std::vector<WifiInfoElem> infoElems;
int64_t features;
int64_t timestamp;
}

连接流程

<center>Wifi连接流程时序图

应用获取到扫描结果后,选择一个Wifi网络进行连接,调用Native Js的connectToDevice接口调用到WifiDevice,然后通过IPC通信,WifiDeviceProxy作为客户端代理发送WIFI_SVR_CMD_CONNECT2_TO消息,WifiDeviceStub作为服务端接收到消息后调用IPC服务的实现类WifiDeviceServiceImpl进行发起连接流程。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_device_service_impl.cpp。

ErrCode WifiDeviceServiceImpl::ConnectToDevice(const WifiDeviceConfig &config)
{
......
if (!IsStaServiceRunning()) {
return WIFI_OPT_STA_NOT_OPENED;
}
IStaService *pService = WifiServiceManager::GetInstance().GetStaServiceInst();
if (pService == nullptr) {
return WIFI_OPT_STA_NOT_OPENED;
}
return pService->ConnectToDevice(config); //调用StaService的ConnectToDevice
}

获取StaService的接口实例,调用其ConnectToDevice方法。

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_service.cpp。

ErrCode StaService::ConnectToDevice(const WifiDeviceConfig &config) const
{
LOGI("Enter StaService::ConnectToDevice.\n");
int netWorkId = AddDeviceConfig(config);//调用AddDeviceConfig
if(netWorkId == INVALID_NETWORK_ID) {
LOGD("StaService::ConnectTo AddDeviceConfig failed!");
return WIFI_OPT_FAILED;
}
LOGD("StaService::ConnectTo AddDeviceConfig succeed!");
pStaStateMachine->SendMessage(WIFI_SVR_CMD_STA_CONNECT_NETWORK, netWorkId, NETWORK_SELECTED_BY_THE_USER);//向StaStateMachine发送WIFI_SVR_CMD_STA_CONNECT_NETWORK消息
return WIFI_OPT_SUCCESS;
}

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

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

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

StaService在调用AddDeviceConfig得到networkid并且设置配置参数到supplicant成功后,向StaStateMachine发送“WIFI_SVR_CMD_STA_CONNECT_NETWORK”消息,

目录:foundation/communication/wifi/services/wifi_standard/wifi_framework/wifi_manage/wifi_sta/sta_state_machine.cpp。

StaStateMachine接收WIFI_SVR_CMD_STA_CONNECT_NETWORK消息调用DealConnectToUserSelectedNetwork。

staSmHandleFuncMap[WIFI_SVR_CMD_STA_CONNECT_NETWORK] = &StaStateMachine::DealConnectToUserSelectedNetwork;
void StaStateMachine::DealConnectToUserSelectedNetwork(InternalMessage *msg)
{
LOGI("enter DealConnectToUserSelectedNetwork.\n");
...... //一些状态设置
StartConnectToNetwork(networkId); //开启连接
}

StartConnectToNetwork:

void StaStateMachine::StartConnectToNetwork(int networkId)
{
targetNetworkId = networkId;
SetRandomMac(targetNetworkId); //设置随机Mac
//EnableNetwork
if (WifiStaHalInterface::GetInstance().EnableNetwork(targetNetworkId) != WIFI_IDL_OPT_OK) {
LOGE("EnableNetwork() failed!");
return;
}
//Connect
if (WifiStaHalInterface::GetInstance().Connect(targetNetworkId) != WIFI_IDL_OPT_OK) {
LOGE("Connect failed!");
staCallback.OnStaConnChanged(OperateResState::CONNECT_SELECT_NETWORK_FAILED, linkedInfo);
return;
}
//SaveDeviceConfig
if (WifiStaHalInterface::GetInstance().SaveDeviceConfig() != WIFI_IDL_OPT_OK) {
LOGE("SaveDeviceConfig() failed!");
}

StopTimer(static_cast<int>(CMD_NETWORK_CONNECT_TIMEOUT));
StartTimer(static_cast<int>(CMD_NETWORK_CONNECT_TIMEOUT), STA_NETWORK_CONNECTTING_DELAY);
}

这里主要做了三个操作:发起RPC调用EnableNetwork,Connect,SaveDeviceConfig。

目录:foundation/communication/wifi/services/wifi_standard/wifi_hal/wifi_hal_sta_interface.c。

EnableNetwork:

WifiErrorNo EnableNetwork(int networkId)
{
LOGD("EnableNetwork() networkid [%{public}d]", networkId);
WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);
if (pStaIfc == NULL) {
return WIFI_HAL_SUPPLICANT_NOT_INIT;
}
int ret = pStaIfc->wpaCliCmdEnableNetwork(pStaIfc, networkId);
if (ret < 0) {
LOGE("WpaCliCmdEnableNetwork failed! ret=%{public}d", ret);
return WIFI_HAL_FAILED;
}
return WIFI_HAL_SUCCESS;
}

Connect:

WifiErrorNo Connect(int networkId)
{
LOGD("Connect() networkid %{public}d", networkId);
WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);
if (pStaIfc == NULL) {
return WIFI_HAL_SUPPLICANT_NOT_INIT;
}
int ret = pStaIfc->wpaCliCmdSelectNetwork(pStaIfc, networkId);
if (ret < 0) {
LOGE("WpaCliCmdSelectNetwork failed! ret=%{public}d", ret);
return WIFI_HAL_FAILED;
}
return WIFI_HAL_SUCCESS;
}

SaveDeviceConfig:

WifiErrorNo SaveNetworkConfig(void)
{
LOGD("SaveNetworkConfig()");
WifiWpaStaInterface *pStaIfc = GetWifiStaInterface(0);
if (pStaIfc == NULL) {
return WIFI_HAL_SUPPLICANT_NOT_INIT;
}
int ret = pStaIfc->wpaCliCmdSaveConfig(pStaIfc);
if (ret < 0) {
LOGE("WpaCliCmdSaveConfig failed! ret=%{public}d", ret);
return WIFI_HAL_FAILED;
}
return WIFI_HAL_SUCCESS;
}

这里按调用先后向supplicant发送EnableNetwork、SELECT_NETWORK以及SAVE_CONFIG命令,supplicant根据收到的命令完成AP的连接管理。

Supplicant连接成功后,回送WPA_EVENT_CONNECTED事件,调用WifiHalCbNotifyConnectChanged(WPA_CB_CONNECTED,id,pMacPos)通知连接成功,经过Hal再由StaMonitor的处理回调,发送消息WIFI_SVR_CMD_STA_NETWORK_CONNECTION_EVENT给StaStateMachine,调用DealConnectionEvent方法来处理连接事件,状态机进入getIpState状态,获取ip地址,静态ip或者dhcp动态获取成功后,继续调用StaNetworkCheck检查网络连接状态。

Wifi-STA接口说明及使用

接口说明

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

表 1 WLAN功能接口(JS接口)。

表 1 WLAN事件接口(JS接口)。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-06-20 22:37:25

Linux操作系统命令

2022-06-15 11:02:40

网络安全运营

2022-06-13 14:18:39

电源管理子系统耗电量服务

2022-04-02 20:45:04

Hi3516开发板操作系统鸿蒙

2022-06-07 10:33:29

Camera组件鸿蒙

2022-05-11 15:08:52

驱动开发系统移植

2022-04-15 14:31:02

鸿蒙操作系统

2022-06-15 08:21:49

Linux运维工程师

2022-06-15 08:25:07

Python天气数据可视化分析

2022-04-18 10:37:01

鸿蒙操作系统开发工具

2022-06-09 18:04:46

网络攻击网络安全

2022-06-09 14:40:14

系统移植鸿蒙

2022-05-30 15:08:33

包管理子系统包安装模块

2022-05-24 15:06:57

AbilityeTS FA鸿蒙

2022-05-24 15:55:37

避障小车华为

2022-04-07 15:28:16

HarmonyOS鸿蒙操作系统

2022-04-19 11:23:26

release3.1子系统鸿蒙

2022-06-06 10:55:51

本地服务鸿蒙

2022-04-20 20:28:40

HDF 驱动框架鸿蒙操作系统

2022-06-22 09:19:55

HDC鸿蒙ADB命令

同话题下的热门内容

OHOS构建自定义服务实战基于OpenHarmony3.1的购物车应用的实现啃论文俱乐部—数据密集型应用内存压缩HarmonyOS - 方舟开发框架ArkUI 流光按钮效果华为智慧屏分布式语音音乐软件,可见即可说OpenHarmony3.1-Ace-Formcomponent源码解析HarmonyOS Canvas 实现时钟HarmonyOS门锁品类的临时密码、照片编解码工具技术

编辑推荐

HarmonyOS 2.0鸿蒙第二期开发者Beta公测申请指南HarmonyOS LYEVK-3861开发板播放《蜜雪冰城》鸿蒙HarmonyOS分布式软总线:构建低时延、高带宽的多设备虚拟网络华为HarmonyOS的强势突围: 直面物联网迷宫的蓄力进击鸿蒙HarmonyOS2.0发布会现场回忆录
我收藏的内容
点赞
收藏

51CTO技术栈公众号