原址

WifiConfigStore在Android的无线网络部分,主要负责网络配置信息的管理工作,包括保存、读取配置信息等。当我们在Settings中触发一个保存网络、连接网络或者auto_connect自动重连动作时,都会调用到WifiConfigStore中的方法。

[java] view plain copy
  1. public class WifiConfigStore extends IpConfigStore

WifiConfigStore继承自IpConfigStore,它提供了一套API去管理用户配置过的网络。下面介绍一些framework中经常调用到的API接口。

一、saveNetwork()、selectNetwork()

WifiStateMachine中,WifiConfigStore对象的创建发生在其构造函数中:

[java] view plain copy
  1. mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);

我们传入了Context、当前的WifiStateMachine对象和一个WifiNative对象。通过mWifiNative对象可以向wpa_s下发一系列连接、选择的命令。

我们在连接一个网络的时候,会先保存该网络的配置信息,调用:

[java] view plain copy
  1. /**
  2. * Add/update the specified configuration and save config
  3. *
  4. * @param config WifiConfiguration to be saved
  5. * @return network update result
  6. */
  7. NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
  8. WifiConfiguration conf;
  9. // A new network cannot have null SSID
  10. if (config == null || (config.networkId == INVALID_NETWORK_ID &&
  11. config.SSID == null)) {
  12. return new NetworkUpdateResult(INVALID_NETWORK_ID);
  13. }
  14. if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
  15. if (VDBG) {
  16. loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
  17. + " SSID=" + config.SSID
  18. + " Uid=" + Integer.toString(config.creatorUid)
  19. + "/" + Integer.toString(config.lastUpdateUid));
  20. }
  21. if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
  22. if (VDBG) {
  23. loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);
  24. }
  25. // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
  26. // below, since we're creating/modifying a config.
  27. }
  28. boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
  29. NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
  30. int netId = result.getNetworkId();
  31. if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);
  32. /* enable a new network */
  33. if (newNetwork && netId != INVALID_NETWORK_ID) {
  34. if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);
  35. mWifiNative.enableNetwork(netId, false);
  36. conf = mConfiguredNetworks.get(netId);
  37. if (conf != null)
  38. conf.status = Status.ENABLED;
  39. }
  40. conf = mConfiguredNetworks.get(netId);
  41. if (conf != null) {
  42. if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
  43. if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);
  44. // reenable autojoin, since new information has been provided
  45. conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
  46. enableNetworkWithoutBroadcast(conf.networkId, false);
  47. }
  48. if (VDBG) {
  49. loge("WifiConfigStore: saveNetwork got config back netId="
  50. + Integer.toString(netId)
  51. + " uid=" + Integer.toString(config.creatorUid));
  52. }
  53. }
  54. mWifiNative.saveConfig();
  55. sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?
  56. WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
  57. return result;
  58. }

saveNetwork()主要负责根据WifiConfiguration对象更新、保存网络的各配置信息;WifiConfiguration代表一个配置过的网络,主要包括该网络的加密方式、SSID、密钥等等信息。重要的一个操作是调用addOrUpdateNetworkNative()来更新配置信息、并保存到本地;该函数的函数实现虽然较多,看起来复杂,但实际处理却还是较为简单的:

  1. 首先从mConfiguredNetworks中根据传入的config对象获取到先前保存过的同netId的savedConfig对象;mConfiguredNetworks是一个HasMap结构,它以某个网络的netId为key,以对应的WifiConfiguration对象作为value,由此可知它以键值对的形式保存了当前所有配置过的网络信息。后续的操作都是比对config和savedConfig直接的差异,保存到wpa_s配置文件中并进行更新,最后再将更新过的WifiConfiguration对象保存到mConfiguredNetworks中。
  2. 调用writeIpAndProxyConfigurationsOnChange()将新的配置信息保存到本地文件/misc/wifi/ipconfig.txt中。后面会说到,当我们重新打开Wifi时,会从该文件中读取我们所配置过的网络信息,并进行重连。
