前言:wifi ap使用过程中会由于各种原因变得不可靠,比如密码错误,认证失败等等,这时候需要禁用该类型的AP使得WiFi不自动连接该AP,优化用户体验
1.ClientModeImpl
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mWifiDiagnostics.captureBugReportData(
WifiDiagnostics.REPORT_REASON_AUTH_FAILURE);
mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
int disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_FAILURE;
reasonCode = message.arg1;
// Check if this is a permanent wrong password failure.
if (isPermanentWrongPasswordFailure(mTargetNetworkId, reasonCode)) {
disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_BY_WRONG_PASSWORD;
WifiConfiguration targetedNetwork =
mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (targetedNetwork != null) {
mWrongPasswordNotifier.onWrongPasswordError(
targetedNetwork.SSID);
}
} else if (reasonCode == WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE) {
int errorCode = message.arg2;
handleEapAuthFailure(mTargetNetworkId, errorCode);
if (errorCode == WifiNative.EAP_SIM_NOT_SUBSCRIBED) {
disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_NO_SUBSCRIPTION;
}
}
mWifiConfigManager.updateNetworkSelectionStatus(
mTargetNetworkId, disableReason);
mWifiConfigManager.clearRecentFailureReason(mTargetNetworkId);
...
break;
大致逻辑是如果监听到底层上报上来的WifiMonitor.AUTHENTICATION_FAILURE_EVENT事件,则再细分判断一下类型
1.1 永久的密码错误
/**
* Determine if the specified auth failure is considered to be a permanent wrong password
* failure. The criteria for such failure is when wrong password error is detected
* and the network had never been connected before.
*
* For networks that have previously connected successfully, we consider wrong password
* failures to be temporary, to be on the conservative side. Since this might be the
* case where we are trying to connect to a wrong network (e.g. A network with same SSID
* but different password).
*/
private boolean isPermanentWrongPasswordFailure(int networkId, int reasonCode) {
if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD) {
return false;
}
WifiConfiguration network = mWifiConfigManager.getConfiguredNetwork(networkId);
if (network != null && network.getNetworkSelectionStatus().getHasEverConnected()) {
return false;
}
return true;
}
注解说的很明白,只有未成功连接过的network 报密码错误才会认为是真正的密码错误
1.2 ERROR_AUTH_FAILURE_EAP_FAILURE
else if (reasonCode == WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE) {
int errorCode = message.arg2;
handleEapAuthFailure(mTargetNetworkId, errorCode);
if (errorCode == WifiNative.EAP_SIM_NOT_SUBSCRIBED) {
disableReason = WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_NO_SUBSCRIPTION;
}
}
private void handleEapAuthFailure(int networkId, int errorCode) {
WifiConfiguration targetedNetwork =
mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (targetedNetwork != null) {
switch (targetedNetwork.enterpriseConfig.getEapMethod()) {
case WifiEnterpriseConfig.Eap.SIM:
case WifiEnterpriseConfig.Eap.AKA:
case WifiEnterpriseConfig.Eap.AKA_PRIME:
if (errorCode == WifiNative.EAP_SIM_VENDOR_SPECIFIC_CERT_EXPIRED) {
getTelephonyManager()
.createForSubscriptionId(
SubscriptionManager.getDefaultDataSubscriptionId())
.resetCarrierKeysForImsiEncryption();
}
break;
default:
// Do Nothing
}
}
}
由于eap-sim aka aka_prime与sim卡有关,所有失败处理看起来是和tele reset相关
2. WifiConfigManager
/**
* Update a network's status (both internal and public) according to the update reason and
* its current state.
*
* Each network has 2 status:
* 1. NetworkSelectionStatus: This is internal selection status of the network. This is used
* for temporarily disabling a network for Network Selector.
* 2. Status: This is the exposed status for a network. This is mostly set by
* the public API's {@link WifiManager#enableNetwork(int, boolean)} &
* {@link WifiManager#disableNetwork(int)}.
*
* @param networkId network ID of the network that needs the update.
* @param reason reason to update the network.
* @return true if the input configuration has been updated, false otherwise.
*/
public boolean updateNetworkSelectionStatus(int networkId, int reason) {
WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return false;
}
return updateNetworkSelectionStatus(config, reason);
}
更新WiFi configuration里的状态
/**
* Update a network's status (both internal and public) according to the update reason and
* its current state.
*
* @param config network to be updated.
* @param reason reason code for update.
* @return true if the input configuration has been updated, false otherwise.
*/
private boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (reason != NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
// Do not update SSID blacklist with information if this is the only
// SSID be observed. By ignoring it we will cause additional failures
// which will trigger Watchdog.
if (reason == NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION
|| reason == NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE
|| reason == NetworkSelectionStatus.DISABLED_DHCP_FAILURE) {
if (mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate()) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Ignore update network selection status "
+ "since Watchdog trigger is activated");
}
return false;
}
}
WiFi禁用有例外条件,当该ap是当前WifiConnectivityManager唯一可候选的AP并且该AP之前连接上过,那么就不能disable它,Google估计是基于这是唯一可能连上WiFi的考虑,即使WiFi重启也不disable。
networkStatus.incrementDisableReasonCounter(reason);
// For network disable reasons, we should only update the status if we cross the
// threshold.
int disableReasonCounter = networkStatus.getDisableReasonCounter(reason);
int disableReasonThreshold = NETWORK_SELECTION_DISABLE_THRESHOLD[reason];
if (disableReasonCounter < disableReasonThreshold) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Disable counter for network " + config.getPrintableSsid()
+ " for reason "
+ NetworkSelectionStatus.getNetworkDisableReasonString(reason) + " is "
+ networkStatus.getDisableReasonCounter(reason) + " and threshold is "
+ disableReasonThreshold);
}
return true;
}
}
return setNetworkSelectionStatus(config, reason);
}
增加该configuration相应失败reason的计数,每个reason有个阈值,当达到阈值的时候就disable这个AP
阈值是如下定义
/**
* Network Selection disable reason thresholds. These numbers are used to debounce network
* failures before we disable them.
* These are indexed using the disable reason constants defined in
* {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
*/
@VisibleForTesting
public static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
-1, // threshold for NETWORK_SELECTION_ENABLE
1, // threshold for DISABLED_BAD_LINK
5, // threshold for DISABLED_ASSOCIATION_REJECTION
5, // threshold for DISABLED_AUTHENTICATION_FAILURE
5, // threshold for DISABLED_DHCP_FAILURE
5, // threshold for DISABLED_DNS_FAILURE
1, // threshold for DISABLED_NO_INTERNET_TEMPORARY
1, // threshold for DISABLED_WPS_START
6, // threshold for DISABLED_TLS_VERSION_MISMATCH
1, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
1, // threshold for DISABLED_NO_INTERNET_PERMANENT
1, // threshold for DISABLED_BY_WIFI_MANAGER
1, // threshold for DISABLED_BY_USER_SWITCH
1, // threshold for DISABLED_BY_WRONG_PASSWORD
1 // threshold for DISABLED_AUTHENTICATION_NO_SUBSCRIBED
};
在打开WiFi详细日志后每个ap的summary里也会显示各个大于0计数的reason。
/**
* Sets a network's status (both internal and public) according to the update reason and
* its current state.
*
* This updates the network's {@link WifiConfiguration#mNetworkSelectionStatus} field and the
* public {@link WifiConfiguration#status} field if the network is either enabled or
* permanently disabled.
*
* @param config network to be updated.
* @param reason reason code for update.
* @return true if the input configuration has been updated, false otherwise.
*/
private boolean setNetworkSelectionStatus(WifiConfiguration config, int reason) {
NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (reason < 0 || reason >= NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX) {
Log.e(TAG, "Invalid Network disable reason " + reason);
return false;
}
if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
setNetworkSelectionEnabled(config);
setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
} else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
setNetworkSelectionTemporarilyDisabled(config, reason);
} else {
setNetworkSelectionPermanentlyDisabled(config, reason);
setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
}
localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
+ " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
+ networkStatus.getNetworkDisableReasonString() + " at="
+ createDebugTimeStampString(mClock.getWallClockMillis()));
saveToStore(false);
return true;
}
然后禁用分暂时禁用和永久禁用
2.1 暂时禁用reason列表
//Quality Network disabled reasons
/**
* Default value. Means not disabled
*/
public static final int NETWORK_SELECTION_ENABLE = 0;
/**
* The starting index for network selection disabled reasons
*/
public static final int NETWORK_SELECTION_DISABLED_STARTING_INDEX = 1;
/**
* @deprecated it is not used any more.
* This network is disabled because higher layer (>2) network is bad
*/
public static final int DISABLED_BAD_LINK = 1;
/**
* This network is disabled because multiple association rejects
*/
public static final int DISABLED_ASSOCIATION_REJECTION = 2;
/**
* This network is disabled because multiple authentication failure
*/
public static final int DISABLED_AUTHENTICATION_FAILURE = 3;
/**
* This network is disabled because multiple DHCP failure
*/
public static final int DISABLED_DHCP_FAILURE = 4;
/**
* This network is disabled because of security network but no credentials
*/
public static final int DISABLED_DNS_FAILURE = 5;
/**
* This network is temporarily disabled because it has no Internet access.
*/
public static final int DISABLED_NO_INTERNET_TEMPORARY = 6;
/**
* This network is disabled because we started WPS
*/
public static final int DISABLED_WPS_START = 7;
2.2 永久禁用
(注释写的不大对,从代码来看DISABLED_TLS_VERSION_MISMATCH也是永久禁用)
/**
* This network is disabled because EAP-TLS failure
*/
public static final int DISABLED_TLS_VERSION_MISMATCH = 8;
// Values above are for temporary disablement; values below are for permanent disablement.
/**
* This network is disabled due to absence of user credentials
*/
public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 9;
/**
* This network is permanently disabled because it has no Internet access and user does not
* want to stay connected.
*/
public static final int DISABLED_NO_INTERNET_PERMANENT = 10;
/**
* This network is disabled due to WifiManager disable it explicitly
*/
public static final int DISABLED_BY_WIFI_MANAGER = 11;
/**
* This network is disabled due to user switching
*/
public static final int DISABLED_DUE_TO_USER_SWITCH = 12;
/**
* This network is disabled due to wrong password
*/
public static final int DISABLED_BY_WRONG_PASSWORD = 13;
/**
* This network is disabled because service is not subscribed
*/
public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 14;
/**
* This Maximum disable reason value
*/
public static final int NETWORK_SELECTION_DISABLED_MAX = 15;