一、问题描述
打开苹果手机热点,设备wifi连接苹果手机热点,当苹果手机重新开启热点,设备无法自动连接。安卓手机重新打开热点后,不会出现这种情况,windows11系统thinkpad也不会出现这种情况。
设备F8 pro rockpi3A 用4.1.0.4版本的社区代码都会出现这种问题。
二、问题定位
之前没有弄过wifi驱动,只在实习的时候搞过几个月的蓝牙协议栈,所以只能先看OpenHarmony的wifi框架原理,然后一点一点看hilog的日志,定位一下问题大概出现在哪里。
1.wifi子系统架构
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驱动移植人员编译进内核。
2.问题分析
设备作为STA去连接作为AP的iphone,而作为AP的安卓机却没有这种问题,所以关键点在于分析设备与苹果手机和安卓手机的连接机制区别。用串口分析连接过程后,会发现iphone手机重新打开热点后,会重新配置一个mac地址,而安卓手机还是原来的mac地址,这就是不同点。但是仔细看代码会发现,在发起连接机制中,作为mac地址的bssid并不会影响连接过程,所以初步判断问题没有出现在连接过程。深入分析代码,hilog落盘,看wifi打印日志,能够发现wifi重新连接调用的是一个周期函数sta_service中的AutoConnectService。
三、解决方案
加log日志,发现卡在了OnScanInfosReadyHandler
ErrCode StaService::AutoConnectService(const std::vector<InterScanInfo> &scanInfos)
{
LOGI(" ConnectToDevice, Enter StaService::AutoConnectService\n");
WIFI_LOGI("Enter AutoConnectService.\n");
CHECK_NULL_AND_RETURN(pStaAutoConnectService, WIFI_OPT_FAILED);
#ifndef OHOS_ARCH_LITE
if (IsOtherVapConnect()) {
LOGI("gly::AutoConnectService: p2p or hml connected, and hotspot is enable");
return WIFI_OPT_FAILED;
}
const std::string wifiBrokerFrameProcessName = ANCO_SERVICE_BROKER;
std::string ancoBrokerFrameProcessName = GetRunningProcessNameByPid(GetCallingUid(), GetCallingPid());
if (ancoBrokerFrameProcessName == wifiBrokerFrameProcessName) {
WifiConfigCenter::GetInstance().SetWifiConnectedMode(true, m_instId);
WIFI_LOGD("gly::StaService %{public}s, anco, %{public}d", __func__, m_instId);
} else {
WifiConfigCenter::GetInstance().SetWifiConnectedMode(false, m_instId);
WIFI_LOGD("gly::StaService %{public}s,not anco, %{public}d", __func__, m_instId);
}
#endif
pStaAutoConnectService->OnScanInfosReadyHandler(scanInfos);
return WIFI_OPT_SUCCESS;
}
pNetworkSelectionManager->SelectNetwork判断错误
void StaAutoConnectService::OnScanInfosReadyHandler(const std::vector<InterScanInfo> &scanInfos)
{
WIFI_LOGI("Enter OnScanInfosReadyHandler.\n");
WIFI_LOGI("gly::StaAutoConnectService::OnScanInfosReadyHandler\n");
ClearOvertimeBlockedBssid(); /* Refreshing the BSSID Blocklist */
WifiLinkedInfo info;
WifiSettings::GetInstance().GetLinkedInfo(info, m_instId);
if (info.supplicantState == SupplicantState::ASSOCIATING ||
info.supplicantState == SupplicantState::AUTHENTICATING ||
info.supplicantState == SupplicantState::FOUR_WAY_HANDSHAKE ||
info.supplicantState == SupplicantState::GROUP_HANDSHAKE) {
WIFI_LOGE("Supplicant is under transient state.\n");
WIFI_LOGI("gly::Supplicant is under transient state\n");
return;
}
if (info.connState == ConnState::CONNECTED) {
ClearAllBlockedBssids();
}
std::vector<std::string> blockedBssids;
GetBlockedBssids(blockedBssids);
if (!AllowAutoSelectDevice(info) || !IsAllowAutoJoin()) {
WIFI_LOGI("gly::AllowAutoSelectDevice(info) faield\n");
return;
}
NetworkSelectionResult networkSelectionResult;
if (pNetworkSelectionManager->SelectNetwork(networkSelectionResult, NetworkSelectType::AUTO_CONNECT, scanInfos)) {
int networkId = networkSelectionResult.wifiDeviceConfig.networkId;
std::string &bssid = networkSelectionResult.interScanInfo.bssid;
std::string &ssid = networkSelectionResult.interScanInfo.ssid;
WIFI_LOGI("gly::AutoSelectDevice networkId: %{public}d, ssid: %{public}s, bssid: %{public}s.", networkId,
SsidAnonymize(ssid).c_str(), MacAnonymize(bssid).c_str());
auto message = pStaStateMachine->CreateMessage(WIFI_SVR_CMD_STA_CONNECT_SAVED_NETWORK);
message->SetParam1(networkId);
message->SetParam2(NETWORK_SELECTED_BY_AUTO);
message->AddStringMessageBody(bssid);
pStaStateMachine->SendMessage(message);
} else {
WIFI_LOGI("AutoSelectDevice return fail.");
WIFI_LOGI("gly::AutoSelectDevice return fail.\n");
return;
}
}
加日志,发现可以拿到候选者,但是在候选者过滤后,设备为0。
bool NetworkSelectionManager::SelectNetwork(NetworkSelectionResult &networkSelectionResult,
NetworkSelectType type,
const std::vector<InterScanInfo> &scanInfos)
{
if (scanInfos.empty()) {
WIFI_LOGI("gly::scanInfos is empty, ignore this selection");
return false;
}
/* networkCandidates must be declared before networkSelector,
* so it can be accessed in the destruct of networkSelector and wifiFilter */
std::vector<NetworkSelection::NetworkCandidate> networkCandidates;
auto networkSelectorOptional = pNetworkSelectorFactory->GetNetworkSelector(type);
if (!(networkSelectorOptional.has_value())) {
WIFI_LOGE("gly::Get NetworkSelector failed for type %{public}d", static_cast<int>(type));
return false;
}
auto &networkSelector = networkSelectorOptional.value();
WIFI_LOGI("gly::NetworkSelector: %{public}s", networkSelector->GetNetworkSelectorMsg().c_str());
/* Get the device config for each scanInfo, then create networkCandidate and put it into networkCandidates */
GetAllDeviceConfigs(networkCandidates, scanInfos);
/* Traverse networkCandidates and reserve qualified networkCandidate */
TryNominate(networkCandidates, networkSelector);
/* Get best networkCandidate from the reserved networkCandidates */
std::vector<NetworkSelection::NetworkCandidate *> bestNetworkCandidates;
networkSelector->GetBestCandidates(bestNetworkCandidates);
if (bestNetworkCandidates.empty()) {
WIFI_LOGI("gly::bestNetworkCandidates.empty");
return false;
}
WIFI_LOGI("gly::NetworkSelectionManager::SelectNetwork success\n");
/* if bestNetworkCandidates is not empty, assign the value of first bestNetworkCandidate
* to the network selection result, and return true which means the network selection is successful */
networkSelectionResult.wifiDeviceConfig = bestNetworkCandidates.at(0)->wifiDeviceConfig;
networkSelectionResult.interScanInfo = bestNetworkCandidates.at(0)->interScanInfo;
return true;
}
问题卡在TryNominate(networkCandidates, networkSelector);深入分析networkSelector,打印筛选条件,可以发现其中有个matchuserselect条件,看到代码是基于bssid匹配筛选的,这下直接对上了与安卓机的区别,于是注销掉了下面这行代码。
auto andFilter = make_shared<AndWifiFilter>();
andFilter->AddFilter(make_shared<SavedWifiFilter>());
andFilter->AddFilter(make_shared<PassPointWifiFilter>());
andFilter->AddFilter(make_shared<EphemeralWifiFilter>());
andFilter->AddFilter(make_shared<DisableWifiFilter>());
// andFilter->AddFilter(make_shared<MatchedUserSelectBssidWifiFilter>());
便不会出现这个BUG。