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

来源:互联网 发布:淘宝前端属于ucd 编辑:程序博客网 时间:2024/06/03 23:06

Android -- WifiConfigStore简介


WifiConfigStore在Android的无线网络部分,主要负责网络配置信息的管理工作,包括保存、读取配置信息等。当我们在Settings中触发一个保存网络、连接网络或者auto_connect自动重连动作时,都会调用到WifiConfigStore中的方法。
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. public class WifiConfigStore extends IpConfigStore  
WifiConfigStore继承自IpConfigStore,它提供了一套API去管理用户配置过的网络。下面介绍一些framework中经常调用到的API接口。

一、saveNetwork()、selectNetwork()

WifiStateMachine中,WifiConfigStore对象的创建发生在其构造函数中:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);  
我们传入了Context、当前的WifiStateMachine对象和一个WifiNative对象。通过mWifiNative对象可以向wpa_s下发一系列连接、选择的命令。
我们在连接一个网络的时候,会先保存该网络的配置信息,调用:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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.   
  9.     super.writeIpAndProxyConfigurations(ipConfigFile, networks);//in IpConfigStore  
  10. }  
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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<IpConfiguration> networks对象中(可以看做是一个集合);ipConfigFile的值就是路径:"/misc/wifi/ipconfig.txt"。
在IpConfigStore::writeIpAndProxyConfigurations和IpConfigStore::writeConfig()中,我们会遍历networks集合,并按照
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  1. switch (config.ipAssignment)   
  2. switch (config.proxySettings)  
的分类,将信息写入ipconfig.txt文件中;这里的写入也是有一定的规则的,每一个标签后面跟一个该标签对应的值。这样做方法后面的数据读取。定义的标签值有:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  1. mWifiConfigStore.loadAndEnableAllNetworks();  
[java] view plain copy 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
  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 在CODE上查看代码片派生到我的代码片
    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 在CODE上查看代码片派生到我的代码片
    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当前的网络。这时,除非手动触发连接,否则都不会自动重连了。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 健身房锻炼手臂抻的肌肉疼怎么办 人累了可以休息 心累了怎么办 上来两天班感觉不想做了怎么办 休产假的时候公司解散了怎么办 上三天班老板不给结工资怎么办 老师说孩子上课纪律不好我该怎么办 大班的孩子记不住拼音怎么办呢 家长跟孩子沟通出现问题该怎么办? 如果孩子入学分配出现问题该怎么办 2岁多的宝宝喜欢动手打人怎么办 儿童新长出的大门牙像两边撇怎么办 被烫伤了怎么办的活动反思怎么写 生完孩子脸上起蝴蝶斑了怎么办 鼻子部位突然长了晒斑怎么办 我脸上长有日晒斑.该怎么办 做为小领导同事不听你的怎么办 二年级的小孩叫写作业不听怎么办 苹果手机微信出现黑框怎么办 百度网盘下载原画视频会闪退怎么办 已发布的公众号推文段落重复怎么办 谷歌商店找不到方舟手游怎么办 染头发的颜色弄到衣服上怎么办 橡皮把桌面油漆弄掉了怎么办 手机被调成静音不知道放哪了怎么办 金丝熊吃大米吃撑了怎么办 部落有可疑记录被暂时禁封怎么办 鼻子通向嘴那里痒得难受怎么办 小孩上嘴唇中间的连线碰掉了怎么办 秋田犬夏天退毛严重么 怎么办 初中数学基本没学过高中怎么办 老师家纺突然想日语文老师怎么办 微信聊天表情小企鹅不动了怎么办 微信自带小表情不全怎么办 爱奇艺电视果有图像无声音怎么办 微信表情包保存不到手机相册怎么办 才出生的兔宝宝被母兔抓伤了怎么办 老婆生气了说恨我一辈子我该怎么办 华为手机微信表情不显示含义怎么办 地下城游戏登录链接一直失败怎么办 聊天时别人打听家人不想回答怎么办 微信钱包零钱密码忘了怎么办