[java] view plain copy
  1. /* Compare current and new configuration and write to file on change */
  2. private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
  3. WifiConfiguration currentConfig,
  4. WifiConfiguration newConfig) {
  5. boolean ipChanged = false;
  6. boolean proxyChanged = false;
  7. if (VDBG) {
  8. loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +
  9. newConfig.SSID + " path: " + ipConfigFile);
  10. }
  11. switch (newConfig.getIpAssignment()) {
  12. case STATIC:
  13. if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
  14. ipChanged = true;
  15. } else {
  16. ipChanged = !Objects.equals(
  17. currentConfig.getStaticIpConfiguration(),
  18. newConfig.getStaticIpConfiguration());
  19. }
  20. break;
  21. case DHCP:
  22. if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
  23. ipChanged = true;
  24. }
  25. break;
  26. case UNASSIGNED:
  27. /* Ignore */
  28. break;
  29. default:
  30. loge("Ignore invalid ip assignment during write");
  31. break;
  32. }
  33. switch (newConfig.getProxySettings()) {
  34. case STATIC:
  35. case PAC:
  36. ProxyInfo newHttpProxy = newConfig.getHttpProxy();
  37. ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
  38. if (newHttpProxy != null) {
  39. proxyChanged = !newHttpProxy.equals(currentHttpProxy);
  40. } else {
  41. proxyChanged = (currentHttpProxy != null);
  42. }
  43. break;
  44. case NONE:
  45. if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
  46. proxyChanged = true;
  47. }
  48. break;
  49. case UNASSIGNED:
  50. /* Ignore */
  51. break;
  52. default:
  53. loge("Ignore invalid proxy configuration during write");
  54. break;
  55. }
  56. if (ipChanged) {
  57. currentConfig.setIpAssignment(newConfig.getIpAssignment());
  58. currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
  59. log("IP config changed SSID = " + currentConfig.SSID);
  60. if (currentConfig.getStaticIpConfiguration() != null) {
  61. log(" static configuration: " +
  62. currentConfig.getStaticIpConfiguration().toString());
  63. }
  64. }
  65. if (proxyChanged) {
  66. currentConfig.setProxySettings(newConfig.getProxySettings());
  67. currentConfig.setHttpProxy(newConfig.getHttpProxy());
  68. log("proxy changed SSID = " + currentConfig.SSID);
  69. if (currentConfig.getHttpProxy() != null) {
  70. log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
  71. }
  72. }
  73. if (ipChanged || proxyChanged) {
  74. writeIpAndProxyConfigurations();
  75. sendConfiguredNetworksChangedBroadcast(currentConfig,
  76. WifiManager.CHANGE_REASON_CONFIG_CHANGE);
  77. }
  78. return new NetworkUpdateResult(ipChanged, proxyChanged);
  79. }

函数中涉及到IpAssignment和ProxySettings两个枚举类型:

[java] view plain copy
  1. public enum IpAssignment {
  2. /* Use statically configured IP settings. Configuration can be accessed
  3. * with staticIpConfiguration */
  4. STATIC,
  5. /* Use dynamically configured IP settigns */
  6. DHCP,
  7. /* no IP details are assigned, this is used to indicate
  8. * that any existing IP settings should be retained */
  9. UNASSIGNED
  10. }
  11. public enum ProxySettings {
  12. /* No proxy is to be used. Any existing proxy settings
  13. * should be cleared. */
  14. NONE,
  15. /* Use statically configured proxy. Configuration can be accessed
  16. * with httpProxy. */
  17. STATIC,
  18. /* no proxy details are assigned, this is used to indicate
  19. * that any existing proxy settings should be retained */
  20. UNASSIGNED,
  21. /* Use a Pac based proxy.
  22. */
  23. PAC
  24. }

IpAssignment代表当前获取IP使用的方式,我们可以根据自己的需求在里面添加自定义的方式,比如PPPoE;同理,ProxySettings表示当前网络使用的代理方式。

IpAssignment类型的值一般由设置根据用户选择的IP模式来赋值,并传递给framework,以让底层可以知道该使用什么方式去获取IP地址。例如,如果用户选择Static IP,则在WifiStateMachine::ObtainingIpState中会有:

[java] view plain copy
  1. if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
  2. if (isRoaming()) {
  3. renewDhcp();
  4. } else {
  5. // Remove any IP address on the interface in case we're switching from static
  6. // IP configuration to DHCP. This is safe because if we get here when not
  7. // roaming, we don't have a usable address.
  8. clearIPv4Address(mInterfaceName);
  9. startDhcp();
  10. }
  11. obtainingIpWatchdogCount++;
  12. logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
  13. // Get Link layer stats so as we get fresh tx packet counters
  14. getWifiLinkLayerStats(true);
  15. sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
  16. obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
  17. } else {
  18. // stop any running dhcp before assigning static IP
  19. stopDhcp();
  20. StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
  21. mLastNetworkId);
  22. if (config.ipAddress == null) {
  23. logd("Static IP lacks address");
  24. sendMessage(CMD_STATIC_IP_FAILURE);
  25. } else {
  26. InterfaceConfiguration ifcg = new InterfaceConfiguration();
  27. ifcg.setLinkAddress(config.ipAddress);
  28. ifcg.setInterfaceUp();
  29. try {
  30. mNwService.setInterfaceConfig(mInterfaceName, ifcg);
  31. if (DBG) log("Static IP configuration succeeded");
  32. DhcpResults dhcpResults = new DhcpResults(config);
  33. sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
  34. } catch (RemoteException re) {
  35. loge("Static IP configuration failed: " + re);
  36. sendMessage(CMD_STATIC_IP_FAILURE);
  37. } catch (IllegalStateException e) {
  38. loge("Static IP configuration failed: " + e);
  39. sendMessage(CMD_STATIC_IP_FAILURE);
  40. }
  41. }
  42. }

通过WifiConfigStore.isUsingStaticIp(mLastNetworkId)方法获知当前用户使用的获取IP地址类型,具体方法定义:

[java] view plain copy
  1. /**
  2. * Return if the specified network is using static IP
  3. * @param netId id
  4. * @return {@code true} if using static ip for netId
  5. */
  6. boolean isUsingStaticIp(int netId) {
  7. WifiConfiguration config = mConfiguredNetworks.get(netId);
  8. if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
  9. return true;
  10. }
  11. return false;
  12. }

