1.临时禁用
WifiConfigManager
/**
* Helper method to mark a network temporarily disabled for network selection.
*/
private void setNetworkSelectionTemporarilyDisabled(
WifiConfiguration config, int disableReason) {
NetworkSelectionStatus status = config.getNetworkSelectionStatus();
status.setNetworkSelectionStatus(
NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
// Only need a valid time filled in for temporarily disabled networks.
status.setDisableTime(mClock.getElapsedSinceBootMillis());
status.setNetworkSelectionDisableReason(disableReason);
if (mListener != null) {
mListener.onSavedNetworkTemporarilyDisabled(config.networkId, disableReason);
}
}
可以看到临时禁用会回调一个listener,看下这个listener是哪里被初始化的
WifiConnectivityManager
// Listen to WifiConfigManager network update events
mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener());
private class OnSavedNetworkUpdateListener implements
WifiConfigManager.OnSavedNetworkUpdateListener {
...
@Override
public void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason) {
if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
mConnectivityHelper.removeNetworkIfCurrent(networkId);
}
@Override
public void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason) {
// For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network
// because supplicant won't be trying to reconnect. If this is due to a
// preventAutomaticReconnect request from ConnectivityService, that service
// will disconnect as appropriate.
if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
mConnectivityHelper.removeNetworkIfCurrent(networkId);
updatePnoScan();
}
private void updatePnoScan() {
// Update the PNO scan network list when screen is off. Here we
// rely on startConnectivityScan() to perform all the checks and clean up.
if (!mScreenOn) {
localLog("Saved networks updated");
startConnectivityScan(false);
}
}
}
看下ConnectivityHelper的逻辑
/**
* Remove the request |networkId| from supplicant if it's the current network,
* if the current configured network matches |networkId|.
*
* @param networkId network id of the network to be removed from supplicant.
*/
public void removeNetworkIfCurrent(int networkId) {
mWifiNative.removeNetworkIfCurrent(mWifiNative.getClientInterfaceName(), networkId);
}
/** Remove the request |networkId| from supplicant if it's the current network,
* if the current configured network matches |networkId|.
*
* @param ifaceName Name of the interface.
* @param networkId network id of the network to be removed from supplicant.
*/
public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
mSupplicantStaIfaceHal.removeNetworkIfCurrent(ifaceName, networkId);
}
/**
* Remove the request |networkId| from supplicant if it's the current network,
* if the current configured network matches |networkId|.
*
* @param ifaceName Name of the interface.
* @param networkId network id of the network to be removed from supplicant.
*/
public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
synchronized (mLock) {
if (getCurrentNetworkId(ifaceName) == networkId) {
// Currently we only save 1 network in supplicant.
removeAllNetworks(ifaceName);
}
}
}
/**
* Remove all networks from supplicant
*
* @param ifaceName Name of the interface.
*/
public boolean removeAllNetworks(@NonNull String ifaceName) {
synchronized (mLock) {
ArrayList<Integer> networks = listNetworks(ifaceName);
if (networks == null) {
Log.e(TAG, "removeAllNetworks failed, got null networks");
return false;
}
for (int id : networks) {
if (!removeNetwork(ifaceName, id)) {
Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
return false;
}
}
// Reset current network info. Probably not needed once we add support to remove/reset
// current network on receiving disconnection event from supplicant (b/32898136).
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
return true;
}
}
大概意思是如果该网络被禁用掉了,那么同步通知到supplicant将该网络移除
2.临时禁用的意义
2.1 pno
/**
* Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
*
* PNO network list sent to the firmware has limited size. If there are a lot of saved
* networks, this list will be truncated and we might end up not sending the networks
* with the highest chance of connecting to the firmware.
* So, re-sort the network list based on the frequency of connection to those networks
* and whether it was last seen in the scan results.
*
* @return list of networks in the order of priority.
*/
public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
// Remove any permanently or temporarily disabled networks.
Iterator<WifiConfiguration> iter = networks.iterator();
while (iter.hasNext()) {
WifiConfiguration config = iter.next();
if (config.ephemeral || config.isPasspoint()
|| config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
|| config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
iter.remove();
}
}
...
if (mVerboseLoggingEnabled) {
Log.v(TAG, "retrievePnoNetworkList " + pnoNetwork.ssid + ":"
+ Arrays.toString(pnoNetwork.frequencies));
}
}
return pnoList;
}
pno扫描会提前移除被禁用的
2.2 SavedNetworkEvaluator
/**
* Evaluate all the networks from the scan results and return
* the WifiConfiguration of the network chosen for connection.
*
* @return configuration of the chosen network;
* null if no network in this category is available.
*/
@Override
public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
WifiConfiguration currentNetwork, String currentBssid, boolean connected,
boolean untrustedNetworkAllowed,
@NonNull OnConnectableListener onConnectableListener) {
int highestScore = Integer.MIN_VALUE;
ScanResult scanResultCandidate = null;
WifiConfiguration candidate = null;
StringBuffer scoreHistory = new StringBuffer();
for (ScanDetail scanDetail : scanDetails) {
ScanResult scanResult = scanDetail.getScanResult();
// One ScanResult can be associated with more than one network, hence we calculate all
// the scores and use the highest one as the ScanResult's score.
// TODO(b/112196799): this has side effects, rather not do that in an evaluator
WifiConfiguration network =
mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
if (network == null) {
continue;
}
/**
* Ignore Passpoint and Ephemeral networks. They are configured networks,
* but without being persisted to the storage. They are evaluated by
* {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
* respectively.
*/
if (network.isPasspoint() || network.isEphemeral()) {
continue;
}
WifiConfiguration.NetworkSelectionStatus status =
network.getNetworkSelectionStatus();
// TODO (b/112196799): another side effect
status.setSeenInLastQualifiedNetworkSelection(true);
if (!status.isNetworkEnabled()) {
continue;
已保存网络评估器会直接不考虑被禁用的网络
2.3 评分
看起来是WifiConfigManager里对扫描列表做排序用的
/**
* Returns an integer representing a score for each configuration. The scores are assigned
* based on the status of the configuration. The scores are assigned according to the order:
* Fully enabled network > Temporarily disabled network > Permanently disabled network.
*/
private int getNetworkStatusScore(WifiConfiguration config) {
if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
return ENABLED_NETWORK_SCORE;
} else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
return TEMPORARY_DISABLED_NETWORK_SCORE;
} else {
return PERMANENTLY_DISABLED_NETWORK_SCORE;
}
}
3.临时禁用何时恢复
/**
* network was temporary disabled. Can be re-enabled after a time period expire
*/
public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1;
从定义来看是过一段时间会重启启用,那这段逻辑是哪里的呢?
WifiConnectivityManager
/**
* Select the best network from the ones in range.
*
* @param scanDetails List of ScanDetail for all the APs in range
* @param bssidBlacklist Blacklisted BSSIDs
* @param wifiInfo Currently connected network
* @param connected True if the device is connected
* @param disconnected True if the device is disconnected
* @param untrustedNetworkAllowed True if untrusted networks are allowed for connection
* @return Configuration of the selected network, or Null if nothing
*/
@Nullable
public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) {
mFilteredNetworks.clear();
mConnectableNetworks.clear();
if (scanDetails.size() == 0) {
localLog("Empty connectivity scan result");
return null;
}
WifiConfiguration currentNetwork =
mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
// Always get the current BSSID from WifiInfo in case that firmware initiated
// roaming happened.
String currentBssid = wifiInfo.getBSSID();
// Shall we start network selection at all?
if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
return null;
}
// Update all configured networks before initiating network selection.
updateConfiguredNetworks();
/**
* Iterate thru the list of configured networks (includes all saved network configurations +
* any ephemeral network configurations created for passpoint networks, suggestions, carrier
* networks, etc) and do the following:
* a) Try to re-enable any temporarily enabled networks (if the blacklist duration has expired).
* b) Clear the {@link WifiConfiguration.NetworkSelectionStatus#getCandidate()} field for all
* of them to identify networks that are present in the current scan result.
* c) Log any disabled networks.
*/
private void updateConfiguredNetworks() {
List<WifiConfiguration> configuredNetworks = mWifiConfigManager.getConfiguredNetworks();
if (configuredNetworks.size() == 0) {
localLog("No configured networks.");
return;
}
StringBuffer sbuf = new StringBuffer();
for (WifiConfiguration network : configuredNetworks) {
// If a configuration is temporarily disabled, re-enable it before trying
// to connect to it.
mWifiConfigManager.tryEnableNetwork(network.networkId);
下一次评分的时候会将临时禁用的恢复回来
/**
* Attempt to re-enable a network for network selection, if this network was either:
* a) Previously temporarily disabled, but its disable timeout has expired, or
* b) Previously disabled because of a user switch, but is now visible to the current
* user.
*
* @param config configuration for the network to be re-enabled for network selection. The
* network corresponding to the config must be visible to the current user.
* @return true if the network identified by {@param config} was re-enabled for qualified
* network selection, false otherwise.
*/
private boolean tryEnableNetwork(WifiConfiguration config) {
NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (networkStatus.isNetworkTemporaryDisabled()) {
long timeDifferenceMs =
mClock.getElapsedSinceBootMillis() - networkStatus.getDisableTime();
int disableReason = networkStatus.getNetworkSelectionDisableReason();
long disableTimeoutMs = NETWORK_SELECTION_DISABLE_TIMEOUT_MS[disableReason];
if (timeDifferenceMs >= disableTimeoutMs) {
return updateNetworkSelectionStatus(
config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
}
} else if (networkStatus.isDisabledByReason(
NetworkSelectionStatus.DISABLED_DUE_TO_USER_SWITCH)) {
return updateNetworkSelectionStatus(
config, NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
}
return false;
}
临时禁用有个超时机制
/**
* Network Selection disable timeout for each kind of error. After the timeout milliseconds,
* enable the network again.
* These are indexed using the disable reason constants defined in
* {@link android.net.wifi.WifiConfiguration.NetworkSelectionStatus}.
*/
@VisibleForTesting
public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE
15 * 60 * 1000, // threshold for DISABLED_BAD_LINK
5 * 60 * 1000, // threshold for DISABLED_ASSOCIATION_REJECTION
5 * 60 * 1000, // threshold for DISABLED_AUTHENTICATION_FAILURE
5 * 60 * 1000, // threshold for DISABLED_DHCP_FAILURE
5 * 60 * 1000, // threshold for DISABLED_DNS_FAILURE
10 * 60 * 1000, // threshold for DISABLED_NO_INTERNET_TEMPORARY
0 * 60 * 1000, // threshold for DISABLED_WPS_START
Integer.MAX_VALUE, // threshold for DISABLED_TLS_VERSION
Integer.MAX_VALUE, // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
Integer.MAX_VALUE, // threshold for DISABLED_NO_INTERNET_PERMANENT
Integer.MAX_VALUE, // threshold for DISABLED_BY_WIFI_MANAGER
Integer.MAX_VALUE, // threshold for DISABLED_BY_USER_SWITCH
Integer.MAX_VALUE, // threshold for DISABLED_BY_WRONG_PASSWORD
Integer.MAX_VALUE // threshold for DISABLED_AUTHENTICATION_NO_SUBSCRIBED
};
3.总结
临时禁用设计的比较巧妙,主要是来避免接下来即刻的自动连接的,根据禁用原因设置了不同的超时时间,当超时时间到了,并且下次framework扫描到了该ap,该ap就会从临时禁用的状态恢复回来。