This article introduces the Enable process of WiFi on Android S
1. WiFi activation process
1. Framework process
WifiManager.setWifiEnabled(true) -> WifiServiceImpl.enable(pkgName, true) -> setWifiEnabledInternal(packageName, enable, callingUid, callingPid, isPrivileged)
-> mSettingsStore.handleWifiToggled(enable) -> Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state) && mPersistWifiState = state 记下wifi状态
-> mActiveModeWarden.wifiToggled(new WorkSource(callingUid, packageName)) The value of enable is not relevant here.
Next, the CMD_WIFI_TOGGLED message is processed by the WifiController inside ActiveModeWarden
When wifi is turned on, wifi is disabled at this time and is processed by DisabledState
-> handleStaToggleChangeInDisabledState((WorkSource) msg.obj) -> startPrimaryOrScanOnlyClientModeManager(requestorWs) and then directly enter the enabled state
private boolean startPrimaryOrScanOnlyClientModeManager(WorkSource requestorWs) {
ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager();
if (role == ROLE_CLIENT_PRIMARY) {
return startPrimaryClientModeManager(requestorWs);
} else if (role == ROLE_CLIENT_SCAN_ONLY) {
return startScanOnlyClientModeManager(requestorWs);
} else {
return false;
}
}
private boolean startPrimaryClientModeManager(WorkSource requestorWs) {
Log.d(TAG, "Starting primary ClientModeManager in connect mode");
ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager(
new ClientListener(), requestorWs, ROLE_CLIENT_PRIMARY, mVerboseLoggingEnabled);
mClientModeManagers.add(manager);
mLastPrimaryClientModeManagerRequestorWs = requestorWs;
return true;
}
Open wifi, the corresponding role isROLE_CLIENT_PRIMARY
, then call startPrimaryClientModeManager()
.
This method internally creates a newConcreteClientModeManager
object through WifiInjector. Note that we passed ROLE_CLIENT_PRIMARY
to ConcreteClientModeManager
ConcreteClientModeManager
After the initialization is completed, the construction method of will directly send itself a CMD_START
message, which will be processed by its internal ClientModeStateMachine
state machine
CMD_START is handled by the initial state IdleState
// packages/modules/Wifi/service/java/com/android/server/wifi/ConcreteClientModeManager.java
ConcreteClientModeManager.ClientModeStateMachine.IdleState.processMessage(Message message) {
switch (message.what) {
case CMD_START:
// 1
mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode(
mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs);
if (roleChangeInfo.role instanceof ClientConnectivityRole) {
// 2
sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo);
transitionTo(mStartedState);
} else {
mScanRoleChangeInfoToSetOnTransition = roleChangeInfo;
transitionTo(mScanOnlyModeState);
}
// ... ...
}
setupInterfaceForClientInScanMode
passed in onemWifiNativeInterfaceCallback
for the iface status callback from the native layer
private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
@Override
public void onDestroyed(String ifaceName) {
if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
Log.d(getTag(), "STA iface " + ifaceName + " was destroyed, "
+ "stopping client mode");
// we must immediately clean up state in ClientModeImpl to unregister
// all client mode related objects
// Note: onDestroyed is only called from the main Wifi thread
if (mClientModeImpl == null) {
Log.w(getTag(), "Received mWifiNativeInterfaceCallback.onDestroyed "
+ "callback when no ClientModeImpl instance is active.");
} else {
mClientModeImpl.handleIfaceDestroyed();
}
sendMessage(CMD_INTERFACE_DESTROYED);
}
}
@Override
public void onUp(String ifaceName) {
if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
}
}
@Override
public void onDown(String ifaceName) {
if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
}
}
};
Then enter StartedState
or ScanOnlyModeState
depending on the role. After entering StartedState
, receive a>IdleState
SentCMD_SWITCH_TO_CONNECT_MODE
Message
// packages/modules/Wifi/service/java/com/android/server/wifi/ConcreteClientModeManager.java
ConcreteClientModeManager.ClientModeStateMachine.StartedState.processMessage(Message message) {
switch (message.what) {
case CMD_SWITCH_TO_CONNECT_MODE: {
updateConnectModeState(roleChangeInfo.role,
WifiManager.WIFI_STATE_ENABLING,
WifiManager.WIFI_STATE_DISABLED);
// 1
mWifiNative.switchClientInterfaceToConnectivityMode(mClientInterfaceName, roleChangeInfo.requestorWs)
mConnectRoleChangeInfoToSetOnTransition = roleChangeInfo;
transitionTo(mConnectModeState);
}
// ... ...
}
switchClientInterfaceToConnectivityMode
After success, enterConnectModeState
and create mClientModeImpl
as needed.
Note that mClientModeImpl will be destroyed when exiting ConnectModeState.
// packages/modules/Wifi/service/java/com/android/server/wifi/ConcreteClientModeManager.java
ConcreteClientModeManager.ClientModeStateMachine.ConnectModeState.Enter() {
mClientModeImpl = mWifiInjector.makeClientModeImpl(
mClientInterfaceName, ConcreteClientModeManager.this,
mVerboseLoggingEnabled);
mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore);
}
public void exit() {
mClientModeImpl.stop();
mClientModeImpl = null;
}
At this point, the wifi has actually been started successfully and switched to the connectable state. The next step is actually the home page of ClientModeImpl.
The three state machines involved in the above analysis are as follows. We will introduce the following state machines later.
-
ActiveModeWarden.WifiController
Record the upper WiFi switch status (DisabledState/EnabledState)
DisabledState -> EnabledState
-
ConcreteClientModeManager.ClientModeStateMachine
WiFi device status (IdleState/StartedState/ScanOnlyModeState/ConnectModeState)
IdleState -> StartedState -> ConnectModeState
-
ClientModeImpl
Manage wifi connection status
DisconnectedState
Putting aside the various state transitions in the Framework layer, in fact the final operation of Enable WiFi is completed by the two methods below WifiNative
/**
* Setup an interface for client mode (for scan) operations.
*
* This method configures an interface in STA mode in the native daemons
* (wificond, vendor HAL).
*
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @param requestorWs Requestor worksource.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForClientInScanMode(
@NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs);
/**
* Switches an existing Client mode interface from scan mode
* {@link Iface#IFACE_TYPE_STA_FOR_SCAN} to connectivity mode
* {@link Iface#IFACE_TYPE_STA_FOR_CONNECTIVITY}.
*
* @param ifaceName Name of the interface.
* @param requestorWs Requestor worksource.
* @return true if the operation succeeded, false if there is an error or the iface is already
* in scan mode.
*/
public boolean switchClientInterfaceToConnectivityMode(@NonNull String ifaceName,
@NonNull WorkSource requestorWs);
In vernacular terms, the setupInterfaceForClientInScanMode() method is responsible for loading the driver, vendor hal, wificon and configuring the interface
The switchClientInterfaceToConnectivityMode() method is responsible for starting the supplicant
2. WiFiNative
Next, we will introduce the two most important functions related to WifiNative.
2.1 setupInterfaceForClientInScanMode initialize interface
Start scanmode first
The main code is as follows (removing some error handling)
// 1. 初始化驱动和vendor hal
startHal();
// 2. 初始化interface
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN);
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface, requestorWs);
// 3. 初始化wificond
mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
new NormalScanEventCallback(iface.name),
new PnoScanEventCallback(iface.name));
//4. 监听interface的down/up
iface.networkObserver = new NetworkObserverInternal(iface.id);
registerNetworkObserver(iface.networkObserver)
// 5. 启动supplicant监听(但是此时supplicant进程还未启动)
mWifiMonitor.startMonitoring(iface.name);
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
mWifiVendorHal.enableLinkLayerStats(iface.name);
// 6. 获取芯片支持的wifi feature
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
WifiNative -> WifiVendorFal -> HalDeviceManager
2.1.1 Driver and WiFi HAL initialization
先starthal()
WifiNative.startHal() -> WifiVendorHal.startVendorHal() -> HalDeviceManager.start() -> HalDeviceManager.startWifi()
The startWifi() method actually consists of two lines of code
// 通过hal接口启动
WifiStatus status = mWifi.start();
// 获取chip info
WifiChipInfo[] wifiChipInfos = getAllChipInfo();
Wifi.Start()
hardware/interface/wifi1.6/default/wifi.cpp
Return<void> 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();
// ... ...
// Create the chip instance once the HAL is started.
android::hardware::wifi::V1_0::ChipId chipId = kPrimaryChipId;
for (auto& hal : legacy_hals_) {
chips_.push_back(
new WifiChip(chipId, chipId == kPrimaryChipId, hal, mode_controller_,
std::make_shared<iface_util::WifiIfaceUtil>(iface_tool_, hal),
feature_flags_, on_subsystem_restart_callback));
chipId++;
}
}
WifiStatus Wifi::initializeModeControllerAndLegacyHal() {
// ... ...
mode_controller_->initialize(); // 加载驱动 driver_tool_->LoadDriver()
legacy_hals_ = legacy_hal_factory_->getHals();
for (auto& hal : legacy_hals_) {
legacy_hal::wifi_error legacy_status = hal->initialize();
// ... ...
}
// ... ...
}
std::vector<std::shared_ptr<WifiLegacyHal>> WifiLegacyHalFactory::getHals() {
if (legacy_hals_.empty()) {
// 先从已链接的so库中初始化vendor hal的接口(函数指针赋值)
// 如果失败,证明是多wifi芯片的设备,需要从descriptor.xml初始化
if (!initVendorHalDescriptorFromLinked()) initVendorHalsDescriptorList();
for (auto& desc : descs_) {
std::shared_ptr<WifiLegacyHal> hal =
std::make_shared<WifiLegacyHal>(iface_tool_, desc.fn, desc.primary);
legacy_hals_.push_back(hal);
}
}
return legacy_hals_;
}
In fact, it mainly consists of the following steps:
- Load the driver through wifi_mode_controller
- Initialize all HAL interfaces (legacy_hal_factory_->getHals())
- initVendorHalDescriptorFromLinked
- initVendorHalsDescriptorList
Starting from Android S (12), multiple wifi chips are supported, and the HAL layer has also been modified. WiFi HAL 1.5 and later versions support multiple wifi hals. The vendor provides a HAL so for each chip and uses an xml file. Describe them. This modification comes from commit 2dddd79e1645ea7d2ec41991bd00d6bfe4170ec7
commit 2dddd79e1645ea7d2ec41991bd00d6bfe4170ec7
Author: Jimmy Chen <[email protected]>
Date: Mon Dec 23 17:50:39 2019 +0200
Wifi: support multiple WIFI chips
The WIFI HAL API has support for multiple WIFI chips(IWifiChip instances) however the implementation is hard-coded to support only a single WIFI chip. This change reworks the implementation so multiple WIFI chips will be supported.
Support for multiple chips is based on the concept that each chip is represented by its own vendor HAL library. The implementation will look for descriptor files for vendor HAL libraries under /vendor/etc/wifi/vendor_hals. It will parse descriptors, dynamically load vendor HAL libraries and create WifiLegacyHal and WifiChip objects for each loaded vendor HAL library.
One of the descriptors should be marked with "primary" flag. The implementation will create the first WifiChip object for this library. Typically it is the one providing the best WIFI functionality, which was previously used as the only WIFI chip.
Additional support is added inside WifiChip and WifiLegacyHal for getting available chip modes and concurrency combinations from the vendor HAL if available, and allowing the chip to override network interface name when creating interfaces.
The mechanism for getting chip capabilities is improved to allow getting chip-global capabilities, which are independent of any created interfaces.
For example, if the framework needs to start a SoftAP on the 60GHz band, it needs to find a chip which supports this band, but before creating any interface on the chip. The new mechanism allows this.
Bug: 146922967
Test: atest VtsHalWifiV1_0TargetTest VtsHalWifiNanV1_0TargetTest VtsHalWifiApV1_0TargetTest \
VtsHalWifiV1_1TargetTest \
VtsHalWifiV1_2TargetTest VtsHalWifiNanV1_2TargetTest \
VtsHalWifiV1_3TargetTest \
VtsHalWifiApV1_4TargetTest VtsHalWifiNanV1_4TargetTest VtsHalWifiRttV1_4TargetTest
Change-Id: Ibdff93ea56aff186d4b5361ac77f6f448a0dfd45
Now let's look back at the two methods initVendorHalDescriptorFromLinked and initVendorHalsDescriptorList and it will be easy to understand.
The initVendorHalDescriptorFromLinked() method finds the init_wifi_vendor_hal_func_table method in the linked library and uses it to initialize the descriptor. In fact, the most important thing is to initialize the function pointer.
bool WifiLegacyHalFactory::initVendorHalDescriptorFromLinked() {
wifi_hal_lib_desc desc;
if (!initLinkedHalFunctionTable(&desc.fn)) return false;
desc.primary = true;
desc.handle = NULL;
descs_.push_back(desc);
return true;
}
bool WifiLegacyHalFactory::initLinkedHalFunctionTable(wifi_hal_fn* hal_fn) {
init_wifi_vendor_hal_func_table_t initfn;
// 在已经链接的库中查找 init_wifi_vendor_hal_func_table 方法
initfn = (init_wifi_vendor_hal_func_table_t)dlsym(RTLD_DEFAULT,
"init_wifi_vendor_hal_func_table");
if (!initfn) {
LOG(INFO) << "no vendor HAL library linked, will try dynamic load";
return false;
}
// 先给 wifi_hal_fn 对象内部的所有函数指针赋一个空实现,否则会是null,后面上层调用会报错
if (!initHalFuncTableWithStubs(hal_fn)) {
LOG(ERROR) << "Can not initialize the basic function pointer table";
return false;
}
// 这里依赖于vendor提供的legacy_hal实现,一般只是初始化函数指针,可以参考
// http://aospxref.com/android-13.0.0_r3/xref/hardware/qcom/wlan/qcwcn/wifi_hal/wifi_hal.cpp?r=&mo=17295&fi=547#547
if (initfn(hal_fn) != WIFI_SUCCESS) {
LOG(ERROR) << "Can not initialize the vendor function pointer table";
return false;
}
return true;
}
Library configuration and multi-chip configuration are in the Android.mk of libwifi-hal. LIB_WIFI_HAL is the library of legacy wifi hal. If definedWIFI_MULTIPLE_VENDOR_HALS
then LIB_WIFI_HAL is empty, but must be passed descriptor.xml definition
# Pick a vendor provided HAL implementation library.
# ============================================================
ifeq ($(WIFI_MULTIPLE_VENDOR_HALS), true)
# vendor HALs are loaded dynamically and not linked here
LIB_WIFI_HAL :=
else
LIB_WIFI_HAL ?= libwifi-hal-fallback
VENDOR_LOCAL_SHARED_LIBRARIES :=
ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
LIB_WIFI_HAL := libwifi-hal-bcm
VENDOR_LOCAL_SHARED_LIBRARIES := libcrypto
ifneq ($(wildcard vendor/google/libraries/GoogleWifiConfigLib),)
VENDOR_LOCAL_SHARED_LIBRARIES += \
google_wifi_firmware_config_version_c_wrapper
endif
else ifeq ($(BOARD_WLAN_DEVICE), synadhd)
LIB_WIFI_HAL := libwifi-hal-syna
VENDOR_LOCAL_SHARED_LIBRARIES := libcrypto
ifneq ($(wildcard vendor/google/libraries/GoogleWifiConfigLib),)
VENDOR_LOCAL_SHARED_LIBRARIES += \
google_wifi_firmware_config_version_c_wrapper
endif
else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
LIB_WIFI_HAL := libwifi-hal-qcom
VENDOR_LOCAL_SHARED_LIBRARIES := libcld80211
VENDOR_LOCAL_SHARED_LIBRARIES += libcrypto
else ifeq ($(BOARD_WLAN_DEVICE), mrvl)
# this is commented because none of the nexus devices
# that sport Marvell's wifi have support for HAL
# LIB_WIFI_HAL := libwifi-hal-mrvl
else ifeq ($(BOARD_WLAN_DEVICE), MediaTek)
# support MTK WIFI HAL
LIB_WIFI_HAL := libwifi-hal-mt66xx
else ifeq ($(BOARD_WLAN_DEVICE), realtek)
# support Realtek WIFI HAL
LIB_WIFI_HAL := libwifi-hal-rtk
else ifeq ($(BOARD_WLAN_DEVICE), emulator)
LIB_WIFI_HAL := libwifi-hal-emu
else ifeq ($(BOARD_WLAN_DEVICE), slsi)
LIB_WIFI_HAL := libwifi-hal-slsi
endif
endif
# The WiFi HAL that you should be linking.
# ============================================================
include $(CLEAR_VARS)
LOCAL_MODULE := libwifi-hal
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_PROPRIETARY_MODULE := true
LOCAL_CFLAGS := $(wifi_hal_cflags)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/include
LOCAL_EXPORT_HEADER_LIBRARY_HEADERS := libhardware_legacy_headers
LOCAL_HEADER_LIBRARIES := libhardware_legacy_headers
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
liblog \
libnl \
libutils \
$(VENDOR_LOCAL_SHARED_LIBRARIES)
LOCAL_SRC_FILES := \
driver_tool.cpp \
hal_tool.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := $(LIB_WIFI_HAL) libwifi-hal-common
include $(BUILD_SHARED_LIBRARY)
We takelibwifi-hal-qcom
as an example. Theinit_wifi_vendor_hal_func_table()
method is as follows, which is just to assign a value to the function pointer
// hardware/qcom/wlan/qcwcn/wifi_hal/wifi_hal.cpp
/*initialize function pointer table with Qualcomm HAL API*/
wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) {
if (fn == NULL) {
return WIFI_ERROR_UNKNOWN;
}
fn->wifi_initialize = wifi_initialize;
fn->wifi_wait_for_driver_ready = wifi_wait_for_driver_ready;
fn->wifi_cleanup = wifi_cleanup;
fn->wifi_event_loop = wifi_event_loop;
fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set;
fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix;
fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui;
fn->wifi_get_ifaces = wifi_get_ifaces;
fn->wifi_get_iface_name = wifi_get_iface_name;
fn->wifi_set_iface_event_handler = wifi_set_iface_event_handler;
fn->wifi_reset_iface_event_handler = wifi_reset_iface_event_handler;
fn->wifi_start_gscan = wifi_start_gscan;
fn->wifi_stop_gscan = wifi_stop_gscan;
fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results;
fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist;
fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist;
fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler;
fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler;
fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities;
fn->wifi_set_link_stats = wifi_set_link_stats;
fn->wifi_get_link_stats = wifi_get_link_stats;
fn->wifi_clear_link_stats = wifi_clear_link_stats;
fn->wifi_get_valid_channels = wifi_get_valid_channels;
fn->wifi_rtt_range_request = wifi_rtt_range_request;
fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel;
fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities;
fn->wifi_rtt_get_responder_info = wifi_rtt_get_responder_info;
fn->wifi_enable_responder = wifi_enable_responder;
fn->wifi_disable_responder = wifi_disable_responder;
fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag;
fn->wifi_start_logging = wifi_start_logging;
fn->wifi_set_epno_list = wifi_set_epno_list;
fn->wifi_reset_epno_list = wifi_reset_epno_list;
fn->wifi_set_country_code = wifi_set_country_code;
fn->wifi_enable_tdls = wifi_enable_tdls;
fn->wifi_disable_tdls = wifi_disable_tdls;
fn->wifi_get_tdls_status = wifi_get_tdls_status;
fn->wifi_get_tdls_capabilities = wifi_get_tdls_capabilities;
fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump;
fn->wifi_set_log_handler = wifi_set_log_handler;
fn->wifi_reset_log_handler = wifi_reset_log_handler;
fn->wifi_set_alert_handler = wifi_set_alert_handler;
fn->wifi_reset_alert_handler = wifi_reset_alert_handler;
fn->wifi_get_firmware_version = wifi_get_firmware_version;
fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status;
fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set;
fn->wifi_get_ring_data = wifi_get_ring_data;
fn->wifi_get_driver_version = wifi_get_driver_version;
fn->wifi_set_passpoint_list = wifi_set_passpoint_list;
fn->wifi_reset_passpoint_list = wifi_reset_passpoint_list;
fn->wifi_set_lci = wifi_set_lci;
fn->wifi_set_lcr = wifi_set_lcr;
fn->wifi_start_sending_offloaded_packet =
wifi_start_sending_offloaded_packet;
fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet;
fn->wifi_start_rssi_monitoring = wifi_start_rssi_monitoring;
fn->wifi_stop_rssi_monitoring = wifi_stop_rssi_monitoring;
fn->wifi_nan_enable_request = nan_enable_request;
fn->wifi_nan_disable_request = nan_disable_request;
fn->wifi_nan_publish_request = nan_publish_request;
fn->wifi_nan_publish_cancel_request = nan_publish_cancel_request;
fn->wifi_nan_subscribe_request = nan_subscribe_request;
fn->wifi_nan_subscribe_cancel_request = nan_subscribe_cancel_request;
fn->wifi_nan_transmit_followup_request = nan_transmit_followup_request;
fn->wifi_nan_stats_request = nan_stats_request;
fn->wifi_nan_config_request = nan_config_request;
fn->wifi_nan_tca_request = nan_tca_request;
fn->wifi_nan_beacon_sdf_payload_request = nan_beacon_sdf_payload_request;
fn->wifi_nan_register_handler = nan_register_handler;
fn->wifi_nan_get_version = nan_get_version;
fn->wifi_set_packet_filter = wifi_set_packet_filter;
fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities;
fn->wifi_read_packet_filter = wifi_read_packet_filter;
fn->wifi_nan_get_capabilities = nan_get_capabilities;
fn->wifi_nan_data_interface_create = nan_data_interface_create;
fn->wifi_nan_data_interface_delete = nan_data_interface_delete;
fn->wifi_nan_data_request_initiator = nan_data_request_initiator;
fn->wifi_nan_data_indication_response = nan_data_indication_response;
fn->wifi_nan_data_end = nan_data_end;
fn->wifi_configure_nd_offload = wifi_configure_nd_offload;
fn->wifi_get_driver_memory_dump = wifi_get_driver_memory_dump;
fn->wifi_get_wake_reason_stats = wifi_get_wake_reason_stats;
fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring;
fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates;
fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates;
fn->wifi_get_roaming_capabilities = wifi_get_roaming_capabilities;
fn->wifi_configure_roaming = wifi_configure_roaming;
fn->wifi_enable_firmware_roaming = wifi_enable_firmware_roaming;
fn->wifi_select_tx_power_scenario = wifi_select_tx_power_scenario;
fn->wifi_reset_tx_power_scenario = wifi_reset_tx_power_scenario;
fn->wifi_set_radio_mode_change_handler = wifi_set_radio_mode_change_handler;
fn->wifi_virtual_interface_create = wifi_virtual_interface_create;
fn->wifi_virtual_interface_delete = wifi_virtual_interface_delete;
fn->wifi_set_latency_mode = wifi_set_latency_mode;
fn->wifi_set_thermal_mitigation_mode = wifi_set_thermal_mitigation_mode;
fn->wifi_set_dtim_config = wifi_set_dtim_config;
return WIFI_SUCCESS;
After successfully obtaining the vendor hal interface, initialize all legacy hal
hal->initialize();
This method currently does nothing but returns WIFI_SUCCESS directly.
// hardware/interface/1.6/default/wifi_legacy_hal.cpp
wifi_error WifiLegacyHal::initialize() {
LOG(DEBUG) << "Initialize legacy HAL";
// this now does nothing, since HAL function table is provided
// to the constructor
return WIFI_SUCCESS;
}
2.1.2 Initialize interface
// packages/modules/Wifi/service/java/com/android/server/wifi/WifiNative.java
public String setupInterfaceForClientInScanMode(
@NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs) {
// ... ...
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN);
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface, requestorWs);
// ... ...
}
allocateIface()
It's very simple, just create a new Iface object through WifiNative.IfaceManager.
createStaIface
, here isVendorHalSupported
No need to pay attention. The source codes currently encountered are all supported. The implementation is to find whether the halservice list contains the wifi hal service
// packages/modules/Wifi/service/java/com/android/server/wifi/WifiNative.java
private String createStaIface(@NonNull Iface iface, @NonNull WorkSource requestorWs) {
synchronized (mLock) {
if (mWifiVendorHal.isVendorHalSupported()) {
return mWifiVendorHal.createStaIface(
new InterfaceDestoyedListenerInternal(iface.id), requestorWs);
} else {
Log.i(TAG, "Vendor Hal not supported, ignoring createStaIface.");
return handleIfaceCreationWhenVendorHalNotSupported(iface);
}
}
}
public String createStaIface(@Nullable InterfaceDestroyedListener destroyedListener,
@NonNull WorkSource requestorWs) {
synchronized (sLock) {
IWifiStaIface iface = mHalDeviceManager.createStaIface(
new StaInterfaceDestroyedListenerInternal(destroyedListener), mHalEventHandler,
requestorWs);
String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);
// 注册android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback,包括onBackgroundScanFailure,onBackgroundFullScanResult,onBackgroundFullScanResults,onRssiThresholdBreached。注册到wifihal
registerStaIfaceCallback(iface);
retrieveWifiChip((IWifiIface) iface);
mIWifiStaIfaces.put(ifaceName, iface);
return ifaceName;
}
}
Main methods of analysismHalDeviceManager.createStaIface
HalDeviceManager.java
–> createStaIface(CHIP_CAPABILITY_ANY, destroyedListener, handler, requestorWs)
–>createIface(HDM_CREATE_IFACE_STA, CHIP_CAPABILITY_ANY/*requiredChipCapabilities*/, destroyedListener, handler, requestorWs)
private IWifiIface createIface(@HdmIfaceTypeForCreation int createIfaceType,
long requiredChipCapabilities, InterfaceDestroyedListener destroyedListener,
Handler handler, WorkSource requestorWs) {
synchronized (mLock) {
WifiChipInfo[] chipInfos = getAllChipInfo();
// ... ...
return createIfaceIfPossible(
chipInfos, createIfaceType /* HDM_CREATE_IFACE_STA */, requiredChipCapabilities /* CHIP_CAPABILITY_ANY */,
destroyedListener, handler, requestorWs);
}
}
First obtain all chipInfo, and then select the most suitable chip through thecreateIfaceIfPossible
method.
private IWifiIface createIfaceIfPossible(
WifiChipInfo[] chipInfos, @HdmIfaceTypeForCreation int createIfaceType /* HDM_CREATE_IFACE_STA */,
long requiredChipCapabilities/* CHIP_CAPABILITY_ANY */, InterfaceDestroyedListener destroyedListener,
Handler handler, WorkSource requestorWs) {
int targetHalIfaceType = HAL_IFACE_MAP.get(createIfaceType);
synchronized (mLock) {
IfaceCreationData bestIfaceCreationProposal = getBestIfaceCreationProposal(chipInfos,
createIfaceType, requiredChipCapabilities, requestorWs);
if (bestIfaceCreationProposal != null) {
IWifiIface iface = executeChipReconfiguration(bestIfaceCreationProposal,
createIfaceType /* HDM_CREATE_IFACE_STA */);
// ... ...
return iface;
}
}
return null
}
There are two main methods:
-
getBestIfaceCreationProposal
Get the most suitable iface
We won’t analyze it for now, because we are currently using a single chip.
-
executeChipReconfiguration
placement iface
Look at it carefullyexecuteChipReconfiguration
Method
private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
@HdmIfaceTypeForCreation int createIfaceType) {
// ... ...
switch (createIfaceType) {
case HDM_CREATE_IFACE_STA:
ifaceCreationData.chipInfo.chip.createStaIface(
(WifiStatus status, IWifiStaIface iface) -> {
statusResp.value = status;
ifaceResp.value = iface;
});
break;
// ... ...
}
// ... ...
updateRttControllerWhenInterfaceChanges();
return ifaceResp.value;
}
ifaceCreationData.chipInfo.chip.createStaIface()
The WiFi HAL is called
// hardware/interfaces/wifi/1.6/default/wifi_chip.cpp
Return<void> WifiChip::createStaIface(createStaIface_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
&WifiChip::createStaIfaceInternal, hidl_status_cb);
}
createStaIfaceInternal()
Divided into the following steps
-
allocateStaIfaceName()
Gets the interface name, which is configured through properties on my platform. If the vendor hal supports thewifi_get_supported_iface_name()
interface, the iface name is obtained from the vendor hal first, otherwise it uses the attributewifi.interface
orwifi.interface.0
device/oneplus/sdm845-common/rootdir/etc/init.qcom.rc: setprop wifi.interface wlan0
-
createVirtualInterface()
Create interfaceCall vendor hal
wifi_virtual_interface_create()
to create an interface through the nl80211 interface.Reference http://aospxref.com/android-13.0.0_r3/xref/hardware/qcom/wlan/qcwcn/wifi_hal/wificonfig.cpp#821
At this point, the wifi interface has been created successfully.
2.1.3 wificond initialization
After steps 1 and 2 above, the driver and hal have been successfully loaded, and the interface has been created.
The next step is to configure through WifiNl80211Manager
’s setupInterfaceForClientMode()
method.
public boolean setupInterfaceForClientMode(@NonNull String ifaceName,
@NonNull @CallbackExecutor Executor executor,
@NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) {
// 1. 获取 WIFI_NL80211_SERVICE(wifinl80211)
retrieveWificondAndRegisterForDeath();
// 2. wificond对ifaceName代表的网卡进行初始化,返回了一个ClientInterfaceImpl的代理ClientInterfaceBinder (system/connectivity/wificond/client_interface_binder.cpp)
IClientInterface clientInterface = mWificond.createClientInterface(ifaceName);
mClientInterfaces.put(ifaceName, clientInterface);
// 3. subscribeScanEvents, subscribePnoScanEvents
IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
mWificondScanners.put(ifaceName, wificondScanner);
canEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback);
mScanEventHandlers.put(ifaceName, scanEventHandler);
wificondScanner.subscribeScanEvents(scanEventHandler);
PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor,
pnoScanCallback);
mPnoScanEventHandlers.put(ifaceName, pnoScanEventHandler);
wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
}
wificond (wifi connectivity daemon) currently only supports network card configuration and scanning, and the connection is still through wpa_supplicant.
The code of wificond will not be expanded here. The core is to communicate with the kernel nl80211 module through netlink, which is used to manage Wi-Fi hardware and provide Wi-Fi related information and control functions.
At this point, the 1, 2, 3 drivers and HAL have been loaded, the network card has been created and initialized successfully, and the wificond module, which is mainly responsible for managing wifi and scanning, has also been initialized successfully.
So why is it called setupInterfaceForClientInScanMode? Because so far, it cannot provide connection functions for the upper layer, only scanning.
The next step is to start the wpa_supplicant process that provides the connection function
2.2 switchClientInterfaceToConnectivityMode 启动supplicant
// packages/modules/Wifi/service/java/com/android/server/wifi/WifiNative.java
public boolean switchClientInterfaceToConnectivityMode(@NonNull String ifaceName,
@NonNull WorkSource requestorWs) {
// ... ...
startSupplicant();
// ... ...
mSupplicantStaIfaceHal.setupIface(iface.name);
// ... ...
}
2.2.1 Start supplicant
startSupplicant() --> startAndWaitForSupplicantConnection()
private boolean startAndWaitForSupplicantConnection() {
// Start initialization if not already started.
if (!mSupplicantStaIfaceHal.isInitializationStarted()
&& !mSupplicantStaIfaceHal.initialize()) {
return false;
}
if (!mSupplicantStaIfaceHal.startDaemon()) {
Log.e(TAG, "Failed to startup supplicant");
return false;
}
boolean connected = false;
int connectTries = 0;
while (!connected && connectTries++ < CONNECT_TO_SUPPLICANT_RETRY_TIMES) {
// Check if the initialization is complete.
connected = mSupplicantStaIfaceHal.isInitializationComplete();
if (connected) {
break;
}
try {
Thread.sleep(CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS);
} catch (InterruptedException ignore) {
}
}
return connected;
}
This function is very simple. CallmSupplicantStaIfaceHal.startDaemon()
to start the supplicant; then check it every CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS
100ms, up to 50 times, through the method < a i=3>Check whether the supplicant is started successfully. mSupplicantStaIfaceHal.isInitializationComplete()
Supplicant启动: SupplicantStaIfaceHal.startDaemon() --> ISupplicantStaIfaceHal.startDaemon() --> SupplicantStaIfaceHalAidlImpl
// packages/modules/Wifi/service/java/com/android/server/wifi/SupplicantStaIfaceHalAidlImpl.java
public boolean startDaemon() {
final String methodStr = "startDaemon";
if (mISupplicant != null) {
Log.i(TAG, "Service is already initialized, skipping " + methodStr);
return true;
}
mISupplicant = getSupplicantMockable();
if (mISupplicant == null) {
Log.e(TAG, "Unable to obtain ISupplicant binder.");
return false;
}
Log.i(TAG, "Obtained ISupplicant binder.");
try {
IBinder serviceBinder = getServiceBinderMockable();
if (serviceBinder == null) {
return false;
}
serviceBinder.linkToDeath(mSupplicantDeathRecipient, /* flags= */ 0);
return true;
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
main methodgetSupplicantMockable()
private static final String HAL_INSTANCE_NAME = ISupplicant.DESCRIPTOR + "/default";
protected ISupplicant getSupplicantMockable() {
synchronized (mLock) {
try {
return ISupplicant.Stub.asInterface(
ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME));
} catch (Exception e) {
Log.e(TAG, "Unable to get ISupplicant service, " + e);
return null;
}
}
}
Expand the service name of supplicantandroid.hardware.wifi.supplicant.ISupplicant/default
. Since wpa_supplicant will exit after the wifi is closed, the service cannot be found when it is opened again at this time, and the system needs to pull it up
09-12 17:09:38.988 645 645 I servicemanager: Found android.hardware.wifi.supplicant.ISupplicant/default in device VINTF manifest.
09-12 17:09:38.988 645 645 I servicemanager: Since 'android.hardware.wifi.supplicant.ISupplicant/default' could not be found, trying to start it as a lazy AIDL service
09-12 17:09:38.514 0 0 I init : starting service 'wpa_supplicant'...
09-12 17:09:38.527 0 0 I init : Control message: Processed ctl.interface_start for 'aidl/android.hardware.wifi.supplicant.ISupplicant/default' from pid: 645 (/system/bin/servicemanager)
The rc file of supplicant hal (/vendor/etc/init/android.hardware.wifi.supplicant-service.rc) is defined as follows
service wpa_supplicant /vendor/bin/hw/wpa_supplicant \
-O/data/vendor/wifi/wpa/sockets -dd \
-g@android:wpa_wlan0
# we will start as root and wpa_supplicant will switch to user wifi
# after setting up the capabilities required for WEXT
# user wifi
# group wifi inet keystore
interface aidl android.hardware.wifi.supplicant.ISupplicant/default
class main
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
At this time SupplicantStaIfaceHalAidlImpl
has successfully started the supplicnat process and bound it to the supplicant hal service. If you need to communicate with the supplicant later, the Framework layer can directly call its interface through SupplicantStaIfaceHalAidlImpl
.
3. ClientModeImpl initiates scanning
Initiate a connectivity scan immediately after entering the initial state DisconnectedState
DisconnectedState.enter() {
mWifiConnectivityManager.handleConnectionStateChanged(
mClientModeManager,
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
}
Let’s take a look at what the handleConnectionStateChanged method does when disconnected
public void handleConnectionStateChanged(
ConcreteClientModeManager clientModeManager, int state) {
if (mWifiState == WIFI_STATE_DISCONNECTED) {
scheduleWatchdogTimer();
// Switch to the disconnected scanning schedule
setSingleScanningSchedule(mDisconnectedSingleScanScheduleSec);
setSingleScanningType(mDisconnectedSingleScanType);
startConnectivityScan(SCAN_IMMEDIATELY);
}
}
mDisconnectedSingleScanScheduleSec: Default {20, 40, 80, 160}, can be customized through config_wifiDisconnectedScanIntervalScheduleSec
mDisconnectedSingleScanType: 默认WifiScanner.SCAN_TYPE_HIGH_ACCURACY
Finally, call the startConnectivityScan() interface to directly initiate the scan.
4. State machine process
5. Summary
To be added