Android -- WifiConfigStore简介


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.   
  10.         // A new network cannot have null SSID  
  11.         if (config == null || (config.networkId == INVALID_NETWORK_ID &&  
  12.                 config.SSID == null)) {  
  13.             return new NetworkUpdateResult(INVALID_NETWORK_ID);  
  14.         }  
  15.         if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);  
  16.         if (VDBG) {  
  17.             loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()  
  18.                     + " SSID=" + config.SSID  
  19.                     + " Uid=" + Integer.toString(config.creatorUid)  
  20.                     + "/" + Integer.toString(config.lastUpdateUid));  
  21.         }  
  22.   
  23.         if (mDeletedEphemeralSSIDs.remove(config.SSID)) {  
  24.             if (VDBG) {  
  25.                 loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);  
  26.             }  
  27.             // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call  
  28.             // below, since we're creating/modifying a config.  
  29.         }  
  30.   
  31.         boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);  
  32.         NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);  
  33.         int netId = result.getNetworkId();  
  34.   
  35.         if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);  
  36.   
  37.         /* enable a new network */  
  38.         if (newNetwork && netId != INVALID_NETWORK_ID) {  
  39.             if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);  
  40.   
  41.             mWifiNative.enableNetwork(netId, false);  
  42.             conf = mConfiguredNetworks.get(netId);  
  43.             if (conf != null)  
  44.                 conf.status = Status.ENABLED;  
  45.         }  
  46.   
  47.         conf = mConfiguredNetworks.get(netId);  
  48.         if (conf != null) {  
  49.             if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {  
  50.                 if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);  
  51.   
  52.                 // reenable autojoin, since new information has been provided  
  53.                 conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);  
  54.                 enableNetworkWithoutBroadcast(conf.networkId, false);  
  55.             }  
  56.             if (VDBG) {  
  57.                 loge("WifiConfigStore: saveNetwork got config back netId="  
  58.                         + Integer.toString(netId)  
  59.                         + " uid=" + Integer.toString(config.creatorUid));  
  60.             }  
  61.         }  
  62.   
  63.         mWifiNative.saveConfig();  
  64.         sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?  
  65.                 WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);  
  66.         return result;  
  67.     }  
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.   
  8.     if (VDBG) {  
  9.         loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +  
  10.                 newConfig.SSID + " path: " + ipConfigFile);  
  11.     }  
  12.   
  13.   
  14.     switch (newConfig.getIpAssignment()) {  
  15.         case STATIC:  
  16.             if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {  
  17.                 ipChanged = true;  
  18.             } else {  
  19.                 ipChanged = !Objects.equals(  
  20.                         currentConfig.getStaticIpConfiguration(),  
  21.                         newConfig.getStaticIpConfiguration());  
  22.             }  
  23.             break;  
  24.         case DHCP:  
  25.             if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {  
  26.                 ipChanged = true;  
  27.             }  
  28.             break;  
  29.         case UNASSIGNED:  
  30.             /* Ignore */  
  31.             break;  
  32.         default:  
  33.             loge("Ignore invalid ip assignment during write");  
  34.             break;  
  35.     }  
  36.   
  37.     switch (newConfig.getProxySettings()) {  
  38.         case STATIC:  
  39.         case PAC:  
  40.             ProxyInfo newHttpProxy = newConfig.getHttpProxy();  
  41.             ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();  
  42.   
  43.             if (newHttpProxy != null) {  
  44.                 proxyChanged = !newHttpProxy.equals(currentHttpProxy);  
  45.             } else {  
  46.                 proxyChanged = (currentHttpProxy != null);  
  47.             }  
  48.             break;  
  49.         case NONE:  
  50.             if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {  
  51.                 proxyChanged = true;  
  52.             }  
  53.             break;  
  54.         case UNASSIGNED:  
  55.             /* Ignore */  
  56.             break;  
  57.         default:  
  58.             loge("Ignore invalid proxy configuration during write");  
  59.             break;  
  60.     }  
  61.   
  62.     if (ipChanged) {  
  63.         currentConfig.setIpAssignment(newConfig.getIpAssignment());  
  64.         currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());  
  65.         log("IP config changed SSID = " + currentConfig.SSID);  
  66.         if (currentConfig.getStaticIpConfiguration() != null) {  
  67.             log(" static configuration: " +  
  68.                 currentConfig.getStaticIpConfiguration().toString());  
  69.         }  
  70.     }  
  71.   
  72.     if (proxyChanged) {  
  73.         currentConfig.setProxySettings(newConfig.getProxySettings());  
  74.         currentConfig.setHttpProxy(newConfig.getHttpProxy());  
  75.         log("proxy changed SSID = " + currentConfig.SSID);  
  76.         if (currentConfig.getHttpProxy() != null) {  
  77.             log(" proxyProperties: " + currentConfig.getHttpProxy().toString());  
  78.         }  
  79.     }  
  80.   
  81.     if (ipChanged || proxyChanged) {  
  82.         writeIpAndProxyConfigurations();  
  83.         sendConfiguredNetworksChangedBroadcast(currentConfig,  
  84.                 WifiManager.CHANGE_REASON_CONFIG_CHANGE);  
  85.     }  
  86.     return new NetworkUpdateResult(ipChanged, proxyChanged);  
  87. }  
