[Android][WIFI]关闭WLAN耗时分析
前言
- 基于Android P(9) 分析
-
流程梳理详见:
[Android]Android P(9) WIFI学习笔记 – Framework (2)
和
[Android]Android P(9) WIFI学习笔记 – HAL (2)
背景
引发这个思考,是由于一个现象:
- 在热点打开的时候,关闭WLAN耗时约为1s;
- 在热点关闭后,再关闭WLAN,耗时在2s以上;
这么一个一致性很差的现象,确实很难让人理解,于是翻开之前的流程图,开始了梳理之路;
梳理流程
先放上之前梳理处的,关闭WLAN时的流程图:
其中,在
WifiNative.stopHalAndWificondIfNecessary()
处,实际上除了
WificondControl.tearDownInterfaces()
,还有一个
WifiVendorHal.stopVendorHal()
:
//WifiNative.java
/** Helper method invoked to stop HAL if there are no more ifaces */
private void stopHalAndWificondIfNecessary() {
synchronized (mLock) {
/*
* 此时STA的iface已经删除,因此如果热点也是关闭状态
* 那么此处mIfaceMgr.hasAnyIface()返回为false,取非后为true
*/
if (!mIfaceMgr.hasAnyIface()) {
if (!mWificondControl.tearDownInterfaces()) {
Log.e(TAG, "Failed to teardown ifaces from wificond");
}
//由于已经没有热点与STA的iface,此处会通知IWifi HAL进行一些反初始化(清理)操作
if (mWifiVendorHal.isVendorHalSupported()) {
mWifiVendorHal.stopVendorHal();
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring stop.");
}
}
}
}
//WifiVendorHal.java
/**
* Stops the HAL
*/
public void stopVendorHal() {
synchronized (sLock) {
mHalDeviceManager.stop();
clearState();
mLog.info("Vendor Hal stopped").flush();
}
}
//HalDeviceManager.java
/**
* Stops Wi-Fi. Will also dispatch any registeredManagerStatusCallback.onStop().
*
* Note: direct call to HIDL - failure is not-expected.
*/
public void stop() {
stopWifi();
}
...
private void stopWifi() {
if (VDBG) Log.d(TAG, "stopWifi");
synchronized (mLock) {
try {
if (mWifi == null) {
Log.w(TAG, "stopWifi called but mWifi is null!?");
} else {
WifiStatus status = mWifi.stop();
if (status.code != WifiStatusCode.SUCCESS) {
Log.e(TAG, "Cannot stop IWifi: " + statusString(status));
}
// even on failure since WTF??
teardownInternal();
}
} catch (RemoteException e) {
Log.e(TAG, "stopWifi exception: " + e);
}
}
}
可以看到这里关键调用为
IWifi.stop()
;
那么来到IWifi HAL这边,找到
IWifi::stop()
这个HIDL接口的实现:
//hardware/interfaces/wifi/1.2/default/wifi.cpp
Return<void> Wifi::stop(stop_cb hidl_status_cb) {
//老套路,模板指向最终实现为stopInternal函数
return validateAndCallWithLock(this, WifiStatusCode::ERROR_UNKNOWN,
&Wifi::stopInternal, hidl_status_cb);
}
...
WifiStatus Wifi::stopInternal(
/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock) {
if (run_state_ == RunState::STOPPED) {
return createWifiStatus(WifiStatusCode::SUCCESS);
} else if (run_state_ == RunState::STOPPING) {
return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE,
"HAL is stopping");
}
// Clear the chip object and its child objects since the HAL is now
// stopped.
if (chip_.get()) {
chip_->invalidate();
chip_.clear();
}
WifiStatus wifi_status = stopLegacyHalAndDeinitializeModeController(lock);
if (wifi_status.code == WifiStatusCode::SUCCESS) {
for (const auto& callback : event_cb_handler_.getCallbacks()) {
if (!callback->onStop().isOk()) {
LOG(ERROR) << "Failed to invoke onStop callback";
};
}
LOG(INFO) << "Wifi HAL stopped";
} else {
for (const auto& callback : event_cb_handler_.getCallbacks()) {
if (!callback->onFailure(wifi_status).isOk()) {
LOG(ERROR) << "Failed to invoke onFailure callback";
}
}
LOG(ERROR) << "Wifi HAL stop failed";
}
return wifi_status;
}
通过添加时间戳日志,确定
stopLegacyHalAndDeinitializeModeController
函数耗时在1s以上,是导致差异的主要原因;
继续定位,确认耗时主要在
stopLegacyHalAndDeinitializeModeController
函数内部的
mode_controller_->deinitialize()
函数调用上:
WifiStatus Wifi::stopLegacyHalAndDeinitializeModeController(
/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock) {
run_state_ = RunState::STOPPING;
legacy_hal::wifi_error legacy_status =
legacy_hal_->stop(lock, [&]() { run_state_ = RunState::STOPPED; });
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
LOG(ERROR) << "Failed to stop legacy HAL: "
<< legacyErrorToString(legacy_status);
return createWifiStatusFromLegacyError(legacy_status);
}
//耗时1s以上
if (!mode_controller_->deinitialize()) {
LOG(ERROR) << "Failed to deinitialize firmware mode controller";
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
}
return createWifiStatus(WifiStatusCode::SUCCESS);
}
//wifi_mode_controller.cpp
bool WifiModeController::deinitialize() {
if (!driver_tool_->UnloadDriver()) {
LOG(ERROR) << "Failed to unload WiFi driver";
return false;
}
return true;
}
此处的
driver_tool_->UnloadDriver()
实现在
libwifi-hal
模块中:
//frameworks/opt/net/wifi/libwifi_hal/driver_tool.cpp
bool DriverTool::UnloadDriver() {
return ::wifi_unload_driver() == 0;
}
//frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
int wifi_unload_driver() {
if (!is_wifi_driver_loaded()) {
return 0;
}
usleep(200000); /* allow to finish interface down */
#ifdef WIFI_DRIVER_MODULE_PATH
if (rmmod(DRIVER_MODULE_NAME) == 0) {
int count = 20; /* wait at most 10 seconds for completion */
while (count-- > 0) {
if (!is_wifi_driver_loaded()) break;
usleep(500000);
}
usleep(500000); /* allow card removal */
if (count) {
return 0;
}
return -1;
} else
return -1;
#else
#ifdef WIFI_DRIVER_STATE_CTRL_PARAM
if (is_wifi_driver_loaded()) {
if (wifi_change_driver_state(WIFI_DRIVER_STATE_OFF) < 0) return -1;
}
#endif
property_set(DRIVER_PROP_NAME, "unloaded");
return 0;
#endif
}
到这里,就一目了然了,如果定义了宏
WIFI_DRIVER_MODULE_PATH
,那么这里会有一个
rmmod
的操作,实测这一步骤耗时会在700ms以上,如果按照这里的等待机制计算,则至少会耗费1200ms;
跟进
关于为什么有的项目会定义宏
WIFI_DRIVER_MODULE_PATH
,有的则不会;
根据调查,是取决于项目是否在BoardConfig.mk(及其依赖)中是否声明另一个宏
PRODUCT_WLAN_DRIVER_ALWAYS_LOADED
:
ifneq ($(PRODUCT_WLAN_DRIVER_ALWAYS_LOADED), true)
ifeq ($(PRODUCT_VENDOR_MOVE_ENABLED), true)
WIFI_DRIVER_MODULE_PATH := "xxx"
else
WIFI_DRIVER_MODULE_PATH := "yyy"
endif
...
endif
总结
导致这一差异的主要原因,是由于热点没有关闭的情况下关闭WLAN,IWifi HAL由于还需要支持热点工作,不能将WLAN网卡驱动卸载,因此耗时较短;
而当热点关闭后,再关闭WLAN时,如果项目配置时未定义
PRODUCT_WLAN_DRIVER_ALWAYS_LOADED
,那么
libwifi-hal
模块会在此时通过调用
rmmod
来卸载WLAN网卡驱动,这一步骤会比较耗时;
改进计划
-
评估是否可以开启
PRODUCT_WLAN_DRIVER_ALWAYS_LOADED
-
wifi_unload_driver()
函数里面的等待时长调整; - 要求供应商优化卸载驱动耗时;