Hostapd系统源代码学习

1、热点启动流程

APP调用WifiManager.java的startSoftAp的函数,用来启动热点. 在android 12之后,wifi框架相关试下被Google移动到了/packages/modules/Wifi/中.

热点相关启动流程如下:

从流程图看,WifiNative.java为启动流程的关键类,主要启动过程包含如下三个步骤,启动Hal层,启动hostapd程序,创建WIFI热点网卡

java 复制代码
 public String setupInterfaceForSoftApMode(
            @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs,
            @SoftApConfiguration.BandType int band, boolean isBridged,
            @NonNull SoftApManager softApManager, int type) {
        synchronized (mLock) {
            String bugTitle = "Wi-Fi BugReport (softAp interface failure)";
            String errorMsg = "";

            //启动HAL层程序
            if (!startHal()) {
                errorMsg = "Failed to start softAp Hal";
                Log.e(TAG, errorMsg);
                mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
                takeBugReportInterfaceFailureIfNeeded(bugTitle, errorMsg);
                return null;
            }
            
            //启动hostapd程序
            if (!startHostapd()) {
                errorMsg = "Failed to start softAp hostapd";
                Log.e(TAG, errorMsg);
                mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
                takeBugReportInterfaceFailureIfNeeded(bugTitle, errorMsg);
                return null;
            }

            //创建热点网卡
            Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);
            if (iface == null) {
                Log.e(TAG, "Failed to allocate new AP iface");
                return null;
            }
            iface.externalListener = interfaceCallback;
            iface.name = createApIface(iface, requestorWs, band, isBridged, softApManager, type);

            .......

}

1.1、启动Hal层

拉起IWifi服务,具体实现在WifiHalHidlImpl.java中。

java 复制代码
private void initServiceManagerIfNecessaryLocked() {
        if (mServiceManager != null) {
            Log.i(TAG, "mServiceManager already exists");
            return;
        }
        Log.i(TAG, "initServiceManagerIfNecessaryLocked");

        // Failures of IServiceManager are most likely system breaking.
        // Behavior here will be to WTF and continue.
        mServiceManager = getServiceManagerMockable();
        if (mServiceManager == null) {
            Log.wtf(TAG, "Failed to get IServiceManager instance");
            return;
        }

        try {
            if (!mServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
                Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
                mServiceManager = null;
                return;
            }
            
            //注册IWIFI服务状态是否ready,在mServiceNotificationCallback回调中出初始化化完成
            if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
                    mServiceNotificationCallback)) {
                Log.wtf(TAG, "Failed to register a listener for IWifi service");
                mServiceManager = null;
            }
        } catch (RemoteException e) {
            Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
            mServiceManager = null;
            return;
        }
        mIsVendorHalSupported = isSupportedInternal();
    }

在WifiHalHidlImpl.java中mServiceNotificationCallback函数中保存private android.hardware.wifi.V1_0.IWifi mWifi;对象。IWifi最终实现在hardware/interfaces/wifi/1.5/default/wifi.cpp中。

1.2、启动hostapd程序

为IHostapd服务,其中HostapdHalHidlImp.java为Framework和Hal层交互的关键类,mIServiceManager.registerForNotifications监听IHostapd服务的启动,当IHostapd启动后,HostapdHalHidlImp获取到IHostapd实例。

java 复制代码
    @Override
    public boolean initialize() {
        synchronized (mLock) {
            if (mVerboseLoggingEnabled) {
                Log.i(TAG, "Registering IHostapd service ready callback.");
            }
            mIHostapd = null;
            if (mIServiceManager != null) {
                // Already have an IServiceManager and serviceNotification registered, don't
                // don't register another.
                return true;
            }
            try {
                mIServiceManager = getServiceManagerMockable();
                if (mIServiceManager == null) {
                    Log.e(TAG, "Failed to get HIDL Service Manager");
                    return false;
                }
                if (!linkToServiceManagerDeath()) {
                    return false;
                }
                /* TODO(b/33639391) : Use the new IHostapd.registerForNotifications() once it
                   exists */

                // 此处Framework监听android.hardware.wifi.hostapd@1.0::IHostapd/default
                // 是否启动完成,启动完成后框架可以调用IHostapd接口
                if (!mIServiceManager.registerForNotifications(
                        IHostapd.kInterfaceName, "", mServiceNotificationCallback)) {
                    Log.e(TAG, "Failed to register for notifications to "
                            + IHostapd.kInterfaceName);
                    mIServiceManager = null; // Will need to register a new ServiceNotification
                    return false;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Exception while trying to register a listener for IHostapd service: "
                        + e);
                hostapdServiceDiedHandler(mDeathRecipientCookie);
                mIServiceManager = null; // Will need to register a new ServiceNotification
                return false;
            }
            return true;
        }
    }

