Wifi连接隐藏SSID及wifi主要广播的处理

private void confirmToJoin(WifiConfiguration config, WifiManager.ActionListener saveListener,
                               WifiManager.ActionListener connectListener){
        backupId =mWifiManager.getConnectionInfo().getNetworkId();
        mDisableNetWorkId = backupId;
        if(backupId != -1){
            mWifiManager.disableNetwork(backupId);
        }

        if(config.preSharedKey != null){
            Log.e(TAG, "security: PSK  " + "ssid: " + config.SSID +"  config.preSharedKey: " + config.preSharedKey);
        }else if(config.wepKeys[0] != null){
            Log.e(TAG, "security: WEP" + "ssid: " + config.SSID +"  config.wepKeys[0]: " + config.wepKeys[0]);
        }else {
            Log.e(TAG, "security: NONE" + "ssid: " + config.SSID );
        }

        Utils.getInstance().removeWifi(mWifiManager, config.SSID.replace("\"", ""));
        mWifiManager.save(config, saveListener);
        mWifiManager.connect(config, connectListener);
        mAccessPointSecurity = -1;
        handler.sendEmptyMessage(2);
    }

如果当前有已连接的网络,必须先断开,不能使用disconnect(),而要使用WifiManager#disableNetwork(networkId);
否则会自动重连回原来的网络。

使用enableNetwork()的方式去连接隐藏SSID无法成功,即使第二个参数disableOther为true;关于connect(config,connectListener)这个方法中的connectListener是在什么情况下回调onSuccesss的,
不得而知,无论密码是否正确,是否有这个ap,都会回调onSuccess()。估计是代表认证成功吧,因为如果使用WPA/WPA2的话,使用的认证方式是Open System Authentication,这种验证其实等同于没有验证,因为无论谁来验证都会被通过。所以即使密码错误也会回调onSuccess()。

调用disableNetwork()断开网络时,会触发一次CONNECTED的广播。感觉这是个系统bug。

使用disableNetwork()断开网络后,然后去连接另一个网络,如果连接失败后,不会重连会被disable的网络,这个方法
中有关于该特性的注释。

连接隐藏SSID时,在构造这个WifiConfiguration时也是需要小心谨慎,有点不对都会连接失败。
以下是连接NONE,WEP,PSK三种加密方式的WifiConfiguration构造:

private WifiConfiguration createAddNetworkConfig(){
        WifiConfiguration config = new WifiConfiguration();
        config.SSID = AccessPoint.convertToQuotedString(
                mAddNetworkSsidEt.getText().toString());
        config.hiddenSSID = true;
        
        switch (mAccessPointSecurity) {
            case AccessPoint.SECURITY_NONE:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                break;

            case AccessPoint.SECURITY_WEP:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
                config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
                if (mAddNetworkPasswordEt.length() != 0) {
                    int length = mAddNetworkPasswordEt.length();
                    String password = mAddNetworkPasswordEt.getText().toString();
                    if (password.length() < 8) {
                        Toast.makeText(getActivity(), getText(R.string.password_num_less_than_eight), Toast.LENGTH_SHORT).show();
                        return null;
                    }
                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                    if ((length == 10 || length == 26 || length == 58) &&
                            password.matches("[0-9A-Fa-f]*")) {
                        config.wepKeys[0] = password;
                    } else {
                        config.wepKeys[0] = '"' + password + '"';
                    }
                }else {
                    return null;
                }
                break;

            case AccessPoint.SECURITY_PSK:
                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
                if (mAddNetworkPasswordEt.length() != 0) {
                    String password = mAddNetworkPasswordEt.getText().toString();
                    if (password.length() < 8) {
                        Toast.makeText(getActivity(), getText(R.string.password_num_less_than_eight), Toast.LENGTH_SHORT).show();
                        return null;
                    }
                    if (password.matches("[0-9A-Fa-f]{64}")) {
                        config.preSharedKey = password;
                    } else {
                        config.preSharedKey = '"' + password + '"';
                    }
                }else{
                    return null;
                }
                break;

            case AccessPoint.SECURITY_EAP:
                return null;
            default:
                return null;
        }
        return config;
    }

跟配置上面的属性有关的内容可以从以下两篇文章中了解:

https://blog.csdn.net/b1480521874/article/details/79049582

