1. 程式人生 > >(九十五)Android O wpa_supplicant.conf配置檔案探究

(九十五)Android O wpa_supplicant.conf配置檔案探究

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: 
[email protected]
@mkdir -p $(dir [email protected]) $(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \ bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > [email protected] ########################

先看一下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

[email protected]:~/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

[email protected]:~/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

執行下看下效果

自己寫個例子試下

2.4 supplicant啟動流程

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;

如下是確保配置檔案存在的邏輯程式碼

  1. 嘗試對/data/misc/wifi/wpa_supplicant.conf其進行讀寫,如果已經有該檔案則返回,不能讀寫的話嘗試改寫讀寫許可權。
  2. 先看下/system/etc/wifi有沒有配置檔案,沒有的話再看下vendor/etc/wifi
  3. 開啟/data/misc/wifi/wpa_supplicant.conf
  4. 將etc/wifi下的配置檔案拷貝到/data/misc/wifi/wpa_supplicant.conf
  5. 修改許可權使用者組
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;
}

到這裡也梳理完了如下兩個配置檔案的由來。

  1. /data/misc/wifi/wpa_supplicant.conf
  2. /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是由/vendor/etc/wifi/wpa_supplicant.conf 拷貝過來的。