函数中涉及到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.   
  12.  public enum ProxySettings {  
  13.      /* No proxy is to be used. Any existing proxy settings 
  14.       * should be cleared. */  
  15.      NONE,  
  16.      /* Use statically configured proxy. Configuration can be accessed 
  17.       * with httpProxy. */  
  18.      STATIC,  
  19.      /* no proxy details are assigned, this is used to indicate 
  20.       * that any existing proxy settings should be retained */  
  21.      UNASSIGNED,  
  22.      /* Use a Pac based proxy. 
  23.       */  
  24.      PAC  
  25.  }  
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 networks = new SparseArray();  
  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.   
  9.     super.writeIpAndProxyConfigurations(ipConfigFile, networks);//in IpConfigStore  
  10. }  
[java] view plain copy
  1. public void IpConfigStore::writeIpAndProxyConfigurations(String filePath,  
  2.                                           final SparseArray 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.   
  5.      try {  
  6.          switch (config.ipAssignment) {  
  7.              case STATIC:  
  8.                  out.writeUTF(IP_ASSIGNMENT_KEY);  
  9.                  out.writeUTF(config.ipAssignment.toString());  
  10.                  StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;  
  11.                  if (staticIpConfiguration != null) {  
  12.                      if (staticIpConfiguration.ipAddress != null) {  
  13.                          LinkAddress ipAddress = staticIpConfiguration.ipAddress;  
  14.                          out.writeUTF(LINK_ADDRESS_KEY);  
  15.                          out.writeUTF(ipAddress.getAddress().getHostAddress());  
  16.                          out.writeInt(ipAddress.getPrefixLength());  
  17.                      }  
  18.                      if (staticIpConfiguration.gateway != null) {  
  19.                          out.writeUTF(GATEWAY_KEY);  
  20.                          out.writeInt(0);  // Default route.  
  21.                          out.writeInt(1);  // Have a gateway.  
  22.                          out.writeUTF(staticIpConfiguration.gateway.getHostAddress());  
  23.                      }  
  24.                      for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {  
  25.                          out.writeUTF(DNS_KEY);  
  26.                          out.writeUTF(inetAddr.getHostAddress());  
  27.                      }  
  28.                  }  
  29.                  written = true;  
  30.                  break;  
  31.              case DHCP:  
  32.                  out.writeUTF(IP_ASSIGNMENT_KEY);  
  33.                  out.writeUTF(config.ipAssignment.toString());  
  34.                  written = true;  
  35.                  break;  
  36.              case UNASSIGNED:  
  37.              /* Ignore */  
  38.                  break;  
  39.              default:  
  40.                  loge("Ignore invalid ip assignment while writing");  
  41.                  break;  
  42.          }  
  43.   
  44.          switch (config.proxySettings) {  
  45.              case STATIC:  
  46.                  ProxyInfo proxyProperties = config.httpProxy;  
  47.                  String exclusionList = proxyProperties.getExclusionListAsString();  
  48.                  out.writeUTF(PROXY_SETTINGS_KEY);  
  49.                  out.writeUTF(config.proxySettings.toString());  
  50.                  out.writeUTF(PROXY_HOST_KEY);  
  51.                  out.writeUTF(proxyProperties.getHost());  
  52.                  out.writeUTF(PROXY_PORT_KEY);  
  53.                  out.writeInt(proxyProperties.getPort());  
  54.                  if (exclusionList != null) {  
  55.                      out.writeUTF(EXCLUSION_LIST_KEY);  
  56.                      out.writeUTF(exclusionList);  
  57.                  }  
  58.                  written = true;  
  59.                  break;  
  60.              case PAC:  
  61.                  ProxyInfo proxyPacProperties = config.httpProxy;  
  62.                  out.writeUTF(PROXY_SETTINGS_KEY);  
  63.                  out.writeUTF(config.proxySettings.toString());  
  64.                  out.writeUTF(PROXY_PAC_FILE);  
  65.                  out.writeUTF(proxyPacProperties.getPacFileUrl().toString());  
  66.                  written = true;  
  67.                  break;  
  68.              case NONE:  
  69.                  out.writeUTF(PROXY_SETTINGS_KEY);  
  70.                  out.writeUTF(config.proxySettings.toString());  
  71.                  written = true;  
  72.                  break;  
  73.              case UNASSIGNED:  
  74.                  /* Ignore */  
  75.                      break;  
  76.                  default:  
  77.                      loge("Ignore invalid proxy settings while writing");  
  78.                      break;  
  79.          }  
  80.   
  81.          if (written) {  
  82.              out.writeUTF(ID_KEY);  
  83.              out.writeInt(configKey);  
  84.          }  
  85.      } catch (NullPointerException e) {  
  86.          loge("Failure in writing " + config + e);  
  87.      }  
  88.      out.writeUTF(EOS);  
  89.   
  90.      return written;  
  91.  }  