IHostapd hal层服务实现在高通平台external\wpa_supplicant_8\hostapd\hidl\1.3\hidl.cpp,在初始化函数hostapd_hidl_init中调用service->registerAsService,将IHostapd服务注册到系统中。

cpp 复制代码
int hostapd_hidl_init(struct hapd_interfaces *interfaces)
{
	wpa_printf(MSG_DEBUG, "Initing hidl control");
#ifdef ARCH_ARM_32
	android::hardware::ProcessState::initWithMmapSize(getHWBinderMmapSize());
#endif /* ARCH_ARM_32 */

	IPCThreadState::self()->setupPolling(&hidl_fd);
	if (hidl_fd < 0)
		goto err;

	wpa_printf(MSG_INFO, "Processing hidl events on FD %d", hidl_fd);
	// Look for read events from the hidl socket in the eloop.
	if (eloop_register_read_sock(
		hidl_fd, hostapd_hidl_sock_handler, interfaces, NULL) < 0)
		goto err;
	service = new Hostapd(interfaces);
	if (!service)
		goto err;
	if (interfaces->hidl_service_name) {
		wpa_printf(MSG_DEBUG, "Override HIDL service name: %s",
			   interfaces->hidl_service_name);

        //注册Hidl服务,保证上框架层获取到服务实例
		if (service->registerAsService(interfaces->hidl_service_name)
		    != android::NO_ERROR)
			goto err;
	} else {
		wpa_printf(MSG_DEBUG, "Using default HIDL service name");
		if (service->registerAsService() != android::NO_ERROR)
			goto err;
	}

    .......

	return 0;
err:
	hostapd_hidl_deinit(interfaces);
	return -1;
}

hostapd_hidl_init 在external\wpa_supplicant_8\hostapd\main.c的main函数中初始化。其中IHostapd为在rc中注册为service服务,框架侧涉及为lazy服务,就是需要的时候启动,不需要的的时候退出。

/vendor/etc/init $ cat hostapd.android.rc

init.rc fragment for hostapd on Android

Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>

This software may be distributed under the terms of the BSD license.

See README for more details.

on post-fs-data

mkdir /data/vendor/wifi 0770 wifi wifi

mkdir /data/vendor/wifi/hostapd 0770 wifi wifi

mkdir /data/vendor/wifi/hostapd/sockets 0770 wifi wifi

service hostapd /vendor/bin/hw/hostapd -dd -g /data/vendor/wifi/hostapd/global

interface android.hardware.wifi.hostapd@1.0::IHostapd default

interface android.hardware.wifi.hostapd@1.1::IHostapd default

interface android.hardware.wifi.hostapd@1.2::IHostapd default

interface android.hardware.wifi.hostapd@1.3::IHostapd default

interface vendor.qti.hardware.wifi.hostapd@1.0::IHostapdVendor default

interface vendor.qti.hardware.wifi.hostapd@1.1::IHostapdVendor default

interface vendor.qti.hardware.wifi.hostapd@1.2::IHostapdVendor default

1.3、创建wlan网口

启动完成上面进程后,通过WifiChip.java创建wifi虚拟网卡。高通平台创建网卡名在hardware/interfaces/wifi/1.5/default/wifi_chip.cpp中实现,具体逻辑如下

cpp 复制代码
uint32_t WifiChip::startIdxOfApIface() {

    //如果wifi支持双STA,则AP使用网口为WLAN2
    if (isDualStaConcurrencyAllowedInCurrentMode()) {
        // When the HAL support dual STAs, AP should start with idx 2.
        LOG(ERROR) << "startIdxOfApIface = 2 ";
        return 2;
    } else if (isStaApConcurrencyAllowedInCurrentMode()) {
        //  When the HAL support STA + AP but it doesn't support dual STAs.
        //  AP should start with idx 1.
         LOG(ERROR) << "startIdxOfApIface = 1 ";
        return 1;
    }
    // No concurrency support.
     LOG(ERROR) << "startIdxOfApIface = 0 ";
    return 0;
}

