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
      */