根据传入的netId,从mConfiguredNetworks集合中获取对应网络的WifiConfiguration对象,再获取该对象配置的IpAssignment值,来区分不用的网络方式,进而控制流程走不同的分支。如果我们有加入别的方式,可以仿照这个原生例子,写出自己的程序。

riteIpAndProxyConfigurationsOnChange()中会根据IpAssignment、ProxySettings的类型是否改变,去更新currentConfig对象,并writeIpAndProxyConfigurations()方法写入到本地磁盘文件:

[java] view plain copy
  1. private void writeIpAndProxyConfigurations() {
  2. final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
  3. for(WifiConfiguration config : mConfiguredNetworks.values()) {
  4. if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {
  5. networks.put(configKey(config), config.getIpConfiguration());
  6. }
  7. }
  8. super.writeIpAndProxyConfigurations(ipConfigFile, networks);//in IpConfigStore
  9. }
[java] view plain copy
  1. public void IpConfigStore::writeIpAndProxyConfigurations(String filePath,
  2. final SparseArray<IpConfiguration> networks) {
  3. mWriter.write(filePath, new DelayedDiskWrite.Writer() {
  4. public void onWriteCalled(DataOutputStream out) throws IOException{
  5. out.writeInt(IPCONFIG_FILE_VERSION);
  6. for(int i = 0; i < networks.size(); i++) {
  7. writeConfig(out, networks.keyAt(i), networks.valueAt(i));
  8. }
  9. }
  10. });
  11. }
[java] view plain copy
  1. private boolean writeConfig(DataOutputStream out, int configKey,
  2. IpConfiguration config) throws IOException {
  3. boolean written = false;
  4. try {
  5. switch (config.ipAssignment) {
  6. case STATIC:
  7. out.writeUTF(IP_ASSIGNMENT_KEY);
  8. out.writeUTF(config.ipAssignment.toString());
  9. StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;
  10. if (staticIpConfiguration != null) {
  11. if (staticIpConfiguration.ipAddress != null) {
  12. LinkAddress ipAddress = staticIpConfiguration.ipAddress;
  13. out.writeUTF(LINK_ADDRESS_KEY);
  14. out.writeUTF(ipAddress.getAddress().getHostAddress());
  15. out.writeInt(ipAddress.getPrefixLength());
  16. }
  17. if (staticIpConfiguration.gateway != null) {
  18. out.writeUTF(GATEWAY_KEY);
  19. out.writeInt(0);  // Default route.
  20. out.writeInt(1);  // Have a gateway.
  21. out.writeUTF(staticIpConfiguration.gateway.getHostAddress());
  22. }
  23. for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
  24. out.writeUTF(DNS_KEY);
  25. out.writeUTF(inetAddr.getHostAddress());
  26. }
  27. }
  28. written = true;
  29. break;
  30. case DHCP:
  31. out.writeUTF(IP_ASSIGNMENT_KEY);
  32. out.writeUTF(config.ipAssignment.toString());
  33. written = true;
  34. break;
  35. case UNASSIGNED:
  36. /* Ignore */
  37. break;
  38. default:
  39. loge("Ignore invalid ip assignment while writing");
  40. break;
  41. }
  42. switch (config.proxySettings) {
  43. case STATIC:
  44. ProxyInfo proxyProperties = config.httpProxy;
  45. String exclusionList = proxyProperties.getExclusionListAsString();
  46. out.writeUTF(PROXY_SETTINGS_KEY);
  47. out.writeUTF(config.proxySettings.toString());
  48. out.writeUTF(PROXY_HOST_KEY);
  49. out.writeUTF(proxyProperties.getHost());
  50. out.writeUTF(PROXY_PORT_KEY);
  51. out.writeInt(proxyProperties.getPort());
  52. if (exclusionList != null) {
  53. out.writeUTF(EXCLUSION_LIST_KEY);
  54. out.writeUTF(exclusionList);
  55. }
  56. written = true;
  57. break;
  58. case PAC:
  59. ProxyInfo proxyPacProperties = config.httpProxy;
  60. out.writeUTF(PROXY_SETTINGS_KEY);
  61. out.writeUTF(config.proxySettings.toString());
  62. out.writeUTF(PROXY_PAC_FILE);
  63. out.writeUTF(proxyPacProperties.getPacFileUrl().toString());
  64. written = true;
  65. break;
  66. case NONE:
  67. out.writeUTF(PROXY_SETTINGS_KEY);
  68. out.writeUTF(config.proxySettings.toString());
  69. written = true;
  70. break;
  71. case UNASSIGNED:
  72. /* Ignore */
  73. break;
  74. default:
  75. loge("Ignore invalid proxy settings while writing");
  76. break;
  77. }
  78. if (written) {
  79. out.writeUTF(ID_KEY);
  80. out.writeInt(configKey);
  81. }
  82. } catch (NullPointerException e) {
  83. loge("Failure in writing " + config + e);
  84. }
  85. out.writeUTF(EOS);
  86. return written;
  87. }

最终写入文件的操作是父类IpConfigStore::writeConfig()方法处理的。在WifiConfigStore::writeIpAndProxyConfigurations()中,我们会将所有保存的网络配置信息从mConfiguredNetworks集合中取出,重新按照<一个唯一int值,IpConfiguration>的形式保存到一个SparseArray<IpConfiguration> networks对象中(可以看做是一个集合);ipConfigFile的值就是路径:"/misc/wifi/ipconfig.txt"。