std::string getWlanIfaceName(unsigned idx) {
    if (idx >= kMaxWlanIfaces) {
        CHECK(false) << "Requested interface beyond wlan" << kMaxWlanIfaces;
        return {};
    }

    std::array<char, PROPERTY_VALUE_MAX> buffer;
    if (idx == 0 || idx == 1) {
        const char* altPropName =
            (idx == 0) ? "wifi.interface" : "wifi.concurrent.interface";
        auto res = property_get(altPropName, buffer.data(), nullptr);
        if (res > 0) return buffer.data();
    }
    std::string propName = "wifi.interface." + std::to_string(idx);
    auto res = property_get(propName.c_str(), buffer.data(), nullptr);
    if (res > 0) {
         LOG(ERROR) << "getWlanIfaceName buffer.data() = " << buffer.data();
         return buffer.data();
    }

    //AP使用网口为"wlan2", 相关代码解析以高通6490wifi芯片进行分析
    LOG(ERROR) << "getWlanIfaceName idx = " << std::to_string(idx);
    return "wlan" + std::to_string(idx);
}

相关Log如下:

02-12 15:05:13.832 2037 2572 I WifiNative: startHal is success !!!

02-12 15:05:13.849 2037 2572 I HostapdHalHidlImp: serviceDeclared interfaceName = android.hardware.wifi.hostapd@1.0::IHostapd

02-12 15:05:13.946 5463 5463 I hostapd : main argv[0] = /vendor/bin/hw/hostapd

02-12 15:05:13.946 5463 5463 I hostapd : main argv[0] = /vendor/bin/hw/hostapd

02-12 15:05:13.947 5463 5463 I hostapd : main argv[1] = -dd

02-12 15:05:13.947 5463 5463 I hostapd : main argv[2] = -g

02-12 15:05:13.947 5463 5463 I hostapd : main argv[3] = /data/vendor/wifi/hostapd/global

02-12 15:05:13.947 5463 5463 I hostapd : main argv[3] = /data/vendor/wifi/hostapd/global

02-12 15:05:13.947 5463 5463 E hostapd : debug, set loglevel

02-12 15:05:13.947 5463 5463 D hostapd : Using existing control interface directory

02-12 15:05:13.950 5463 5463 I HidlServiceManagement: Registered android.hardware.wifi.hostapd@1.3::IHostapd/default

02-12 15:05:13.980 2037 2572 I WifiNative: startHostapd is success !!!

02-12 15:05:13.985 2037 2572 I WifiNative: CreateApIface - vendor bridge=false

02-12 15:05:13.995 1171 1171 E android.hardware.wifi@1.0-service: startIdxOfApIface = 2

02-12 15:05:13.995 1171 1171 E android.hardware.wifi@1.0-service: getWlanIfaceName idx = 2

02-12 15:05:13.995 1171 1171 E android.hardware.wifi@1.0-service: allocate ap ifname = wlan2

02-12 15:05:14.026 2037 2572 I WifiNative: Successfully setup Iface:{Name=wlan2,Id=1,Type=AP}

02-12 15:05:14.102 2037 2572 D WifiNative: onSetCountryCodeSucceeded: CN

02-12 15:05:14.183 2037 2572 I WifiNative: Interface state changed on Iface:{Name=wlan2,Id=1,Type=AP}, isUp=true

1.4、添加热点

在SoftApManager.java中startSoftAp来启动启热点,最后通过HAL层调用到比hostapd.cpp中的addSingleAccessPoin函数。

