Android WiFi startup process (based on Android S)

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

ConcreteClientModeManagerAfter 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);
            }
        // ... ...
}

setupInterfaceForClientInScanModepassed 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>IdleStateSentCMD_SWITCH_TO_CONNECT_MODEMessage

// 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 carefullyexecuteChipReconfigurationMethod

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 the wifi_get_supported_iface_name() interface, the iface name is obtained from the vendor hal first, otherwise it uses the attribute wifi.interface or wifi.interface.0

    device/oneplus/sdm845-common/rootdir/etc/init.qcom.rc: setprop wifi.interface wlan0

  • createVirtualInterface()Create interface

    Call vendor halwifi_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

State machine changes when WiFi is turned on

5. Summary

To be added

Guess you like

Origin blog.csdn.net/weixin_40588186/article/details/132837372