在IpConfigStore::writeIpAndProxyConfigurations和IpConfigStore::writeConfig()中,我们会遍历networks集合,并按照

[java] view plain copy
  1. switch (config.ipAssignment)
  2. switch (config.proxySettings)

的分类,将信息写入ipconfig.txt文件中;这里的写入也是有一定的规则的,每一个标签后面跟一个该标签对应的值。这样做方法后面的数据读取。定义的标签值有:

[java] view plain copy
  1. /* IP and proxy configuration keys */
  2. protected static final String ID_KEY = "id";
  3. protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";
  4. protected static final String LINK_ADDRESS_KEY = "linkAddress";
  5. protected static final String GATEWAY_KEY = "gateway";
  6. protected static final String DNS_KEY = "dns";
  7. protected static final String PROXY_SETTINGS_KEY = "proxySettings";
  8. protected static final String PROXY_HOST_KEY = "proxyHost";
  9. protected static final String PROXY_PORT_KEY = "proxyPort";
  10. protected static final String PROXY_PAC_FILE = "proxyPac";
  11. protected static final String EXCLUSION_LIST_KEY = "exclusionList";
  12. protected static final String EOS = "eos";
  13. protected static final int IPCONFIG_FILE_VERSION = 2;

从这里我们可以看到一些可以定制的地方。现在,有一部分Android手机上的Wifi功能是支持无线PPPoE的;要使用PPPoE,就要用到账户信息;此时,我们是否可以在WifiConfiguration或IpConfiguration中添加对应的账户属性字段,在保存网络时,加入对账户密码字段的写入保存动作;同时,在从ipconfig.txt读取信息时,将该信息重新封装到WifiConfiguration或IpConfiguration对象中,供无线PPPoE获取IP时使用。

最后还会涉及到writeKnownNetworkHistory()的调用,它会向/misc/wifi/networkHistory.txt中写入每个WifiConfiguration对象中的一些字段值,包括优先级、SSID等等;写入方式跟前面相同。这里,saveNetwork()的处理就结束了。
selectNetwork()的作用是选择一个特定的网络去准备连接,这里会涉及到网络优先级更新和enable网络的部分。
[java] view plain copy
  1. /**
  2. * Selects the specified network for connection. This involves
  3. * updating the priority of all the networks and enabling the given
  4. * network while disabling others.
  5. *
  6. * Selecting a network will leave the other networks disabled and
  7. * a call to enableAllNetworks() needs to be issued upon a connection
  8. * or a failure event from supplicant
  9. *
  10. * @param config network to select for connection
  11. * @param updatePriorities makes config highest priority network
  12. * @return false if the network id is invalid
  13. */
  14. boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
  15. if (VDBG) localLog("selectNetwork", config.networkId);
  16. if (config.networkId == INVALID_NETWORK_ID) return false;
  17. // Reset the priority of each network at start or if it goes too high.
  18. if (mLastPriority == -1 || mLastPriority > 1000000) {
  19. for(WifiConfiguration config2 : mConfiguredNetworks.values()) {
  20. if (updatePriorities) {
  21. if (config2.networkId != INVALID_NETWORK_ID) {
  22. config2.priority = 0;
  23. setNetworkPriorityNative(config2.networkId, config.priority);
  24. }
  25. }
  26. }
  27. mLastPriority = 0;
  28. }
  29. // Set to the highest priority and save the configuration.
  30. if (updatePriorities) {
  31. config.priority = ++mLastPriority;
  32. setNetworkPriorityNative(config.networkId, config.priority);
  33. buildPnoList();
  34. }
  35. if (config.isPasspoint()) {
  36. /* need to slap on the SSID of selected bssid to work */
  37. if (getScanDetailCache(config).size() != 0) {
  38. ScanDetail result = getScanDetailCache(config).getFirst();
  39. if (result == null) {
  40. loge("Could not find scan result for " + config.BSSID);
  41. } else {
  42. log("Setting SSID for " + config.networkId + " to" + result.getSSID());
  43. setSSIDNative(config.networkId, result.getSSID());
  44. config.SSID = result.getSSID();
  45. }
  46. } else {
  47. loge("Could not find bssid for " + config);
  48. }
  49. }
  50. if (updatePriorities)
  51. mWifiNative.saveConfig();
  52. else
  53. mWifiNative.selectNetwork(config.networkId);
  54. updateLastConnectUid(config, uid);
  55. writeKnownNetworkHistory(false);
  56. /* Enable the given network while disabling all other networks */
  57. enableNetworkWithoutBroadcast(config.networkId, true);
  58. /* Avoid saving the config & sending a broadcast to prevent settings
  59. * from displaying a disabled list of networks */
  60. return true;
  61. }

mLastPriority是一个int类型的整数值,它代表当前网络中的优先级的最大值。越是最近连接过的网络,它的priority优先级值就越大。updatePriorities代表是否需要更新优先级。当当前的最大优先级值为-1或1000000时,都会重新设置mLastPriority值;如果updatePriorities为true,也会将更改更新到wpa_s.conf文件中。

[java] view plain copy
  1. // Set to the highest priority and save the configuration.
  2. if (updatePriorities) {
  3. config.priority = ++mLastPriority;
  4. setNetworkPriorityNative(config.networkId, config.priority);
  5. buildPnoList();
  6. }

