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)