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

来源:互联网 发布:java怎么求质数 编辑:程序博客网 时间:2024/06/09 21:37

Android -- WifiConfigStore简介


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

一、saveNetwork()、selectNetwork()

WifiStateMachine中,WifiConfigStore对象的创建发生在其构造函数中:
     mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);
我们传入了Context、当前的WifiStateMachine对象和一个WifiNative对象。通过mWifiNative对象可以向wpa_s下发一系列连接、选择的命令。
我们在连接一个网络的时候,会先保存该网络的配置信息,调用:
/**     * Add/update the specified configuration and save config     *     * @param config WifiConfiguration to be saved     * @return network update result     */    NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {        WifiConfiguration conf;        // A new network cannot have null SSID        if (config == null || (config.networkId == INVALID_NETWORK_ID &&                config.SSID == null)) {            return new NetworkUpdateResult(INVALID_NETWORK_ID);        }        if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);        if (VDBG) {            loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()                    + " SSID=" + config.SSID                    + " Uid=" + Integer.toString(config.creatorUid)                    + "/" + Integer.toString(config.lastUpdateUid));        }        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {            if (VDBG) {                loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);            }            // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call            // below, since we're creating/modifying a config.        }        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);        int netId = result.getNetworkId();        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);        /* enable a new network */        if (newNetwork && netId != INVALID_NETWORK_ID) {            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);            mWifiNative.enableNetwork(netId, false);            conf = mConfiguredNetworks.get(netId);            if (conf != null)                conf.status = Status.ENABLED;        }        conf = mConfiguredNetworks.get(netId);        if (conf != null) {            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);                // reenable autojoin, since new information has been provided                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);                enableNetworkWithoutBroadcast(conf.networkId, false);            }            if (VDBG) {                loge("WifiConfigStore: saveNetwork got config back netId="                        + Integer.toString(netId)                        + " uid=" + Integer.toString(config.creatorUid));            }        }        mWifiNative.saveConfig();        sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);        return result;    }
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时,会从该文件中读取我们所配置过的网络信息,并进行重连。
    /* Compare current and new configuration and write to file on change */    private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(            WifiConfiguration currentConfig,            WifiConfiguration newConfig) {        boolean ipChanged = false;        boolean proxyChanged = false;        if (VDBG) {            loge("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> " +                    newConfig.SSID + " path: " + ipConfigFile);        }        switch (newConfig.getIpAssignment()) {            case STATIC:                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {                    ipChanged = true;                } else {                    ipChanged = !Objects.equals(                            currentConfig.getStaticIpConfiguration(),                            newConfig.getStaticIpConfiguration());                }                break;            case DHCP:                if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {                    ipChanged = true;                }                break;            case UNASSIGNED:                /* Ignore */                break;            default:                loge("Ignore invalid ip assignment during write");                break;        }        switch (newConfig.getProxySettings()) {            case STATIC:            case PAC:                ProxyInfo newHttpProxy = newConfig.getHttpProxy();                ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();                if (newHttpProxy != null) {                    proxyChanged = !newHttpProxy.equals(currentHttpProxy);                } else {                    proxyChanged = (currentHttpProxy != null);                }                break;            case NONE:                if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {                    proxyChanged = true;                }                break;            case UNASSIGNED:                /* Ignore */                break;            default:                loge("Ignore invalid proxy configuration during write");                break;        }        if (ipChanged) {            currentConfig.setIpAssignment(newConfig.getIpAssignment());            currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());            log("IP config changed SSID = " + currentConfig.SSID);            if (currentConfig.getStaticIpConfiguration() != null) {                log(" static configuration: " +                    currentConfig.getStaticIpConfiguration().toString());            }        }        if (proxyChanged) {            currentConfig.setProxySettings(newConfig.getProxySettings());            currentConfig.setHttpProxy(newConfig.getHttpProxy());            log("proxy changed SSID = " + currentConfig.SSID);            if (currentConfig.getHttpProxy() != null) {                log(" proxyProperties: " + currentConfig.getHttpProxy().toString());            }        }        if (ipChanged || proxyChanged) {            writeIpAndProxyConfigurations();            sendConfiguredNetworksChangedBroadcast(currentConfig,                    WifiManager.CHANGE_REASON_CONFIG_CHANGE);        }        return new NetworkUpdateResult(ipChanged, proxyChanged);    }