从这可以看出,每个当前正在连接的网络,都具有最高的优先级。最后enableNetworkWithoutBroadcast()中,会在mConfiguredNetworks将选中网络的status属性设为Status.ENABLED,其他的设置为Status.DISABLED:

[java] view plain copy
  1. /* Mark all networks except specified netId as disabled */
  2. private void markAllNetworksDisabledExcept(int netId) {
  3. for(WifiConfiguration config : mConfiguredNetworks.values()) {
  4. if(config != null && config.networkId != netId) {
  5. if (config.status != Status.DISABLED) {
  6. config.status = Status.DISABLED;
  7. config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
  8. }
  9. }
  10. }
  11. <p>    }</p>

二、重新打开Wifi时,ipconfig.txt文件的读取

当我们重新打开Wifi时,Wifi正常情况下都会有网络自动重连的动作。此时,WifiStateMachine中:

[java] view plain copy
  1. mWifiConfigStore.loadAndEnableAllNetworks();
[java] view plain copy
  1. /**
  2. * Fetch the list of configured networks
  3. * and enable all stored networks in supplicant.
  4. */
  5. void loadAndEnableAllNetworks() {
  6. if (DBG) log("Loading config and enabling all networks ");
  7. loadConfiguredNetworks();
  8. enableAllNetworks();
  9. }

看loadConfiguredNetworks():

[java] view plain copy
  1. void loadConfiguredNetworks() {
  2. mLastPriority = 0;
  3. mConfiguredNetworks.clear();
  4. int last_id = -1;
  5. boolean done = false;
  6. while (!done) {
  7. String listStr = mWifiNative.listNetworks(last_id);
  8. if (listStr == null)
  9. return;
  10. String[] lines = listStr.split("\n");
  11. if (showNetworks) {
  12. localLog("WifiConfigStore: loadConfiguredNetworks:  ");
  13. for (String net : lines) {
  14. localLog(net);
  15. }
  16. }
  17. // Skip the first line, which is a header
  18. for (int i = 1; i < lines.length; i++) {
  19. String[] result = lines[i].split("\t");
  20. // network-id | ssid | bssid | flags
  21. WifiConfiguration config = new WifiConfiguration();
  22. try {
  23. config.networkId = Integer.parseInt(result[0]);
  24. last_id = config.networkId;
  25. } catch(NumberFormatException e) {
  26. loge("Failed to read network-id '" + result[0] + "'");
  27. continue;
  28. }
  29. if (result.length > 3) {
  30. if (result[3].indexOf("[CURRENT]") != -1)
  31. config.status = WifiConfiguration.Status.CURRENT;
  32. else if (result[3].indexOf("[DISABLED]") != -1)
  33. config.status = WifiConfiguration.Status.DISABLED;
  34. else
  35. config.status = WifiConfiguration.Status.ENABLED;
  36. } else {
  37. config.status = WifiConfiguration.Status.ENABLED;
  38. }
  39. readNetworkVariables(config);
  40. Checksum csum = new CRC32();
  41. if (config.SSID != null) {
  42. csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);
  43. long d = csum.getValue();
  44. if (mDeletedSSIDs.contains(d)) {
  45. loge(" got CRC for SSID " + config.SSID + " -> " + d + ", was deleted");
  46. }
  47. }
  48. if (config.priority > mLastPriority) {
  49. mLastPriority = config.priority;
  50. }
  51. config.setIpAssignment(IpAssignment.DHCP);//默认设置DHCP
  52. config.setProxySettings(ProxySettings.NONE);//默认设置NONE
  53. if (mConfiguredNetworks.getByConfigKey(config.configKey()) != null) {
  54. // That SSID is already known, just ignore this duplicate entry
  55. if (showNetworks) localLog("discarded duplicate network ", config.networkId);
  56. } else if(WifiServiceImpl.isValid(config)){
  57. mConfiguredNetworks.put(config.networkId, config);
  58. if (showNetworks) localLog("loaded configured network", config.networkId);
  59. } else {
  60. if (showNetworks) log("Ignoring loaded configured for network " + config.networkId
  61. + " because config are not valid");
  62. }
  63. }
  64. done = (lines.length == 1);
  65. }
  66. readPasspointConfig();
  67. readIpAndProxyConfigurations();//读取ipconfig.txt
  68. readNetworkHistory();//读取networkHistory.txt
  69. readAutoJoinConfig();
  70. buildPnoList();
  71. sendConfiguredNetworksChangedBroadcast();
  72. if (showNetworks) localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.size() + " networks");
  73. if (mConfiguredNetworks.isEmpty()) {
  74. // no networks? Lets log if the file contents
  75. logKernelTime();
  76. logContents(SUPPLICANT_CONFIG_FILE);
  77. logContents(SUPPLICANT_CONFIG_FILE_BACKUP);
  78. logContents(networkHistoryConfigFile);
  79. }
  80. }

函数开始就会清空mConfiguredNetworks集合:

  1. 从wp_s读取保存的网络配置列表,并保存到mConfiguredNetworks中
  2. 调用readIpAndProxyConfigurations()方法,从ipconfig.txt中读取保存的IpConfiguration对象,更新到mConfiguredNetworks保存的各WifiConfiguration对象中
  3. 调用mConfiguredNetworks()方法,从/misc/wifi/networkHistory.txt文件中读取保存的信息,更新到mConfiguredNetworks保存的各WifiConfiguration对象中
