上一篇讲到SoftApManager.IdleState状态中处理CMD_START消息,这个消息用来打开热点功能。我们从这里继续看热点功能基本流程。
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
WifiConfiguration config = (WifiConfiguration) message.obj;
mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
mWifiNativeInterfaceCallback);
int result = startSoftAp((WifiConfiguration) message.obj);
transitionTo(mStartedState);
}
}
这里有两个重要的方法,一个是WifiNative.setupInterfaceForSoftApMode和startSoftAp,现在先看setupInterfaceForSoftApMode的实现。
@WifiNative.java
public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
startHal();
startHostapd()
}
startHal用来启动WIFI hal层代码,对hal进行初始化。将通过HIDL和hal进行通信。
startHal() -> WifiNative.java
startVendorHal() -> WifiVendorHal.java
start() -> HalDeviceManager.java
mWifi = IWifi.getService(true /* retry */);
mWifi.start();
mWifi是hal层中实现的一个服务,现在需要进入到hardware/interfaces/wifi/1.3/default/wifi.h 目录下去查看start()的实现。
@hardware/interfaces/wifi/1.3/default/wifi.cpp
Return Wifi::start(start_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,
&Wifi::startInternal, hidl_status_cb);
}
WifiStatus Wifi::startInternal() {
WifiStatus wifi_status = initializeModeControllerAndLegacyHal();
chip_ = new WifiChip(kChipId, legacy_hal_, mode_controller_,
iface_util_, feature_flags_);
return wifi_status;
}
initializeModeControllerAndLegacyHal()函数中将原始hal层的init函数。legacy_hal_为WifiLegacyHal类的实例。WifiLegacyHal::initialize()方法为:
@hardware/interfaces/wifi/1.3/default/wifi_legacy_hal.cpp
wifi_error WifiLegacyHal::initialize() {
initHalFuncTableWithStubs(&global_func_table_)) ;
wifi_error status = init_wifi_vendor_hal_func_table(&global_func_table_);
}
这里的func_tab在hardware/qcom/wlan/qcwcn/wifi_hal/wifi_hal.cpp中实现,里面有wifi操作的各种方法,通过这个table完成对WIFI的操作。init方法中主要做的事情是操作和驱动程序的nl socket连接,完成通信基础。能和驱动建立连接,hal层就算初始化完成了。
在初始化hal完成后, 实例化了一个WifiChip元素,WifiChip构造方法参数中是一些使用的参数,比如legacy_hal_,可以用来操作WIFI芯片。
到这里,在HalDeviceManager中的mWifi.start()就基本完成。
在WifiNative中,除了有startHal()外,还有startHostapd(),我们看startHostapd实现。
private boolean startAndWaitForHostapdConnection() {
// Start initialization if not already started.
if (!mHostapdHal.isInitializationStarted()
&& !mHostapdHal.initialize()) {
return false;
}
if (!mHostapdHal.startDaemon()) {
Log.e(TAG, "Failed to startup hostapd");
return false;
}
boolean connected = false;
int connectTries = 0;
while (!connected && connectTries++ < CONNECT_TO_HOSTAPD_RETRY_TIMES) {
// Check if the initialization is complete.
connected = mHostapdHal.isInitializationComplete();
if (connected) {
break;
}
try {
Thread.sleep(CONNECT_TO_HOSTAPD_RETRY_INTERVAL_MS);
} catch (InterruptedException ignore) {
}
}
return connected;
这里调用mHostapdHal.startDaemon()来启动hostapd进程。startDaemon()方法,猜测是通过过去一个没有打开的服务,来记过服务。hostapd的服务定义在external/wpa_supplicant_8/hostapd/hostapd.android.rc.
14 service hostapd /vendor/bin/hw/hostapd -dd -g /data/vendor/wifi/hostapd/global
15 interface android.hardware.wifi.hostapd@1.0::IHostapd default
16 interface android.hardware.wifi.hostapd@1.1::IHostapd default
17 class main
18 capabilities NET_ADMIN NET_RAW
19 user wifi
20 group wifi net_raw net_admin
21 disabled
22 oneshot
这样hostapd进程就运行起来。
退回到SoftApManager类的Idle状态,setupInterfaceForSoftApMode()启动wifi hal层和hostapd后,执行startSoftAp()方法。
@SoftApManager.java
private int startSoftAp(WifiConfiguration config) {
WifiConfiguration localConfig = new WifiConfiguration(config);
int result = ApConfigUtil.updateApChannelConfig(
mWifiNative, mCountryCode,
mWifiApConfigStore.getAllowed2GChannel(), localConfig);
mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener);
}
@WifiNative.java
public boolean startSoftAp(
@NonNull String ifaceName, WifiConfiguration config, SoftApListener listener) {
// 注册回调处理方法
mWificondControl.registerApListener(ifaceName, listener);
// 启动热点
mHostapdHal.addVendorAccessPoint(ifaceName, config, listener);
// 设置WIFI版本
WifiInjector.getInstance().getWifiApConfigStore().setWifiGeneration(wifiGeneration);
}
我们看addVendorAccessPoint的实现方法:
@HostapdHal.java
public boolean addVendorAccessPoint(@NonNull String ifaceName, @NonNull WifiConfiguration config, SoftApListener listener) {
// ap配置信息,包括信道信息,ssid,加密方式信息,密码,是否隐藏
// vendorIfaceParams1_1Z在hidl定义,将java中的配置信息设置到hidl结构中
vendorIfaceParams1_1.VendorV1_0 = vendorIfaceParams;
vendorIfaceParams1_1.vendorChannelParams.channelParams = ifaceParams.channelParams;
vendorIfaceParams1_1.vendorEncryptionType = getVendorEncryptionType(config);
vendorIfaceParams1_1.oweTransIfaceName = (config.oweTransIfaceName != null) ? config.oweTransIfaceName : "";
// 根据是否打开acs选择信道
if (mEnableAcs) {
vendorIfaceParams1_1.vendorChannelParams.acsChannelRanges.addAll(mVendorAcsChannelRanges);
}
// 进入到hal中,在hal中将通过打开hostap打开热点,同事将配置信息通过高通sdk设置到驱动
HostapdStatus status =
iHostapdVendorV1_1.addVendorAccessPoint_1_1(vendorIfaceParams1_1, nwParams);
}
addVendorAccessPoint_1_1()函数在hidl中实现,进入到external/wpa_supplicant_8/hostapd/hidl/vendor/1.1/hostapd_vendor.cpp查看其实现。
@hostapd_vendor.cpp
HostapdStatus HostapdVendor::__addVendorAccessPointInternal_1_1(
const VendorIfaceParams& v_iface_params, const NetworkParams& nw_params)
{
// 通过qsap,也就是高通sdk设置参数
const auto conf_file_path =
AddOrUpdateHostapdConfig(v_iface_params, nw_params);
// 通过hostapd来打开热点
hostapd_add_iface(interfaces_, add_iface_param_vec.data());
hostapd_enable_iface(iface_hapd->iface);
}
AddOrUpdateHostapdConfig()通过qsap这个高通sdk进行操作,其主要工作有两个:
void qsap_disassociate_sta(s8 *pVal, s8 *presp, u32 *plen)
{
sock = socket(AF_INET, SOCK_DGRAM, 0);
strlcpy(wrq.ifr_name, pif, sizeof(wrq.ifr_name));
ret = ioctl(sock, QCSAP_IOCTL_DISASSOC_STA, &wrq);
close(sock);
}
我们回到AddOrUpdateHostapdConfig()方法实现如下:
@hostapd_vendor.cpp
constexpr char kQsapSetFmt[] = "softap qccmd set%s %s=%s";
std::string AddOrUpdateHostapdConfig(
const IHostapdVendor::VendorIfaceParams& v_iface_params,
const IHostapd::NetworkParams& nw_params)
{
const std::string ssid_as_string = ss.str();
qsap_cmd(StringPrintf(kQsapSetFmt, dual_mode_str, "ssid2", ssid_as_string.c_str()));
qsap_cmd(StringPrintf(kQsapSetFmt, dual_mode_str, "channel", std::to_string(channelParams.channel).c_str()));
}
...
}
qsap_cmd()在qsap中实现,用来设置指令。
// 将字符串指令拆分
int qsap_cmd(std::string cmd)
{
for(auto& s: tokens) {
if (argc >= max_arg_size) {
wpa_printf(MSG_ERROR, "Command too long");
return -1;
}
data[argc] = strdup(s.c_str());
argc++;
}
run_qsap_cmd(argc, argv);
}
int run_qsap_cmd(int argc, char** argv) {
int ret = 0;
if (argc < 3)
return -1;
if (!strcmp(argv[1], "qccmd")) {
ret = qsap_hostd_exec(argc, argv);
} else if (!strcmp(argv[1], "create") &&
qsap_add_or_remove_interface(argv[2], 1)) {
} else if (!strcmp(argv[1], "remove") &&
qsap_add_or_remove_interface(argv[2], 0)) {
} else if (!strcmp(argv[1], "bridge")) {
ret = qsap_control_bridge(argc, argv);
} else if (!strcmp(argv[1], "setsoftap")) {
ret = qsapsetSoftap(argc, argv);
} else {
ret = -1;
}
return ret;
}