最终写入文件的操作是父类IpConfigStore::writeConfig()方法处理的。在WifiConfigStore::writeIpAndProxyConfigurations()中,我们会将所有保存的网络配置信息从mConfiguredNetworks集合中取出,重新按照<一个唯一int值,IpConfiguration>的形式保存到一个SparseArray 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.   
  14. 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.   
  18.     // Reset the priority of each network at start or if it goes too high.  
  19.     if (mLastPriority == -1 || mLastPriority > 1000000) {  
  20.         for(WifiConfiguration config2 : mConfiguredNetworks.values()) {  
  21.             if (updatePriorities) {  
  22.                 if (config2.networkId != INVALID_NETWORK_ID) {  
  23.                     config2.priority = 0;  
  24.                     setNetworkPriorityNative(config2.networkId, config.priority);  
  25.                 }  
  26.             }  
  27.         }  
  28.         mLastPriority = 0;  
  29.     }  
  30.   
  31.     // Set to the highest priority and save the configuration.  
  32.     if (updatePriorities) {  
  33.         config.priority = ++mLastPriority;  
  34.         setNetworkPriorityNative(config.networkId, config.priority);  
  35.         buildPnoList();  
  36.     }  
  37.   
  38.     if (config.isPasspoint()) {  
  39.         /* need to slap on the SSID of selected bssid to work */  
  40.         if (getScanDetailCache(config).size() != 0) {  
  41.             ScanDetail result = getScanDetailCache(config).getFirst();  
  42.             if (result == null) {  
  43.                 loge("Could not find scan result for " + config.BSSID);  
  44.             } else {  
  45.                 log("Setting SSID for " + config.networkId + " to" + result.getSSID());  
  46.                 setSSIDNative(config.networkId, result.getSSID());  
  47.                 config.SSID = result.getSSID();  
  48.             }  
  49.   
  50.         } else {  
  51.             loge("Could not find bssid for " + config);  
  52.         }  
  53.     }  
  54.   
  55.     if (updatePriorities)  
  56.         mWifiNative.saveConfig();  
  57.     else  
  58.         mWifiNative.selectNetwork(config.networkId);  
  59.   
  60.     updateLastConnectUid(config, uid);  
  61.     writeKnownNetworkHistory(false);  
  62.   
  63.     /* Enable the given network while disabling all other networks */  
  64.     enableNetworkWithoutBroadcast(config.networkId, true);  
  65.   
  66.    /* Avoid saving the config & sending a broadcast to prevent settings 
  67.     * from displaying a disabled list of networks */  
  68.     return true;  
  69. }  
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.     }

      

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

     

  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.   
  65.                             // Also blacklist the BSSId if we find it  
  66.                             ScanResult result = null;  
  67.                             String bssidDbg = "";  
  68.                             if (getScanDetailCache(config) != null && BSSID != null) {  
  69.                                 result = getScanDetailCache(config).get(BSSID);  
  70.                             }  
  71.                             if (result != null) {  
  72.                                 result.numIpConfigFailures ++;  
  73.                                 bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;  
  74.                                 if (result.numIpConfigFailures > 3) {  
  75.                                     // Tell supplicant to stop trying this BSSID  
  76.                                     mWifiNative.addToBlacklist(BSSID);  
  77.                                     result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);  
  78.                                 }  
  79.                             }  
  80.   
  81.                             if (DBG) {  
  82.                                 loge("blacklisted " + config.configKey() + " to "  
  83.                                         + config.autoJoinStatus  
  84.                                         + " due to IP config failures, count="  
  85.                                         + config.numIpConfigFailures  
  86.                                         + " disableReason=" + config.disableReason  
  87.                                         + " " + bssidDbg);  
  88.                             }  
  89.                         } else if (message.contains("CONN_FAILED")) {  
  90.                             config.numConnectionFailures++;  
  91.                             if (config.numConnectionFailures > maxConnectionErrorsToBlacklist) {  
  92.                                 config.setAutoJoinStatus  
  93.                                         (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);  
  94.                                 disableNetwork(netId,  
  95.                                         WifiConfiguration.DISABLED_ASSOCIATION_REJECT);  
  96.                                 loge("Connection failure, blacklist " + config.configKey() + " "  
  97.                                         + config.networkId  
  98.                                         + " num failures " + config.numConnectionFailures);  
  99.                             }  
  100.                         }  
  101.                         message.replace("\n""");  
  102.                         message.replace("\r""");  
  103.                         config.lastFailure = message;  
  104.                     }  
  105.                 }  
  106.             }  
  107.         }  
  108.     }  