读取的方式跟前面介绍的写入的方式基本相似。经过上所述的两次读取操作,我们持有的WifiConfiguration对象的信息就是比较完整的了。
如果有我们前面说过的无线PPPoE的场景,readIpAndProxyConfigurations()方法中就会把我们事先写入的账号密码信息也读取出来,存到mConfiguredNetworks中。走auto_connect流程时,获取到最近一次连接的网络netId,从mConfiguredNetworks中取出的对应的WifiConfiguration对象中就保存有PPPoE的账号密码,这样我们在PPPoE获取IP时,就有可用的账户信息了。

PS:

WifiConfigStore中定义了一个比较有意义的默认变量值:

[java] view plain copy
  1. /**
  2. * The maximum number of times we will retry a connection to an access point
  3. * for which we have failed in acquiring an IP address from DHCP. A value of
  4. * N means that we will make N+1 connection attempts in all.
  5. * <p>
  6. * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
  7. * value if a Settings value is not present.
  8. */
  9. private static final int DEFAULT_MAX_DHCP_RETRIES = 9;

从原生注释中,我们得知这个变量控制了当一个网络获取IP失败时,之后会继续重试的次数;如果值定义为9,那么实际的重连次数将是9+1,为10。

如果我们有定制这个值,那么重连次数将以我们自定义配置的值为准:

[java] view plain copy
  1. int WifiConfigStore::getMaxDhcpRetries() {
  2. return Settings.Global.getInt(mContext.getContentResolver(),
  3. Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
  4. DEFAULT_MAX_DHCP_RETRIES);
  5. }

我们可以配置Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT这个字段值来定制这部分:

[java] view plain copy
  1. /**
  2. * The maximum number of times we will retry a connection to an access
  3. * point for which we have failed in acquiring an IP address from DHCP.
  4. * A value of N means that we will make N+1 connection attempts in all.
  5. */
  6. public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";

getMaxDhcpRetries()在WifiConfigStore中只在handleSSIDStateChange()函数中有使用:

