参考:https://blog.csdn.net/kris_fei/article/details/73650602
1.wpa_supplicant.conf简介
首先说下源码位置,与wpa_supplicant.conf相关的配置文件、脚本如下红框标注处。
其中wpa_supplicant.conf 不是手机中的wpa_supplicant.conf,打开看一下是wpa_supplicant.conf的配置说明。
##### Example wpa_supplicant configuration file ###############################
#
# This file describes configuration file format and lists all available option.
# Please also take a look at simpler configuration examples in 'examples'
# subdirectory.
#
# Empty lines and lines starting with # are ignored
# NOTE! This file may contain password information and should probably be made
# readable only by root user on multiuser systems.
# Note: All file paths in this configuration file should use full (absolute,
# not relative to working directory) path in order to allow working directory
# to be changed. This can happen if wpa_supplicant is run in the background.
# Whether to allow wpa_supplicant to update (overwrite) configuration
#
# This option can be used to allow wpa_supplicant to overwrite configuration
# file whenever configuration is changed (e.g., new network block is added with
# wpa_cli or wpa_gui, or a password is changed). This is required for
# wpa_cli/wpa_gui to be able to store the configuration changes permanently.
# Please note that overwriting configuration file will remove the comments from
# it.
#update_config=1
简单翻译一下,wpa_supplicant.conf是wpa_supplicant.conf配置文件的一个例子,这个文件描述了配置文件格式以及列举了所有的可选选项。请看一下在examples文件夹下的例子。
请注意文件有可能包含密码信息,所以尽可能地在多用户系统中将这个文件设为root用户只读。
注意,所有这个文件中的路径请用绝对路径,不用用相对路径,只是为了允许工作目录可以改变。在supplicant运行在后台时有可能会发生这个事情。
那顺势看下wpa_supplicant.conf在example的例子吧:
是指这个么
2. 探讨wpa_supplicant.conf由来
2.1 wpa_supplicant_conf.mk
# Include this makefile to generate your hardware specific wpa_supplicant.conf
# Requires: WIFI_DRIVER_SOCKET_IFACE
LOCAL_PATH := $(call my-dir)
########################
include $(CLEAR_VARS)
LOCAL_MODULE := wpa_supplicant.conf
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/wifi
include $(BUILD_SYSTEM)/base_rules.mk
WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf
WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh
$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE)
$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE)
$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT)
$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT)
@echo Target wpa_supplicant.conf: $@
@mkdir -p $(dir $@)
$(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \
bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > $@
########################
先看一下mk文件,模块名直接就是wpa_supplicant.conf,试着make一下
ninja: no work to do.
ninja: no work to do.
wildcard(out/target/product/generic_x86_64/clean_steps.mk) was changed, regenerating...
$(shell uname -rsm) was changed, regenerating...
[526/824] including system/sepolicy/Android.mk ...
system/sepolicy/Android.mk:79: warning: BOARD_SEPOLICY_VERS not specified, assuming current platform version
[824/824] including tools/tradefederation/core/Android.mk ...
[ 93% 14/15] glob tools/metalava/src/main/java/**/*.kt
ninja: error: unknown target 'wpa_supplicant.conf'
10:28:42 ninja failed with: exit status 1
#### failed to build some targets (02:08 (mm:ss)) ####
emmm,本地编译有点问题,编译其他的是可以的,待续
还是回头来看下mk文件,其中用到了两个其他的文件,分别是wpa_supplicant_template.conf 和 wpa_supplicant_conf.sh
脚本+文件最后生成在$(TARGET_OUT_VENDOR)/etc/wifi目录下的wpa_supplicant.conf
LOCAL_MODULE := wpa_supplicant.conf
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/wifi
可以看手机的vendor/etc/wifi下看到该文件
另外在/data/misc/wifi下也有该文件
2.2 wpa_supplicant_template.conf
jiatai@jiatai:~/expand/aosp/aosp/external/wpa_supplicant_8/wpa_supplicant$ cat wpa_supplicant_template.conf
##### wpa_supplicant configuration file template #####
update_config=1
eapol_version=1
ap_scan=1
fast_reauth=1
pmf=1
p2p_add_cli_chan=1
2.3 wpa_supplicant_conf.sh
jiatai@jiatai:~/expand/aosp/aosp/external/wpa_supplicant_8/wpa_supplicant$ cat wpa_supplicant_conf.sh
#!/bin/bash
#
# Copyright (C) 2010 The Android Open Source Project
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
#
# Generate a wpa_supplicant.conf from the template.
# $1: the template file name
if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ]
then
sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/"
else
sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1
fi
学习一下sed命令的功能。
sed
作用:主要用于替换指定的字符;查找或替换指定字符串时,必须把字符串用//来注释下,比如root 必须是/root/;
sed 只要不适用-i参数,一般都是在输出终端上显示而已,无法更改源文件;
参数-e: --expression,多重编辑;
参数-n:不带-n则列出文件所有内容,加上-n只列出结果sed特殊处理的那一行;
参数-i:直接修改读取的内容文件,而不是输出到终端;
功能s:替换、取代;
功能d:删除;
功能a:新增;
如上命令应该是去除注释、去除空格、去除空行,替换wlan0为WIFI_DRIVER_SOCKET_IFACE
执行下看下效果
自己写个例子试下
空格、tab键、空格+tab键的单独行,末尾不要加字符也试了下,会被删除。
2.4 supplicant启动流程
回头在看下五十九)Android O WiFi启动流程梳理续——enableSupplicant 启动流程中与wpa_supplicant_conf的关系。
bool SupplicantManager::StartSupplicant() {
char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
int count = 200; /* wait at most 20 seconds for completion */
const prop_info* pi;
unsigned serial = 0;
/* Check whether already running */
if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
strcmp(supp_status, "running") == 0) {
return true;
}
/* Before starting the daemon, make sure its config file exists */
if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
LOG(ERROR) << "Wi-Fi will not be enabled";
return false;
}
/*
* Some devices have another configuration file for the p2p interface.
* However, not all devices have this, and we'll let it slide if it
* is missing. For devices that do expect this file to exist,
* supplicant will refuse to start and emit a good error message.
* No need to check for it here.
*/
(void)ensure_config_file_exists(kP2pConfigFile);
/*
* Get a reference to the status property, so we can distinguish
* the case where it goes stopped => running => stopped (i.e.,
* it start up, but fails right away) from the case in which
* it starts in the stopped state and never manages to start
* running at all.
*/
pi = __system_property_find(kSupplicantInitProperty);
if (pi != NULL) {
serial = __system_property_serial(pi);
}
property_set("ctl.start", kSupplicantServiceName);
sched_yield();
while (count-- > 0) {
if (pi == NULL) {
pi = __system_property_find(kSupplicantInitProperty);
}
if (pi != NULL) {
/*
* property serial updated means that init process is scheduled
* after we sched_yield, further property status checking is based on this
*/
if (__system_property_serial(pi) != serial) {
__system_property_read(pi, NULL, supp_status);
if (strcmp(supp_status, "running") == 0) {
return true;
} else if (strcmp(supp_status, "stopped") == 0) {
return false;
}
}
}
usleep(100000);
}
return false;
}
关注下如下代码
/* Before starting the daemon, make sure its config file exists */
if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
LOG(ERROR) << "Wi-Fi will not be enabled";
return false;
}
看下对应常量声明
const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
const char kSupplicantConfigTemplatePath[] =
"/etc/wifi/wpa_supplicant.conf";
const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
const char kSupplicantServiceName[] = "wpa_supplicant";
constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
如下是确保配置文件存在的逻辑代码
- 尝试对/data/misc/wifi/wpa_supplicant.conf其进行读写,如果已经有该文件则返回,不能读写的话尝试改写读写权限。
- 先看下/system/etc/wifi有没有配置文件,没有的话再看下vendor/etc/wifi
- 打开/data/misc/wifi/wpa_supplicant.conf
- 将etc/wifi下的配置文件拷贝到/data/misc/wifi/wpa_supplicant.conf
- 修改权限用户组
int ensure_config_file_exists(const char* config_file) {
char buf[2048];
int srcfd, destfd;
int nread;
int ret;
std::string templatePath;
ret = access(config_file, R_OK | W_OK);
if ((ret == 0) || (errno == EACCES)) {
if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
<< strerror(errno);
return false;
}
return true;
} else if (errno != ENOENT) {
LOG(ERROR) << "Cannot access \"" << config_file << "\": "
<< strerror(errno);
return false;
}
std::string configPathSystem =
std::string("/system") + std::string(kSupplicantConfigTemplatePath);
std::string configPathVendor =
std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
templatePath = configPathSystem;
if (srcfd < 0) {
int errnoSystem = errno;
srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
templatePath = configPathVendor;
if (srcfd < 0) {
int errnoVendor = errno;
LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
<< strerror(errnoSystem);
LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
<< strerror(errnoVendor);
return false;
}
}
destfd = TEMP_FAILURE_RETRY(open(config_file,
O_CREAT | O_RDWR,
kConfigFileMode));
if (destfd < 0) {
close(srcfd);
LOG(ERROR) << "Cannot create \"" << config_file << "\": "
<< strerror(errno);
return false;
}
while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
if (nread < 0) {
LOG(ERROR) << "Error reading \"" << templatePath
<< "\": " << strerror(errno);
close(srcfd);
close(destfd);
unlink(config_file);
return false;
}
TEMP_FAILURE_RETRY(write(destfd, buf, nread));
}
close(destfd);
close(srcfd);
/* chmod is needed because open() didn't set permisions properly */
if (chmod(config_file, kConfigFileMode) < 0) {
LOG(ERROR) << "Error changing permissions of " << config_file
<< " to 0660: " << strerror(errno);
unlink(config_file);
return false;
}
return true;
}
到这里也梳理完了如下两个配置文件的由来。
- /data/misc/wifi/wpa_supplicant.conf
- /vendor/etc/wifi/wpa_supplicant.conf
3. 总结
/vendor/etc/wifi/wpa_supplicant.conf 这个配置文件是由wpa_supplicant下的wpa_supplicant_template.conf作简单处理生成的。
/data/misc/wifi/wpa_supplicant.conf是在supplicant第一次启动的时候由/vendor/etc/wifi/wpa_supplicant.conf 拷贝过来的。