summaryrefslogtreecommitdiffstats
path: root/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
diff options
context:
space:
mode:
Diffstat (limited to 'service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java')
-rw-r--r--service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java284
1 files changed, 276 insertions, 8 deletions
diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
index 00fffdb4b..e429d835a 100644
--- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
@@ -17,26 +17,36 @@
package com.android.server.wifi;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
import android.text.TextUtils;
+import android.util.Base64;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.ScanDetailUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -70,6 +80,10 @@ public class WifiQualifiedNetworkSelector {
//usable only when current state is connected state default 10 s
private static final int MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL = 10 * 1000;
+ private static final int CARRIER_SSID = 0;
+ private static final int CARRIER_KEY = 1;
+ private static final int CARRIER_EAP_METHOD = 2;
+
//if current network is on 2.4GHz band and has a RSSI over this, need not new network selection
public static final int QUALIFIED_RSSI_24G_BAND = -73;
//if current network is on 5GHz band and has a RSSI over this, need not new network selection
@@ -109,6 +123,8 @@ public class WifiQualifiedNetworkSelector {
private int mUserPreferedBand = WifiManager.WIFI_FREQUENCY_BAND_AUTO;
private Map<String, BssidBlacklistStatus> mBssidBlacklist =
new HashMap<String, BssidBlacklistStatus>();
+ private List<WifiConfiguration> mCarrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
+ private Context mContext;
/**
* class save the blacklist status of a given BSSID
@@ -127,6 +143,11 @@ public class WifiQualifiedNetworkSelector {
}
}
+ @VisibleForTesting
+ public void setCarrierConfiguredNetworks(List<WifiConfiguration> carrierConfiguredNetworks) {
+ mCarrierConfiguredNetworks = carrierConfiguredNetworks;
+ }
+
private void localLoge(String log) {
mLocalLog.log(log);
}
@@ -167,6 +188,7 @@ public class WifiQualifiedNetworkSelector {
mWifiConfigManager = configureStore;
mWifiInfo = wifiInfo;
mClock = clock;
+ mContext = context;
mScoreManager =
(NetworkScoreManager) context.getSystemService(Context.NETWORK_SCORE_SERVICE);
if (mScoreManager != null) {
@@ -193,8 +215,73 @@ public class WifiQualifiedNetworkSelector {
mNoIntnetPenalty = (mWifiConfigManager.mThresholdSaturatedRssi24.get() + mRssiScoreOffset)
* mRssiScoreSlope + mWifiConfigManager.mBandAward5Ghz.get()
+ mWifiConfigManager.mCurrentNetworkBoost.get() + mSameBssidAward + mSecurityAward;
+
+ context.registerReceiver(mBroadcastReceiver, new IntentFilter(
+ CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ }
+
+ @VisibleForTesting
+ public List<WifiConfiguration> parseCarrierSuppliedWifiInfo(String[] wifiArray) {
+ List<WifiConfiguration> carrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
+ for (String config : wifiArray) {
+ String[] wc = config.split("\\|");
+ if (wc.length != 3) {
+ continue;
+ }
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ try {
+ byte[] decodedBytes = Base64.decode(wc[CARRIER_SSID], Base64.DEFAULT);
+ String ssid = new String(decodedBytes);
+ wifiConfig.SSID = "\"" + ssid + "\"";
+ } catch (IllegalArgumentException ex) {
+ localLog("mBroadcaseReceiver: Could not decode base64 string");
+ continue;
+ }
+ try {
+ int key = Integer.parseInt(wc[CARRIER_KEY]);
+ wifiConfig.allowedKeyManagement.set(key);
+ int eapType = Integer.parseInt(wc[CARRIER_EAP_METHOD]);
+ wifiConfig.enterpriseConfig = new WifiEnterpriseConfig();
+ wifiConfig.enterpriseConfig.setEapMethod(eapType);
+ } catch (NumberFormatException e) {
+ localLog("mBroadcastReceiver: not an integer:" + wc[CARRIER_KEY] + "," +
+ wc[CARRIER_EAP_METHOD]);
+ continue;
+ } catch (IllegalArgumentException e) {
+ localLog("mBroadcastReceiver: invalid config" + wc[CARRIER_KEY] + "," +
+ wc[CARRIER_EAP_METHOD]);
+ }
+ carrierConfiguredNetworks.add(wifiConfig);
+ localLog("mBroadcastReceiver: carrier config:" + wifiConfig.SSID);
+ }
+ return carrierConfiguredNetworks;
}
+ final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ localLog("mBroadcastReceiver: onReceive " + intent.getAction());
+ String[] wifiArray = null;
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager != null) {
+ PersistableBundle b = carrierConfigManager.getConfig();
+ if (b != null) {
+ wifiArray = b.getStringArray(
+ CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY);
+ }
+ }
+
+ if (wifiArray == null) {
+ return;
+ }
+ mCarrierConfiguredNetworks = parseCarrierSuppliedWifiInfo(wifiArray);
+ boolean hasCarrierNetworks = (mCarrierConfiguredNetworks == null ||
+ mCarrierConfiguredNetworks.size() == 0) ? false : true;
+ mWifiConfigManager.setHasCarrierNetworks(hasCarrierNetworks);
+ }
+ };
+
void enableVerboseLogging(int verbose) {
mDbg = verbose > 0 || FORCE_DEBUG;
}
@@ -240,7 +327,7 @@ public class WifiQualifiedNetworkSelector {
// Current network band must match with user preference selection
if (mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ)) {
- localLog("Current band dose not match user preference. Start Qualified Network"
+ localLog("Current band does not match user preference. Start Qualified Network"
+ " Selection Current band = " + (mWifiInfo.is24GHz() ? "2.4GHz band"
: "5GHz band") + "UserPreference band = " + mUserPreferedBand);
return false;
@@ -591,6 +678,11 @@ public class WifiQualifiedNetworkSelector {
return status == null ? false : status.mIsBlacklisted;
}
+ private boolean isCarrierNetwork(ScanResult scanResult) {
+ return (getMatchingConfigForEAPNetworks(scanResult,
+ mCarrierConfiguredNetworks) != null ? true : false);
+ }
+
/**
* ToDo: This should be called in Connectivity Manager when it gets new scan result
* check whether a network slection is needed. If need, check all the new scan results and
@@ -614,7 +706,7 @@ public class WifiQualifiedNetworkSelector {
* false -- supplicant is not in a transient state
* @return the qualified network candidate found. If no available candidate, return null
*/
- public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork ,
+ public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork,
boolean isUntrustedConnectionsAllowed, List<ScanDetail> scanDetails,
boolean isLinkDebouncing, boolean isConnected, boolean isDisconnected,
boolean isSupplicantTransient) {
@@ -641,8 +733,11 @@ public class WifiQualifiedNetworkSelector {
int currentHighestScore = Integer.MIN_VALUE;
ScanResult scanResultCandidate = null;
WifiConfiguration networkCandidate = null;
+ WifiConfiguration carrierCandidate = null;
final ExternalScoreEvaluator externalScoreEvaluator =
new ExternalScoreEvaluator(mLocalLog, mDbg);
+ final CarrierScoreEvaluator carrierScoreEvaluator =
+ new CarrierScoreEvaluator(mLocalLog, mDbg);
String lastUserSelectedNetWorkKey = mWifiConfigManager.getLastSelectedConfiguration();
WifiConfiguration lastUserSelectedNetwork =
mWifiConfigManager.getWifiConfiguration(lastUserSelectedNetWorkKey);
@@ -660,6 +755,10 @@ public class WifiQualifiedNetworkSelector {
StringBuffer noValidSsid = new StringBuffer();
StringBuffer scoreHistory = new StringBuffer();
ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
+ boolean scanResultsHaveCurrentBssid = false;
+
+ localLog("isCarrierNetworkEnabledByUser: " +
+ mWifiConfigManager.getIsCarrierNetworkEnabledByUser());
//iterate all scan results and find the best candidate with the highest score
for (ScanDetail scanDetail : mScanDetails) {
@@ -673,6 +772,12 @@ public class WifiQualifiedNetworkSelector {
continue;
}
+ //check if the scan results contain the current connected
+ //BSSID.
+ if (scanResult.BSSID.equals(mCurrentBssid)) {
+ scanResultsHaveCurrentBssid = true;
+ }
+
final String scanId = toScanId(scanResult);
//check whether this BSSID is blocked or not
if (mWifiConfigManager.isBssidBlacklisted(scanResult.BSSID)
@@ -734,12 +839,26 @@ public class WifiQualifiedNetworkSelector {
// Evaluate the potentially ephemeral network as a possible candidate if untrusted
// connections are allowed and we have an external score for the scan result.
if (potentiallyEphemeral) {
- if (isUntrustedConnectionsAllowed) {
- Integer netScore = getNetworkScore(scanResult, false);
- if (netScore != null
- && !mWifiConfigManager.wasEphemeralNetworkDeleted(scanResult.SSID)) {
- externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult);
- // scanDetail is for available ephemeral network
+ if (!mWifiConfigManager.wasEphemeralNetworkDeleted(
+ ScanDetailUtil.createQuotedSSID(scanResult.SSID))) {
+ if (isUntrustedConnectionsAllowed) {
+ Integer netScore = getNetworkScore(scanResult, false);
+ if (netScore != null) {
+ externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult);
+ // scanDetail is for available ephemeral network
+ filteredScanDetails.add(Pair.create(scanDetail,
+ potentialEphemeralCandidate));
+ }
+ // Evaluate the carrier network as a possible candidate.
+ // todo need to add flag isCarrierConnectionsAllowed, config in settings.
+ } else if (!mCarrierConfiguredNetworks.isEmpty() &&
+ isCarrierNetwork(scanResult) &&
+ mWifiConfigManager.getIsCarrierNetworkEnabledByUser()) {
+ localLog("Checking the carrierScoreEvaluator for candidates...");
+ carrierScoreEvaluator.evalCarrierCandidate(scanResult,
+ getCarrierScore(scanResult, mCurrentConnectedNetwork,
+ (mCurrentBssid == null ? false :
+ mCurrentBssid.equals(scanResult.BSSID))));
filteredScanDetails.add(Pair.create(scanDetail,
potentialEphemeralCandidate));
}
@@ -826,6 +945,16 @@ public class WifiQualifiedNetworkSelector {
localLog(scoreHistory.toString());
}
+ //QNS listens to all single scan results. Some scan requests may not include
+ //the channel of the currently connected network, so the currently connected network
+ //won't show up in the scan results. We don't act on these scan results to avoid
+ //aggressive network switching which might trigger disconnection.
+ if (isConnected && !scanResultsHaveCurrentBssid) {
+ localLog("Current connected BSSID " + mCurrentBssid + " is not in the scan results."
+ + " Skip network selection.");
+ return null;
+ }
+
//we need traverse the whole user preference to choose the one user like most now
if (scanResultCandidate != null) {
WifiConfiguration tempConfig = networkCandidate;
@@ -862,6 +991,15 @@ public class WifiQualifiedNetworkSelector {
}
if (scanResultCandidate == null) {
+ networkCandidate = getCarrierScoreCandidate(carrierScoreEvaluator);
+ localLog("Carrier candidate::" + networkCandidate);
+ if (networkCandidate != null) {
+ scanResultCandidate =
+ mWifiConfigManager.getScanResultCandidate(networkCandidate);
+ }
+ }
+
+ if (scanResultCandidate == null) {
localLog("Can not find any suitable candidates");
return null;
}
@@ -946,6 +1084,37 @@ public class WifiQualifiedNetworkSelector {
}
/**
+ * Returns the best candidate network according to the given CarrierScoreEvaluator.
+ */
+ @Nullable
+ WifiConfiguration getCarrierScoreCandidate(CarrierScoreEvaluator scoreEvaluator) {
+
+ ScanResult untrustedCarrierScanResult = scoreEvaluator.getScanResultCandidate();
+ if (untrustedCarrierScanResult == null) {
+ return null;
+ }
+
+ WifiConfiguration untrustedCandidateConfig = getMatchingConfigForEAPNetworks(
+ untrustedCarrierScanResult, mCarrierConfiguredNetworks);
+
+ if (untrustedCandidateConfig == null) {
+ return null;
+ }
+
+ WifiConfiguration newUntrustedCandidateConfig =
+ new WifiConfiguration(untrustedCandidateConfig);
+
+ // Mark this config as ephemeral so it isn't persisted.
+ newUntrustedCandidateConfig.ephemeral = true;
+ // Mark this config as a Carrier Network.
+ newUntrustedCandidateConfig.isCarrierNetwork = true;
+
+ mWifiConfigManager.saveNetworkAndSetCandidate(
+ newUntrustedCandidateConfig, untrustedCarrierScanResult);
+ return newUntrustedCandidateConfig;
+ }
+
+ /**
* Returns the available external network score or NULL if no score is available.
*
* @param scanResult The scan result of the network to score.
@@ -963,6 +1132,43 @@ public class WifiQualifiedNetworkSelector {
}
/**
+ * Returns the available external network score or NULL if no score is available.
+ *
+ * @param scanResult The scan result of the network to score.
+ * @return A valid external score if one is available or NULL.
+ */
+ int getCarrierScore(ScanResult scanResult, WifiConfiguration currentNetwork,
+ boolean sameBssid) {
+ localLog("Calc Carrier score: w/" + sameBssid);
+ if (currentNetwork != null) {
+ localLog("scoring: compare::" + scanResult.SSID + ", with:" + currentNetwork.SSID);
+ }
+ int score = 0;
+ // Calculate the RSSI score.
+ int rssi = scanResult.level <= mWifiConfigManager.mThresholdSaturatedRssi24.get()
+ ? scanResult.level : mWifiConfigManager.mThresholdSaturatedRssi24.get();
+ score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
+
+ // 5GHz band bonus.
+ if (scanResult.is5GHz()) {
+ score += BAND_AWARD_5GHz;
+ }
+
+ //same network award
+ if ((currentNetwork != null) && currentNetwork.SSID.equals(scanResult.SSID)) {
+ score += mWifiConfigManager.mCurrentNetworkBoost.get();
+ }
+
+ //same BSSID award
+ if (sameBssid) {
+ score += mSameBssidAward;
+ }
+
+ localLog("Calc Carrier score:" + score);
+ return score;
+ }
+
+ /**
* Formats the given ScanResult as a scan ID for logging.
*/
private static String toScanId(@Nullable ScanResult scanResult) {
@@ -1051,4 +1257,66 @@ public class WifiQualifiedNetworkSelector {
}
}
}
+
+ /**
+ * Used to track and evaluate networks that are assigned by the Carriers.
+ */
+ static class CarrierScoreEvaluator {
+ // Always set to the best known candidate
+ private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
+ private ScanResult mScanResultCandidate;
+ private final LocalLog mLocalLog;
+ private final boolean mDbg;
+
+ CarrierScoreEvaluator(LocalLog localLog, boolean dbg) {
+ mLocalLog = localLog;
+ mDbg = dbg;
+ }
+
+ // Determines whether or not the given scan result is the best one its seen so far.
+ void evalCarrierCandidate(ScanResult scanResult, int score) {
+ if (score > mHighScore) {
+ mHighScore = score;
+ mScanResultCandidate = scanResult;
+ localLog(toScanId(scanResult) +
+ " become the new untrusted carrier network candidate");
+ }
+ }
+
+ int getHighScore() {
+ return mHighScore;
+ }
+
+ public ScanResult getScanResultCandidate() {
+ return mScanResultCandidate;
+ }
+
+ private void localLog(String log) {
+ if (mDbg) {
+ mLocalLog.log(log);
+ }
+ }
+ }
+
+ private WifiConfiguration getMatchingConfigForEAPNetworks(
+ ScanResult scanResult, List<WifiConfiguration> candidateConfigs) {
+ if (scanResult == null || candidateConfigs == null) {
+ return null;
+ }
+ // TODO currently we only support EAP. We'll add to this in OC.
+ if (!scanResult.capabilities.contains("EAP")) {
+ return null;
+ }
+ String ssid = "\"" + scanResult.SSID + "\"";
+ for (WifiConfiguration config : candidateConfigs) {
+ if (config.SSID.equals(ssid)) {
+ // TODO currently we only support EAP. We'll add to this in OC.
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
+ config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ return config;
+ }
+ }
+ }
+ return null;
+ }
}