[java] view plain copy
  1. void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {
  2. WifiConfiguration config = mConfiguredNetworks.get(netId);
  3. if (config != null) {
  4. if (enabled) {
  5. loge("Ignoring SSID re-enabled from supplicant:  " + config.configKey() +
  6. " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
  7. + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
  8. //We should not re-enable the BSSID based on Supplicant reanable.
  9. // Framework will re-enable it after its own blacklist timer expires
  10. } else {
  11. loge("SSID temp disabled for  " + config.configKey() +
  12. " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
  13. + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
  14. if (message != null) {
  15. loge(" message=" + message);
  16. }
  17. if (config.selfAdded && config.lastConnected == 0) {
  18. // This is a network we self added, and we never succeeded,
  19. // the user did not create this network and never entered its credentials,
  20. // so we want to be very aggressive in disabling it completely.
  21. removeConfigAndSendBroadcastIfNeeded(config.networkId);
  22. } else {
  23. if (message != null) {
  24. if (message.contains("no identity")) {
  25. config.setAutoJoinStatus(
  26. WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS);
  27. if (DBG) {
  28. loge("no identity blacklisted " + config.configKey() + " to "
  29. + Integer.toString(config.autoJoinStatus));
  30. }
  31. } else if (message.contains("WRONG_KEY")
  32. || message.contains("AUTH_FAILED")) {
  33. // This configuration has received an auth failure, so disable it
  34. // temporarily because we don't want auto-join to try it out.
  35. // this network may be re-enabled by the "usual"
  36. // enableAllNetwork function
  37. config.numAuthFailures++;
  38. if (config.numAuthFailures > maxAuthErrorsToBlacklist) {
  39. config.setAutoJoinStatus
  40. (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
  41. disableNetwork(netId,
  42. WifiConfiguration.DISABLED_AUTH_FAILURE);
  43. loge("Authentication failure, blacklist " + config.configKey() + " "
  44. + Integer.toString(config.networkId)
  45. + " num failures " + config.numAuthFailures);
  46. }
  47. } else if (message.contains("DHCP FAILURE")) {
  48. config.numIpConfigFailures++;
  49. config.lastConnectionFailure = System.currentTimeMillis();
  50. int maxRetries = getMaxDhcpRetries();
  51. // maxRetries == 0 means keep trying forever
  52. if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {
  53. /**
  54. * If we've exceeded the maximum number of retries for DHCP
  55. * to a given network, disable the network
  56. */
  57. config.setAutoJoinStatus
  58. (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
  59. disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);
  60. loge("DHCP failure, blacklist " + config.configKey() + " "
  61. + Integer.toString(config.networkId)
  62. + " num failures " + config.numIpConfigFailures);
  63. }
  64. // Also blacklist the BSSId if we find it
  65. ScanResult result = null;
  66. String bssidDbg = "";
  67. if (getScanDetailCache(config) != null && BSSID != null) {
  68. result = getScanDetailCache(config).get(BSSID);
  69. }
  70. if (result != null) {
  71. result.numIpConfigFailures ++;
  72. bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;
  73. if (result.numIpConfigFailures > 3) {
  74. // Tell supplicant to stop trying this BSSID
  75. mWifiNative.addToBlacklist(BSSID);
  76. result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);
  77. }
  78. }
  79. if (DBG) {
  80. loge("blacklisted " + config.configKey() + " to "
  81. + config.autoJoinStatus
  82. + " due to IP config failures, count="
  83. + config.numIpConfigFailures
  84. + " disableReason=" + config.disableReason
  85. + " " + bssidDbg);
  86. }
  87. } else if (message.contains("CONN_FAILED")) {
  88. config.numConnectionFailures++;
  89. if (config.numConnectionFailures > maxConnectionErrorsToBlacklist) {
  90. config.setAutoJoinStatus
  91. (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
  92. disableNetwork(netId,
  93. WifiConfiguration.DISABLED_ASSOCIATION_REJECT);
  94. loge("Connection failure, blacklist " + config.configKey() + " "
  95. + config.networkId
  96. + " num failures " + config.numConnectionFailures);
  97. }
  98. }
  99. message.replace("\n", "");
  100. message.replace("\r", "");
  101. config.lastFailure = message;
  102. }
  103. }
  104. }
  105. }
  106. }

在WifiStateMachine中,如果一个网络DHCP获取IP失败、或STATIC IP配置失败、或网络的配置信息丢失,都会间接调用到handleSSIDStateChange()函数,在配置的次数内尝试网络重连。我们看一个例子:

[java] view plain copy
  1. private void WifiStateMachine::handleIpConfigurationLost() {
  2. mWifiInfo.setInetAddress(null);
  3. mWifiInfo.setMeteredHint(false);
  4. mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,
  5. "DHCP FAILURE", mWifiInfo.getBSSID());//函数调用
  6. /* DHCP times out after about 30 seconds, we do a
  7. * disconnect thru supplicant, we will let autojoin retry connecting to the network
  8. */
  9. mWifiNative.disconnect();
  10. }

这里调用时,netId为当前使用的网络的netId,用以在WifiConfigStore获取到对应的WifiConfiguration,enabled为false,message为DHCP_FALURE;对照handleSSIDStateChange()实现,我们可以分析得出:

  • 根据传入的message,实现中会根据message的内容,判断当前网络发生的错误是什么,并记录相应错误的次数;比如是WRONG_KEY、还是AUTH_FAILED、还是DHCP FAILURE;然后会更新WifiConfiguration对象中各个错误对应的字段值,例如:WRONG_KEY和AUTH_FAILED对应的字段就是numAuthFailures,它记录了授权失败的次数,如果授权失败的次数大于一定的值,就会将该网络的config.autoJoinStatus设为AUTO_JOIN_DISABLED_ON_AUTH_FAILURE,并disable当前网络。那么在之后的auto_connect流程中,判断autoJoinStatus不合法,就不会去继续重连流程。

    [java] view plain copy
    1. if (config.numAuthFailures > maxAuthErrorsToBlacklist) {
    2. config.setAutoJoinStatus
    3. (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);//设置autostatus
    4. disableNetwork(netId,
    5. WifiConfiguration.DISABLED_AUTH_FAILURE);//disable网络
    6. loge("Authentication failure, blacklist " + config.configKey() + " "
    7. + Integer.toString(config.networkId)
    8. + " num failures " + config.numAuthFailures);
    9. }
  • 这里我们传入的message是DHCP FAILURE:
    [java] view plain copy
    1. else if (message.contains("DHCP FAILURE")) {
    2. config.numIpConfigFailures++;
    3. config.lastConnectionFailure = System.currentTimeMillis();
    4. int maxRetries = getMaxDhcpRetries();
    5. // maxRetries == 0 means keep trying forever
    6. if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {
    7. /**
    8. * If we've exceeded the maximum number of retries for DHCP
    9. * to a given network, disable the network
    10. */
    11. config.setAutoJoinStatus
    12. (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);//设置autostatus
    13. disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);//disable网络
    14. loge("DHCP failure, blacklist " + config.configKey() + " "
    15. + Integer.toString(config.networkId)
    16. + " num failures " + config.numIpConfigFailures);
    17. }
    18. // Also blacklist the BSSId if we find it
    19. ScanResult result = null;
    20. String bssidDbg = "";
    21. if (getScanDetailCache(config) != null && BSSID != null) {
    22. result = getScanDetailCache(config).get(BSSID);
    23. }
    24. if (result != null) {
    25. result.numIpConfigFailures ++;
    26. bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;
    27. if (result.numIpConfigFailures > 3) {
    28. // Tell supplicant to stop trying this BSSID
    29. mWifiNative.addToBlacklist(BSSID);//也有可能将当前网络加入到blacklist中
    30. result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);//设置autostatus
    31. }
    32. }
    33. if (DBG) {
    34. loge("blacklisted " + config.configKey() + " to "
    35. + config.autoJoinStatus
    36. + " due to IP config failures, count="
    37. + config.numIpConfigFailures
    38. + " disableReason=" + config.disableReason
    39. + " " + bssidDbg);
    40. }
    41. }

    首先numIpConfigFailures自增1,该字段代表当前网络DHCP失败的次数。如果当前网络的DHCP失败次数numIpConfigFailures大于配置的DHCP最大重连次数;则会将config的autoJoinStatus设为WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE;并disable当前的网络。这时,除非手动触发连接,否则都不会自动重连了。

