Merge "WifiSupplicantControl: Strip out FT flags in WifiConfiguration"
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index a4ae565..b59a75e 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -899,9 +899,11 @@
existingInternalConfig, config, uid);
}
- // Update the keys for enterprise networks.
+ // Update the keys for non-Passpoint enterprise networks. For Passpoint, the certificates
+ // and keys are installed at the time the provider is installed.
if (config.enterpriseConfig != null
- && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
+ && !config.isPasspoint()) {
if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
@@ -2558,8 +2560,8 @@
ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- // Don't persist ephemeral networks to store.
- if (!config.ephemeral) {
+ // Don't persist ephemeral and passpoint networks to store.
+ if (!config.ephemeral && !config.isPasspoint()) {
// We push all shared networks & private networks not belonging to the current
// user to the shared store. Ideally, private networks for other users should
// not even be in memory,
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index bd4887a..f71b781 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -33,6 +33,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
import com.android.server.wifi.util.ScanResultUtil;
import java.io.FileDescriptor;
@@ -118,7 +119,8 @@
// Saved network evaluator priority
private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1;
- private static final int RECOMMENDED_NETWORK_EVALUATOR_PRIORITY = 2;
+ private static final int PASSPOINT_NETWORK_EVALUATOR_PRIORITY = 2;
+ private static final int RECOMMENDED_NETWORK_EVALUATOR_PRIORITY = 3;
private final WifiStateMachine mStateMachine;
private final WifiScanner mScanner;
@@ -471,7 +473,8 @@
WifiLastResortWatchdog wifiLastResortWatchdog, WifiMetrics wifiMetrics,
Looper looper, Clock clock, boolean enable, FrameworkFacade frameworkFacade,
SavedNetworkEvaluator savedNetworkEvaluator,
- RecommendedNetworkEvaluator recommendedNetworkEvaluator) {
+ RecommendedNetworkEvaluator recommendedNetworkEvaluator,
+ PasspointNetworkEvaluator passpointNetworkEvaluator) {
mStateMachine = stateMachine;
mScanner = scanner;
mConfigManager = configManager;
@@ -518,6 +521,8 @@
// Register the network evaluators
mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
SAVED_NETWORK_EVALUATOR_PRIORITY);
+ mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
+ PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
mNetworkSelector.registerNetworkEvaluator(recommendedNetworkEvaluator,
RECOMMENDED_NETWORK_EVALUATOR_PRIORITY);
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 5fcfb5d..902cc64 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -42,6 +42,7 @@
import com.android.server.net.DelayedDiskWrite;
import com.android.server.net.IpConfigStore;
import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
import com.android.server.wifi.hotspot2.PasspointObjectFactory;
import com.android.server.wifi.util.WifiPermissionsUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
@@ -91,6 +92,7 @@
private final WifiConfigManager mWifiConfigManager;
private final WifiNetworkSelector mWifiNetworkSelector;
private final SavedNetworkEvaluator mSavedNetworkEvaluator;
+ private final PasspointNetworkEvaluator mPasspointNetworkEvaluator;
private final RecommendedNetworkEvaluator mRecommendedNetworkEvaluator;
private final WifiNetworkScoreCache mWifiNetworkScoreCache;
private final NetworkScoreManager mNetworkScoreManager;
@@ -168,6 +170,11 @@
context.getContentResolver(), mWifiStateMachineHandlerThread.getLooper(),
mFrameworkFacade, mWifiNetworkScoreCache, mNetworkScoreManager, mWifiConfigManager,
localLog, externalScoreEvaluator);
+ mSimAccessor = new SIMAccessor(mContext);
+ mPasspointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
+ mSimAccessor, new PasspointObjectFactory());
+ mPasspointNetworkEvaluator = new PasspointNetworkEvaluator(
+ mPasspointManager, mWifiConfigManager, localLog);
mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade,
mWifiStateMachineHandlerThread.getLooper(), UserManager.get(mContext),
this, mBackupManagerProxy, mCountryCode, mWifiNative);
@@ -182,9 +189,6 @@
mWifiPermissionsWrapper = new WifiPermissionsWrapper(mContext);
mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext,
mSettingsStore, UserManager.get(mContext), new NetworkScorerAppManager(mContext));
- mSimAccessor = new SIMAccessor(mContext);
- mPasspointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
- mSimAccessor, new PasspointObjectFactory());
}
/**
@@ -376,7 +380,7 @@
mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiLastResortWatchdog,
mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(), mClock,
hasConnectionRequests, mFrameworkFacade, mSavedNetworkEvaluator,
- mRecommendedNetworkEvaluator);
+ mRecommendedNetworkEvaluator, mPasspointNetworkEvaluator);
}
public WifiPermissionsUtil getWifiPermissionsUtil() {
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 3d730d9..1ceb03b 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -1812,7 +1812,7 @@
}
}
- if (extendedCaps.is80211McRTTResponder) {
+ if (extendedCaps.is80211McRTTResponder()) {
result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
} else {
result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
@@ -1844,8 +1844,9 @@
if(DBG) {
Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
+ " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
- + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
- ? "Support RTT reponder: " : "Do not support RTT responder")
+ + " mCenterfreq1: " + result.centerFreq1
+ + (extendedCaps.is80211McRTTResponder() ? "Support RTT reponder: "
+ : "Do not support RTT responder")
+ " Capabilities: " + result.capabilities);
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 540fbd8..5786d8b 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -429,7 +429,6 @@
// Channel for sending replies.
private AsyncChannel mReplyChannel = new AsyncChannel();
- private WifiP2pServiceImpl mWifiP2pServiceImpl;
private WifiAwareManager mWifiAwareManager;
// Used to initiate a connection with WifiP2pService
@@ -876,6 +875,7 @@
.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
mWifiConfigManager = mWifiInjector.getWifiConfigManager();
+ mWifiApConfigStore = mWifiInjector.getWifiApConfigStore();
mWifiSupplicantControl = mWifiInjector.getWifiSupplicantControl();
mWifiSupplicantControl.setSystemSupportsFastBssTransition(
mContext.getResources().getBoolean(R.bool.config_wifi_fast_bss_transition_enabled));
@@ -892,13 +892,6 @@
mLinkProperties = new LinkProperties();
- IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
- mWifiP2pServiceImpl = (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
-
- if (mAwareSupported) {
- mWifiAwareManager = mContext.getSystemService(WifiAwareManager.class);
- }
-
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -3653,6 +3646,31 @@
}
}
+ /**
+ * WifiStateMachine needs to enable/disable other services when wifi is in client mode. This
+ * method allows WifiStateMachine to get these additional system services.
+ *
+ * At this time, this method is used to setup variables for P2P service and Wifi Aware.
+ */
+ private void getAdditionalWifiServiceInterfaces() {
+ // First set up Wifi Direct
+ // TODO: b/34193861 determine if we can avoid starting WIFI_P2P_SERVICE when not supported
+ IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
+ WifiP2pServiceImpl wifiP2pServiceImpl =
+ (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
+
+ if (wifiP2pServiceImpl != null) {
+ mWifiP2pChannel = new AsyncChannel();
+ mWifiP2pChannel.connect(mContext, getHandler(),
+ wifiP2pServiceImpl.getP2pStateMachineMessenger());
+ }
+
+ // Set up Wifi Aware
+ if (mAwareSupported) {
+ mWifiAwareManager = mContext.getSystemService(WifiAwareManager.class);
+ }
+ }
+
/********************************************************
* HSM states
*******************************************************/
@@ -3716,6 +3734,8 @@
}
break;
case CMD_BOOT_COMPLETED:
+ // get other services that we need to manage
+ getAdditionalWifiServiceInterfaces();
maybeRegisterNetworkFactory();
break;
case CMD_SCREEN_STATE_CHANGED:
@@ -3944,16 +3964,8 @@
@Override
public void enter() {
cleanup();
- if (mWifiP2pChannel == null && mWifiP2pServiceImpl != null) {
- mWifiP2pChannel = new AsyncChannel();
- mWifiP2pChannel.connect(mContext, getHandler(),
- mWifiP2pServiceImpl.getP2pStateMachineMessenger());
- }
-
- if (mWifiApConfigStore == null) {
- mWifiApConfigStore = mWifiInjector.getWifiApConfigStore();
- }
}
+
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
@@ -4690,8 +4702,9 @@
// hence record the time we were connected last
WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
if (config != null) {
- if (config.ephemeral) {
- // Remove ephemeral WifiConfigurations from file
+ // Remove WifiConfiguration for ephemeral or Passpoint networks, since they're
+ // temporary networks.
+ if (config.ephemeral || config.isPasspoint()) {
mWifiConfigManager.removeNetwork(mLastNetworkId, Process.WIFI_UID);
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 905b5b8..f9894f4 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -298,7 +298,7 @@
if (DBG) {
Log.d(TAG, mSSID + "ChannelWidth is: " + mChannelWidth + " PrimaryFreq: " + mPrimaryFreq
+ " mCenterfreq0: " + mCenterfreq0 + " mCenterfreq1: " + mCenterfreq1
- + (extendedCapabilities.is80211McRTTResponder ? "Support RTT reponder"
+ + (extendedCapabilities.is80211McRTTResponder() ? "Support RTT responder"
: "Do not support RTT responder"));
Log.v("WifiMode", mSSID
+ ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
@@ -434,10 +434,6 @@
return mRoamingConsortiums;
}
- public Long getExtendedCapabilities() {
- return mExtendedCapabilities.extendedCapabilities;
- }
-
public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
return mANQPElements;
}
@@ -463,7 +459,7 @@
}
public boolean is80211McResponderSupport() {
- return mExtendedCapabilities.is80211McRTTResponder;
+ return mExtendedCapabilities.is80211McRTTResponder();
}
public boolean isSSID_UTF8() {
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
deleted file mode 100644
index dccd2fd..0000000
--- a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package com.android.server.wifi.hotspot2;
-
-import com.android.server.wifi.ScanDetail;
-import com.android.server.wifi.hotspot2.anqp.ANQPElement;
-import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
-import com.android.server.wifi.hotspot2.anqp.HSConnectionCapabilityElement;
-import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
-import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
-import com.android.server.wifi.hotspot2.anqp.ProtocolPortTuple;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO(b/32714185): update using the new HomeSP object.
- */
-public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> {
- private final PasspointMatch mPasspointMatch;
- private final ScanDetail mScanDetail;
- private final int mScore;
-
- private static final Map<Integer, Integer> sIP4Scores = new HashMap<>();
- private static final Map<Integer, Integer> sIP6Scores = new HashMap<>();
-
- private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>();
-
- private static final int IPPROTO_ICMP = 1;
- private static final int IPPROTO_TCP = 6;
- private static final int IPPROTO_UDP = 17;
- private static final int IPPROTO_ESP = 50;
- private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>();
-
- static {
- // These are all arbitrarily chosen scores, subject to tuning.
-
- sAntScores.put(NetworkDetail.Ant.FreePublic, 4);
- sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 4);
- sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 4);
- sAntScores.put(NetworkDetail.Ant.Private, 4);
- sAntScores.put(NetworkDetail.Ant.Personal, 2);
- sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2);
- sAntScores.put(NetworkDetail.Ant.Wildcard, 1);
- sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0);
-
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE, 0);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED, 1);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_SINGLE_NAT, 1);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT, 1);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_UNKNOWN, 1);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_PUBLIC, 2);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_SINGLE_NAT, 2);
- sIP4Scores.put(IPAddressTypeAvailabilityElement.IPV4_DOUBLE_NAT, 2);
-
- sIP6Scores.put(IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE, 0);
- sIP6Scores.put(IPAddressTypeAvailabilityElement.IPV6_UNKNOWN, 1);
- sIP6Scores.put(IPAddressTypeAvailabilityElement.IPV6_AVAILABLE, 2);
-
- Map<Integer, Integer> tcpMap = new HashMap<>();
- tcpMap.put(20, 1);
- tcpMap.put(21, 1);
- tcpMap.put(22, 3);
- tcpMap.put(23, 2);
- tcpMap.put(25, 8);
- tcpMap.put(26, 8);
- tcpMap.put(53, 3);
- tcpMap.put(80, 10);
- tcpMap.put(110, 6);
- tcpMap.put(143, 6);
- tcpMap.put(443, 10);
- tcpMap.put(993, 6);
- tcpMap.put(1723, 7);
-
- Map<Integer, Integer> udpMap = new HashMap<>();
- udpMap.put(53, 10);
- udpMap.put(500, 7);
- udpMap.put(5060, 10);
- udpMap.put(4500, 4);
-
- sPortScores.put(IPPROTO_TCP, tcpMap);
- sPortScores.put(IPPROTO_UDP, udpMap);
- }
-
-
- public PasspointMatchInfo(PasspointMatch passpointMatch,
- ScanDetail scanDetail) {
- mPasspointMatch = passpointMatch;
- mScanDetail = scanDetail;
-
- int score;
- if (passpointMatch == PasspointMatch.HomeProvider) {
- score = 100;
- }
- else if (passpointMatch == PasspointMatch.RoamingProvider) {
- score = 0;
- }
- else {
- score = -1000; // Don't expect to see anything not home or roaming.
- }
-
- if (getNetworkDetail().getHSRelease() != null) {
- score += getNetworkDetail().getHSRelease() != NetworkDetail.HSRelease.Unknown ? 50 : 0;
- }
-
- if (getNetworkDetail().hasInterworking()) {
- score += getNetworkDetail().isInternet() ? 20 : -20;
- }
-
- score += (Math.max(200-getNetworkDetail().getStationCount(), 0) *
- (255-getNetworkDetail().getChannelUtilization()) *
- getNetworkDetail().getCapacity()) >>> 26;
- // Gives a value of 23 max capped at 200 stations and max cap 31250
-
- if (getNetworkDetail().hasInterworking()) {
- score += sAntScores.get(getNetworkDetail().getAnt());
- }
-
- Map<ANQPElementType, ANQPElement> anqp = getNetworkDetail().getANQPElements();
-
- if (anqp != null) {
- HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics);
-
- if (wm != null) {
- if (wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped()) {
- score -= 1000;
- } else {
- long scaledSpeed =
- wm.getDownlinkSpeed() * (255 - wm.getDownlinkLoad()) * 8
- + wm.getUplinkSpeed() * (255 - wm.getUplinkLoad()) * 2;
- score += Math.min(scaledSpeed, 255000000L) >>> 23;
- // Max value is 30 capped at 100Mb/s
- }
- }
-
- IPAddressTypeAvailabilityElement ipa =
- (IPAddressTypeAvailabilityElement) anqp.get(ANQPElementType.ANQPIPAddrAvailability);
-
- if (ipa != null) {
- Integer as14 = sIP4Scores.get(ipa.getV4Availability());
- Integer as16 = sIP6Scores.get(ipa.getV6Availability());
- as14 = as14 != null ? as14 : 1;
- as16 = as16 != null ? as16 : 1;
- // Is IPv4 twice as important as IPv6???
- score += as14 * 2 + as16;
- }
-
- HSConnectionCapabilityElement cce =
- (HSConnectionCapabilityElement) anqp.get(ANQPElementType.HSConnCapability);
-
- if (cce != null) {
- score = Math.min(Math.max(protoScore(cce) >> 3, -10), 10);
- }
- }
-
- mScore = score;
- }
-
- public PasspointMatch getPasspointMatch() {
- return mPasspointMatch;
- }
-
- public ScanDetail getScanDetail() {
- return mScanDetail;
- }
-
- public NetworkDetail getNetworkDetail() {
- return mScanDetail.getNetworkDetail();
- }
-
- public int getScore() {
- return mScore;
- }
-
- @Override
- public int compareTo(PasspointMatchInfo that) {
- return getScore() - that.getScore();
- }
-
- private static int protoScore(HSConnectionCapabilityElement cce) {
- int score = 0;
- for (ProtocolPortTuple tuple : cce.getStatusList()) {
- int sign = tuple.getStatus() == ProtocolPortTuple.PROTO_STATUS_OPEN
- ? 1 : -1;
-
- int elementScore = 1;
- if (tuple.getProtocol() == IPPROTO_ICMP) {
- elementScore = 1;
- }
- else if (tuple.getProtocol() == IPPROTO_ESP) {
- elementScore = 5;
- }
- else {
- Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol());
- if (protoMap != null) {
- Integer portScore = protoMap.get(tuple.getPort());
- elementScore = portScore != null ? portScore : 0;
- }
- }
- score += elementScore * sign;
- }
- return score;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- PasspointMatchInfo that = (PasspointMatchInfo)thatObject;
-
- return getNetworkDetail().equals(that.getNetworkDetail()) &&
- getPasspointMatch().equals(that.getPasspointMatch());
- }
-
- @Override
- public int hashCode() {
- int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0;
- result = 31 * result + getNetworkDetail().hashCode();
- return result;
- }
-
- @Override
- public String toString() {
- return "PasspointMatchInfo{" +
- ", mPasspointMatch=" + mPasspointMatch +
- ", mNetworkInfo=" + getNetworkDetail().getSSID() +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
new file mode 100644
index 0000000..77b29a8
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.net.wifi.WifiConfiguration;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.server.wifi.NetworkUpdateResult;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.WifiNetworkSelector;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is the WifiNetworkSelector.NetworkEvaluator implementation for
+ * Passpoint networks.
+ */
+public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
+ private static final String NAME = "PasspointNetworkEvaluator";
+
+ private final PasspointManager mPasspointManager;
+ private final WifiConfigManager mWifiConfigManager;
+ private final LocalLog mLocalLog;
+
+ public PasspointNetworkEvaluator(PasspointManager passpointManager,
+ WifiConfigManager wifiConfigManager, LocalLog localLog) {
+ mPasspointManager = passpointManager;
+ mWifiConfigManager = wifiConfigManager;
+ mLocalLog = localLog;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void update(List<ScanDetail> scanDetails) {}
+
+ @Override
+ public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid,
+ boolean connected, boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ // Go through each ScanDetail and find the best provider for each ScanDetail.
+ List<Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>>> providerList =
+ new ArrayList<>();
+ for (ScanDetail scanDetail : scanDetails) {
+ // Skip non-Passpoint APs.
+ if (!scanDetail.getNetworkDetail().isInterworking()) {
+ continue;
+ }
+
+ List<Pair<PasspointProvider, PasspointMatch>> matchedProviders =
+ mPasspointManager.matchProvider(scanDetail);
+
+ // Find the best provider for this ScanDetail.
+ Pair<PasspointProvider, PasspointMatch> bestProvider =
+ findBestProvider(matchedProviders);
+ if (bestProvider != null) {
+ providerList.add(Pair.create(scanDetail, bestProvider));
+ }
+ }
+
+ // Done if no matching provider is found.
+ if (providerList.isEmpty()) {
+ return null;
+ }
+
+ // Find the best Passpoint network among all matches.
+ Pair<PasspointProvider, ScanDetail> bestNetwork = findBestNetwork(providerList,
+ currentNetwork == null ? null : currentNetwork.SSID);
+
+ // Return the configuration for the current connected network if it is the best network.
+ if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID,
+ ScanResultUtil.createQuotedSSID(bestNetwork.second.getSSID()))) {
+ connectableNetworks.add(Pair.create(bestNetwork.second, currentNetwork));
+ return currentNetwork;
+ }
+
+ WifiConfiguration config =
+ createWifiConfigForProvider(bestNetwork.first, bestNetwork.second);
+ connectableNetworks.add(Pair.create(bestNetwork.second, config));
+ return config;
+ }
+
+ /**
+ * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider.
+ * The newly created WifiConfiguration will also be added to WifiConfigManager.
+ *
+ * @param provider The provider to create WifiConfiguration from
+ * @param scanDetail The ScanDetail to create WifiConfiguration from
+ * @return {@link WifiConfiguration}
+ */
+ private WifiConfiguration createWifiConfigForProvider(PasspointProvider provider,
+ ScanDetail scanDetail) {
+ WifiConfiguration config = provider.getWifiConfig();
+ config.SSID = ScanResultUtil.createQuotedSSID(scanDetail.getSSID());
+
+ // Add the newly created WifiConfiguration to WifiConfigManager.
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
+ if (!result.isSuccess()) {
+ localLog("Failed to add passpoint network");
+ return null;
+ }
+ mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(),
+ scanDetail.getScanResult(), 0);
+ return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ }
+
+ /**
+ * Given a list of provider associated with a ScanDetail, determine and return the best
+ * provider from the list.
+ *
+ * Currently the only criteria is to prefer home provider over roaming provider. Additional
+ * criteria will be added when Hotspot 2.0 Release 2 support is added.
+ *
+ * A null will be returned if no match is found (providerList is empty).
+ *
+ * @param providerList The list of matched providers
+ * @return Pair of {@link PasspointProvider} with its matching status
+ */
+ private Pair<PasspointProvider, PasspointMatch> findBestProvider(
+ List<Pair<PasspointProvider, PasspointMatch>> providerList) {
+ Pair<PasspointProvider, PasspointMatch> bestMatch = null;
+ for (Pair<PasspointProvider, PasspointMatch> providerMatch : providerList) {
+ if (providerMatch.second == PasspointMatch.HomeProvider) {
+ // Home provider found, done.
+ bestMatch = providerMatch;
+ break;
+ } else if (bestMatch == null) {
+ bestMatch = providerMatch;
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
+ * Given a list of Passpoint networks (with both provider and scan info), find and return
+ * the one with highest score. The score is calculated using
+ * {@link PasspointNetworkScore#calculateScore}.
+ *
+ * @param networkList List of Passpoint networks
+ * @param currentNetworkSsid The SSID of the currently connected network, null if not connected
+ * @return {@link PasspointProvider} and {@link ScanDetail} associated with the network
+ */
+ private Pair<PasspointProvider, ScanDetail> findBestNetwork(
+ List<Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>>> networkList,
+ String currentNetworkSsid) {
+ ScanDetail bestScanDetail = null;
+ PasspointProvider bestProvider = null;
+ int bestScore = Integer.MIN_VALUE;
+ for (Pair<ScanDetail, Pair<PasspointProvider, PasspointMatch>> candidate : networkList) {
+ ScanDetail scanDetail = candidate.first;
+ PasspointProvider provider = candidate.second.first;
+ PasspointMatch match = candidate.second.second;
+
+ boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid,
+ ScanResultUtil.createQuotedSSID(scanDetail.getSSID()));
+ int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider,
+ scanDetail, isActiveNetwork);
+
+ if (score > bestScore) {
+ bestScanDetail = scanDetail;
+ bestProvider = provider;
+ bestScore = score;
+ }
+ }
+ return Pair.create(bestProvider, bestScanDetail);
+ }
+
+ private void localLog(String log) {
+ if (mLocalLog != null) {
+ mLocalLog.log(log);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java
new file mode 100644
index 0000000..2dc3789
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.net.RssiCurve;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
+import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is an utility class for calculating score for Passpoint networks.
+ */
+public class PasspointNetworkScore {
+ /**
+ * Award points for network that's a Passpoint home provider.
+ */
+ @VisibleForTesting
+ public static final int HOME_PROVIDER_AWARD = 100;
+
+ /**
+ * Award points for network that provides Internet access.
+ */
+ @VisibleForTesting
+ public static final int INTERNET_ACCESS_AWARD = 50;
+
+ /**
+ * Award points for public or private network.
+ */
+ @VisibleForTesting
+ public static final int PUBLIC_OR_PRIVATE_NETWORK_AWARDS = 4;
+
+ /**
+ * Award points for personal or emergency network.
+ */
+ @VisibleForTesting
+ public static final int PERSONAL_OR_EMERGENCY_NETWORK_AWARDS = 2;
+
+ /**
+ * Award points for network providing restricted or unknown IP address.
+ */
+ @VisibleForTesting
+ public static final int RESTRICTED_OR_UNKNOWN_IP_AWARDS = 1;
+
+ /**
+ * Award points for network providing unrestricted IP address.
+ */
+ @VisibleForTesting
+ public static final int UNRESTRICTED_IP_AWARDS = 2;
+
+ /**
+ * Penalty points for network with WAN port that's down or the load already reached the max.
+ */
+ @VisibleForTesting
+ public static final int WAN_PORT_DOWN_OR_CAPPED_PENALTY = 50;
+
+ // Award points for availability of IPv4 and IPv6 addresses.
+ private static final Map<Integer, Integer> IPV4_SCORES = new HashMap<>();
+ private static final Map<Integer, Integer> IPV6_SCORES = new HashMap<>();
+
+ // Award points based on access network type.
+ private static final Map<NetworkDetail.Ant, Integer> NETWORK_TYPE_SCORES = new HashMap<>();
+
+ /**
+ * Curve for calculating score for RSSI level.
+ */
+ @VisibleForTesting
+ public static final RssiCurve RSSI_SCORE = new RssiCurve(-80 /* start */, 20 /* bucketWidth */,
+ new byte[] {-10, 0, 10, 20, 30, 40} /* rssiBuckets */,
+ 20 /* activeNetworkRssiBoost */);
+
+ static {
+ // These are all arbitrarily chosen scores, subject to tuning.
+
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.FreePublic, PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.ChargeablePublic,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.PrivateWithGuest,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Private,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Personal, PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.EmergencyOnly,
+ PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Wildcard, 0);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.TestOrExperimental, 0);
+
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE, 0);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_SINGLE_NAT,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_UNKNOWN,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PUBLIC, UNRESTRICTED_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_SINGLE_NAT, UNRESTRICTED_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_DOUBLE_NAT, UNRESTRICTED_IP_AWARDS);
+
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE, 0);
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_UNKNOWN,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_AVAILABLE,
+ UNRESTRICTED_IP_AWARDS);
+ }
+
+
+ /**
+ * Calculate and return a score associated with the given Passpoint network.
+ * The score is calculated with the following preferences:
+ * - Prefer home provider
+ * - Prefer network that provides Internet access
+ * - Prefer network with active WAN port with available load
+ * - Prefer network that provides unrestricted IP address
+ * - Prefer currently active network
+ * - Prefer AP with higher RSSI
+ *
+ * This can be expanded for additional preference in the future (e.g. AP station count, link
+ * speed, and etc).
+ *
+ * @param isHomeProvider Flag indicating home provider
+ * @param scanDetail The ScanDetail associated with the AP
+ * @param isActiveNetwork Flag indicating current active network
+ * @return integer score
+ */
+ public static int calculateScore(boolean isHomeProvider, ScanDetail scanDetail,
+ boolean isActiveNetwork) {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ int score = 0;
+ if (isHomeProvider) {
+ score += HOME_PROVIDER_AWARD;
+ }
+
+ // Adjust score based on Internet accessibility.
+ score += (networkDetail.isInternet() ? 1 : -1) * INTERNET_ACCESS_AWARD;
+
+ // Adjust score based on the network type.
+ score += NETWORK_TYPE_SCORES.get(networkDetail.getAnt());
+
+ Map<ANQPElementType, ANQPElement> anqp = networkDetail.getANQPElements();
+ if (anqp != null) {
+ HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics);
+ if (wm != null) {
+ if (wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped()) {
+ score -= WAN_PORT_DOWN_OR_CAPPED_PENALTY;
+ }
+ }
+
+ IPAddressTypeAvailabilityElement ipa = (IPAddressTypeAvailabilityElement)
+ anqp.get(ANQPElementType.ANQPIPAddrAvailability);
+
+ if (ipa != null) {
+ Integer v4Score = IPV4_SCORES.get(ipa.getV4Availability());
+ Integer v6Score = IPV6_SCORES.get(ipa.getV6Availability());
+ v4Score = v4Score != null ? v4Score : 0;
+ v6Score = v6Score != null ? v6Score : 0;
+ score += (v4Score + v6Score);
+ }
+ }
+
+ score += RSSI_SCORE.lookupScore(scanDetail.getScanResult().level, isActiveNetwork);
+ return score;
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index 268ddce..9d7de24 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -261,37 +261,52 @@
}
}
+ /**
+ * This IE contained a bit field indicating the capabilities being advertised by the STA.
+ * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
+ *
+ * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
+ * bit.
+ *
+ * Here is the wire format of this IE:
+ * | Element ID | Length | Capabilities |
+ * 1 1 n
+ */
public static class ExtendedCapabilities {
private static final int RTT_RESP_ENABLE_BIT = 70;
- private static final long SSID_UTF8_BIT = 0x0001000000000000L;
+ private static final int SSID_UTF8_BIT = 48;
- public Long extendedCapabilities = null;
- public boolean is80211McRTTResponder = false;
+ public BitSet capabilitiesBitSet;
+
+ /**
+ * @return true if SSID should be interpreted using UTF-8 encoding
+ */
+ public boolean isStrictUtf8() {
+ return capabilitiesBitSet.get(SSID_UTF8_BIT);
+ }
+
+ /**
+ * @return true if 802.11 MC RTT Response is enabled
+ */
+ public boolean is80211McRTTResponder() {
+ return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
+ }
public ExtendedCapabilities() {
+ capabilitiesBitSet = new BitSet();
}
public ExtendedCapabilities(ExtendedCapabilities other) {
- extendedCapabilities = other.extendedCapabilities;
- is80211McRTTResponder = other.is80211McRTTResponder;
+ capabilitiesBitSet = other.capabilitiesBitSet;
}
- public boolean isStrictUtf8() {
- return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0;
- }
-
+ /**
+ * Parse an ExtendedCapabilities from the IE containing raw bytes.
+ *
+ * @param ie The Information element data
+ */
public void from(InformationElement ie) {
- ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
- extendedCapabilities =
- ByteBufferReader.readInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length);
-
- int index = RTT_RESP_ENABLE_BIT / 8;
- byte offset = RTT_RESP_ENABLE_BIT % 8;
- if (ie.bytes.length < index + 1) {
- is80211McRTTResponder = false;
- } else {
- is80211McRTTResponder = (ie.bytes[index] & ((byte) 0x1 << offset)) != 0;
- }
+ capabilitiesBitSet = BitSet.valueOf(ie.bytes);
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index f3c0573..56af3fb 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -259,7 +259,7 @@
return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner,
mWifiConfigManager, mWifiInfo, mWifiNS,
mWifiLastResortWatchdog, mWifiMetrics, mLooper.getLooper(), mClock, true,
- mFrameworkFacade, null, null);
+ mFrameworkFacade, null, null, null);
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 349f269..5fb6513 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -837,7 +837,7 @@
@Test
public void normalLogRecSizeIsUsedByDefault() {
for (int i = 0; i < WifiStateMachine.NUM_LOG_RECS_NORMAL * 2; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
}
mLooper.dispatchAll();
assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
@@ -851,7 +851,7 @@
assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
mWsm.enableVerboseLogging(1);
for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE * 2; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
}
mLooper.dispatchAll();
assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
@@ -865,7 +865,7 @@
public void disablingVerboseLoggingClearsRecordsAndDecreasesLogRecSize() {
mWsm.enableVerboseLogging(1);
for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
}
mLooper.dispatchAll();
assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
@@ -873,7 +873,7 @@
mWsm.enableVerboseLogging(0);
assertEquals(0, mWsm.getLogRecSize());
for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
}
mLooper.dispatchAll();
assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
new file mode 100644
index 0000000..7312b33
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.server.wifi.NetworkUpdateResult;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator}.
+ */
+@SmallTest
+public class PasspointNetworkEvaluatorTest {
+ private static final int TEST_NETWORK_ID = 1;
+ private static final String TEST_SSID1 = "ssid1";
+ private static final String TEST_SSID2 = "ssid2";
+ private static final String TEST_FQDN1 = "test1.com";
+ private static final String TEST_FQDN2 = "test2.com";
+ private static final WifiConfiguration TEST_CONFIG1 = generateWifiConfig(TEST_FQDN1);
+ private static final WifiConfiguration TEST_CONFIG2 = generateWifiConfig(TEST_FQDN2);
+ private static final PasspointProvider TEST_PROVIDER1 = generateProvider(TEST_CONFIG1);
+ private static final PasspointProvider TEST_PROVIDER2 = generateProvider(TEST_CONFIG2);
+
+ @Mock PasspointManager mPasspointManager;
+ @Mock WifiConfigManager mWifiConfigManager;
+ PasspointNetworkEvaluator mEvaluator;
+
+ /**
+ * Helper function for generating {@link WifiConfiguration} for testing.
+ *
+ * @param fqdn The FQDN associated with the configuration
+ * @return {@link WifiConfiguration}
+ */
+ private static WifiConfiguration generateWifiConfig(String fqdn) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.FQDN = fqdn;
+ return config;
+ }
+
+ /**
+ * Helper function for generating {@link PasspointProvider} for testing.
+ *
+ * @param config The WifiConfiguration associated with the provider
+ * @return {@link PasspointProvider}
+ */
+ private static PasspointProvider generateProvider(WifiConfiguration config) {
+ PasspointProvider provider = mock(PasspointProvider.class);
+ when(provider.getWifiConfig()).thenReturn(config);
+ return provider;
+ }
+
+ /**
+ * Helper function for generating {@link ScanDetail} for testing.
+ *
+ * @param ssid The SSID associated with the scan
+ * @param rssiLevel The RSSI level associated with the scan
+ * @return {@link ScanDetail}
+ */
+ private static ScanDetail generateScanDetail(String ssid) {
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInterworking()).thenReturn(true);
+ when(networkDetail.getAnt()).thenReturn(NetworkDetail.Ant.FreePublic);
+
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getSSID()).thenReturn(ssid);
+ when(scanDetail.getScanResult()).thenReturn(new ScanResult());
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+ return scanDetail;
+ }
+
+ /**
+ * Test setup.
+ */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ mEvaluator = new PasspointNetworkEvaluator(mPasspointManager, mWifiConfigManager, null);
+ }
+
+ /**
+ * Verify that null will be returned when evaluating scans without any matching providers.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNoMatch() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = new ArrayList<>();
+ when(mPasspointManager.matchProvider(any(ScanDetail.class))).thenReturn(matchedProviders);
+ assertEquals(null, mEvaluator.evaluateNetworks(
+ scanDetails, null, null, false, false, connectableNetworks));
+ assertTrue(connectableNetworks.isEmpty());
+ }
+
+ /**
+ * Verify that provider matching will not be performed when evaluating scans with no
+ * interworking support, and null will be returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaulateScansWithNoInterworkingAP() throws Exception {
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInterworking()).thenReturn(false);
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {scanDetail});
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ assertEquals(null, mEvaluator.evaluateNetworks(
+ scanDetails, null, null, false, false, connectableNetworks));
+ assertTrue(connectableNetworks.isEmpty());
+ // Verify that no provider matching is performed.
+ verify(mPasspointManager, never()).matchProvider(any(ScanDetail.class));
+ }
+
+ /**
+ * Verify that when both home provider and roaming provider is found for the same network,
+ * home provider is preferred.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNetworkMatchingHomeAndRoamingProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+ Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create(
+ TEST_PROVIDER2, PasspointMatch.RoamingProvider);
+ List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = new ArrayList<>();
+ matchedProviders.add(homeProvider);
+ matchedProviders.add(roamingProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return matchedProviders for the first ScanDetail (TEST_SSID1) and an empty list for
+ // for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanDetail.class))).thenReturn(matchedProviders)
+ .thenReturn(new ArrayList<Pair<PasspointProvider, PasspointMatch>>());
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ }
+
+ /**
+ * Verify that when a network matches a roaming provider is found, the correct network
+ * information (WifiConfiguration) is setup and returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNetworkMatchingRoamingProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.RoamingProvider);
+ List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = new ArrayList<>();
+ matchedProviders.add(roamingProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return matchedProviders for the first ScanDetail (TEST_SSID1) and an empty list for
+ // for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanDetail.class))).thenReturn(matchedProviders)
+ .thenReturn(new ArrayList<Pair<PasspointProvider, PasspointMatch>>());
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ }
+
+ /**
+ * Verify that when a network matches a home provider and another network matches a roaming
+ * provider are found, the network that matched to a home provider is preferred.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithHomeProviderNewtorkAndRoamingProviderNetwork() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+ Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create(
+ TEST_PROVIDER2, PasspointMatch.RoamingProvider);
+ List<Pair<PasspointProvider, PasspointMatch>> providerForScanDetail1 = new ArrayList<>();
+ providerForScanDetail1.add(homeProvider);
+ List<Pair<PasspointProvider, PasspointMatch>> providerForScanDetail2 = new ArrayList<>();
+ providerForScanDetail2.add(roamingProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return providerForScanDetail1 for the first ScanDetail (TEST_SSID1) and
+ // providerForScanDetail2 for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanDetail.class)))
+ .thenReturn(providerForScanDetail1).thenReturn(providerForScanDetail2);
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ }
+
+ /**
+ * Verify that when two networks both matches a home provider, with one of them being the
+ * active network, the active network is preferred.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithActiveNetworkMatchingHomeProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for both ScanDetail.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+ List<Pair<PasspointProvider, PasspointMatch>> matchedProviders = new ArrayList<>();
+ matchedProviders.add(homeProvider);
+
+ // Setup currently connected network
+ WifiConfiguration currentNetwork = new WifiConfiguration();
+ currentNetwork.networkId = TEST_NETWORK_ID;
+ currentNetwork.SSID = ScanResultUtil.createQuotedSSID(TEST_SSID2);
+ String currentBssid = "12:23:34:45:12:0F";
+
+ // Returning the same matching provider for both ScanDetail.
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ when(mPasspointManager.matchProvider(any(ScanDetail.class)))
+ .thenReturn(matchedProviders).thenReturn(matchedProviders);
+ WifiConfiguration config = mEvaluator.evaluateNetworks(scanDetails, currentNetwork,
+ currentBssid, true, false, connectableNetworks);
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify no new network is added to WifiConfigManager.
+ verify(mWifiConfigManager, never()).addOrUpdateNetwork(
+ any(WifiConfiguration.class), anyInt());
+
+ // Verify current active network is returned.
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID2), config.SSID);
+ assertEquals(TEST_NETWORK_ID, config.networkId);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java
new file mode 100644
index 0000000..cca3212
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.ScanResult;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
+import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointNetworkScore}.
+ */
+@SmallTest
+public class PasspointNetworkScoreTest {
+ private static class TestData {
+ public final boolean isHomeProvider;
+ public final boolean isActiveNetwork;
+ public final int rssiLevel;
+ public final boolean internetAccess;
+ public final NetworkDetail.Ant networkType;
+ public final Map<ANQPElementType, ANQPElement> anqpElements;
+ public int expectedScore;
+
+ TestData(boolean homeProvider, boolean activeNetwork, int rssi, boolean internet,
+ NetworkDetail.Ant type, Map<ANQPElementType, ANQPElement> elements,
+ int score) {
+ isHomeProvider = homeProvider;
+ isActiveNetwork = activeNetwork;
+ rssiLevel = rssi;
+ internetAccess = internet;
+ networkType = type;
+ anqpElements = elements;
+ expectedScore = score;
+ }
+ }
+
+ private static final HSWanMetricsElement WAN_PORT_DOWN_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_DOWN /* status */, true /* symmetric */,
+ false /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final HSWanMetricsElement WAN_PORT_UP_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_UP /* status */, true /* symmetric */,
+ false /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final HSWanMetricsElement WAN_PORT_CAPPED_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_UP /* status */, true /* symmetric */,
+ true /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final IPAddressTypeAvailabilityElement UNRESTRICTED_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(IPAddressTypeAvailabilityElement.IPV4_PUBLIC,
+ IPAddressTypeAvailabilityElement.IPV6_AVAILABLE);
+
+ private static final IPAddressTypeAvailabilityElement UNAVAILABLE_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(
+ IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE,
+ IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE);
+
+ private static final IPAddressTypeAvailabilityElement UNKNOWN_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(
+ IPAddressTypeAvailabilityElement.IPV4_UNKNOWN,
+ IPAddressTypeAvailabilityElement.IPV6_UNKNOWN);
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_DOWN =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_UP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_CAPPED =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNRESTRICTED_IP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNAVAILABLE_IP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNKNOWN_IP =
+ new HashMap<>();
+
+ // List of test data.
+ private static final List<TestData> TEST_DATA_LIST = new ArrayList<>();
+ static {
+ // Setup ANQP elements map for testing.
+ TEST_ANQP_WITH_WAN_PORT_DOWN.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_DOWN_ELEMENT);
+ TEST_ANQP_WITH_WAN_PORT_UP.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_UP_ELEMENT);
+ TEST_ANQP_WITH_WAN_PORT_CAPPED.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_CAPPED_ELEMENT);
+ TEST_ANQP_WITH_UNRESTRICTED_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNRESTRICTED_IP_ADDRESS_ELEMENT);
+ TEST_ANQP_WITH_UNAVAILABLE_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNAVAILABLE_IP_ADDRESS_ELEMENT);
+ TEST_ANQP_WITH_UNKNOWN_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNKNOWN_IP_ADDRESS_ELEMENT);
+
+ // Home provider public network with Internet access that's not the current
+ // active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's the current active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, true /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, true)));
+
+ // Home provider public network without Internet access that's not the current
+ // active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, false /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ - PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider personal network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.Personal /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PERSONAL_OR_EMERGENCY_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is up.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_UP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is down.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_DOWN /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ - PasspointNetworkScore.WAN_PORT_DOWN_OR_CAPPED_PENALTY));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is capped (max load reached).
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_CAPPED /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ - PasspointNetworkScore.WAN_PORT_DOWN_OR_CAPPED_PENALTY));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are available.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNRESTRICTED_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ + PasspointNetworkScore.UNRESTRICTED_IP_AWARDS * 2 /* one for IPv4 and IPv6 */));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are available.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNRESTRICTED_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ /* one each for IPv4 and IPv6. */
+ + PasspointNetworkScore.UNRESTRICTED_IP_AWARDS * 2));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are
+ // unavailable.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNAVAILABLE_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are unknown.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNKNOWN_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ /* one each for IPv4 and IPv6. */
+ + PasspointNetworkScore.RESTRICTED_OR_UNKNOWN_IP_AWARDS * 2));
+
+ // Roaming provider public network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Roaming provider public network with Internet access that's the current active network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, true /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, true)));
+
+ // Roaming provider public network without Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, false /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ - PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Roaming provider personal network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.Personal /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PERSONAL_OR_EMERGENCY_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+ }
+
+ /**
+ * Helper function for generating a {@link ScanDetail} for testing.
+ *
+ * @param rssiLevel RSSI level of the network
+ * @param internetAccess Flag indicating if the network provides Internet access
+ * @param networkType The type of the network
+ * @param anqpElements The list of ANQP elements
+ * @return {@link ScanDetail}
+ */
+ private static ScanDetail generateScanDetail(int rssiLevel, boolean internetAccess,
+ NetworkDetail.Ant networkType, Map<ANQPElementType, ANQPElement> anqpElements) {
+ // Setup ScanResult.
+ ScanResult scanResult = new ScanResult();
+ scanResult.level = -60;
+
+ // Setup NetworkDetail.
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInternet()).thenReturn(internetAccess);
+ when(networkDetail.getAnt()).thenReturn(networkType);
+ when(networkDetail.getANQPElements()).thenReturn(anqpElements);
+
+ // Setup ScanDetail.
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getScanResult()).thenReturn(scanResult);
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+
+ return scanDetail;
+ }
+
+ /**
+ * Go through the list of the test data {@link #TEST_DATA_LIST} and verify the score for each.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void calculateScore() throws Exception {
+ for (TestData data : TEST_DATA_LIST) {
+ ScanDetail scanDetail = generateScanDetail(data.rssiLevel, data.internetAccess,
+ data.networkType, data.anqpElements);
+ assertEquals(data.expectedScore, PasspointNetworkScore.calculateScore(
+ data.isHomeProvider, scanDetail, data.isActiveNetwork));
+ }
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
index 4c037ff..d3e8c70 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.net.wifi.ScanResult.InformationElement;
import android.test.suitebuilder.annotation.SmallTest;
@@ -410,6 +412,61 @@
}
/**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with no bits set.
+ * Both ExtendedCapabilities#isStrictUtf8() and ExtendedCapabilites#is80211McRTTResponder()
+ * should return false.
+ */
+ @Test
+ public void buildExtendedCapabilities_emptyBitSet() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[8];
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertFalse(extendedCap.isStrictUtf8());
+ assertFalse(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with UTF-8 SSID
+ * bit set (bit 48). ExtendedCapabilities#isStrictUtf8() should return true.
+ */
+ @Test
+ public void buildExtendedCapabilites_strictUtf8() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 };
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertTrue(extendedCap.isStrictUtf8());
+ assertFalse(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with RTT Response
+ * Enable bit set (bit 70). ExtendedCapabilities#is80211McRTTResponder() should return true.
+ */
+ @Test
+ public void buildExtendedCapabilites_80211McRTTResponder() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x40 };
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertFalse(extendedCap.isStrictUtf8());
+ assertTrue(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
* Test a that a correctly formed TIM Information Element is decoded into a valid TIM element,
* and the values are captured
*/