https://blog.csdn.net/b1480521874/article/details/79270353


解决连接SSID不成功后,重连会之前断开的网络:
由于连接隐藏SSID必须使用disableNetwork,所以必须要在断开网络时,记录被断开网络的networkId。
在收到WifiManager.SUPPLICANT_STATE_CHANGED_ACTION广播,intent.getIntExtra(
WifiManager.EXTRA_SUPPLICANT_ERROR, 123) == WifiManager.ERROR_AUTHENTICATING
时,就去重连记录的networkId。

private WifiConfiguration getWifiConfigurationBySsid(String ssid){
        List configurations = mWifiManager.getConfiguredNetworks();
        if(configurations == null) return null;
        for (WifiConfiguration configuration : configurations) {
            Log.e(TAG, "configuration.SSID: " + configuration.SSID + "   ssid: " + ssid);
            if (configuration.SSID.equals("\"" + ssid + "\"")) {
                return configuration;

            }
        }
        return  null;
    }

获取到的之前保存好的WifiConfiguration,不能直接用于调用connect(WifiConfiguration),因为获取到的可能经过底层的一些改动,如:密码参数换成了星号*。可以使用WifiConfiguration#networkId去连接。

/**
     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
     * enabling, disabling, or unknown. One extra provides this state as an int.
     * Another extra provides the previous state, if available.
     *
     * @see #EXTRA_WIFI_STATE
     * @see #EXTRA_PREVIOUS_WIFI_STATE
     */
WifiManager.WIFI_STATE_CHANGED_ACTION


/**
     * Broadcast intent action indicating that the state of Wi-Fi connectivity
     * has changed. One extra provides the new state
     * in the form of a {@link android.net.NetworkInfo} object. If the new
     * state is CONNECTED, additional extras may provide the BSSID and WifiInfo of
     * the access point.
     * as a {@code String}.
     * @see #EXTRA_NETWORK_INFO
     * @see #EXTRA_BSSID
     * @see #EXTRA_WIFI_INFO
     */
WifiManager.NETWORK_STATE_CHANGED_ACTION



/**
     * Broadcast intent action indicating that the state of establishing a connection to
     * an access point has changed.One extra provides the new
     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
     * is not generally the most useful thing to look at if you are just interested in
     * the overall state of connectivity.
     * @see #EXTRA_NEW_STATE
     * @see #EXTRA_SUPPLICANT_ERROR
     */
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION

