Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android -- 無線網絡配置信息的管理者WifiConfigStore簡介

Android -- 無線網絡配置信息的管理者WifiConfigStore簡介

編輯:關於Android編程

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()來更新配置信息、並保存到本地;該函數的函數實現雖然較多,看起來復雜,但實際處理卻還是較為簡單的:

 

首先從mConfiguredNetworks中根據傳入的config對象獲取到先前保存過的同netId的savedConfig對象;mConfiguredNetworks是一個HasMap結構,它以某個網絡的netId為key,以對應的WifiConfiguration對象作為value,由此可知它以鍵值對的形式保存了當前所有配置過的網絡信息。後續的操作都是比對config和savedConfig直接的差異,保存到wpa_s配置文件中並進行更新,最後再將更新過的WifiConfiguration對象保存到mConfiguredNetworks中。調用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 networks = new SparseArray();
        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 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 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;
                }
            }
        }

}

二、重新打開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集合:

 

從wp_s讀取保存的網絡配置列表,並保存到mConfiguredNetworks中調用readIpAndProxyConfigurations()方法,從ipconfig.txt中讀取保存的IpConfiguration對象,更新到mConfiguredNetworks保存的各WifiConfiguration對象中調用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.
     * 

* 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當前的網絡。這時,除非手動觸發連接,否則都不會自動重連了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved