上一篇博客介绍了MMC子系统和SDIO相关知识及架构,这一篇博客则分析一下安卓的WLAN架构及上层如何调用WLAN驱动的流程。
虽然我工作的开发环境是安卓系统,但由于我不是专业的安卓应用/框架开发工程师,所以对安卓层的一些代码并不是特别熟悉,只能大致梳理流程,不专业的地方请谅解。
开发环境:安卓9.0。
本文部分内容摘抄自网络,若有侵权,请联系删除。
WIreless-FIdelity,翻译成中文就是无线保真,英文简称WiFi。
Wireless Local Area Networks, 无线局域网络。
WiFi是实现WLAN的一种技术。
Station, 类似于无线终端,sta本身并不接受无线的接入,它可以连接到AP,一般无线网卡即工作在该模式。
Access Point,提供无线接入服务,允许其它无线设备接入,提供数据访问,一般的无线路由/网桥工作在该模式下。AP和AP之间允许相互连接。
在应用程序框架级别是应用程序代码,它使用各种 android.net.wifi API与Wi-Fi框架和硬件进行交互。在内部,此代码通过Binder IPC机制调用Wi-Fi进程。
Wi-Fi服务在系统服务中运行,位于中 frameworks/opt/net/wifi
。Wi-Fi服务通过HIDL与Wi-Fi HAL通信。
有各种Wi-Fi服务:
在Wi-Fi框架还包括一个独立的过程——wificond,位于system/connectivity/wificond
。该wificond与Wi-Fi驱动程序通过标准流程进行通信nl80211
的命令。
Wi-Fi框架具有三个Wi-Fi HAL表面(HIDL):
hardware/interfaces/wifi/1.x
。hardware/interfaces/supplicant/1.x
。hardware/interfaces/hostapd/1.x
。供应商HAL提供特定于Android的命令。对于基础结构站(STA)和软AP(SAP)模式,它是可选的(不是必需的)。但是,对于Wi-Fi Aware和 Wi-Fi RTT服务是强制性的。
HIDL之前的版本(即Android 8.0之前的版本)Android使用了HAL机制,现在称为传统HAL。Android源代码当前使用在旧式HAL之上运行的填充程序提供HIDL的默认实现。
旧版HAL标头位于 hardware/libhardware_legacy/include/hardware_legacy/
。
基于旧式HAL的新式HAL实现位于hardware/interfaces/wifi/1.x/default
。
Supplicant HAL为wpa_supplicant守护程序提供了一个HIDL接口。
wpa_supplicant的作用是,用于控制无线连接。
wpa_supplicant源代码位于中 external/wpa_supplicant_8/wpa_supplicant
。提供HIDL接口的wpa_supplicant代码位于hidl
子目录中。
Hostapd HAL为hostapd守护程序提供了HIDL接口。
hostapd能够使得无线网卡切换为master模式,模拟AP(通常可以认为是路由器)功能,也就是我们说的软AP(Soft AP)。
hostapd源代码位于中external/wpa_supplicant_8/hostapd
。提供HIDL接口的hostapd代码位于hidl
子目录中。
上一章梳理了一下安卓的WLAN框架,可能大家还有不明白的地方,这里,来做一下小小的总结。
框架第一层,属于应用层(WiFi Setting),这一层可以让普通的用户进行ui的界面操作,比如打开关闭WiFi,选择某个AP进行连接等。
框架第二层,属于服务层(WiFi Service),这一层是整个框架的核心部分,application层触发事件后,会调用这个service里面的方法来处理事件,包括加载驱动,开启wpa_supplicant,扫描AP都是调用这个service里面的方法实现。
框架第三层,属于接口层(WiFi Hidl),这一层将框架与HAL隔离,让安卓可以只升级框架而不修改HAL。简单理解,这一层就是提供接口让WiFi Service和WiFi HAL通信的一层。
框架第四层,属于硬件抽象层(WiFi Hal),这一层将安卓与kernel隔离。但在这里与其他驱动框架有所不同,hal层不直接与kernel的WiFi驱动进行通信,而是与wpa_supplicant或者hostapd这两个守护进程进行通信。
框架第五层,属于守护进程层(Wpa_supplicant/Hostapd),这一层接收hal层传送过来的命令和消息,然后接收到的命令和消息发送给WiFi驱动,wpa_supplicant向hal提供了WiFi的配置、连接、断开等接口。
框架第六层,属于内核驱动层(WiFi Driver)。
安卓系统启动WiFi Service是在android/frameworks/base/services/java/com/android/server/SystemServer.java文件中启动。
代码片段如下:
// 必须先启动WiFi服务才能启动其他WiFi相关的内容.
traceBeginAndSlog("StartWifi");
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
traceEnd();
WiFi Hidl代码在android/hardware/interfaces/wifi/1.2/default。
HIDL 接口具有客户端和服务器实现:
- HIDL 接口的客户端实现是指通过在该接口上调用方法来使用该接口的代码。
- 服务器实现是指 HIDL 接口的实现,它可接收来自客户端的调用并返回结果(如有必要)。
在从libhardware HAL 转换为 HIDL HAL 的过程中,HAL 实现成为服务器,而调用 HAL 的进程则成为客户端。默认实现可提供直通和 Binder 化 HAL。
上图为HAL的几个发展历程,目前这个安卓版本所用的是第四种binderrized模式。框架和HAL之间通过HIDL接口实现通信。
以下的内容都是Vendor HAL的hidl,Supplicant HAL和Hostapd HAL的hidl实现方式都类似,就不一一介绍了。
上面说了HIDL接口具有客户端和服务器端的实现,这里,就来介绍一下HIDL服务器端的编译。
编译代码内容如下:
### android.hardware.wifi daemon
include $(CLEAR_VARS)
LOCAL_MODULE := [email protected]
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CPPFLAGS := -Wall -Werror -Wextra
LOCAL_SRC_FILES := \
service.cpp
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
libhidlbase \
libhidltransport \
liblog \
libnl \
libutils \
libwifi-hal \
libwifi-system-iface \
[email protected] \
[email protected] \
[email protected]
LOCAL_STATIC_LIBRARIES := \ 89 [email protected]
LOCAL_INIT_RC := [email protected]
include $(BUILD_EXECUTABLE)
[email protected]就是我们编译生成的结果,WiFi Service层会通过这个服务来与我们的Wifi Hal进行通信。
注意看一下里面的这个lib([email protected]),这个lib就是支持各种WiFi功能的接口,比如sta啊,ap啊等等。
编译过程如下:
### android.hardware.wifi static library
include $(CLEAR_VARS)
LOCAL_MODULE := [email protected]
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CPPFLAGS := -Wall -Werror -Wextra
LOCAL_SRC_FILES := \
hidl_struct_util.cpp \
hidl_sync_util.cpp \
ringbuffer.cpp \
wifi.cpp \
wifi_ap_iface.cpp \
wifi_chip.cpp \
wifi_feature_flags.cpp \
wifi_legacy_hal.cpp \
wifi_legacy_hal_stubs.cpp \
wifi_mode_controller.cpp \
wifi_nan_iface.cpp \
wifi_p2p_iface.cpp \
wifi_rtt_controller.cpp \
wifi_sta_iface.cpp \
wifi_status_util.cpp
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
libhidlbase \
libhidltransport \
liblog \
libnl \
libutils \
libwifi-hal \
libwifi-system-iface \
[email protected] \
[email protected] \
[email protected]
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
可以看到SRC_FILE里面有很多cpp文件,比如wifi_sta_iface.cpp,这里面就有sta功能的一系列hidl接口。
启动[email protected]在[email protected]文件中实现。
代码内容如下:
service vendor.wifi_hal_legacy /vendor/bin/hw/[email protected]
class hal
capabilities NET_ADMIN NET_RAW SYS_MODULE
user wifi
group wifi gps root
WiFi Hal层代码路径:android/frameworks/opt/net/wifi/libwifi_hal。
WiFi Hal层代码编译之后会生成一个叫做libwifi_hal.so的文件。
下面以打开WiFi为例,来简单介绍一下安卓系统从用户点击打开WiFi按钮到底层WiFi驱动的大致流程。
@Override
public void onStart() {
super.onStart();
//创建一个WifiEnabler对象,实现开关WiFi功能
mWifiEnabler = createWifiEnabler();
if (mIsRestricted) {
restrictUi();
return;
}
onWifiStateChanged(mWifiManager.getWifiState());
}
在上述代码中,可以看到,这里调用了createWifiEnabler函数,创建了一个WifiEnabler对象。这个对象可以实现对WiFi的开关功能,它的实现就是1.2的WiFiEnabler.java。
public boolean onSwitchToggled(boolean isChecked) {
if (!mWifiManager.setWifiEnabled(isChecked)) {
mSwitchWidget.setEnabled(true);
Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
当WiFi开关按钮状态发生变化时,onSwitchToggled这个函数会被调用。
可以看到,函数内部有这么一段代码——mWifiManager.setWifiEnabled(isChecked),这里就是开关WiFi的操作。
到这,应用层就结束了,因为mWifiManager这个对象是属于java框架层的。
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
可以看到这里调用了mService的setWifiEnabled,显然,这里是调用到了WiFi Service层的东西了。
不过,java框架层想要与WiFi Service通信,需要跨进程,所以就需要用到aidl了。
boolean setWifiEnabled(String packageName, boolean enable);
aidl可以帮助WifiManager跨进程访问WifiService的接口。
mWifiManager.setWifiEnabled通过aidl跨进程调用到了WifiServiceImpl.setWifiEnabled,其中WifiServiceImpl是WifiService的实现类。
@Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable)
throws RemoteException {
……
mWifiController.sendMessage(CMD_WIFI_TOGGLED);
……
}
这里省略了大部分代码,我们只关注mWifiController.sendMessage这个调用。
WifiController,它是一个状态机。WifiController在WIfiServiceImpl的构造函数中初始化、并开始运行。
WifiController 和WifiStateMachine 不同,WifiStateMachine是一个复杂的状态机,它维护了Wifi的启动、扫描、连接、断开等多个状态。WifiController 是高级别的wifi状态机,因为它管理的状态是wifi开关,wifi热点开关等状态,只有在wifi开关等具体状态下,判断wifi处于启动扫描附近热点状态等才是有意义的。
WifiController状态机各状态关系:
可以看到,WifiController状态机初始状态为StaDisabledState。他会从初始状态开始处理从WiFi Service那里发过来的消息。
WifiController状态机在StaDisabledState状态下没做什么处理,我们看到DeviceActiveState状态。
class DeviceActiveState extends State {
@Override
public void enter() {
mWifiStateMachinePrime.enterClientMode();
mWifiStateMachine.setHighPerfModeEnabled(false);
}
……
}
可以看到在DeviceActiveState状态下主要做了两个操作mWifiStateMachinePrime.enterClientMode()和mWifiStateMachine.setHighPerfModeEnabled(false),主要看mWifiStateMachinePrime.enterClientMode()。
public void enterClientMode() {
changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
}
private void changeMode(int newMode) {
mModeStateMachine.sendMessage(newMode);
}
ModeStateMachine又是一个状态机,不过这个状态机比较简单只有三个状态,初始状态为WifiDisabledState。
ModeStateMachine状态机在WifiDisabledState并没有做太多事情,状态转换到ClientModeActiveState。
class ClientModeActiveState extends ModeActiveState {
……
ClientListener mListener;
private class ClientListener implements ClientModeManager.Listener {
@Override
……
}
public void enter() {
Log.d(TAG, "Entering ClientModeActiveState");
mListener = new ClientListener();
mManager = mWifiInjector.makeClientModeManager(mListener);
mManager.start();
mActiveModeManagers.add(mManager);
updateBatteryStatsWifiState(true);
}
……
}
这里的mManager是ActiveModeManager,是个接口,这里的ClientModeManager实现了这个接口。我们继续走下去,去看ClientModeManager,主要看mManager.start()这个调用。
public void start() {
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}
ClientModeStateMachine也是个状态机,该状态机只有两个状态,初始状态为IdleState。
private class IdleState extends State {
……
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(false, mWifiNativeInterfaceCallback);
……
}
可以看到,函数里面有这样mWifiNative.setupInterfaceForClientMode的调用,继续。
public String setupInterfaceForClientMode(boolean lowPrioritySta,
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (!startSupplicant()) {
Log.e(TAG, "Failed to start supplicant");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
Log.e(TAG, "Failed to setup iface in wificond on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
mWifiMonitor.startMonitoring(iface.name);
……
}
}
到这里就可以看到一些关键性的操作:
启动Hal:startHal();
启动supplicant:startSupplicant();
加载驱动(loadDriver):setupInterfaceForClientMode();
启动WifiMonitor:WifiMonitor.startMonitoring()。
WifiMonitor.startMonitoring():这一步主要是在WifiMonitor中建立与wpa_supplicant通信的socket通道、创建一个线程接收底层事件并分发处理。这里会创建两个socket通道与wpa_s通信,一个用于下发指令,另一个用于接收事件。成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT;收到这个消息就表示Wifi已经启动成功了。
到这里,我们选择startHal()这一条线看下去。
private boolean startHal() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyIface())
if (mWifiVendorHal.isVendorHalSupported()) {
if (!mWifiVendorHal.startVendorHal()) {
Log.e(TAG, "Failed to start vendor HAL");
return false;
}
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring start.");
}
return true;
}
}
看mWifiVendorHal.startVendorHal()。
public boolean startVendorHal() {
synchronized (sLock) {
if (!mHalDeviceManager.start()) {
mLog.err("Failed to start vendor HAL").flush();
return false;
}
mLog.info("Vendor Hal started successfully").flush();
return true;
}
}
看mHalDeviceaManager.start()。
public boolean start() {
return startWifi();
}
继续。
private boolean startWifi() {
……
synchronized (mLock) {
try {
WifiStatus status = mWifi.start();
……
}
}
}
看WifiStatus status = mWifi.start(),先看下mWifi是什么。
//java通过import来导入他想引用的库,下面的库就是Vendor Hal Hidl编译生成的库。
import android.hardware.wifi.V1_0.IWifi;
……
public class HalDeviceManager {
private IWifi mWifi;
……
}
protected IWifi getWifiServiceMockable() {
try {
return IWifi.getService();
} catch (RemoteException e) {
Log.e(TAG, "Exception getting IWifi service: " + e);
return null;
}
}
可以看到,代码来到了IWifi.getService(),也就是获取[email protected]。
至此,就进入Hidl层了。
IWifi.hal这个文件,在编译之后,会在out/soong/.intermediates/hardware/interfaces/wifi/1.0/android.hardware.wifi_V1.0-java_gen_java/gen/android/hardware/wifi/V1_0/IWifi.java路径下生成一个名为IWifi.java的文件,我们来看到这个文件。
public static IWifi getService(String serviceName) throws android.os.RemoteException {
return IWifi.asInterface(android.os.HwBinder.getService("[email protected]::IWifi",serviceName));
}
再看一下 IWifi.java的asInterface方法。
IWifi.asInterface(android.os.HwBinder.getService("[email protected]::IWifi",serviceName));
从而我们就可以知道IWifi对应的服务端了。
4.1里提供了获取service的方法,那么我们回到3.7,在那里调用了mWifi.start(),这个start其实就是IWifi对应的服务端里面的start方法。
Return Wifi::start(start_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN, &Wifi::startInternal, hidl_status_cb);
}
WifiStatus Wifi::startInternal() {
……
WifiStatus wifi_status = initializeModeControllerAndLegacyHal();
……
}
WifiStatus Wifi::initializeModeControllerAndLegacyHal() {
if (!mode_controller_->initialize()) {
LOG(ERROR) << "Failed to initialize firmware mode controller";
return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
}
legacy_hal::wifi_error legacy_status = legacy_hal_->initialize();
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
LOG(ERROR) << "Failed to initialize legacy HAL: "
<< legacyErrorToString(legacy_status);
return createWifiStatusFromLegacyError(legacy_status);
}
return createWifiStatus(WifiStatusCode::SUCCESS);
}
可以看到,代码中有这么一段调用mode_controller_->initialize,那么,这个mode_controller_是什么呢?
std::shared_ptr mode_controller_;
mode_controller_在wifi.h中定义,其实mode_controller_就是wifi_mode_controller.cpp的一个对象。
bool WifiModeController::initialize() {
if (!driver_tool_->LoadDriver()) {
LOG(ERROR) << "Failed to load WiFi driver";
return false;
}
return true;
}
可以看到,这里有这么一个调用driver_tool_->LoadDriver。
std::unique_ptr driver_tool_;
可以看到driver_tool_是由wifi_hal中的DriverTool实例而来,显然,现在就进入到了WiFi Hal层了。
bool DriverTool::LoadDriver() {
return ::wifi_load_driver() == 0;
}
int wifi_load_driver() {
//如果WiFi驱动已经加载,则函数调用结束,直接返回0
if (is_wifi_driver_loaded()) {
return 0;
}
//加载WiFi驱动模块,失败返回-1
if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
return -1;
……
//如果成功加载驱动模块,则会设置安卓属性为ok
property_set(DRIVER_PROP_NAME, "ok");
}
到这里,打开WiFi的调用流程也就分析完了,因为只是打开WiFi,没有涉及到连接AP或者其他功能,所以就没有进入到WiFi Driver层了。
不过其他操作的流程也大体一致,有耐心的朋友可以看一看。