函数中涉及到IpAssignment和ProxySettings两个枚举类型:
   public enum IpAssignment {        /* Use statically configured IP settings. Configuration can be accessed         * with staticIpConfiguration */        STATIC,        /* Use dynamically configured IP settigns */        DHCP,        /* no IP details are assigned, this is used to indicate         * that any existing IP settings should be retained */        UNASSIGNED    }    public enum ProxySettings {        /* No proxy is to be used. Any existing proxy settings         * should be cleared. */        NONE,        /* Use statically configured proxy. Configuration can be accessed         * with httpProxy. */        STATIC,        /* no proxy details are assigned, this is used to indicate         * that any existing proxy settings should be retained */        UNASSIGNED,        /* Use a Pac based proxy.         */        PAC    }
IpAssignment代表当前获取IP使用的方式,我们可以根据自己的需求在里面添加自定义的方式,比如PPPoE;同理,ProxySettings表示当前网络使用的代理方式。IpAssignment类型的值一般由设置根据用户选择的IP模式来赋值,并传递给framework,以让底层可以知道该使用什么方式去获取IP地址。例如,如果用户选择Static IP,则在WifiStateMachine::ObtainingIpState中会有:
            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {                if (isRoaming()) {                    renewDhcp();                } else {                    // Remove any IP address on the interface in case we're switching from static                    // IP configuration to DHCP. This is safe because if we get here when not                    // roaming, we don't have a usable address.                    clearIPv4Address(mInterfaceName);                    startDhcp();                }                obtainingIpWatchdogCount++;                logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);                // Get Link layer stats so as we get fresh tx packet counters                getWifiLinkLayerStats(true);                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);            } else {                // stop any running dhcp before assigning static IP                stopDhcp();                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(                        mLastNetworkId);                if (config.ipAddress == null) {                    logd("Static IP lacks address");                    sendMessage(CMD_STATIC_IP_FAILURE);                } else {                    InterfaceConfiguration ifcg = new InterfaceConfiguration();                    ifcg.setLinkAddress(config.ipAddress);                    ifcg.setInterfaceUp();                    try {                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);                        if (DBG) log("Static IP configuration succeeded");                        DhcpResults dhcpResults = new DhcpResults(config);                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);                    } catch (RemoteException re) {                        loge("Static IP configuration failed: " + re);                        sendMessage(CMD_STATIC_IP_FAILURE);                    } catch (IllegalStateException e) {                        loge("Static IP configuration failed: " + e);                        sendMessage(CMD_STATIC_IP_FAILURE);                    }                }            }
通过WifiConfigStore.isUsingStaticIp(mLastNetworkId)方法获知当前用户使用的获取IP地址类型,具体方法定义:
    /**     * Return if the specified network is using static IP     * @param netId id     * @return {@code true} if using static ip for netId     */    boolean isUsingStaticIp(int netId) {        WifiConfiguration config = mConfiguredNetworks.get(netId);        if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {            return true;        }        return false;    }
根据传入的netId,从mConfiguredNetworks集合中获取对应网络的WifiConfiguration对象,再获取该对象配置的IpAssignment值,来区分不用的网络方式,进而控制流程走不同的分支。如果我们有加入别的方式,可以仿照这个原生例子,写出自己的程序。
riteIpAndProxyConfigurationsOnChange()中会根据IpAssignment、ProxySettings的类型是否改变,去更新currentConfig对象,并writeIpAndProxyConfigurations()方法写入到本地磁盘文件:
    private void writeIpAndProxyConfigurations() {        final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();        for(WifiConfiguration config : mConfiguredNetworks.values()) {            if (!config.ephemeral && config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_DELETED) {                networks.put(configKey(config), config.getIpConfiguration());            }        }        super.writeIpAndProxyConfigurations(ipConfigFile, networks);//in IpConfigStore    }
    public void IpConfigStore::writeIpAndProxyConfigurations(String filePath,                                              final SparseArray<IpConfiguration> networks) {        mWriter.write(filePath, new DelayedDiskWrite.Writer() {            public void onWriteCalled(DataOutputStream out) throws IOException{                out.writeInt(IPCONFIG_FILE_VERSION);                for(int i = 0; i < networks.size(); i++) {                    writeConfig(out, networks.keyAt(i), networks.valueAt(i));                }            }        });    }
   private boolean writeConfig(DataOutputStream out, int configKey,                                IpConfiguration config) throws IOException {        boolean written = false;        try {            switch (config.ipAssignment) {                case STATIC:                    out.writeUTF(IP_ASSIGNMENT_KEY);                    out.writeUTF(config.ipAssignment.toString());                    StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;                    if (staticIpConfiguration != null) {                        if (staticIpConfiguration.ipAddress != null) {                            LinkAddress ipAddress = staticIpConfiguration.ipAddress;                            out.writeUTF(LINK_ADDRESS_KEY);                            out.writeUTF(ipAddress.getAddress().getHostAddress());                            out.writeInt(ipAddress.getPrefixLength());                        }                        if (staticIpConfiguration.gateway != null) {                            out.writeUTF(GATEWAY_KEY);                            out.writeInt(0);  // Default route.                            out.writeInt(1);  // Have a gateway.                            out.writeUTF(staticIpConfiguration.gateway.getHostAddress());                        }                        for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {                            out.writeUTF(DNS_KEY);                            out.writeUTF(inetAddr.getHostAddress());                        }                    }                    written = true;                    break;                case DHCP:                    out.writeUTF(IP_ASSIGNMENT_KEY);                    out.writeUTF(config.ipAssignment.toString());                    written = true;                    break;                case UNASSIGNED:                /* Ignore */                    break;                default:                    loge("Ignore invalid ip assignment while writing");                    break;            }            switch (config.proxySettings) {                case STATIC:                    ProxyInfo proxyProperties = config.httpProxy;                    String exclusionList = proxyProperties.getExclusionListAsString();                    out.writeUTF(PROXY_SETTINGS_KEY);                    out.writeUTF(config.proxySettings.toString());                    out.writeUTF(PROXY_HOST_KEY);                    out.writeUTF(proxyProperties.getHost());                    out.writeUTF(PROXY_PORT_KEY);                    out.writeInt(proxyProperties.getPort());                    if (exclusionList != null) {                        out.writeUTF(EXCLUSION_LIST_KEY);                        out.writeUTF(exclusionList);                    }                    written = true;                    break;                case PAC:                    ProxyInfo proxyPacProperties = config.httpProxy;                    out.writeUTF(PROXY_SETTINGS_KEY);                    out.writeUTF(config.proxySettings.toString());                    out.writeUTF(PROXY_PAC_FILE);                    out.writeUTF(proxyPacProperties.getPacFileUrl().toString());                    written = true;                    break;                case NONE:                    out.writeUTF(PROXY_SETTINGS_KEY);                    out.writeUTF(config.proxySettings.toString());                    written = true;                    break;                case UNASSIGNED:                    /* Ignore */                        break;                    default:                        loge("Ignore invalid proxy settings while writing");                        break;            }            if (written) {                out.writeUTF(ID_KEY);                out.writeInt(configKey);            }        } catch (NullPointerException e) {            loge("Failure in writing " + config + e);        }        out.writeUTF(EOS);        return written;    }
最终写入文件的操作是父类IpConfigStore::writeConfig()方法处理的。在WifiConfigStore::writeIpAndProxyConfigurations()中,我们会将所有保存的网络配置信息从mConfiguredNetworks集合中取出,重新按照<一个唯一int值,IpConfiguration>的形式保存到一个SparseArray<IpConfiguration> networks对象中(可以看做是一个集合);ipConfigFile的值就是路径:"/misc/wifi/ipconfig.txt"。
在IpConfigStore::writeIpAndProxyConfigurations和IpConfigStore::writeConfig()中,我们会遍历networks集合,并按照
switch (config.ipAssignment) switch (config.proxySettings)
的分类,将信息写入ipconfig.txt文件中;这里的写入也是有一定的规则的,每一个标签后面跟一个该标签对应的值。这样做方法后面的数据读取。定义的标签值有:
    /* IP and proxy configuration keys */    protected static final String ID_KEY = "id";    protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";    protected static final String LINK_ADDRESS_KEY = "linkAddress";    protected static final String GATEWAY_KEY = "gateway";    protected static final String DNS_KEY = "dns";    protected static final String PROXY_SETTINGS_KEY = "proxySettings";    protected static final String PROXY_HOST_KEY = "proxyHost";    protected static final String PROXY_PORT_KEY = "proxyPort";    protected static final String PROXY_PAC_FILE = "proxyPac";    protected static final String EXCLUSION_LIST_KEY = "exclusionList";    protected static final String EOS = "eos";    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网络的部分。
    /**     * Selects the specified network for connection. This involves     * updating the priority of all the networks and enabling the given     * network while disabling others.     *     * Selecting a network will leave the other networks disabled and     * a call to enableAllNetworks() needs to be issued upon a connection     * or a failure event from supplicant     *     * @param config network to select for connection     * @param updatePriorities makes config highest priority network     * @return false if the network id is invalid     */    boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {        if (VDBG) localLog("selectNetwork", config.networkId);        if (config.networkId == INVALID_NETWORK_ID) return false;        // Reset the priority of each network at start or if it goes too high.        if (mLastPriority == -1 || mLastPriority > 1000000) {            for(WifiConfiguration config2 : mConfiguredNetworks.values()) {                if (updatePriorities) {                    if (config2.networkId != INVALID_NETWORK_ID) {                        config2.priority = 0;                        setNetworkPriorityNative(config2.networkId, config.priority);                    }                }            }            mLastPriority = 0;        }        // Set to the highest priority and save the configuration.        if (updatePriorities) {            config.priority = ++mLastPriority;            setNetworkPriorityNative(config.networkId, config.priority);            buildPnoList();        }        if (config.isPasspoint()) {            /* need to slap on the SSID of selected bssid to work */            if (getScanDetailCache(config).size() != 0) {                ScanDetail result = getScanDetailCache(config).getFirst();                if (result == null) {                    loge("Could not find scan result for " + config.BSSID);                } else {                    log("Setting SSID for " + config.networkId + " to" + result.getSSID());                    setSSIDNative(config.networkId, result.getSSID());                    config.SSID = result.getSSID();                }            } else {                loge("Could not find bssid for " + config);            }        }        if (updatePriorities)            mWifiNative.saveConfig();        else            mWifiNative.selectNetwork(config.networkId);        updateLastConnectUid(config, uid);        writeKnownNetworkHistory(false);        /* Enable the given network while disabling all other networks */        enableNetworkWithoutBroadcast(config.networkId, true);       /* Avoid saving the config & sending a broadcast to prevent settings        * from displaying a disabled list of networks */        return true;    }
mLastPriority是一个int类型的整数值,它代表当前网络中的优先级的最大值。越是最近连接过的网络,它的priority优先级值就越大。updatePriorities代表是否需要更新优先级。当当前的最大优先级值为-1或1000000时,都会重新设置mLastPriority值;如果updatePriorities为true,也会将更改更新到wpa_s.conf文件中。
        // Set to the highest priority and save the configuration.        if (updatePriorities) {            config.priority = ++mLastPriority;            setNetworkPriorityNative(config.networkId, config.priority);            buildPnoList();        }
从这可以看出,每个当前正在连接的网络,都具有最高的优先级。最后enableNetworkWithoutBroadcast()中,会在mConfiguredNetworks将选中网络的status属性设为Status.ENABLED,其他的设置为Status.DISABLED:
    /* Mark all networks except specified netId as disabled */    private void markAllNetworksDisabledExcept(int netId) {        for(WifiConfiguration config : mConfiguredNetworks.values()) {            if(config != null && config.networkId != netId) {                if (config.status != Status.DISABLED) {                    config.status = Status.DISABLED;                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;                }            }        }<p>    }</p>

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

当我们重新打开Wifi时,Wifi正常情况下都会有网络自动重连的动作。此时,WifiStateMachine中:
    mWifiConfigStore.loadAndEnableAllNetworks();
    /**     * Fetch the list of configured networks     * and enable all stored networks in supplicant.     */    void loadAndEnableAllNetworks() {        if (DBG) log("Loading config and enabling all networks ");        loadConfiguredNetworks();        enableAllNetworks();    }
看loadConfiguredNetworks():
    void loadConfiguredNetworks() {        mLastPriority = 0;        mConfiguredNetworks.clear();        int last_id = -1;        boolean done = false;        while (!done) {            String listStr = mWifiNative.listNetworks(last_id);            if (listStr == null)                return;            String[] lines = listStr.split("\n");            if (showNetworks) {                localLog("WifiConfigStore: loadConfiguredNetworks:  ");                for (String net : lines) {                    localLog(net);                }            }            // Skip the first line, which is a header            for (int i = 1; i < lines.length; i++) {                String[] result = lines[i].split("\t");                // network-id | ssid | bssid | flags                WifiConfiguration config = new WifiConfiguration();                try {                    config.networkId = Integer.parseInt(result[0]);                    last_id = config.networkId;                } catch(NumberFormatException e) {                    loge("Failed to read network-id '" + result[0] + "'");                    continue;                }                if (result.length > 3) {                    if (result[3].indexOf("[CURRENT]") != -1)                        config.status = WifiConfiguration.Status.CURRENT;                    else if (result[3].indexOf("[DISABLED]") != -1)                        config.status = WifiConfiguration.Status.DISABLED;                    else                        config.status = WifiConfiguration.Status.ENABLED;                } else {                    config.status = WifiConfiguration.Status.ENABLED;                }                readNetworkVariables(config);                Checksum csum = new CRC32();                if (config.SSID != null) {                    csum.update(config.SSID.getBytes(), 0, config.SSID.getBytes().length);                    long d = csum.getValue();                    if (mDeletedSSIDs.contains(d)) {                        loge(" got CRC for SSID " + config.SSID + " -> " + d + ", was deleted");                    }                }                if (config.priority > mLastPriority) {                    mLastPriority = config.priority;                }                config.setIpAssignment(IpAssignment.DHCP);//默认设置DHCP                config.setProxySettings(ProxySettings.NONE);//默认设置NONE                if (mConfiguredNetworks.getByConfigKey(config.configKey()) != null) {                    // That SSID is already known, just ignore this duplicate entry                    if (showNetworks) localLog("discarded duplicate network ", config.networkId);                } else if(WifiServiceImpl.isValid(config)){                    mConfiguredNetworks.put(config.networkId, config);                    if (showNetworks) localLog("loaded configured network", config.networkId);                } else {                    if (showNetworks) log("Ignoring loaded configured for network " + config.networkId                        + " because config are not valid");                }            }            done = (lines.length == 1);        }        readPasspointConfig();        readIpAndProxyConfigurations();//读取ipconfig.txt        readNetworkHistory();//读取networkHistory.txt        readAutoJoinConfig();        buildPnoList();        sendConfiguredNetworksChangedBroadcast();        if (showNetworks) localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.size() + " networks");        if (mConfiguredNetworks.isEmpty()) {            // no networks? Lets log if the file contents            logKernelTime();            logContents(SUPPLICANT_CONFIG_FILE);            logContents(SUPPLICANT_CONFIG_FILE_BACKUP);            logContents(networkHistoryConfigFile);        }    }
函数开始就会清空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中定义了一个比较有意义的默认变量值:
    /**     * The maximum number of times we will retry a connection to an access point     * for which we have failed in acquiring an IP address from DHCP. A value of     * N means that we will make N+1 connection attempts in all.     * <p>     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default     * value if a Settings value is not present.     */    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
从原生注释中,我们得知这个变量控制了当一个网络获取IP失败时,之后会继续重试的次数;如果值定义为9,那么实际的重连次数将是9+1,为10。
如果我们有定制这个值,那么重连次数将以我们自定义配置的值为准:
    int WifiConfigStore::getMaxDhcpRetries() {        return Settings.Global.getInt(mContext.getContentResolver(),                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,                DEFAULT_MAX_DHCP_RETRIES);    }
我们可以配置Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT这个字段值来定制这部分:
       /**        * The maximum number of times we will retry a connection to an access        * point for which we have failed in acquiring an IP address from DHCP.        * A value of N means that we will make N+1 connection attempts in all.        */       public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
getMaxDhcpRetries()在WifiConfigStore中只在handleSSIDStateChange()函数中有使用:
void handleSSIDStateChange(int netId, boolean enabled, String message, String BSSID) {        WifiConfiguration config = mConfiguredNetworks.get(netId);        if (config != null) {            if (enabled) {                loge("Ignoring SSID re-enabled from supplicant:  " + config.configKey() +                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);                //We should not re-enable the BSSID based on Supplicant reanable.                // Framework will re-enable it after its own blacklist timer expires            } else {                loge("SSID temp disabled for  " + config.configKey() +                        " had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)                        + " self added " + config.selfAdded + " ephemeral " + config.ephemeral);                if (message != null) {                    loge(" message=" + message);                }                if (config.selfAdded && config.lastConnected == 0) {                    // This is a network we self added, and we never succeeded,                    // the user did not create this network and never entered its credentials,                    // so we want to be very aggressive in disabling it completely.                    removeConfigAndSendBroadcastIfNeeded(config.networkId);                } else {                    if (message != null) {                        if (message.contains("no identity")) {                            config.setAutoJoinStatus(                                    WifiConfiguration.AUTO_JOIN_DISABLED_NO_CREDENTIALS);                            if (DBG) {                                loge("no identity blacklisted " + config.configKey() + " to "                                        + Integer.toString(config.autoJoinStatus));                            }                        } else if (message.contains("WRONG_KEY")                                || message.contains("AUTH_FAILED")) {                            // This configuration has received an auth failure, so disable it                            // temporarily because we don't want auto-join to try it out.                            // this network may be re-enabled by the "usual"                            // enableAllNetwork function                            config.numAuthFailures++;                            if (config.numAuthFailures > maxAuthErrorsToBlacklist) {                                config.setAutoJoinStatus                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);                                disableNetwork(netId,                                        WifiConfiguration.DISABLED_AUTH_FAILURE);                                loge("Authentication failure, blacklist " + config.configKey() + " "                                            + Integer.toString(config.networkId)                                            + " num failures " + config.numAuthFailures);                            }                        } else if (message.contains("DHCP FAILURE")) {                            config.numIpConfigFailures++;                            config.lastConnectionFailure = System.currentTimeMillis();                            int maxRetries = getMaxDhcpRetries();                            // maxRetries == 0 means keep trying forever                            if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {                                /**                                 * If we've exceeded the maximum number of retries for DHCP                                 * to a given network, disable the network                                 */                                config.setAutoJoinStatus                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);                                loge("DHCP failure, blacklist " + config.configKey() + " "                                        + Integer.toString(config.networkId)                                        + " num failures " + config.numIpConfigFailures);                            }                            // Also blacklist the BSSId if we find it                            ScanResult result = null;                            String bssidDbg = "";                            if (getScanDetailCache(config) != null && BSSID != null) {                                result = getScanDetailCache(config).get(BSSID);                            }                            if (result != null) {                                result.numIpConfigFailures ++;                                bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;                                if (result.numIpConfigFailures > 3) {                                    // Tell supplicant to stop trying this BSSID                                    mWifiNative.addToBlacklist(BSSID);                                    result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);                                }                            }                            if (DBG) {                                loge("blacklisted " + config.configKey() + " to "                                        + config.autoJoinStatus                                        + " due to IP config failures, count="                                        + config.numIpConfigFailures                                        + " disableReason=" + config.disableReason                                        + " " + bssidDbg);                            }                        } else if (message.contains("CONN_FAILED")) {                            config.numConnectionFailures++;                            if (config.numConnectionFailures > maxConnectionErrorsToBlacklist) {                                config.setAutoJoinStatus                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);                                disableNetwork(netId,                                        WifiConfiguration.DISABLED_ASSOCIATION_REJECT);                                loge("Connection failure, blacklist " + config.configKey() + " "                                        + config.networkId                                        + " num failures " + config.numConnectionFailures);                            }                        }                        message.replace("\n", "");                        message.replace("\r", "");                        config.lastFailure = message;                    }                }            }        }    }
在WifiStateMachine中,如果一个网络DHCP获取IP失败、或STATIC IP配置失败、或网络的配置信息丢失,都会间接调用到handleSSIDStateChange()函数,在配置的次数内尝试网络重连。我们看一个例子:
    private void WifiStateMachine::handleIpConfigurationLost() {        mWifiInfo.setInetAddress(null);        mWifiInfo.setMeteredHint(false);        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,                "DHCP FAILURE", mWifiInfo.getBSSID());//函数调用        /* DHCP times out after about 30 seconds, we do a         * disconnect thru supplicant, we will let autojoin retry connecting to the network         */        mWifiNative.disconnect();    }
这里调用时,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不合法,就不会去继续重连流程。
    if (config.numAuthFailures > maxAuthErrorsToBlacklist) {                                config.setAutoJoinStatus                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);//设置autostatus                                disableNetwork(netId,                                        WifiConfiguration.DISABLED_AUTH_FAILURE);//disable网络                                loge("Authentication failure, blacklist " + config.configKey() + " "                                            + Integer.toString(config.networkId)                                            + " num failures " + config.numAuthFailures);                            }

  • 这里我们传入的message是DHCP FAILURE:
    else if (message.contains("DHCP FAILURE")) {                            config.numIpConfigFailures++;                            config.lastConnectionFailure = System.currentTimeMillis();                            int maxRetries = getMaxDhcpRetries();                            // maxRetries == 0 means keep trying forever                            if (maxRetries > 0 && config.numIpConfigFailures > maxRetries) {                                /**                                 * If we've exceeded the maximum number of retries for DHCP                                 * to a given network, disable the network                                 */                                config.setAutoJoinStatus                                        (WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);//设置autostatus                                disableNetwork(netId, WifiConfiguration.DISABLED_DHCP_FAILURE);//disable网络                                loge("DHCP failure, blacklist " + config.configKey() + " "                                        + Integer.toString(config.networkId)                                        + " num failures " + config.numIpConfigFailures);                            }                            // Also blacklist the BSSId if we find it                            ScanResult result = null;                            String bssidDbg = "";                            if (getScanDetailCache(config) != null && BSSID != null) {                                result = getScanDetailCache(config).get(BSSID);                            }                            if (result != null) {                                result.numIpConfigFailures ++;                                bssidDbg = BSSID + " ipfail=" + result.numIpConfigFailures;                                if (result.numIpConfigFailures > 3) {                                    // Tell supplicant to stop trying this BSSID                                    mWifiNative.addToBlacklist(BSSID);//也有可能将当前网络加入到blacklist中                                    result.setAutoJoinStatus(ScanResult.AUTO_JOIN_DISABLED);//设置autostatus                                }                            }                            if (DBG) {                                loge("blacklisted " + config.configKey() + " to "                                        + config.autoJoinStatus                                        + " due to IP config failures, count="                                        + config.numIpConfigFailures                                        + " disableReason=" + config.disableReason                                        + " " + bssidDbg);                            }                        }
    首先numIpConfigFailures自增1,该字段代表当前网络DHCP失败的次数。如果当前网络的DHCP失败次数numIpConfigFailures大于配置的DHCP最大重连次数;则会将config的autoJoinStatus设为WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE;并disable当前的网络。这时,除非手动触发连接,否则都不会自动重连了。



















0 0