本文正在參加星光計劃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。
下表簡單介紹了各個标準的釋出時間和特點。
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)。
圖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 系統架構簡介
圖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,對于不同業務流程進行了分離。
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的狀态機。
Sta狀态機樹狀圖
Sta狀态機維護了wifi打開、關閉、連接配接、擷取IP及漫遊的狀态及切換。Wifi打開時,會啟動staService,構造sta statemachine并初始化。如sta狀态機樹狀圖所示,sta statemachine在初始化時,會建立狀态樹,建立子狀态必須保證相應的父狀态被建立。當遷移到子狀态,子狀态激活,也就是執行GoInState後,其父節點會同時處于激活狀态,不會調用GoOutState,子節點共同需要處理的事件或者不關心的事件由父狀态處理,子狀态隻負責處理自己感興趣的消息。
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。
Scan狀态機維護了wifi普通掃描,pno掃描(硬體掃描和軟體掃描)的狀态及切換過程。
這裡對PNO掃描稍加說明,PNO掃描即Preferred Network Offload,用于系統在休眠的時候連接配接WiFi,當手機休眠時,存在已經儲存的網絡并且沒有連接配接時,進行PNO掃描,隻掃描已儲存的網絡。PNO模式能讓裝置在熄屏時通過搜尋最近連接配接過的Wifi網絡,進而優先連接配接至Wifi網絡,達到延長續航時間并且減少手機資料流量消耗的目的。
Wifi打開後,啟動scanService同時構造scan statemachine并初始化,與sta statemachine相同,scan statemachine按照Scan狀态機樹狀圖所示建立狀态機各個狀态。
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
wpa_supplicant架構圖
wpa_supplicant是一個獨立運作的守護程序,其核心是一個消息循環,在消息循環中處理WPA狀态機、控制指令、驅動事件、配置資訊等。
wpa_supplicant由啟動,通過建立兩個上行接口,通過這兩個接口進行指令發送和事件監聽;通過通信實作下行接口,與核心進行通信,下發指令和擷取消息。
3. WiFi業務流程
3.1 WiFi Station流程
3.1.1 打開流程
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函數,主要做了三步操作:
- start supplicant
指令:wpa_supplicant -iglan0 -g/data/misc/wifi/sockets
- Add a new interface wlan0
指令:interface_add wlan0 /data/misc/wifi/wpa_supplicant/wpa_supplicant.conf
- 構造并初始化WifiWpaStaInterface,封裝了wpa_supplicant關于STA的操作指令。
以上三步成功後,RPC調用傳回WIFI_HAL_SUCCESS。
StaStateMachine在EnableWifi成功後,執行OnStaOpenRes回調,在此回調裡,廣播wifi狀态改變消息,構造ScanService并初始化。初始化過程做了這幾件事:
- 構造ScanStateMachine并初始化,調用EnrollScanStatusListener綁定Scan狀态上報事件的處理函數。
- 構造ScanMonitor并初始化,在初始化函數中調用RegisterSupplicantEventCallback,注冊supplicant事件回調。
3.1.2 掃描流程
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指令。
接着擷取掃描結果,時序圖如下:
擷取掃描結果
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 連接配接流程
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 裝置發現流程
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 熱點流程
熱點啟動時序圖
上圖為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開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。