在WifiStateMachine中,如果一个网络DHCP获取IP失败、或STATIC IP配置失败、或网络的配置信息丢失,都会间接调用到handleSSIDStateChange()函数,在配置的次数内尝试网络重连。我们看一个例子: [java] view plain copy
  1. private void WifiStateMachine::handleIpConfigurationLost() {  
  2.     mWifiInfo.setInetAddress(null);  
  3.     mWifiInfo.setMeteredHint(false);  
  4.   
  5.     mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,  
  6.             "DHCP FAILURE", mWifiInfo.getBSSID());//函数调用  
  7.   
  8.     /* DHCP times out after about 30 seconds, we do a 
  9.      * disconnect thru supplicant, we will let autojoin retry connecting to the network 
  10.      */  
  11.     mWifiNative.disconnect();  
  12. }  
这里调用时,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.   
    19.                             // Also blacklist the BSSId if we find it  
    20.                             ScanResult result = null;  
    21.                             String bssidDbg = "";  
    22.                             if (getScanDetailCache(config) != null && BSSID != null) {  
    23.                                 result = getScanDetailCache(config).get(BSSID);  
    24.                             }  
    25.                             if (result != null) {  
    26.                                 result.numIpConfigFailures ++;  
    27.                                 bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;  
    28.                                 if (result.numIpConfigFailures > 3) {  
    29.                                     // Tell supplicant to stop trying this BSSID  
    30.                                     mWifiNative.addToBlacklist(BSSID);//也有可能将当前网络加入到blacklist中  
    31.                                     result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);//设置autostatus  
    32.                                 }  
    33.                             }  
    34.   
    35.                             if (DBG) {  
    36.                                 loge("blacklisted " + config.configKey() + " to "  
    37.                                         + config.autoJoinStatus  
    38.                                         + " due to IP config failures, count="  
    39.                                         + config.numIpConfigFailures  
    40.                                         + " disableReason=" + config.disableReason  
    41.                                         + " " + bssidDbg);  
    42.                             }  
    43.                         }  
    首先numIpConfigFailures自增1,该字段代表当前网络DHCP失败的次数。如果当前网络的DHCP失败次数numIpConfigFailures大于配置的DHCP最大重连次数;则会将config的autoJoinStatus设为WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE;并disable当前的网络。这时,除非手动触发连接,否则都不会自动重连了。

更多相关文章

  1. Android中的几种网络请求方式详解 .
  2. android网络编程――使用Android中的网络连接
  3. Android 各大网络请求库的比较及实战
  4. Android之Http网络编程(三)
  5. Android之网络丢包
  6. okhttp的应用详解与源码解析--android网络请求框架发展史

随机推荐

  1. android 中activity 属性说明
  2. android 多种特效TextView
  3. Android 中级教程之------Android MediaP
  4. mono for android 百度map binding项目
  5. Android 进阶之 Android消息机制Handler
  6. Android NDK会带来什么,除去你对NDK的一些
  7. Android自动化工具Monkeyrunner使用(五)
  8. Android中Activity的四种启动模式详解
  9. Android Binder
  10. 安卓017ListView & GridView & ScrollView