cpp 复制代码
V1_2::HostapdStatus Hostapd::addSingleAccessPoint(
    const V1_3::IHostapd::IfaceParams& iface_params,
    const V1_3::IHostapd::ChannelParams& channelParams,
    const V1_3::IHostapd::NetworkParams& nw_params,
    const std::string br_name)
{
	if (hostapd_get_iface(interfaces_, iface_params.V1_2.V1_1.V1_0.ifaceName.c_str())) {
		wpa_printf(
		    MSG_ERROR, "Interface %s already present",
		    iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
		return {V1_2::HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
	}
	const auto conf_params = CreateHostapdConfig(iface_params, channelParams, nw_params, br_name);
	if (conf_params.empty()) {
		wpa_printf(MSG_ERROR, "Failed to create config params");
		return {V1_2::HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
	}
	const auto conf_file_path =
	    WriteHostapdConfig(iface_params.V1_2.V1_1.V1_0.ifaceName, conf_params);
    if (conf_file_path.empty()) {
		wpa_printf(MSG_ERROR, "Failed to write config file");
		return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
	}
	std::string add_iface_param_str = StringPrintf(
	    "%s config=%s", iface_params.V1_2.V1_1.V1_0.ifaceName.c_str(),
	    conf_file_path.c_str());
	std::vector<char> add_iface_param_vec(
	    add_iface_param_str.begin(), add_iface_param_str.end() + 1);
	wpa_printf(MSG_INFO, "hostapd_add_iface %s success", add_iface_param_vec.data());

    //此处调用wpa_supplicant_8包中的hapd_interfaces中的结构体和函数执行相关代码

	if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
		wpa_printf(
		    MSG_ERROR, "Adding interface %s failed",
		    add_iface_param_str.c_str());
		return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
	} else {
		wpa_printf(
		    MSG_INFO, "hostapd_add_iface %s sucess",
		    iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
	}

    ......

}

相关调用流程如下,最终通过driver_nl80211.c调用到wifi固件中,高通平台默认在vendor/qcom/opensource/wlan中,不同WIFI平台不同

hostapd.cpp在external\wpa_supplicant_8代码仓中,其中external\wpa_supplicant_8为wifi开源代码。

参考相关网页https://w1.fi/wpa_supplicant/devel/index.html 查看hostapd相关架构图如下

包含hostapd_cli和hostapd两个程序,hostapd_cli为控制程序,hostapd为热点实现程序。

其中hapd_interfaces为framework和Hidl交互的主要对象

cpp 复制代码
struct hapd_interfaces {
	int (*reload_config)(struct hostapd_iface *iface);
	struct hostapd_config * (*config_read_cb)(const char *config_fname);
    
    //hostapd_cli初始化函数指针
	int (*ctrl_iface_init)(struct hostapd_data *hapd);

    //hostapd_cli去初始化函数指针
	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);

	int (*for_each_interface)(struct hapd_interfaces *interfaces,
				  int (*cb)(struct hostapd_iface *iface,
					    void *ctx), void *ctx);
    //驱动交互函数指针
	int (*driver_init)(struct hostapd_iface *iface);

	size_t count;
	int global_ctrl_sock;
	struct dl_list global_ctrl_dst;
	char *global_iface_path;
	char *global_iface_name;
#ifdef CONFIG_CTRL_IFACE_HIDL
	char *hidl_service_name;
#endif
#ifndef CONFIG_NATIVE_WINDOWS
	gid_t ctrl_iface_group;
#endif /* CONFIG_NATIVE_WINDOWS */
	struct hostapd_iface **iface;

	size_t terminate_on_error;
#ifndef CONFIG_NO_VLAN
	struct dynamic_iface *vlan_priv;
#endif /* CONFIG_NO_VLAN */
#ifdef CONFIG_ETH_P_OUI
	struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
#endif /* CONFI

2、热点认证测试

--- 带宽设置测试

hostapd_cli chan_switch

usage: <cs_count> <freq> [sec_channel_offset=] [center_freq1=] [center_freq2=] [bandwidth=] [blocktx] [ht|vht]

20MHz配置

adb shell "hostapd_cli chan_switch 30 5180 bandwidth=20 ht"

40MHz配置

adb shell "hostapd_cli chan_switch 30 2412 bandwidth=40 sec_channel_offset=1 center_freq1=2422 ht"

adb shell "hostapd_cli chan_switch 30 5180 bandwidth=40 sec_channel_offset=1 center_freq1=5190 ht"

80MHz配置

adb shell "hostapd_cli chan_switch 30 5745 bandwidth=80 sec_channel_offset=1 center_freq1=5775 vht"

160MHz配置

adb shell "hostapd_cli chan_switch 30 5180 bandwidth=160 sec_channel_offset=1 center_freq1=5250 vht"

相关交互流程记录如下:

3、热点断开原因

在开发过程中经常出现热点断开的情况。可以参考如下网站

https://community.cisco.com/t5/wireless-mobility-knowledge-base/802-11-association-status-802-11-deauth-reason-codes/ta-p/3148055/page/2

相关推荐
智先森zhi2 小时前
实战:将 Android 多Module应用迁移到 kmp+cmp
android·ios·kotlin
2501_937145413 小时前
IPTV电视源码系统2026优化版:技术升级,全场景流畅适配
android·电视盒子·源代码管理
Ehtan_Zheng4 小时前
让你的代码更整洁:10 个必知的 Kotlin 扩展函数
android
城东米粉儿4 小时前
Android VSync 笔记
android
城东米粉儿4 小时前
Android SurfaceFlinger 笔记
android
似霰4 小时前
Android 日志系统5——logd 写日志过程分析二
android·log
hewence14 小时前
Kotlin CoroutineContext 详解
android·开发语言·kotlin
Albert Edison4 小时前
【Python】文件
android·服务器·python