Android -- 无线网络配置信息的管理者WifiConfigStore简介相关推荐

  1. 小型企业无线网络配置

    小型企业无线网络配置 提示:如有错误,欢迎指出 文章目录 一.需求分析 二.规划 三.设计 DHCP的配置: 核心交换机的配置: 二层交换机LSW2(LSW3.LSW4.LSW7类似)的配置: AC的 ...

  2. wpa_supplicant无线网络配置imx6ull以及搭建tftp服务器

    文章目录 1- wpa_supplicant介绍 2- wpa_supplicant无线网络配置 (1)手动无线网络配置 (2)开机执行脚本自动无线网络配置 3- tftp介绍 4- tftp服务器搭 ...

  3. linux配置无线网卡驱动,Linux无线网络配置——无线网卡驱动安装与WLAN802.11配置...

    Linux无线网络配置--无线网卡驱动安装与WLAN 802.11配置 WLAN (Wireless Local Area Network)类似于有线以太网,WLAN 的 802.11a 标准使用 5 ...

  4. Windows系统如何备份无线网络配置

    1.以管理员身份运行CMD命令提示符: 2.执行如下命令可将无线网络连接信息导出到当前目录. netsh WLAN export profile key=clear 如下图所示(当前目录为C:\wla ...

  5. 首次使用树莓派2(安装系统+SSH+VNC+无线网络配置)

    准备移植视觉程序到树莓派上运行,所以需要先在树莓派上搭建运行环境.本文将记录首次使用树莓派的基本过程,也是必经之路. 因为树莓派买回来的时候就自己带了一张光碟,里面有安装系统需要的工具和系统. 需要用 ...

  6. linux无线网络密码修改,linux无线网络配置工具----iwconfig

    wconfig iwconfig是Linux Wireless Extensions(LWE)的用户层配置工具之一.LWE是Linux下对无线网络配置的工具,包括内核的支持.用户层配置工具和驱动接口的 ...

  7. linux wlan进程名称,linux无线网络配置无线网卡驱动安装与wlan802.11配置

    Linux无线网络配置--无线网卡驱动安装与WLAN 802.11 配置 Linux无线网络配置--无线网卡驱动安装与WLAN 802.11配置2008-05-10 18:27 20739人阅读 评论 ...

  8. php显示网卡信息,netwox显示网络配置信息

    计算机的网络配置信息包含网络设备接口.IP 地址.MAC 地址和掩码等信息.为了方便用户查看计算机中的这些信息,netwox 工具提供了对应的模块,用于获取网络配置信息. 显示网络配置信息 为了了解当 ...

  9. ac ap方案 华为_华为无线_AC+AP小型无线网络配置实验_v1

    [如果在实验中有什么疑问,欢迎关注微信公众号"IT后院"给我留言,我会抽空回答你的问题] 华为无线-AC+AP小型无线网络配置实验_v1 网络结构图: 步骤一:配置网络连通性 SW ...

最新文章

  1. 24 location对象
  2. php 时钟函数,使用PHP的日期与时间函数技巧
  3. c++ 类 A类调用B类
  4. linux权限值前面的d,linux中rwx权限前的c和d都表示什么意思
  5. [攻防世界 pwn]——time_formatter(内涵peak小知识)
  6. OAF_VO系列1 - Accelerator Keys
  7. String类得常用方法
  8. java数据库配置_java--数据库(文件配置连接,自定义连接池)
  9. 「leetcode」51. N皇后【回溯算法】详细图解!
  10. unity 加载关卡_Unity手游实战:从0开始SLG——本地化篇(四)提取本地化元素
  11. 4.8、漏洞利用-NSF配置漏洞与Rpcbind信息泄露
  12. eclipse 版本 发行版本
  13. js实现图片的透明度运动
  14. 为什么荒野行动服务器信息卡了,荒野行动画面卡顿的原因是什么?荒野行动画面卡顿的快速有效解决方法...
  15. YOLOv7来临:论文解读附代码解析
  16. CAD编辑指南7:新建空白图纸和新建表格、导入图片
  17. java设计模式adapter,java设计模式-适配器模式(Adapter)
  18. Unity3D项目升级URP
  19. java画太极加图片_Android 画一个太极图实例代码
  20. 运维老鸟总结_硬件堡垒机、软件堡垒机、云堡垒机品牌怎么选?

热门文章

  1. 【hdu 4859】海岸线(图论--网络流最小割)
  2. 任务四十七:王牌特工 准备工作(二)
  3. asp.net 百度编辑器 UEditor 上传图片 图片上传配置 编辑器配置 网络连接错误,请检查配置后重试...
  4. Sep 12.1.4112.4156部署客户端
  5. WPF几个核心类的类层次结构
  6. C中位域Struct操作
  7. UcOS-II 和linux比较
  8. 2018年4月计算机组成原理试题,全国2018年4月自考(02318)计算机组成原理试题及答案.pdf...
  9. android获取sd卡mount,Android如何获取所有存储卡挂载路径
  10. zen of python什么意思_Zen of Python