EXTRA_NEW_STATE对应的是public enum SupplicantState implements Parcelable
主要supplicantState如下:
/**
     * This state indicates that client is not associated, but is likely to
     * start looking for an access point. This state is entered when a
     * connection is lost.
     */
    DISCONNECTED,

    /**
     * Interface is disabled
     * 
     * This state is entered if the network interface is disabled.
     * wpa_supplicant refuses any new operations that would
     * use the radio until the interface has been enabled.
     */
    INTERFACE_DISABLED,

    /**
     * Inactive state (wpa_supplicant disabled).
     * 
     * This state is entered if there are no enabled networks in the
     * configuration. wpa_supplicant is not trying to associate with a new
     * network and external interaction (e.g., ctrl_iface call to add or
     * enable a network) is needed to start association.
     */
    INACTIVE,

    /**
     * Scanning for a network.
     * 
     * This state is entered when wpa_supplicant starts scanning for a
     * network.
     */
    SCANNING,

    /**
     * Trying to authenticate with a BSS/SSID
     * 
     * This state is entered when wpa_supplicant has found a suitable BSS
     * to authenticate with and the driver is configured to try to
     * authenticate with this BSS.
     */
    AUTHENTICATING,

    /**
     * Trying to associate with a BSS/SSID.
     * 
     * This state is entered when wpa_supplicant has found a suitable BSS
     * to associate with and the driver is configured to try to associate
     * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
     * state is entered when the driver is configured to try to associate
     * with a network using the configured SSID and security policy.
     */
    ASSOCIATING,

    /**
     * Association completed.
     * 
     * This state is entered when the driver reports that association has
     * been successfully completed with an AP. If IEEE 802.1X is used
     * (with or without WPA/WPA2), wpa_supplicant remains in this state
     * until the IEEE 802.1X/EAPOL authentication has been completed.
     */
    ASSOCIATED,

    /**
     * WPA 4-Way Key Handshake in progress.
     * 
     * This state is entered when WPA/WPA2 4-Way Handshake is started. In
     * case of WPA-PSK, this happens when receiving the first EAPOL-Key
     * frame after association. In case of WPA-EAP, this state is entered
     * when the IEEE 802.1X/EAPOL authentication has been completed.
     */
    FOUR_WAY_HANDSHAKE,

    /**
     * WPA Group Key Handshake in progress.
     *
     * This state is entered when 4-Way Key Handshake has been completed
     * (i.e., when the supplicant sends out message 4/4) and when Group
     * Key rekeying is started by the AP (i.e., when supplicant receives
     * message 1/2).
     */
    GROUP_HANDSHAKE,

    /**
     * All authentication completed.
     * 
     * This state is entered when the full authentication process is
     * completed. In case of WPA2, this happens when the 4-Way Handshake is
     * successfully completed. With WPA, this state is entered after the
     * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
     * completed after dynamic keys are received (or if not used, after
     * the EAP authentication has been completed). With static WEP keys and
     * plaintext connections, this state is entered when an association
     * has been completed.
     * 
     * This state indicates that the supplicant has completed its
     * processing for the association phase and that data connection is
     * fully configured. Note, however, that there may not be any IP
     * address associated with the connection yet. Typically, a DHCP
     * request needs to be sent at this point to obtain an address.
     */
    COMPLETED,

    /**
     * An Android-added state that is reported when a client issues an
     * explicit DISCONNECT command. In such a case, the supplicant is
     * not only dissociated from the current access point (as for the
     * DISCONNECTED state above), but it also does not attempt to connect
     * to any access point until a RECONNECT or REASSOCIATE command
     * is issued by the client.
     */
    DORMANT,

    /**
     * No connection to wpa_supplicant.
     * 
     * This is an additional pseudo-state to handle the case where
     * wpa_supplicant is not running and/or we have not been able
     * to establish a connection to it.
     */
    UNINITIALIZED,

    /**
     * A pseudo-state that should normally never be seen.
     */
    INVALID;





/**
     * @see #WIFI_STATE_DISABLED
     * @see #WIFI_STATE_DISABLING
     * @see #WIFI_STATE_ENABLED
     * @see #WIFI_STATE_ENABLING
     * @see #WIFI_STATE_UNKNOWN
     */
    public static final String EXTRA_WIFI_STATE = "wifi_state";



Parcelable parcelableExtra = intent
                        .getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                if (null != parcelableExtra) {
                        NetworkInfo networkInfo = (NetworkInfo) parcelableExtra;
                        NetworkInfo.State state = networkInfo.getState();}

EXTRA_NETWORK_INFO
public enum State {
        CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
    }


应用层将所有参数打包到WiFiConfiguration,传给wpa_supplicant(wpas),源码文件是wpa_supplicant.c,然后supplicant继续丰满各种参数,传给驱动层,源码文件是driver_nl80211.c。

连接过程中,认证,再关联,再通过RSN(Robust Security Network,强健安全网络)认证(即4-Way Handshake(四次握手))。但是RSNA

WPAS运行过程中得到的无线网络信息都会通过一个"network"配置项保存到此配置文件
中。如果该信息完整,一旦WPAS找到该无线网络就会尝试用保存的信息去加入它(这也是
为什么用户在settings中打开无线网络后,手机能自动加入周围某个曾经登录过的无线网络的
原因)。
    network项包括的内容非常多。network项展示了该无线网络的ssid、密钥管
理方法(key management)、身份认证方法及密码等信息。network中的priority表示无线网络
的优先级。其作用是,如果同时存在多个可用的无线网络,WPAS优先选择priority高的那一
个。
 

配置文件全路径名为/data/misc/wifi/wpa_supplicant.conf
也有文件在/etc/wifi/下。

但是在WifiManager#getScanResult()方法中获得的ScanResult没发得到对方支持单播组播的加密,所以应该wpas层应该会自动去选在单播组播的加密方式,而应用层不用去设置。应用层只需提供SSID,密码,密钥管理方式(none,psk, wep)
 

猜你喜欢

转载自blog.csdn.net/b1480521874/article/details/79267469