Merge "Wifi: Add ether_type to sending offloaded packet"
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java
index 09d041b..d118d83 100644
--- a/service/java/com/android/server/wifi/ActiveModeWarden.java
+++ b/service/java/com/android/server/wifi/ActiveModeWarden.java
@@ -556,6 +556,12 @@
     }  // class ModeStateMachine
 
     private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback {
+        private int mMode;
+
+        private SoftApCallbackImpl(int mode) {
+            mMode = mode;
+        }
+
         @Override
         public void onStateChanged(int state, int reason) {
             if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
@@ -566,17 +572,17 @@
                 updateBatteryStatsWifiState(false);
             }
 
-            if (mSoftApCallback != null) {
+            if (mSoftApCallback != null && mMode == WifiManager.IFACE_IP_MODE_TETHERED) {
                 mSoftApCallback.onStateChanged(state, reason);
             }
         }
 
         @Override
         public void onNumClientsChanged(int numClients) {
-            if (mSoftApCallback != null) {
-                mSoftApCallback.onNumClientsChanged(numClients);
-            } else {
+            if (mSoftApCallback == null) {
                 Log.d(TAG, "SoftApCallback is null. Dropping NumClientsChanged event.");
+            } else if (mMode == WifiManager.IFACE_IP_MODE_TETHERED) {
+                mSoftApCallback.onNumClientsChanged(numClients);
             }
         }
     }
@@ -591,7 +597,7 @@
             config = null;
         }
 
-        SoftApCallbackImpl callback = new SoftApCallbackImpl();
+        SoftApCallbackImpl callback = new SoftApCallbackImpl(softapConfig.getTargetMode());
         ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig);
         callback.setActiveModeManager(manager);
         manager.start();
diff --git a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
index c28d521..7def2f3 100644
--- a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
+++ b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
@@ -38,6 +38,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -140,11 +141,18 @@
     /** System wide identifier for notification in Notification Manager */
     private final int mSystemMessageNotificationId;
 
+    /**
+     * The nominator id for this class, from
+     * {@link com.android.server.wifi.nano.WifiMetricsProto.ConnectionEvent.ConnectionNominator}
+     */
+    private final int mNominatorId;
+
     public AvailableNetworkNotifier(
             String tag,
             String storeDataIdentifier,
             String toggleSettingsName,
             int notificationIdentifier,
+            int nominatorId,
             Context context,
             Looper looper,
             FrameworkFacade framework,
@@ -158,6 +166,7 @@
         mStoreDataIdentifier = storeDataIdentifier;
         mToggleSettingsName = toggleSettingsName;
         mSystemMessageNotificationId = notificationIdentifier;
+        mNominatorId = nominatorId;
         mContext = context;
         mHandler = new Handler(looper);
         mFrameworkFacade = framework;
@@ -425,13 +434,19 @@
                 "User initiated connection to recommended network: "
                         + "\"" + mRecommendedNetwork.SSID + "\"");
         WifiConfiguration network = createRecommendedNetworkConfig(mRecommendedNetwork);
-        Message msg = Message.obtain();
-        msg.what = WifiManager.CONNECT_NETWORK;
-        msg.arg1 = WifiConfiguration.INVALID_NETWORK_ID;
-        msg.obj = network;
-        msg.replyTo = mSrcMessenger;
-        mClientModeImpl.sendMessage(msg);
-        addNetworkToBlacklist(mRecommendedNetwork.SSID);
+
+        NetworkUpdateResult result = mConfigManager.addOrUpdateNetwork(network, Process.WIFI_UID);
+        if (result.isSuccess()) {
+            mWifiMetrics.setNominatorForNetwork(result.netId, mNominatorId);
+
+            Message msg = Message.obtain();
+            msg.what = WifiManager.CONNECT_NETWORK;
+            msg.arg1 = result.netId;
+            msg.obj = null;
+            msg.replyTo = mSrcMessenger;
+            mClientModeImpl.sendMessage(msg);
+            addNetworkToBlacklist(mRecommendedNetwork.SSID);
+        }
 
         mState = STATE_CONNECTING_IN_NOTIFICATION;
         mHandler.postDelayed(
diff --git a/service/java/com/android/server/wifi/CarrierNetworkConfig.java b/service/java/com/android/server/wifi/CarrierNetworkConfig.java
index cf39e0a..dc25721 100644
--- a/service/java/com/android/server/wifi/CarrierNetworkConfig.java
+++ b/service/java/com/android/server/wifi/CarrierNetworkConfig.java
@@ -53,14 +53,22 @@
     private static final int ENCODED_SSID_INDEX = 0;
     private static final int EAP_TYPE_INDEX = 1;
     private static final int CONFIG_ELEMENT_SIZE = 2;
+
     private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier");
 
     private boolean mDbg = false;
 
     private final Map<String, NetworkInfo> mCarrierNetworkMap;
     private boolean mIsCarrierImsiEncryptionInfoAvailable = false;
+    private int mBase64EncodingMethod = Base64.DEFAULT;
     private ImsiEncryptionInfo mLastImsiEncryptionInfo = null; // used for dumpsys only
 
+    // RFC2045: adds Line Feed at each 76 chars and encode it.
+    public static final int ENCODING_METHOD_RFC_2045 = 2045;
+
+    // RFC4648: encodes whole data into one string.
+    public static final int ENCODING_METHOD_RFC_4648 = 4648;
+
     /**
      * Enable/disable verbose logging.
      */
@@ -118,6 +126,13 @@
     }
 
     /**
+     * @return the base64 encoding flag for current carrier.
+     */
+    public int getBase64EncodingFlag() {
+        return mBase64EncodingMethod;
+    }
+
+    /**
      * @return True if carrier IMSI encryption info is available, False otherwise.
      */
     public boolean isCarrierEncryptionInfoAvailable() {
@@ -230,6 +245,16 @@
             return;
         }
 
+        int encodeMethod = carrierConfig.getInt(
+                CarrierConfigManager.KEY_IMSI_ENCODING_METHOD_INT, ENCODING_METHOD_RFC_2045);
+        if (encodeMethod != ENCODING_METHOD_RFC_2045 && encodeMethod != ENCODING_METHOD_RFC_4648) {
+            Log.e(TAG, "Invalid encoding method type: " + encodeMethod);
+            return;
+        }
+        mBase64EncodingMethod = Base64.DEFAULT;
+        if (encodeMethod == ENCODING_METHOD_RFC_4648) {
+            mBase64EncodingMethod = Base64.NO_WRAP;
+        }
         for (String networkConfig : networkConfigs) {
             String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR);
             if (configArr.length != CONFIG_ELEMENT_SIZE) {
@@ -237,9 +262,11 @@
                 continue;
             }
             try {
+
                 String ssid = new String(Base64.decode(
-                        configArr[ENCODED_SSID_INDEX], Base64.DEFAULT));
+                        configArr[ENCODED_SSID_INDEX], mBase64EncodingMethod));
                 int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX]));
+
                 // Verify EAP type, must be a SIM based EAP type.
                 if (eapType == -1) {
                     Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]);
@@ -281,6 +308,7 @@
         pw.println("mCarrierNetworkMap=" + mCarrierNetworkMap);
         pw.println("mIsCarrierImsiEncryptionInfoAvailable="
                 + mIsCarrierImsiEncryptionInfoAvailable);
+        pw.println("mBase64EncodingMethod=" + mBase64EncodingMethod);
         pw.println("mLastImsiEncryptionInfo=" + mLastImsiEncryptionInfo);
     }
 }
diff --git a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java
index b052c7d..323cfc0 100644
--- a/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/CarrierNetworkEvaluator.java
@@ -110,6 +110,17 @@
             }
             config.enterpriseConfig.setEapMethod(eapType);
 
+            // Check if we already have a network with the same credentials in WifiConfigManager
+            // database. If yes, we should check if the network is currently blacklisted.
+            WifiConfiguration existingNetwork =
+                    mWifiConfigManager.getConfiguredNetwork(config.configKey());
+            if (existingNetwork != null
+                    && !existingNetwork.getNetworkSelectionStatus().isNetworkEnabled()
+                    && !mWifiConfigManager.tryEnableNetwork(existingNetwork.networkId)) {
+                mLocalLog.log(TAG + ": Ignoring blacklisted network: "
+                        + WifiNetworkSelector.toNetworkString(existingNetwork));
+                continue;
+            }
             // Add the newly created WifiConfiguration to WifiConfigManager.
             NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(config,
                     Process.WIFI_UID);
@@ -131,7 +142,10 @@
 
             config = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
 
-            WifiConfiguration.NetworkSelectionStatus nss = config.getNetworkSelectionStatus();
+            WifiConfiguration.NetworkSelectionStatus nss = null;
+            if (config != null) {
+                nss = config.getNetworkSelectionStatus();
+            }
             if (nss == null) {
                 mLocalLog.log(TAG + ": null network selection status for: " + config);
                 continue;
diff --git a/service/java/com/android/server/wifi/CarrierNetworkNotifier.java b/service/java/com/android/server/wifi/CarrierNetworkNotifier.java
index 747cf07..5af4001 100644
--- a/service/java/com/android/server/wifi/CarrierNetworkNotifier.java
+++ b/service/java/com/android/server/wifi/CarrierNetworkNotifier.java
@@ -26,7 +26,7 @@
 import android.provider.Settings;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.server.wifi.util.ScanResultUtil;
+import com.android.server.wifi.nano.WifiMetricsProto;
 
 /**
  * This class handles the "carrier wi-fi network available" notification
@@ -50,14 +50,16 @@
             ClientModeImpl clientModeImpl,
             ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) {
         super(TAG, STORE_DATA_IDENTIFIER, TOGGLE_SETTINGS_NAME,
-                SystemMessage.NOTE_CARRIER_NETWORK_AVAILABLE, context, looper, framework, clock,
+                SystemMessage.NOTE_CARRIER_NETWORK_AVAILABLE,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER,
+                context, looper, framework, clock,
                 wifiMetrics, wifiConfigManager, wifiConfigStore, clientModeImpl,
                 connectToNetworkNotificationBuilder);
     }
 
     @Override
     WifiConfiguration createRecommendedNetworkConfig(ScanResult recommendedNetwork) {
-        WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(recommendedNetwork);
+        WifiConfiguration network = super.createRecommendedNetworkConfig(recommendedNetwork);
 
         int eapMethod = recommendedNetwork.carrierApEapType;
         if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) {
diff --git a/service/java/com/android/server/wifi/CellularLinkLayerStats.java b/service/java/com/android/server/wifi/CellularLinkLayerStats.java
new file mode 100644
index 0000000..25f2949
--- /dev/null
+++ b/service/java/com/android/server/wifi/CellularLinkLayerStats.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 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;
+
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.NetworkType;
+
+/**
+ * A class representing the link layer statistics of the primary registered cell
+ * of cellular network
+ */
+public class CellularLinkLayerStats {
+    /** Cellular data network type currently in use on the device for data transmission */
+    private @NetworkType int mDataNetworkType =
+            TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    /**
+     * Cellular signal strength in dBm, NR: CsiRsrp, LTE: Rsrp, WCDMA/TDSCDMA: Rscp,
+     * CDMA: Rssi, EVDO: Rssi, GSM: Rssi
+     */
+    private int mSignalStrengthDbm = SignalStrength.INVALID;
+    /**
+     * Cellular signal strength in dB, NR: CsiSinr, LTE: Rsrq, WCDMA: EcNo, TDSCDMA: invalid,
+     * CDMA: Ecio, EVDO: SNR, GSM: invalid
+     */
+    private int mSignalStrengthDb = SignalStrength.INVALID;
+    /** Whether it is a new or old registered cell */
+    private boolean mIsSameRegisteredCell = false;
+
+    public void setDataNetworkType(@NetworkType int dataNetworkType) {
+        mDataNetworkType = dataNetworkType;
+    }
+
+    public void setSignalStrengthDbm(int signalStrengthDbm) {
+        mSignalStrengthDbm = signalStrengthDbm;
+    }
+
+    public void setIsSameRegisteredCell(boolean isSameRegisteredCell) {
+        mIsSameRegisteredCell = isSameRegisteredCell;
+    }
+
+    public void setSignalStrengthDb(int signalStrengthDb) {
+        mSignalStrengthDb = signalStrengthDb;
+    }
+
+    public @NetworkType int getDataNetworkType() {
+        return mDataNetworkType;
+    }
+
+    public boolean getIsSameRegisteredCell() {
+        return mIsSameRegisteredCell;
+    }
+
+    public int getSignalStrengthDb() {
+        return mSignalStrengthDb;
+    }
+
+    public int getSignalStrengthDbm() {
+        return mSignalStrengthDbm;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append(" CellularLinkLayerStats: ").append('\n')
+                .append(" Data Network Type: ")
+                .append(mDataNetworkType).append('\n')
+                .append(" Signal Strength in dBm: ")
+                .append(mSignalStrengthDbm).append('\n')
+                .append(" Signal Strength in dB: ")
+                .append(mSignalStrengthDb).append('\n')
+                .append(" Is it the same registered cell? ")
+                .append(mIsSameRegisteredCell).append('\n');
+        return sbuf.toString();
+    }
+}
diff --git a/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java b/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java
new file mode 100644
index 0000000..473ced3
--- /dev/null
+++ b/service/java/com/android/server/wifi/CellularLinkLayerStatsCollector.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2019 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;
+
+import static android.telephony.TelephonyManager.NETWORK_TYPE_CDMA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_GSM;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+import android.content.Context;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoNr;
+import android.telephony.CellInfoTdscdma;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
+import android.telephony.CellSignalStrengthTdscdma;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+/**
+ * A class collecting the latest cellular link layer stats
+ */
+public class CellularLinkLayerStatsCollector {
+    private static final String TAG = "CellStatsCollector";
+    private static final boolean DBG = false;
+
+    private Context mContext;
+    private SubscriptionManager mSubManager = null;
+    private TelephonyManager mCachedDefaultDataTelephonyManager = null;
+    private int mCachedDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private CellInfo mLastPrimaryCellInfo = null;
+    private int mLastDataNetworkType = NETWORK_TYPE_UNKNOWN;
+
+    public CellularLinkLayerStatsCollector(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Get the latest DataNetworkType, SignalStrength, CellInfo and other information from
+     * default data sim's TelephonyManager, parse the values of primary registered cell and return
+     * them as an instance of CellularLinkLayerStats
+     */
+    public CellularLinkLayerStats update() {
+        CellularLinkLayerStats cellStats = new CellularLinkLayerStats();
+
+        retrieveDefaultDataTelephonyManager();
+        if (mCachedDefaultDataTelephonyManager == null) {
+            if (DBG) Log.v(TAG, cellStats.toString());
+            return cellStats;
+        }
+
+        SignalStrength signalStrength = mCachedDefaultDataTelephonyManager.getSignalStrength();
+        List<CellSignalStrength> cssList = null;
+        if (signalStrength != null) cssList = signalStrength.getCellSignalStrengths();
+
+        if (mCachedDefaultDataTelephonyManager.getDataNetworkType() == NETWORK_TYPE_UNKNOWN
+                || cssList == null || cssList.size() == 0) {
+            mLastPrimaryCellInfo = null;
+            mLastDataNetworkType = NETWORK_TYPE_UNKNOWN;
+            if (DBG) Log.v(TAG, cellStats.toString());
+            return cellStats;
+        }
+        if (DBG) Log.v(TAG, "Cell Signal Strength List size = " + cssList.size());
+
+        CellSignalStrength primaryCss = cssList.get(0);
+        cellStats.setSignalStrengthDbm(primaryCss.getDbm());
+
+        updateSignalStrengthDbAndNetworkTypeOfCellStats(primaryCss, cellStats);
+
+        int networkType = cellStats.getDataNetworkType();
+        CellInfo primaryCellInfo = getPrimaryCellInfo(mCachedDefaultDataTelephonyManager,
+                networkType);
+        boolean isSameRegisteredCell = getIsSameRegisteredCell(primaryCellInfo, networkType);
+        cellStats.setIsSameRegisteredCell(isSameRegisteredCell);
+
+        // Update for the next call
+        mLastPrimaryCellInfo = primaryCellInfo;
+        mLastDataNetworkType = networkType;
+
+        if (DBG) Log.v(TAG, cellStats.toString());
+        return cellStats;
+    }
+
+    private void retrieveDefaultDataTelephonyManager() {
+        if (!initSubManager()) return;
+
+        int defaultDataSubId = mSubManager.getDefaultDataSubscriptionId();
+        if (DBG) Log.v(TAG, "default Data Sub ID = " + defaultDataSubId);
+        if (defaultDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            mCachedDefaultDataTelephonyManager = null;
+            return;
+        }
+
+        if (defaultDataSubId != mCachedDefaultDataSubId
+                || mCachedDefaultDataTelephonyManager == null) {
+            mCachedDefaultDataSubId = defaultDataSubId;
+            TelephonyManager defaultSubTelephonyManager = (TelephonyManager) mContext
+                    .getSystemService(Context.TELEPHONY_SERVICE);
+            if (defaultDataSubId == mSubManager.getDefaultSubscriptionId()) {
+                mCachedDefaultDataTelephonyManager = defaultSubTelephonyManager;
+            } else {
+                mCachedDefaultDataTelephonyManager =  defaultSubTelephonyManager
+                        .createForSubscriptionId(defaultDataSubId);
+            }
+        }
+    }
+
+    private boolean initSubManager() {
+        if (mSubManager == null) {
+            mSubManager = (SubscriptionManager) mContext.getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        }
+        return (mSubManager != null);
+    }
+
+    /**
+      * Update dB value and network type base on CellSignalStrength subclass type.
+      * It follows the same order as that in SignalStrength.getPrimary().
+      * TODO: NR may move up in the future
+      */
+    private void updateSignalStrengthDbAndNetworkTypeOfCellStats(CellSignalStrength primaryCss,
+            CellularLinkLayerStats cellStats) {
+        if (primaryCss instanceof CellSignalStrengthLte) {
+            CellSignalStrengthLte cssLte = (CellSignalStrengthLte) primaryCss;
+            cellStats.setSignalStrengthDb(cssLte.getRsrq());
+            cellStats.setDataNetworkType(NETWORK_TYPE_LTE);
+        } else if (primaryCss instanceof CellSignalStrengthCdma) {
+            CellSignalStrengthCdma cssCdma = (CellSignalStrengthCdma) primaryCss;
+            int evdoSnr = cssCdma.getEvdoSnr();
+            int cdmaEcio = cssCdma.getCdmaEcio();
+            if (evdoSnr != SignalStrength.INVALID) {
+                cellStats.setSignalStrengthDb(evdoSnr);
+                cellStats.setDataNetworkType(NETWORK_TYPE_EVDO_0);
+            } else {
+                cellStats.setSignalStrengthDb(cdmaEcio);
+                cellStats.setDataNetworkType(NETWORK_TYPE_CDMA);
+            }
+        } else if (primaryCss instanceof CellSignalStrengthTdscdma) {
+            cellStats.setDataNetworkType(NETWORK_TYPE_TD_SCDMA);
+        } else if (primaryCss instanceof CellSignalStrengthWcdma) {
+            CellSignalStrengthWcdma cssWcdma = (CellSignalStrengthWcdma) primaryCss;
+            cellStats.setSignalStrengthDb(cssWcdma.getEcNo());
+            cellStats.setDataNetworkType(NETWORK_TYPE_UMTS);
+        } else if (primaryCss instanceof CellSignalStrengthGsm) {
+            cellStats.setDataNetworkType(NETWORK_TYPE_GSM);
+        } else if (primaryCss instanceof CellSignalStrengthNr) {
+            CellSignalStrengthNr cssNr = (CellSignalStrengthNr) primaryCss;
+            cellStats.setSignalStrengthDb(cssNr.getCsiSinr());
+            cellStats.setDataNetworkType(NETWORK_TYPE_NR);
+        } else {
+            Log.e(TAG, "invalid CellSignalStrength");
+        }
+    }
+
+    private CellInfo getPrimaryCellInfo(TelephonyManager defaultDataTelephonyManager,
+                int networkType) {
+        List<CellInfo> cellInfoList = getRegisteredCellInfo(defaultDataTelephonyManager);
+        int cilSize = cellInfoList.size();
+        CellInfo primaryCellInfo = null;
+        // CellInfo.getCellConnectionStatus() should tell if it is primary serving cell.
+        // However, it currently always returns 0 (CONNECTION_NONE) for registered cells.
+        // Therefore, the workaround of deriving primary serving cell is
+        // to check if the registered cellInfo subclass type matches networkType
+        for (int i = 0; i < cilSize; ++i) {
+            CellInfo cellInfo = cellInfoList.get(i);
+            if ((cellInfo instanceof CellInfoTdscdma && networkType == NETWORK_TYPE_TD_SCDMA)
+                    || (cellInfo instanceof CellInfoCdma && (networkType == NETWORK_TYPE_CDMA
+                    || networkType == NETWORK_TYPE_EVDO_0))
+                    || (cellInfo instanceof CellInfoLte && networkType == NETWORK_TYPE_LTE)
+                    || (cellInfo instanceof CellInfoWcdma && networkType == NETWORK_TYPE_UMTS)
+                    || (cellInfo instanceof CellInfoGsm && networkType == NETWORK_TYPE_GSM)
+                    || (cellInfo instanceof CellInfoNr && networkType == NETWORK_TYPE_NR)) {
+                primaryCellInfo = cellInfo;
+            }
+        }
+        return primaryCellInfo;
+    }
+
+    private boolean getIsSameRegisteredCell(CellInfo primaryCellInfo, int networkType) {
+        boolean isSameRegisteredCell;
+        if (primaryCellInfo != null && mLastPrimaryCellInfo != null) {
+            isSameRegisteredCell = primaryCellInfo.getCellIdentity()
+                .equals(mLastPrimaryCellInfo.getCellIdentity());
+        } else if (primaryCellInfo == null && mLastPrimaryCellInfo == null) {
+            // This is a workaround when it can't find primaryCellInfo for two consecutive times.
+            isSameRegisteredCell = true;
+        } else {
+            // only one of them is null and it is a strong indication of primary cell change.
+            isSameRegisteredCell = false;
+        }
+
+        if (mLastDataNetworkType == NETWORK_TYPE_UNKNOWN || mLastDataNetworkType != networkType) {
+            isSameRegisteredCell = false;
+        }
+        return isSameRegisteredCell;
+    }
+
+    private List<CellInfo> getRegisteredCellInfo(TelephonyManager defaultDataTelephonyManager) {
+        List<CellInfo> allList = defaultDataTelephonyManager.getAllCellInfo();
+        List<CellInfo> cellInfoList = new ArrayList<>();
+        for (CellInfo ci : allList) {
+            if (ci.isRegistered()) cellInfoList.add(ci);
+            if (DBG) Log.v(TAG, ci.toString());
+        }
+        return cellInfoList;
+    }
+}
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 7497aaf..dcf8432 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi;
 
 import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable;
-import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT;
 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
@@ -811,6 +810,7 @@
                 mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
         mWifiConnectivityManager = mWifiInjector.makeWifiConnectivityManager(this);
 
+
         mLinkProperties = new LinkProperties();
         mMcastLockManagerFilterController = new McastLockManagerFilterController();
 
@@ -872,15 +872,6 @@
                     }
                 });
 
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        sendMessage(CMD_BOOT_COMPLETED);
-                    }
-                },
-                new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED));
-
         mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
 
@@ -1172,7 +1163,8 @@
     private boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
         logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
                 + ", forceReconnect = " + forceReconnect);
-        if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
+        WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(netId);
+        if (config == null) {
             loge("connectToUserSelectNetwork Invalid network Id=" + netId);
             return false;
         }
@@ -1191,6 +1183,10 @@
             logi("connectToUserSelectNetwork already connecting/connected=" + netId);
         } else {
             mWifiConnectivityManager.prepareForForcedConnection(netId);
+            if (uid == Process.SYSTEM_UID) {
+                mWifiMetrics.setNominatorForNetwork(config.networkId,
+                        WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
+            }
             startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
         }
         return true;
@@ -1590,19 +1586,20 @@
     }
 
     /**
-     * Method to trigger a disconnect.
-     * Note: To be used from within the wifi stack.
+     * Disconnect from Access Point
      */
-    public void disconnectCommandInternal() {
-        sendMessage(CMD_DISCONNECT, 0 /* fromExternal */);
+    public void disconnectCommand() {
+        sendMessage(CMD_DISCONNECT);
     }
 
     /**
      * Method to trigger a disconnect.
-     * Note: To be used from public API surface.
+     *
+     * @param uid UID of requesting caller
+     * @param reason disconnect reason
      */
-    public void disconnectCommandExternal() {
-        sendMessage(CMD_DISCONNECT, 1 /* fromExternal */);
+    public void disconnectCommand(int uid, int reason) {
+        sendMessage(CMD_DISCONNECT, uid, reason);
     }
 
     /**
@@ -1692,13 +1689,14 @@
      * scanResults per network type({@link WifiManager#PASSPOINT_HOME_NETWORK} and {@link
      * WifiManager#PASSPOINT_ROAMING_NETWORK}).
      */
+    @NonNull
     Map<String, Map<Integer, List<ScanResult>>> syncGetAllMatchingFqdnsForScanResults(
             List<ScanResult> scanResults,
             AsyncChannel channel) {
         Message resultMsg = channel.sendMessageSynchronously(
                 CMD_GET_ALL_MATCHING_FQDNS_FOR_SCAN_RESULTS,
                 scanResults);
-        if (messageIsNull(resultMsg)) return null;
+        if (messageIsNull(resultMsg)) return new HashMap<>();
         Map<String, Map<Integer, List<ScanResult>>> configs =
                 (Map<String, Map<Integer, List<ScanResult>>>) resultMsg.obj;
         resultMsg.recycle();
@@ -1713,12 +1711,13 @@
      * @param channel     Channel for communicating with the state machine
      * @return Map that consists of {@link OsuProvider} and a matching list of {@link ScanResult}.
      */
+    @NonNull
     public Map<OsuProvider, List<ScanResult>> syncGetMatchingOsuProviders(
             List<ScanResult> scanResults,
             AsyncChannel channel) {
         Message resultMsg =
                 channel.sendMessageSynchronously(CMD_GET_MATCHING_OSU_PROVIDERS, scanResults);
-        if (messageIsNull(resultMsg)) return null;
+        if (messageIsNull(resultMsg)) return new HashMap<>();
         Map<OsuProvider, List<ScanResult>> providers =
                 (Map<OsuProvider, List<ScanResult>>) resultMsg.obj;
         resultMsg.recycle();
@@ -1732,12 +1731,13 @@
      * @param channel  AsyncChannel to use for the response
      * @return Map that consists of {@link OsuProvider} and matching {@link PasspointConfiguration}.
      */
+    @NonNull
     public Map<OsuProvider, PasspointConfiguration> syncGetMatchingPasspointConfigsForOsuProviders(
             List<OsuProvider> osuProviders, AsyncChannel channel) {
         Message resultMsg =
                 channel.sendMessageSynchronously(
                         CMD_GET_MATCHING_PASSPOINT_CONFIGS_FOR_OSU_PROVIDERS, osuProviders);
-        if (messageIsNull(resultMsg)) return null;
+        if (messageIsNull(resultMsg)) return new HashMap<>();
         Map<OsuProvider, PasspointConfiguration> result =
                 (Map<OsuProvider, PasspointConfiguration>) resultMsg.obj;
         resultMsg.recycle();
@@ -1755,12 +1755,13 @@
      * @return List of {@link WifiConfiguration} converted from
      * {@link com.android.server.wifi.hotspot2.PasspointProvider}
      */
+    @NonNull
     public List<WifiConfiguration> syncGetWifiConfigsForPasspointProfiles(List<String> fqdnList,
             AsyncChannel channel) {
         Message resultMsg =
                 channel.sendMessageSynchronously(
                         CMD_GET_WIFI_CONFIGS_FOR_PASSPOINT_PROFILES, fqdnList);
-        if (messageIsNull(resultMsg)) return null;
+        if (messageIsNull(resultMsg)) return new ArrayList<>();
         List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
         resultMsg.recycle();
         return result;
@@ -2062,6 +2063,13 @@
     }
 
     /**
+     * Trigger message to handle boot completed event.
+     */
+    public void handleBootCompleted() {
+        sendMessage(CMD_BOOT_COMPLETED);
+    }
+
+    /**
      * Trigger message to handle user switch event.
      */
     public void handleUserSwitch(int userId) {
@@ -3462,7 +3470,7 @@
 
         @Override
         public boolean processMessage(Message message) {
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch (message.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
@@ -3675,7 +3683,7 @@
                     if (removedNetworkIds.contains(mTargetNetworkId)
                             || removedNetworkIds.contains(mLastNetworkId)) {
                         // Disconnect and let autojoin reselect a new network
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case CMD_USER_UNLOCK:
@@ -3746,7 +3754,12 @@
                     loge("Error! unhandled message" + message);
                     break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+
+            return handleStatus;
         }
     }
 
@@ -3978,7 +3991,7 @@
             Set<Integer> removedNetworkIds;
             int reasonCode;
             boolean timedOut;
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch (message.what) {
                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
@@ -4133,7 +4146,7 @@
                     netId = message.arg1;
                     if (netId == mTargetNetworkId || netId == mLastNetworkId) {
                         // Disconnect and let autojoin reselect a new network
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case CMD_ENABLE_NETWORK:
@@ -4157,7 +4170,7 @@
                         replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
                         if (netId == mTargetNetworkId || netId == mLastNetworkId) {
                             // Disconnect and let autojoin reselect a new network
-                            disconnectCommandInternal();
+                            sendMessage(CMD_DISCONNECT);
                         }
                     } else {
                         loge("Failed to disable network");
@@ -4172,7 +4185,7 @@
                         if (config.networkId == mTargetNetworkId
                                 || config.networkId == mLastNetworkId) {
                             // Disconnect and let autojoin reselect a new network
-                            disconnectCommandInternal();
+                            sendMessage(CMD_DISCONNECT);
                         }
                     }
                     break;
@@ -4186,7 +4199,8 @@
                         // Pair<identity, encrypted identity>
                         Pair<String, String> identityPair =
                                 TelephonyUtil.getSimIdentity(getTelephonyManager(),
-                                        new TelephonyUtil(), mTargetWifiConfiguration);
+                                        new TelephonyUtil(), mTargetWifiConfiguration,
+                                        mWifiInjector.getCarrierNetworkConfig());
                         Log.i(TAG, "SUP_REQUEST_IDENTITY: identityPair=" + identityPair);
                         if (identityPair != null && identityPair.first != null) {
                             mWifiNative.simIdentityResponse(mInterfaceName, netId,
@@ -4224,7 +4238,7 @@
                             handle3GAuthRequest(requestData);
                         }
                     } else {
-                        loge("Invalid sim auth request");
+                        loge("Invalid SIM auth request");
                     }
                     break;
                 case CMD_GET_MATCHING_OSU_PROVIDERS:
@@ -4261,7 +4275,7 @@
                     break;
                 case CMD_START_ROAM:
                     mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
-                    return HANDLED;
+                    break;
                 case CMD_START_CONNECT:
                     /* connect command coming from auto-join */
                     netId = message.arg1;
@@ -4332,7 +4346,7 @@
                     if (removedNetworkIds.contains(mTargetNetworkId)
                             || removedNetworkIds.contains(mLastNetworkId)) {
                         // Disconnect and let autojoin reselect a new network.
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case CMD_REMOVE_USER_CONFIGURATIONS:
@@ -4341,7 +4355,7 @@
                     if (removedNetworkIds.contains(mTargetNetworkId)
                             || removedNetworkIds.contains(mLastNetworkId)) {
                         // Disconnect and let autojoin reselect a new network.
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case WifiManager.CONNECT_NETWORK:
@@ -4422,7 +4436,7 @@
                     netId = message.arg1;
                     if (netId == mTargetNetworkId || netId == mLastNetworkId) {
                         // Disconnect and let autojoin reselect a new network
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case CMD_ASSOCIATED_BSSID:
@@ -4438,7 +4452,8 @@
                                     someBssid));
                         }
                     }
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
                     if (mVerboseLoggingEnabled) log("Network connection established");
                     mLastNetworkId = message.arg1;
@@ -4486,7 +4501,7 @@
                     } else {
                         logw("Connected to unknown networkId " + mLastNetworkId
                                 + ", disconnecting...");
-                        disconnectCommandInternal();
+                        sendMessage(CMD_DISCONNECT);
                     }
                     break;
                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
@@ -4519,7 +4534,7 @@
                         if (isProviderOwnedNetwork(mTargetNetworkId, fqdn)
                                 || isProviderOwnedNetwork(mLastNetworkId, fqdn)) {
                             logd("Disconnect from current network since its provider is updated");
-                            disconnectCommandInternal();
+                            sendMessage(CMD_DISCONNECT);
                         }
                         replyToMessage(message, message.what, SUCCESS);
                     } else {
@@ -4532,7 +4547,7 @@
                         if (isProviderOwnedNetwork(mTargetNetworkId, fqdn)
                                 || isProviderOwnedNetwork(mLastNetworkId, fqdn)) {
                             logd("Disconnect from current network since its provider is removed");
-                            disconnectCommandInternal();
+                            sendMessage(CMD_DISCONNECT);
                         }
                         mWifiConfigManager.removePasspointConfiguredNetwork(fqdn);
                         replyToMessage(message, message.what, SUCCESS);
@@ -4622,9 +4637,15 @@
                     mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
                     break;
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+
+            return handleStatus;
         }
     }
 
@@ -4982,7 +5003,7 @@
 
         @Override
         public boolean processMessage(Message message) {
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch (message.what) {
                 case CMD_PRE_DHCP_ACTION:
@@ -5054,6 +5075,10 @@
                     if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
                     mWifiDiagnostics.captureBugReportData(
                             WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST);
+                    mWifiMetrics.logWifiIsUnusableEvent(
+                            WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
+                    mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                            WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST);
                     if (mIpReachabilityDisconnectEnabled) {
                         handleIpReachabilityLost();
                         transitionTo(mDisconnectingState);
@@ -5062,17 +5087,8 @@
                     }
                     break;
                 case CMD_DISCONNECT:
-                    boolean fromExternal = message.arg1 == 1;
-                    if (fromExternal) {
-                        mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
-                                StaEvent.DISCONNECT_API);
-                        // For external disconnect requests, temporarily disable the network.
-                        mWifiConfigManager.updateNetworkSelectionStatus(
-                                mLastNetworkId, DISABLED_BY_WIFI_MANAGER_DISCONNECT);
-                    } else {
-                        mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
-                                StaEvent.DISCONNECT_GENERIC);
-                    }
+                    mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+                            StaEvent.DISCONNECT_GENERIC);
                     mWifiNative.disconnect(mInterfaceName);
                     transitionTo(mDisconnectingState);
                     break;
@@ -5092,7 +5108,8 @@
                         replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
                         break;
                     }
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
                     mWifiInfo.setBSSID((String) message.obj);
                     mLastNetworkId = message.arg1;
@@ -5137,7 +5154,7 @@
                                 mPollRssiIntervalMsecs);
                         if (mVerboseLoggingEnabled) sendRssiChangeBroadcast(mWifiInfo.getRssi());
                         mWifiTrafficPoller.notifyOnDataActivity(mWifiInfo.txSuccess,
-                                                                mWifiInfo.rxSuccess);
+                                mWifiInfo.rxSuccess);
                     } else {
                         // Polling has completed
                     }
@@ -5217,7 +5234,8 @@
                         }
                     }
                     /* allow parent state to reset data for other networks */
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case CMD_START_IP_PACKET_OFFLOAD: {
                     int slot = message.arg1;
                     int intervalSeconds = message.arg2;
@@ -5251,10 +5269,15 @@
                     break;
                 }
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
 
-            return HANDLED;
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+
+            return handleStatus;
         }
 
         /**
@@ -5380,7 +5403,7 @@
 
         @Override
         public boolean processMessage(Message message) {
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch(message.what) {
                 case CMD_START_CONNECT:
@@ -5396,15 +5419,21 @@
                             WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
                             WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case CMD_SET_HIGH_PERF_MODE:
                     mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
                     deferMessage(message);
                     break;
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+            return handleStatus;
         }
     }
 
@@ -5470,8 +5499,9 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            logStateAndMessage(message, this);
             WifiConfiguration config;
+            boolean handleStatus = HANDLED;
+
             switch (message.what) {
                 case CMD_IP_CONFIGURATION_LOST:
                     config = getCurrentWifiConfiguration();
@@ -5479,12 +5509,13 @@
                         mWifiDiagnostics.captureBugReportData(
                                 WifiDiagnostics.REPORT_REASON_AUTOROAM_FAILURE);
                     }
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case CMD_UNWANTED_NETWORK:
                     if (mVerboseLoggingEnabled) {
                         log("Roaming and CS doesnt want the network -> ignore");
                     }
-                    return HANDLED;
+                    break;
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     /**
                      * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
@@ -5586,9 +5617,14 @@
                     }
                     break;
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+            return handleStatus;
         }
 
         @Override
@@ -5633,7 +5669,7 @@
         @Override
         public boolean processMessage(Message message) {
             WifiConfiguration config = null;
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch (message.what) {
                 case CMD_UNWANTED_NETWORK:
@@ -5677,7 +5713,7 @@
                             }
                         }
                     }
-                    return HANDLED;
+                    break;
                 case CMD_NETWORK_STATUS:
                     if (message.arg1 == NetworkAgent.VALID_NETWORK) {
                         // stop collect last-mile stats since validation pass
@@ -5696,16 +5732,17 @@
                                     config.networkId, true);
                         }
                     }
-                    return HANDLED;
+                    break;
                 case CMD_ACCEPT_UNVALIDATED:
                     boolean accept = (message.arg1 != 0);
                     mWifiConfigManager.setNetworkNoInternetAccessExpected(mLastNetworkId, accept);
-                    return HANDLED;
+                    break;
                 case CMD_ASSOCIATED_BSSID:
                     // ASSOCIATING to a new BSSID while already connected, indicates
                     // that driver is roaming
                     mLastDriverRoamAttempt = mClock.getWallClockMillis();
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                     long lastRoam = 0;
                     reportConnectionAttemptEnd(
@@ -5781,9 +5818,15 @@
                     }
                     break;
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+
+            return handleStatus;
         }
 
         @Override
@@ -5820,7 +5863,8 @@
 
         @Override
         public boolean processMessage(Message message) {
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
+
             switch (message.what) {
                 case CMD_DISCONNECT:
                     if (mVerboseLoggingEnabled) {
@@ -5845,9 +5889,14 @@
                     transitionTo(mDisconnectedState);
                     break;
                 default:
-                    return NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return HANDLED;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+            return handleStatus;
         }
     }
 
@@ -5875,9 +5924,7 @@
 
         @Override
         public boolean processMessage(Message message) {
-            boolean ret = HANDLED;
-
-            logStateAndMessage(message, this);
+            boolean handleStatus = HANDLED;
 
             switch (message.what) {
                 case CMD_DISCONNECT:
@@ -5908,6 +5955,9 @@
 
                         // Update Passpoint information before setNetworkDetailedState as
                         // WifiTracker monitors NETWORK_STATE_CHANGED_ACTION to update UI.
+                        mWifiInfo.setFQDN(null);
+                        mWifiInfo.setOsuAp(false);
+                        mWifiInfo.setProviderFriendlyName(null);
                         if (config != null && (config.isPasspoint() || config.osu)) {
                             if (config.isPasspoint()) {
                                 mWifiInfo.setFQDN(config.FQDN);
@@ -5919,7 +5969,7 @@
                     }
                     setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
                     /* ConnectModeState does the rest of the handling */
-                    ret = NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
                     break;
                 case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
                     NetworkInfo info = (NetworkInfo) message.obj;
@@ -5933,16 +5983,21 @@
                         break;
                     } else {
                         // ConnectModeState handles it
-                        ret = NOT_HANDLED;
+                        handleStatus = NOT_HANDLED;
                     }
                     break;
                 case CMD_SCREEN_STATE_CHANGED:
                     handleScreenStateChanged(message.arg1 != 0);
                     break;
                 default:
-                    ret = NOT_HANDLED;
+                    handleStatus = NOT_HANDLED;
+                    break;
             }
-            return ret;
+
+            if (handleStatus == HANDLED) {
+                logStateAndMessage(message, this);
+            }
+            return handleStatus;
         }
 
         @Override
diff --git a/service/java/com/android/server/wifi/CompatibilityScorer.java b/service/java/com/android/server/wifi/CompatibilityScorer.java
index a4783f1..550eca7 100644
--- a/service/java/com/android/server/wifi/CompatibilityScorer.java
+++ b/service/java/com/android/server/wifi/CompatibilityScorer.java
@@ -68,8 +68,6 @@
 
     /**
      * Calculates an individual candidate's score.
-     *
-     * This relies mostly on the scores provided by the evaluator.
      */
     private ScoredCandidate scoreCandidate(Candidate candidate) {
         int rssiSaturationThreshold = mScoringParams.getGoodRssi(candidate.getFrequency());
@@ -94,7 +92,10 @@
         // which evaluator added the candidate.
         score -= 1000 * candidate.getEvaluatorId();
 
-        return new ScoredCandidate(score, 10, candidate);
+        // The old method breaks ties on the basis of RSSI, which we can
+        // emulate easily since our score does not need to be an integer.
+        double tieBreaker = candidate.getScanRssi() / 1000.0;
+        return new ScoredCandidate(score + tieBreaker, 10, candidate);
     }
 
     @Override
diff --git a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java b/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java
index 136d5c7..ddc0b71 100644
--- a/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java
+++ b/service/java/com/android/server/wifi/NetworkSuggestionEvaluator.java
@@ -21,16 +21,21 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiNetworkSuggestion;
 import android.util.LocalLog;
-import android.util.Pair;
+import android.util.Log;
 
 import com.android.server.wifi.util.ScanResultUtil;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
+import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 
 /**
@@ -74,19 +79,10 @@
             WifiConfiguration currentNetwork, String currentBssid, boolean connected,
             boolean untrustedNetworkAllowed,
             @NonNull OnConnectableListener onConnectableListener) {
-        Map<WifiNetworkSuggestion, Pair<WifiConfiguration, ScanDetail>> candidateMap =
-                new HashMap<>();
+        MatchMetaInfo matchMetaInfo = new MatchMetaInfo();
         for (int i = 0; i < scanDetails.size(); i++) {
             ScanDetail scanDetail = scanDetails.get(i);
             ScanResult scanResult = scanDetail.getScanResult();
-            Set<WifiNetworkSuggestion> matchingNetworkSuggestions =
-                    mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail);
-            if (matchingNetworkSuggestions == null || matchingNetworkSuggestions.isEmpty()) {
-                continue;
-            }
-            // All matching network credentials are considered equal. So, put any one of them.
-            WifiNetworkSuggestion matchingNetworkSuggestion =
-                    matchingNetworkSuggestions.stream().findAny().get();
             // If the user previously forgot this network, don't select it.
             if (mWifiConfigManager.wasEphemeralNetworkDeleted(
                     ScanResultUtil.createQuotedSSID(scanResult.SSID))) {
@@ -94,9 +90,20 @@
                         + WifiNetworkSelector.toScanId(scanResult));
                 continue;
             }
+            Set<WifiNetworkSuggestion> matchingNetworkSuggestions =
+                    mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail);
+            if (matchingNetworkSuggestions == null || matchingNetworkSuggestions.isEmpty()) {
+                continue;
+            }
+            // All matching suggestions have the same network credentials type. So, use any one of
+            // them to lookup/add the credentials to WifiConfigManager.
+            // Note: Apps could provide different credentials (password, ceritificate) for the same
+            // network, need to handle that in the future.
+            WifiNetworkSuggestion matchingNetworkSuggestion =
+                    matchingNetworkSuggestions.stream().findAny().get();
             // Check if we already have a network with the same credentials in WifiConfigManager
             // database. If yes, we should check if the network is currently blacklisted.
-            WifiConfiguration  wCmConfiguredNetwork =
+            WifiConfiguration wCmConfiguredNetwork =
                     mWifiConfigManager.getConfiguredNetwork(
                             matchingNetworkSuggestion.wifiConfiguration.configKey());
             if (wCmConfiguredNetwork != null) {
@@ -106,35 +113,23 @@
                             + WifiNetworkSelector.toNetworkString(wCmConfiguredNetwork));
                     continue;
                 }
-                mLocalLog.log(String.format("network suggestion candidate %s (existing)",
-                        WifiNetworkSelector.toNetworkString(wCmConfiguredNetwork)));
-            } else {
-                wCmConfiguredNetwork = addCandidateToWifiConfigManager(
-                        matchingNetworkSuggestion.wifiConfiguration,
-                        matchingNetworkSuggestion.suggestorUid,
-                        matchingNetworkSuggestion.suggestorPackageName);
-                if (wCmConfiguredNetwork == null) continue;
-                mLocalLog.log(String.format("network suggestion candidate %s (new)",
-                        WifiNetworkSelector.toNetworkString(wCmConfiguredNetwork)));
             }
-            candidateMap.put(matchingNetworkSuggestion,
-                    Pair.create(wCmConfiguredNetwork, scanDetail));
-            onConnectableListener.onConnectable(scanDetail, wCmConfiguredNetwork, 0);
+            matchMetaInfo.putAll(matchingNetworkSuggestions, wCmConfiguredNetwork, scanDetail);
         }
-
-        // Pick the matching network suggestion corresponding to the highest RSSI. This will need to
-        // be replaced by a more sophisticated algorithm.
-        Map.Entry<WifiNetworkSuggestion, Pair<WifiConfiguration, ScanDetail>>
-                candidate = candidateMap
-                        .entrySet()
-                        .stream()
-                        .max(Comparator.comparing(e -> e.getValue().second.getScanResult().level))
-                        .orElse(null);
-        if (candidate == null) {
+        // Return early on no match.
+        if (matchMetaInfo.isEmpty()) {
             mLocalLog.log("did not see any matching network suggestions.");
             return null;
         }
-        return candidate.getValue().first;
+        // Note: These matched sets should be very small & hence these additional manipulations that
+        // follow should not be very expensive.
+        PerNetworkSuggestionMatchMetaInfo candidate =
+                matchMetaInfo.findConnectableNetworksAndPickBest(onConnectableListener);
+        if (candidate == null) { // should never happen.
+            Log.wtf(TAG, "Unexepectedly got null");
+            return null;
+        }
+        return candidate.wCmConfiguredNetwork;
     }
 
     // Add and enable this network to the central database (i.e WifiConfigManager).
@@ -169,4 +164,136 @@
     public String getName() {
         return TAG;
     }
+
+    // Container classes to handle book-keeping while we're iterating through the scan list.
+    private class PerNetworkSuggestionMatchMetaInfo {
+        public final WifiNetworkSuggestion wifiNetworkSuggestion;
+        public final ScanDetail matchingScanDetail;
+        public WifiConfiguration wCmConfiguredNetwork; // Added to WifiConfigManager.
+
+        PerNetworkSuggestionMatchMetaInfo(@NonNull WifiNetworkSuggestion wifiNetworkSuggestion,
+                                          @Nullable WifiConfiguration wCmConfiguredNetwork,
+                                          @NonNull ScanDetail matchingScanDetail) {
+            this.wifiNetworkSuggestion = wifiNetworkSuggestion;
+            this.wCmConfiguredNetwork = wCmConfiguredNetwork;
+            this.matchingScanDetail = matchingScanDetail;
+        }
+    }
+
+    private class PerAppMatchMetaInfo {
+        public final List<PerNetworkSuggestionMatchMetaInfo> networkInfos = new ArrayList<>();
+
+        /**
+         * Add the network suggestion & associated info to this package meta info.
+         */
+        public void put(WifiNetworkSuggestion wifiNetworkSuggestion,
+                        WifiConfiguration matchingWifiConfiguration,
+                        ScanDetail matchingScanDetail) {
+            networkInfos.add(new PerNetworkSuggestionMatchMetaInfo(
+                    wifiNetworkSuggestion, matchingWifiConfiguration, matchingScanDetail));
+        }
+
+        /**
+         * Pick the highest priority networks among the current match info candidates for this
+         * app.
+         */
+        public List<PerNetworkSuggestionMatchMetaInfo> getHighestPriorityNetworks() {
+            // Partition the list to a map of network suggestions keyed in by the priorities.
+            // There can be multiple networks with the same priority, hence a list in the value.
+            Map<Integer, List<PerNetworkSuggestionMatchMetaInfo>> matchedNetworkInfosPerPriority =
+                    networkInfos.stream()
+                            .collect(Collectors.toMap(
+                                    e -> e.wifiNetworkSuggestion.wifiConfiguration.priority,
+                                    e -> Arrays.asList(e),
+                                    (v1, v2) -> { // concatenate networks with the same priority.
+                                        List<PerNetworkSuggestionMatchMetaInfo> concatList =
+                                                new ArrayList<>(v1);
+                                        concatList.addAll(v2);
+                                        return concatList;
+                                    }));
+            if (matchedNetworkInfosPerPriority.isEmpty()) { // should never happen.
+                Log.wtf(TAG, "Unexepectedly got empty");
+                return Collections.EMPTY_LIST;
+            }
+            // Return the list associated with the highest priority value.
+            return matchedNetworkInfosPerPriority.get(Collections.max(
+                    matchedNetworkInfosPerPriority.keySet()));
+        }
+    }
+
+    private class MatchMetaInfo {
+        private Map<String, PerAppMatchMetaInfo> mAppInfos = new HashMap<>();
+
+        /**
+         * Add all the network suggestion & associated info.
+         */
+        public void putAll(Set<WifiNetworkSuggestion> wifiNetworkSuggestions,
+                           WifiConfiguration wCmConfiguredNetwork,
+                           ScanDetail matchingScanDetail) {
+            // Separate the suggestions into buckets for each app to allow sorting based on
+            // priorities set by app.
+            for (WifiNetworkSuggestion wifiNetworkSuggestion : wifiNetworkSuggestions) {
+                PerAppMatchMetaInfo appInfo = mAppInfos.computeIfAbsent(
+                        wifiNetworkSuggestion.suggestorPackageName, k -> new PerAppMatchMetaInfo());
+                appInfo.put(wifiNetworkSuggestion, wCmConfiguredNetwork, matchingScanDetail);
+            }
+        }
+
+        /**
+         * Are there any matched candidates?
+         */
+        public boolean isEmpty() {
+            return mAppInfos.isEmpty();
+        }
+
+        /**
+         * Find all the connectable networks and pick the best network among the current match info
+         * candidates.
+         *
+         * Among the highest priority suggestions from different packages, choose the suggestion
+         * with the highest RSSI.
+         * Note: This should need to be replaced by a more sophisticated algorithm.
+         */
+        public PerNetworkSuggestionMatchMetaInfo findConnectableNetworksAndPickBest(
+                @NonNull OnConnectableListener onConnectableListener) {
+            List<PerNetworkSuggestionMatchMetaInfo> allMatchedNetworkInfos = new ArrayList<>();
+            for (PerAppMatchMetaInfo appInfo : mAppInfos.values()) {
+                List<PerNetworkSuggestionMatchMetaInfo> matchedNetworkInfos =
+                        appInfo.getHighestPriorityNetworks();
+                for (PerNetworkSuggestionMatchMetaInfo matchedNetworkInfo : matchedNetworkInfos) {
+                    // if the network does not already exist in WifiConfigManager, add now.
+                    if (matchedNetworkInfo.wCmConfiguredNetwork == null) {
+                        matchedNetworkInfo.wCmConfiguredNetwork = addCandidateToWifiConfigManager(
+                                matchedNetworkInfo.wifiNetworkSuggestion.wifiConfiguration,
+                                matchedNetworkInfo.wifiNetworkSuggestion.suggestorUid,
+                                matchedNetworkInfo.wifiNetworkSuggestion.suggestorPackageName);
+                        if (matchedNetworkInfo.wCmConfiguredNetwork == null) continue;
+                        mLocalLog.log(String.format("network suggestion candidate %s (new)",
+                                WifiNetworkSelector.toNetworkString(
+                                        matchedNetworkInfo.wCmConfiguredNetwork)));
+                    } else {
+                        mLocalLog.log(String.format("network suggestion candidate %s (existing)",
+                                WifiNetworkSelector.toNetworkString(
+                                        matchedNetworkInfo.wCmConfiguredNetwork)));
+                    }
+                    allMatchedNetworkInfos.add(matchedNetworkInfo);
+                    // Invoke onConnectable for the best networks from each app.
+                    onConnectableListener.onConnectable(
+                            matchedNetworkInfo.matchingScanDetail,
+                            matchedNetworkInfo.wCmConfiguredNetwork,
+                            0);
+                }
+            }
+            PerNetworkSuggestionMatchMetaInfo networkInfo = allMatchedNetworkInfos
+                    .stream()
+                    .max(Comparator.comparing(e -> e.matchingScanDetail.getScanResult().level))
+                    .orElse(null);
+            if (networkInfo == null) { // should never happen.
+                Log.wtf(TAG, "Unexepectedly got null");
+                return null;
+            }
+            return networkInfo;
+        }
+    }
+
 }
diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java
index 7f61ed9..97f390f 100644
--- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java
+++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java
@@ -21,6 +21,7 @@
 import android.provider.Settings;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.server.wifi.nano.WifiMetricsProto;
 
 /**
  * This class handles the "open wi-fi network available" notification
@@ -44,7 +45,9 @@
             ClientModeImpl clientModeImpl,
             ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) {
         super(TAG, STORE_DATA_IDENTIFIER, TOGGLE_SETTINGS_NAME,
-                SystemMessage.NOTE_NETWORK_AVAILABLE, context, looper, framework, clock,
+                SystemMessage.NOTE_NETWORK_AVAILABLE,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_OPEN_NETWORK_AVAILABLE,
+                context, looper, framework, clock,
                 wifiMetrics, wifiConfigManager, wifiConfigStore, clientModeImpl,
                 connectToNetworkNotificationBuilder);
     }
diff --git a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
index 0d6af75..6622269 100644
--- a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
@@ -154,7 +154,7 @@
             // Track scan results for open wifi networks
             if (configuredNetwork == null) {
                 if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
-                    scoreTracker.trackUntrustedCandidate(scanResult);
+                    scoreTracker.trackUntrustedCandidate(scanDetail);
                 }
                 continue;
             }
@@ -184,7 +184,8 @@
             onConnectableListener.onConnectable(scanDetail, configuredNetwork, 0);
         }
 
-        return scoreTracker.getCandidateConfiguration();
+
+        return scoreTracker.getCandidateConfiguration(onConnectableListener);
     }
 
     /** Used to track the network with the highest score. */
@@ -198,6 +199,7 @@
         private WifiConfiguration mEphemeralConfig;
         private WifiConfiguration mSavedConfig;
         private ScanResult mScanResultCandidate;
+        private ScanDetail mScanDetailCandidate;
 
         /**
          * Returns the available external network score or null if no score is available.
@@ -219,12 +221,14 @@
             return null;
         }
 
-        /** Track an untrusted {@link ScanResult}. */
-        void trackUntrustedCandidate(ScanResult scanResult) {
+        /** Track an untrusted {@link ScanDetail}. */
+        void trackUntrustedCandidate(ScanDetail scanDetail) {
+            ScanResult scanResult = scanDetail.getScanResult();
             Integer score = getNetworkScore(scanResult, false /* isCurrentNetwork */);
             if (score != null && score > mHighScore) {
                 mHighScore = score;
                 mScanResultCandidate = scanResult;
+                mScanDetailCandidate = scanDetail;
                 mBestCandidateType = EXTERNAL_SCORED_UNTRUSTED_NETWORK;
                 debugLog(WifiNetworkSelector.toScanId(scanResult)
                         + " becomes the new untrusted candidate.");
@@ -241,6 +245,7 @@
             if (score != null && score > mHighScore) {
                 mHighScore = score;
                 mScanResultCandidate = scanResult;
+                mScanDetailCandidate = null;
                 mBestCandidateType = EXTERNAL_SCORED_UNTRUSTED_NETWORK;
                 mEphemeralConfig = config;
                 mWifiConfigManager.setNetworkCandidateScanResult(config.networkId, scanResult, 0);
@@ -262,6 +267,7 @@
                 mHighScore = score;
                 mSavedConfig = config;
                 mScanResultCandidate = scanResult;
+                mScanDetailCandidate = null;
                 mBestCandidateType = EXTERNAL_SCORED_SAVED_NETWORK;
                 mWifiConfigManager.setNetworkCandidateScanResult(config.networkId, scanResult, 0);
                 debugLog(WifiNetworkSelector.toScanId(scanResult)
@@ -271,7 +277,8 @@
 
         /** Returns the best candidate network tracked by this {@link ScoreTracker}. */
         @Nullable
-        WifiConfiguration getCandidateConfiguration() {
+        WifiConfiguration getCandidateConfiguration(
+                @NonNull OnConnectableListener onConnectableListener) {
             int candidateNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
             switch (mBestCandidateType) {
                 case ScoreTracker.EXTERNAL_SCORED_UNTRUSTED_NETWORK:
@@ -305,6 +312,11 @@
                         break;
                     }
                     candidateNetworkId = result.getNetworkId();
+                    if (mScanDetailCandidate == null) {
+                        // This should never happen, but if it does, WNS will log a wtf.
+                        // A message here might help with the diagnosis.
+                        Log.e(TAG, "mScanDetailCandidate is null!");
+                    }
                     mWifiConfigManager.setNetworkCandidateScanResult(candidateNetworkId,
                             mScanResultCandidate, 0);
                     mLocalLog.log(String.format("new ephemeral candidate %s network ID:%d, "
@@ -324,7 +336,13 @@
                     mLocalLog.log("ScoredNetworkEvaluator did not see any good candidates.");
                     break;
             }
-            return mWifiConfigManager.getConfiguredNetwork(candidateNetworkId);
+            WifiConfiguration ans = mWifiConfigManager.getConfiguredNetwork(
+                    candidateNetworkId);
+            if (ans != null && mScanDetailCandidate != null) {
+                // This is a newly created config, so we need to call onConnectable.
+                onConnectableListener.onConnectable(mScanDetailCandidate, ans, 0);
+            }
+            return ans;
         }
     }
 
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 92bec08..9332c59 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX;
-
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.admin.DeviceAdminInfo;
@@ -128,7 +126,6 @@
             1,  //  threshold for DISABLED_NO_INTERNET_TEMPORARY
             1,  //  threshold for DISABLED_WPS_START
             6,  //  threshold for DISABLED_TLS_VERSION_MISMATCH
-            1,  //  threshold for DISABLED_BY_WIFI_MANAGER_DISCONNECT
             1,  //  threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
             1,  //  threshold for DISABLED_NO_INTERNET_PERMANENT
             1,  //  threshold for DISABLED_BY_WIFI_MANAGER
@@ -153,7 +150,6 @@
             10 * 60 * 1000,     // threshold for DISABLED_NO_INTERNET_TEMPORARY
             0 * 60 * 1000,      // threshold for DISABLED_WPS_START
             Integer.MAX_VALUE,  // threshold for DISABLED_TLS_VERSION
-            1 * 60 * 60 * 1000,  //  threshold for DISABLED_BY_WIFI_MANAGER_DISCONNECT
             Integer.MAX_VALUE,  // threshold for DISABLED_AUTHENTICATION_NO_CREDENTIALS
             Integer.MAX_VALUE,  // threshold for DISABLED_NO_INTERNET_PERMANENT
             Integer.MAX_VALUE,  // threshold for DISABLED_BY_WIFI_MANAGER
@@ -940,11 +936,7 @@
             internalConfig.allowedGroupManagementCiphers =
                     (BitSet) externalConfig.allowedGroupManagementCiphers.clone();
         }
-        if (externalConfig.allowedSuiteBCiphers != null
-                && !externalConfig.allowedSuiteBCiphers.isEmpty()) {
-            internalConfig.allowedSuiteBCiphers =
-                    (BitSet) externalConfig.allowedSuiteBCiphers.clone();
-        }
+        // allowedSuiteBCiphers is set internally according to the certificate type
 
         // Copy over the |IpConfiguration| parameters if set.
         if (externalConfig.getIpConfiguration() != null) {
@@ -1068,7 +1060,7 @@
      */
     private void updateRandomizedMacAddress(WifiConfiguration config) {
         // Update randomized MAC address according to stored map
-        final String key = config.configKey();
+        final String key = config.getSsidAndSecurityTypeString();
         // If the key is not found in the current store, then it means this network has never been
         // seen before. So add it to store.
         if (!mRandomizedMacAddressMapping.containsKey(key)) {
@@ -1175,8 +1167,8 @@
                 newInternalConfig) && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)
                 && !mWifiPermissionsUtil.checkNetworkSetupWizardPermission(uid)) {
             Log.e(TAG, "UID " + uid + " does not have permission to modify MAC randomization "
-                    + "Settings " + config.configKey() + ". Must have NETWORK_SETTINGS or"
-                    + "NETWORK_SETUP_WIZARD.");
+                    + "Settings " + config.getSsidAndSecurityTypeString() + ". Must have "
+                    + "NETWORK_SETTINGS or NETWORK_SETUP_WIZARD.");
             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
         }
 
@@ -1568,7 +1560,7 @@
         if (reason == NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
             setNetworkSelectionEnabled(config);
             setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
-        } else if (reason < NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX) {
+        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
             setNetworkSelectionTemporarilyDisabled(config, reason);
         } else {
             setNetworkSelectionPermanentlyDisabled(config, reason);
@@ -2796,7 +2788,8 @@
                 if (TelephonyUtil.isSimConfig(config)) {
                     Pair<String, String> currentIdentity =
                             TelephonyUtil.getSimIdentity(mTelephonyManager,
-                                    new TelephonyUtil(), config);
+                                    new TelephonyUtil(), config,
+                                    mWifiInjector.getCarrierNetworkConfig());
                     if (mVerboseLoggingEnabled) {
                         Log.d(TAG, "New identity for config " + config + ": " + currentIdentity);
                     }
@@ -2878,6 +2871,8 @@
             Log.w(TAG, "User switch before store is read!");
             mConfiguredNetworks.setNewUser(userId);
             mCurrentUserId = userId;
+            // Reset any state from previous user unlock.
+            mDeferredUserUnlockRead = false;
             // Cannot read data from new user's CE store file before they log-in.
             mPendingUnlockStoreRead = true;
             return new HashSet<>();
@@ -2912,12 +2907,16 @@
         if (mVerboseLoggingEnabled) {
             Log.v(TAG, "Handling user unlock for " + userId);
         }
+        if (userId != mCurrentUserId) {
+            Log.e(TAG, "Ignore user unlock for non current user " + userId);
+            return;
+        }
         if (mPendingStoreRead) {
             Log.w(TAG, "Ignore user unlock until store is read!");
             mDeferredUserUnlockRead = true;
             return;
         }
-        if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
+        if (mPendingUnlockStoreRead) {
             handleUserUnlockOrSwitch(mCurrentUserId);
         }
     }
@@ -3041,7 +3040,7 @@
      */
     private void generateRandomizedMacAddresses() {
         for (WifiConfiguration config : getInternalConfiguredNetworks()) {
-            mRandomizedMacAddressMapping.put(config.configKey(),
+            mRandomizedMacAddressMapping.put(config.getSsidAndSecurityTypeString(),
                     config.getOrCreateRandomizedMacAddress().toString());
         }
     }
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index 41a366d..a669508 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -21,6 +21,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.Locale;
 
 /**
@@ -34,6 +36,7 @@
     private final WifiNative mWifiNative;
     private boolean DBG = false;
     private boolean mReady = false;
+    private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
     /** config option that indicate whether or not to reset country code to default when
      * cellular radio indicates country code loss
@@ -41,7 +44,10 @@
     private boolean mRevertCountryCodeOnCellularLoss;
     private String mDefaultCountryCode = null;
     private String mTelephonyCountryCode = null;
-    private String mCurrentCountryCode = null;
+    private String mDriverCountryCode = null;
+    private String mTelephonyCountryTimestamp = null;
+    private String mDriverCountryTimestamp = null;
+    private String mReadyTimestamp = null;
 
     public WifiCountryCode(
             WifiNative wifiNative,
@@ -58,14 +64,11 @@
                 Log.w(TAG, "config_wifi_revert_country_code_on_cellular_loss is set, "
                          + "but there is no default country code.");
                 mRevertCountryCodeOnCellularLoss = false;
-                return;
             }
         }
 
-        if (mRevertCountryCodeOnCellularLoss) {
-            Log.d(TAG, "Country code will be reverted to " + mDefaultCountryCode
-                    + " on MCC loss");
-        }
+        Log.d(TAG, "mDefaultCountryCode " + mDefaultCountryCode
+                + " mRevertCountryCodeOnCellularLoss " + mRevertCountryCodeOnCellularLoss);
     }
 
     /**
@@ -85,7 +88,7 @@
      * phone default one.
      */
     public synchronized void airplaneModeEnabled() {
-        if (DBG) Log.d(TAG, "Airplane Mode Enabled");
+        Log.d(TAG, "Airplane Mode Enabled");
         // Airplane mode is enabled, we need to reset the country code to phone default.
         // Country code will be set upon when wpa_supplicant starts next time.
         mTelephonyCountryCode = null;
@@ -98,8 +101,8 @@
      * started but not yet L2 connected.
      */
     public synchronized void setReadyForChange(boolean ready) {
-        if (DBG) Log.d(TAG, "Set ready: " + ready);
         mReady = ready;
+        mReadyTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
         // We are ready to set country code now.
         // We need to post pending country code request.
         if (mReady) {
@@ -115,11 +118,13 @@
      * @return Returns true if the country code passed in is acceptable.
      */
     public synchronized boolean setCountryCode(String countryCode) {
-        if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode);
+        Log.d(TAG, "Receive set country code request: " + countryCode);
+        mTelephonyCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
+
         // Empty country code.
         if (TextUtils.isEmpty(countryCode)) {
             if (mRevertCountryCodeOnCellularLoss) {
-                if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
+                Log.d(TAG, "Received empty country code, reset to default country code");
                 mTelephonyCountryCode = null;
             }
         } else {
@@ -129,7 +134,10 @@
         // set once wpa_supplicant is ready.
         if (mReady) {
             updateCountryCode();
+        } else {
+            Log.d(TAG, "skip update supplicant not ready yet");
         }
+
         return true;
     }
 
@@ -143,7 +151,7 @@
      * Returns null if no Country Code was sent to driver.
      */
     public synchronized String getCountryCodeSentToDriver() {
-        return mCurrentCountryCode;
+        return mDriverCountryCode;
     }
 
     /**
@@ -161,20 +169,21 @@
      * Method to dump the current state of this WifiCounrtyCode object.
      */
     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mCurrentCountryCode != null) {
-            pw.println("CountryCode sent to driver: " + mCurrentCountryCode);
-        } else {
-            if (pickCountryCode() != null) {
-                pw.println("CountryCode: " + pickCountryCode() + " was not sent to driver");
-            } else {
-                pw.println("CountryCode was not initialized");
-            }
-        }
+
+        pw.println("mRevertCountryCodeOnCellularLoss: " + mRevertCountryCodeOnCellularLoss);
+        pw.println("mDefaultCountryCode: " + mDefaultCountryCode);
+        pw.println("mDriverCountryCode: " + mDriverCountryCode);
+        pw.println("mTelephonyCountryCode: " + mTelephonyCountryCode);
+        pw.println("mTelephonyCountryTimestamp: " + mTelephonyCountryTimestamp);
+        pw.println("mDriverCountryTimestamp: " + mDriverCountryTimestamp);
+        pw.println("mReadyTimestamp: " + mReadyTimestamp);
+        pw.println("mReady: " + mReady);
     }
 
     private void updateCountryCode() {
-        if (DBG) Log.d(TAG, "Update country code");
         String country = pickCountryCode();
+        Log.d(TAG, "updateCountryCode to " + country);
+
         // We do not check if the country code equals the current one.
         // There are two reasons:
         // 1. Wpa supplicant may silently modify the country code.
@@ -200,9 +209,10 @@
     }
 
     private boolean setCountryCodeNative(String country) {
+        mDriverCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
         if (mWifiNative.setCountryCode(mWifiNative.getClientInterfaceName(), country)) {
             Log.d(TAG, "Succeeded to set country code to: " + country);
-            mCurrentCountryCode = country;
+            mDriverCountryCode = country;
             return true;
         }
         Log.d(TAG, "Failed to set country code to: " + country);
diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java
index a5425db..b288ccb 100644
--- a/service/java/com/android/server/wifi/WifiDiagnostics.java
+++ b/service/java/com/android/server/wifi/WifiDiagnostics.java
@@ -530,8 +530,6 @@
     }
 
     private boolean flushDump(int errorCode) {
-        if (mBuildProperties.isUserBuild()) return false;
-
         if (errorCode == REPORT_REASON_USER_ACTION) return false;
 
         long currentTime = mClock.getWallClockMillis();
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 7938108..49f8c85 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -151,6 +151,7 @@
     private final DppManager mDppManager;
     private final LinkProbeManager mLinkProbeManager;
     private final IpMemoryStore mIpMemoryStore;
+    private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
 
     public WifiInjector(Context context) {
         if (context == null) {
@@ -194,8 +195,10 @@
         RttMetrics rttMetrics = new RttMetrics(mClock);
         mWifiP2pMetrics = new WifiP2pMetrics(mClock);
         mDppMetrics = new DppMetrics();
+        mCellularLinkLayerStatsCollector = new CellularLinkLayerStatsCollector(mContext);
         mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, clientModeImplLooper,
-                awareMetrics, rttMetrics, new WifiPowerMetrics(), mWifiP2pMetrics, mDppMetrics);
+                awareMetrics, rttMetrics, new WifiPowerMetrics(), mWifiP2pMetrics, mDppMetrics,
+                mCellularLinkLayerStatsCollector);
         // Modules interacting with Native.
         mWifiMonitor = new WifiMonitor(this);
         mHalDeviceManager = new HalDeviceManager(mClock);
@@ -251,7 +254,7 @@
                 new Handler(clientModeImplLooper));
         mWifiMetrics.setScoringParams(mScoringParams);
         mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiScoreCard, mScoringParams,
-                mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics);
+                mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics, mWifiNative);
         CompatibilityScorer compatibilityScorer = new CompatibilityScorer(mScoringParams);
         mWifiNetworkSelector.registerCandidateScorer(compatibilityScorer);
         ScoreCardBasedScorer scoreCardBasedScorer = new ScoreCardBasedScorer(mScoringParams);
@@ -316,7 +319,7 @@
                 mWifiConfigStore, mWifiNetworkSuggestionsManager, mWifiMetrics.getWakeupMetrics(),
                 this, mFrameworkFacade, mClock);
         mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService(),
-                mClientModeImpl, mFrameworkFacade, clientModeImplLooper);
+                mClientModeImpl, mFrameworkFacade, clientModeImplLooper, mClock, mWifiMetrics);
         mWifiController = new WifiController(mContext, mClientModeImpl, clientModeImplLooper,
                 mSettingsStore, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade,
                 mActiveModeWarden);
@@ -481,6 +484,10 @@
         return mPasspointManager;
     }
 
+    public CarrierNetworkConfig getCarrierNetworkConfig() {
+        return mCarrierNetworkConfig;
+    }
+
     public WakeupController getWakeupController() {
         return mWakeupController;
     }
diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java
index 3054fe5..a22be9b 100644
--- a/service/java/com/android/server/wifi/WifiKeyStore.java
+++ b/service/java/com/android/server/wifi/WifiKeyStore.java
@@ -26,10 +26,13 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.security.Key;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -220,26 +223,51 @@
      * @param config Config corresponding to the network.
      */
     public void removeKeys(WifiEnterpriseConfig config) {
-        String client = config.getClientCertificateAlias();
-        // a valid client certificate is configured
-        if (!TextUtils.isEmpty(client)) {
-            if (mVerboseLoggingEnabled) Log.d(TAG, "removing client private key and user cert");
-            mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
-            mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
+        // Do not remove keys that were manually installed by the user
+        if (config.isAppInstalledDeviceKeyAndCert()) {
+            String client = config.getClientCertificateAlias();
+            // a valid client certificate is configured
+            if (!TextUtils.isEmpty(client)) {
+                if (mVerboseLoggingEnabled) {
+                    Log.d(TAG, "removing client private key and user cert");
+                }
+                mKeyStore.delete(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+                mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
+            }
         }
 
-        String[] aliases = config.getCaCertificateAliases();
-        // a valid ca certificate is configured
-        if (aliases != null) {
-            for (String ca : aliases) {
-                if (!TextUtils.isEmpty(ca)) {
-                    if (mVerboseLoggingEnabled) Log.d(TAG, "removing CA cert: " + ca);
-                    mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
+        // Do not remove CA certs that were manually installed by the user
+        if (config.isAppInstalledCaCert()) {
+            String[] aliases = config.getCaCertificateAliases();
+            // a valid ca certificate is configured
+            if (aliases != null) {
+                for (String ca : aliases) {
+                    if (!TextUtils.isEmpty(ca)) {
+                        if (mVerboseLoggingEnabled) {
+                            Log.d(TAG, "removing CA cert: " + ca);
+                        }
+                        mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
+                    }
                 }
             }
         }
     }
 
+
+    /**
+     * @param certData byte array of the certificate
+     */
+    private X509Certificate buildCACertificate(byte[] certData) {
+        try {
+            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+            InputStream inputStream = new ByteArrayInputStream(certData);
+            X509Certificate caCertificateX509 = (X509Certificate) certificateFactory
+                    .generateCertificate(inputStream);
+            return caCertificateX509;
+        } catch (CertificateException e) {
+            return null;
+        }
+    }
     /**
      * Update/Install keys for given enterprise network.
      *
@@ -267,6 +295,61 @@
                 return false;
             }
         }
+
+        // For WPA3-Enterprise 192-bit networks, set the SuiteBCipher field based on the
+        // CA certificate type. Suite-B requires SHA384, reject other certs.
+        if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) {
+            // Read the first CA certificate, and initialize
+            byte[] certData = mKeyStore.get(
+                    Credentials.CA_CERTIFICATE + config.enterpriseConfig.getCaCertificateAlias(),
+                    android.os.Process.WIFI_UID);
+
+            if (certData == null) {
+                Log.e(TAG, "Failed reading CA certificate for Suite-B");
+                return false;
+            }
+
+            X509Certificate x509CaCert = buildCACertificate(certData);
+
+            if (x509CaCert != null) {
+                String sigAlgOid = x509CaCert.getSigAlgOID();
+                if (mVerboseLoggingEnabled) {
+                    Log.d(TAG, "Signature algorithm: " + sigAlgOid);
+                }
+                config.allowedSuiteBCiphers.clear();
+
+                // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates
+                // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192
+                // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term
+                // Suite-B was already coined in the IEEE 802.11-2016 specification for
+                // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates
+                // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally
+                // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments,
+                // we are supporting both types here.
+                if (sigAlgOid.equals("1.2.840.113549.1.1.12")) {
+                    // sha384WithRSAEncryption
+                    config.allowedSuiteBCiphers.set(
+                            WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+                    if (mVerboseLoggingEnabled) {
+                        Log.d(TAG, "Selecting Suite-B RSA");
+                    }
+                } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) {
+                    // ecdsa-with-SHA384
+                    config.allowedSuiteBCiphers.set(
+                            WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+                    if (mVerboseLoggingEnabled) {
+                        Log.d(TAG, "Selecting Suite-B ECDSA");
+                    }
+                } else {
+                    Log.e(TAG, "Invalid CA certificate type for Suite-B: "
+                            + sigAlgOid);
+                    return false;
+                }
+            } else {
+                Log.e(TAG, "Invalid CA certificate for Suite-B");
+                return false;
+            }
+        }
         return true;
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java
index 7354182..3d4ceb1 100644
--- a/service/java/com/android/server/wifi/WifiLockManager.java
+++ b/service/java/com/android/server/wifi/WifiLockManager.java
@@ -56,12 +56,14 @@
 
     private boolean mVerboseLoggingEnabled = false;
 
+    private final Clock mClock;
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
     private final FrameworkFacade mFrameworkFacade;
     private final ClientModeImpl mClientModeImpl;
     private final ActivityManager mActivityManager;
     private final ClientModeImplInterfaceHandler mCmiIfaceHandler;
+    private final WifiMetrics mWifiMetrics;
     private WifiAsyncChannel mClientModeImplChannel;
 
     private final List<WifiLock> mWifiLocks = new ArrayList<>();
@@ -80,9 +82,11 @@
     private int mFullHighPerfLocksReleased;
     private int mFullLowLatencyLocksAcquired;
     private int mFullLowLatencyLocksReleased;
+    private long mCurrentSessionStartTimeMs;
 
     WifiLockManager(Context context, IBatteryStats batteryStats,
-            ClientModeImpl clientModeImpl, FrameworkFacade frameworkFacade, Looper looper) {
+            ClientModeImpl clientModeImpl, FrameworkFacade frameworkFacade, Looper looper,
+            Clock clock, WifiMetrics wifiMetrics) {
         mContext = context;
         mBatteryStats = batteryStats;
         mClientModeImpl = clientModeImpl;
@@ -90,6 +94,8 @@
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mCmiIfaceHandler = new ClientModeImplInterfaceHandler(looper);
         mCurrentOpMode = WifiManager.WIFI_MODE_NO_LOCKS_HELD;
+        mClock = clock;
+        mWifiMetrics = wifiMetrics;
 
         // Register for UID fg/bg transitions
         registerUidImportanceTransitions();
@@ -540,6 +546,8 @@
         switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
                 ++mFullHighPerfLocksReleased;
+                mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp());
                 // Stop blaming only if blaming was set before (conditions are met).
                 // This is to avoid calling the api unncessarily, since this API is
                 // reference counted in batteryStats and statsd
@@ -550,6 +558,8 @@
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
                 removeWsFromLlWatchList(wifiLock.getWorkSource());
                 ++mFullLowLatencyLocksReleased;
+                mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp());
                 break;
             default:
                 // Do nothing
@@ -581,6 +591,8 @@
                     Slog.e(TAG, "Failed to reset the OpMode from hi-perf to Normal");
                     return false;
                 }
+                mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs);
                 break;
 
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
@@ -588,6 +600,8 @@
                     Slog.e(TAG, "Failed to reset the OpMode from low-latency to Normal");
                     return false;
                 }
+                mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs);
                 break;
 
             case WifiManager.WIFI_MODE_NO_LOCKS_HELD:
@@ -606,6 +620,7 @@
                     Slog.e(TAG, "Failed to set the OpMode to hi-perf");
                     return false;
                 }
+                mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis();
                 break;
 
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
@@ -613,6 +628,7 @@
                     Slog.e(TAG, "Failed to set the OpMode to low-latency");
                     return false;
                 }
+                mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis();
                 break;
 
             case WifiManager.WIFI_MODE_NO_LOCKS_HELD:
@@ -650,20 +666,32 @@
     private boolean setLowLatencyMode(boolean enabled) {
         int lowLatencySupport = getLowLatencyModeSupport();
 
-        if (lowLatencySupport == LOW_LATENCY_SUPPORTED) {
-            return mClientModeImpl.setLowLatencyMode(enabled);
-        } else if (lowLatencySupport == LOW_LATENCY_NOT_SUPPORTED) {
-            // Since low-latency mode is not supported, use power save instead
-            // Note: low-latency mode enabled ==> power-save disabled
-            if (mVerboseLoggingEnabled) {
-                Slog.d(TAG, "low-latency is not supported, using power-save instead");
-            }
-
-            return mClientModeImpl.setPowerSave(!enabled);
-        } else {
-            // Support undefined, no need to attempt either functions
+        if (lowLatencySupport == LOW_LATENCY_SUPPORT_UNDEFINED) {
+            // Support undefined, no action is taken
             return false;
         }
+
+        if (lowLatencySupport == LOW_LATENCY_SUPPORTED) {
+            if (!mClientModeImpl.setLowLatencyMode(enabled)) {
+                Slog.e(TAG, "Failed to set low latency mode");
+                return false;
+            }
+
+            if (!mClientModeImpl.setPowerSave(!enabled)) {
+                Slog.e(TAG, "Failed to set power save mode");
+                // Revert the low latency mode
+                mClientModeImpl.setLowLatencyMode(!enabled);
+                return false;
+            }
+        } else if (lowLatencySupport == LOW_LATENCY_NOT_SUPPORTED) {
+            // Only set power save mode
+            if (!mClientModeImpl.setPowerSave(!enabled)) {
+                Slog.e(TAG, "Failed to set power save mode");
+                return false;
+            }
+        }
+
+        return true;
     }
 
     private synchronized WifiLock findLockByBinder(IBinder binder) {
@@ -810,6 +838,7 @@
         IBinder mBinder;
         int mMode;
         WorkSource mWorkSource;
+        long mAcqTimestamp;
 
         WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
             mTag = tag;
@@ -817,6 +846,7 @@
             mUid = Binder.getCallingUid();
             mMode = lockMode;
             mWorkSource = ws;
+            mAcqTimestamp = mClock.getElapsedSinceBootMillis();
             try {
                 mBinder.linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -836,6 +866,10 @@
             return mBinder;
         }
 
+        protected long getAcqTimestamp() {
+            return mAcqTimestamp;
+        }
+
         public void binderDied() {
             releaseLock(mBinder);
         }
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 077d3db..d70fa30 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Log;
@@ -67,6 +68,7 @@
 import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
 import com.android.server.wifi.nano.WifiMetricsProto.WifiIsUnusableEvent;
 import com.android.server.wifi.nano.WifiMetricsProto.WifiLinkLayerUsageStats;
+import com.android.server.wifi.nano.WifiMetricsProto.WifiLockStats;
 import com.android.server.wifi.nano.WifiMetricsProto.WifiNetworkRequestApiLog;
 import com.android.server.wifi.nano.WifiMetricsProto.WifiNetworkSuggestionApiLog;
 import com.android.server.wifi.nano.WifiMetricsProto.WifiUsabilityStats;
@@ -123,7 +125,7 @@
     private static final int MIN_WIFI_SCORE = 0;
     private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
     private static final int MIN_WIFI_USABILITY_SCORE = 0; // inclusive
-    private static final int MAX_WIFI_USABILITY_SCORE = 60; // inclusive
+    private static final int MAX_WIFI_USABILITY_SCORE = 100; // inclusive
     @VisibleForTesting
     static final int LOW_WIFI_SCORE = 50; // Mobile data score
     @VisibleForTesting
@@ -150,7 +152,7 @@
     private static final int WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED_DEFAULT = 1; // 1 = true
     private static final int WIFI_LINK_SPEED_METRICS_ENABLED_DEFAULT = 1; // 1 = true
     // Max number of WifiUsabilityStatsEntry elements to store in the ringbuffer.
-    public static final int MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE = 20;
+    public static final int MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE = 40;
     // Max number of WifiUsabilityStats elements to store for each type.
     public static final int MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE = 10;
     // Max number of WifiUsabilityStats per labeled type to upload to server
@@ -362,6 +364,19 @@
             {5, 20, 50, 100, 500};
     private final IntHistogram mWifiNetworkSuggestionApiListSizeHistogram =
             new IntHistogram(NETWORK_SUGGESTION_API_LIST_SIZE_HISTOGRAM_BUCKETS);
+    private final WifiLockStats mWifiLockStats = new WifiLockStats();
+    private static final int[] WIFI_LOCK_SESSION_DURATION_HISTOGRAM_BUCKETS =
+            {1, 10, 60, 600, 3600};
+
+    private final IntHistogram mWifiLockHighPerfAcqDurationSecHistogram =
+            new IntHistogram(WIFI_LOCK_SESSION_DURATION_HISTOGRAM_BUCKETS);
+    private final IntHistogram mWifiLockLowLatencyAcqDurationSecHistogram =
+            new IntHistogram(WIFI_LOCK_SESSION_DURATION_HISTOGRAM_BUCKETS);
+
+    private final IntHistogram mWifiLockHighPerfActiveSessionDurationSecHistogram =
+            new IntHistogram(WIFI_LOCK_SESSION_DURATION_HISTOGRAM_BUCKETS);
+    private final IntHistogram mWifiLockLowLatencyActiveSessionDurationSecHistogram =
+            new IntHistogram(WIFI_LOCK_SESSION_DURATION_HISTOGRAM_BUCKETS);
 
     /**
      * (experiment1Id, experiment2Id) =>
@@ -370,6 +385,14 @@
     private Map<Pair<Integer, Integer>, NetworkSelectionExperimentResults>
             mNetworkSelectionExperimentPairNumChoicesCounts = new ArrayMap<>();
 
+    private final CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
+
+    /**
+     * Tracks the nominator for each network (i.e. which entity made the suggestion to connect).
+     * This object should not be cleared.
+     */
+    private final SparseIntArray mNetworkIdToNominatorId = new SparseIntArray();
+
     @VisibleForTesting
     static class NetworkSelectionExperimentResults {
         public static final int MAX_CHOICES = 10;
@@ -641,8 +664,8 @@
                     case WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED:
                         sb.append("NOMINATOR_EXTERNAL_SCORED");
                         break;
-                    case WifiMetricsProto.ConnectionEvent.NOMINATOR_NETREC:
-                        sb.append("NOMINATOR_NETREC");
+                    case WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER:
+                        sb.append("NOMINATOR_SPECIFIER");
                         break;
                     case WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED_USER_CONNECT_CHOICE:
                         sb.append("NOMINATOR_SAVED_USER_CONNECT_CHOICE");
@@ -682,7 +705,8 @@
     public WifiMetrics(Context context, FrameworkFacade facade, Clock clock, Looper looper,
             WifiAwareMetrics awareMetrics, RttMetrics rttMetrics,
             WifiPowerMetrics wifiPowerMetrics, WifiP2pMetrics wifiP2pMetrics,
-            DppMetrics dppMetrics) {
+            DppMetrics dppMetrics,
+            CellularLinkLayerStatsCollector cellularLinkLayerStatsCollector) {
         mContext = context;
         mFacade = facade;
         mClock = clock;
@@ -695,6 +719,7 @@
         mWifiPowerMetrics = wifiPowerMetrics;
         mWifiP2pMetrics = wifiP2pMetrics;
         mDppMetrics = dppMetrics;
+        mCellularLinkLayerStatsCollector = cellularLinkLayerStatsCollector;
         loadSettings();
         mHandler = new Handler(looper) {
             public void handleMessage(Message msg) {
@@ -983,26 +1008,9 @@
                 mCurrentConnectionEvent.mConnectionEvent.useRandomizedMac =
                         config.macRandomizationSetting
                         == WifiConfiguration.RANDOMIZATION_PERSISTENT;
-                if (config.fromWifiNetworkSpecifier) {
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_NETREC;
-                } else if (config.fromWifiNetworkSuggestion) {
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_SUGGESTION;
-                } else if (config.isPasspoint()) {
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_PASSPOINT;
-                } else if (!config.trusted) {
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED;
-                } else if (!config.ephemeral) {
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED;
-                } else {
-                    // TODO(b/127452844): populate other nominator fields
-                    mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
-                            WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN;
-                }
+                mCurrentConnectionEvent.mConnectionEvent.connectionNominator =
+                        mNetworkIdToNominatorId.get(config.networkId,
+                                WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN);
                 ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
                 if (candidate != null) {
                     // Cache the RSSI of the candidate, as the connection event level is updated
@@ -2723,6 +2731,17 @@
                 pw.println("mWifiNetworkSuggestionApiLog:\n" + mWifiNetworkSuggestionApiLog);
                 pw.println("mWifiNetworkSuggestionApiMatchSizeHistogram:\n"
                         + mWifiNetworkRequestApiMatchSizeHistogram);
+                pw.println("mNetworkIdToNominatorId:\n" + mNetworkIdToNominatorId);
+                pw.println("mWifiLockStats:\n" + mWifiLockStats);
+                pw.println("mWifiLockHighPerfAcqDurationSecHistogram:\n"
+                        + mWifiLockHighPerfAcqDurationSecHistogram);
+                pw.println("mWifiLockLowLatencyAcqDurationSecHistogram:\n"
+                        + mWifiLockLowLatencyAcqDurationSecHistogram);
+                pw.println("mWifiLockHighPerfActiveSessionDurationSecHistogram:\n"
+                        + mWifiLockHighPerfActiveSessionDurationSecHistogram);
+                pw.println("mWifiLockLowLatencyActiveSessionDurationSecHistogram:\n"
+                        + mWifiLockLowLatencyActiveSessionDurationSecHistogram);
+
             }
         }
     }
@@ -2759,6 +2778,11 @@
         line.append(",rx_link_speed_mbps=" + entry.rxLinkSpeedMbps);
         line.append(",seq_num_inside_framework=" + entry.seqNumInsideFramework);
         line.append(",is_same_bssid_and_freq=" + entry.isSameBssidAndFreq);
+        line.append(",cellular_data_network_type=" + entry.cellularDataNetworkType);
+        line.append(",cellular_signal_strength_dbm=" + entry.cellularSignalStrengthDbm);
+        line.append(",cellular_signal_strength_db=" + entry.cellularSignalStrengthDb);
+        line.append(",is_same_registered_cell=" + entry.isSameRegisteredCell);
+        line.append(",device_mobility_state=" + entry.deviceMobilityState);
         pw.println(line.toString());
     }
 
@@ -3231,6 +3255,20 @@
             mWifiNetworkSuggestionApiLog.networkListSizeHistogram =
                     mWifiNetworkSuggestionApiListSizeHistogram.toProto();
             mWifiLogProto.wifiNetworkSuggestionApiLog = mWifiNetworkSuggestionApiLog;
+
+            mWifiLockStats.highPerfLockAcqDurationSecHistogram =
+                    mWifiLockHighPerfAcqDurationSecHistogram.toProto();
+
+            mWifiLockStats.lowLatencyLockAcqDurationSecHistogram =
+                    mWifiLockLowLatencyAcqDurationSecHistogram.toProto();
+
+            mWifiLockStats.highPerfActiveSessionDurationSecHistogram =
+                    mWifiLockHighPerfActiveSessionDurationSecHistogram.toProto();
+
+            mWifiLockStats.lowLatencyActiveSessionDurationSecHistogram =
+                    mWifiLockLowLatencyActiveSessionDurationSecHistogram.toProto();
+
+            mWifiLogProto.wifiLockStats = mWifiLockStats;
         }
     }
 
@@ -3406,6 +3444,11 @@
             mWifiNetworkSuggestionApiLog.clear();
             mWifiNetworkRequestApiMatchSizeHistogram.clear();
             mWifiNetworkSuggestionApiListSizeHistogram.clear();
+            mWifiLockHighPerfAcqDurationSecHistogram.clear();
+            mWifiLockLowLatencyAcqDurationSecHistogram.clear();
+            mWifiLockHighPerfActiveSessionDurationSecHistogram.clear();
+            mWifiLockLowLatencyActiveSessionDurationSecHistogram.clear();
+            mWifiLockStats.clear();
         }
     }
 
@@ -4011,6 +4054,8 @@
                 break;
             case WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT:
                 break;
+            case WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST:
+                break;
             default:
                 Log.e(TAG, "Unknown WifiIsUnusableEvent: " + triggerType);
                 return;
@@ -4154,6 +4199,16 @@
             wifiUsabilityStatsEntry.rxLinkSpeedMbps = info.getRxLinkSpeedMbps();
             wifiUsabilityStatsEntry.isSameBssidAndFreq = isSameBssidAndFreq;
             wifiUsabilityStatsEntry.seqNumInsideFramework = mSeqNumInsideFramework;
+            wifiUsabilityStatsEntry.deviceMobilityState = mCurrentDeviceMobilityState;
+
+            CellularLinkLayerStats cls = mCellularLinkLayerStatsCollector.update();
+            if (DBG) Log.v(TAG, "Latest Cellular Link Layer Stats: " + cls);
+            wifiUsabilityStatsEntry.cellularDataNetworkType =
+                    parseDataNetworkTypeToProto(cls.getDataNetworkType());
+            wifiUsabilityStatsEntry.cellularSignalStrengthDbm = cls.getSignalStrengthDbm();
+            wifiUsabilityStatsEntry.cellularSignalStrengthDb = cls.getSignalStrengthDb();
+            wifiUsabilityStatsEntry.isSameRegisteredCell = cls.getIsSameRegisteredCell();
+
             mWifiUsabilityStatsEntriesList.add(wifiUsabilityStatsEntry);
             mWifiUsabilityStatsCounter++;
             if (mWifiUsabilityStatsCounter >= NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD) {
@@ -4173,6 +4228,53 @@
         }
     }
 
+    private int parseDataNetworkTypeToProto(int cellularDataNetworkType) {
+        switch (cellularDataNetworkType) {
+            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN;
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_GSM;
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_CDMA;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_UMTS;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_LTE;
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_NR;
+            default:
+                Log.e(TAG, "Unknown data network type : " + cellularDataNetworkType);
+                return WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    private int parseDataNetworkTypeFromProto(int cellularDataNetworkType) {
+        switch (cellularDataNetworkType) {
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_UNKNOWN:
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_GSM:
+                return TelephonyManager.NETWORK_TYPE_GSM;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_CDMA:
+                return TelephonyManager.NETWORK_TYPE_CDMA;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_EVDO_0:
+                return TelephonyManager.NETWORK_TYPE_EVDO_0;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_UMTS:
+                return TelephonyManager.NETWORK_TYPE_UMTS;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_TD_SCDMA:
+                return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_LTE:
+                return TelephonyManager.NETWORK_TYPE_LTE;
+            case WifiUsabilityStatsEntry.NETWORK_TYPE_NR:
+                return TelephonyManager.NETWORK_TYPE_NR;
+            default:
+                Log.e(TAG, "Unknown data network type : " + cellularDataNetworkType);
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+    }
     /**
      * Send Wifi usability stats.
      * @param seqNum
@@ -4208,6 +4310,7 @@
                 probeStatus = android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_UNKNOWN;
                 Log.e(TAG, "Unknown link probe status: " + s.probeStatusSinceLastUpdate);
         }
+        int cellularDataNetworkType = parseDataNetworkTypeFromProto(s.cellularDataNetworkType);
         return new android.net.wifi.WifiUsabilityStatsEntry(s.timeStampMs, s.rssi,
                 s.linkSpeedMbps, s.totalTxSuccess, s.totalTxRetries,
                 s.totalTxBad, s.totalRxSuccess, s.totalRadioOnTimeMs,
@@ -4216,7 +4319,9 @@
                 s.totalPnoScanTimeMs, s.totalHotspot2ScanTimeMs, s.totalCcaBusyFreqTimeMs,
                 s.totalRadioOnFreqTimeMs, s.totalBeaconRx, probeStatus,
                 s.probeElapsedTimeSinceLastUpdateMs, s.probeMcsRateSinceLastUpdate,
-                s.rxLinkSpeedMbps
+                s.rxLinkSpeedMbps, cellularDataNetworkType,
+                s.cellularSignalStrengthDbm, s.cellularSignalStrengthDb,
+                s.isSameRegisteredCell
         );
     }
 
@@ -4251,6 +4356,11 @@
         out.rxLinkSpeedMbps = s.rxLinkSpeedMbps;
         out.isSameBssidAndFreq = s.isSameBssidAndFreq;
         out.seqNumInsideFramework = s.seqNumInsideFramework;
+        out.cellularDataNetworkType = s.cellularDataNetworkType;
+        out.cellularSignalStrengthDbm = s.cellularSignalStrengthDbm;
+        out.cellularSignalStrengthDb = s.cellularSignalStrengthDb;
+        out.isSameRegisteredCell = s.isSameRegisteredCell;
+        out.deviceMobilityState = s.deviceMobilityState;
         return out;
     }
 
@@ -4280,9 +4390,10 @@
                 // Only add a good event if at least |MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS|
                 // has passed.
                 if (mWifiUsabilityStatsListGood.isEmpty()
-                        || mWifiUsabilityStatsListGood.getLast().stats[0].timeStampMs
+                        || mWifiUsabilityStatsListGood.getLast().stats[mWifiUsabilityStatsListGood
+                        .getLast().stats.length - 1].timeStampMs
                         + MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS
-                        < mWifiUsabilityStatsEntriesList.get(0).timeStampMs) {
+                        < mWifiUsabilityStatsEntriesList.getLast().timeStampMs) {
                     while (mWifiUsabilityStatsListGood.size()
                             >= MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE) {
                         mWifiUsabilityStatsListGood.remove(
@@ -4295,9 +4406,10 @@
                 // Only add a bad event if at least |MIN_DATA_STALL_WAIT_MS|
                 // has passed.
                 if (mWifiUsabilityStatsListBad.isEmpty()
-                        || (mWifiUsabilityStatsListBad.getLast().stats[0].timeStampMs
+                        || (mWifiUsabilityStatsListBad.getLast().stats[mWifiUsabilityStatsListBad
+                        .getLast().stats.length - 1].timeStampMs
                         + MIN_DATA_STALL_WAIT_MS
-                        < mWifiUsabilityStatsEntriesList.get(0).timeStampMs)) {
+                        < mWifiUsabilityStatsEntriesList.getLast().timeStampMs)) {
                     while (mWifiUsabilityStatsListBad.size()
                             >= MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE) {
                         mWifiUsabilityStatsListBad.remove(
@@ -4640,4 +4752,55 @@
             }
         }
     }
+
+    /**
+     * Sets the nominator for a network (i.e. which entity made the suggestion to connect)
+     * @param networkId the ID of the network, from its {@link WifiConfiguration}
+     * @param nominatorId the entity that made the suggestion to connect to this network,
+     *                    from {@link WifiMetricsProto.ConnectionEvent.ConnectionNominator}
+     */
+    public void setNominatorForNetwork(int networkId, int nominatorId) {
+        synchronized (mLock) {
+            if (networkId == WifiConfiguration.INVALID_NETWORK_ID) return;
+            mNetworkIdToNominatorId.put(networkId, nominatorId);
+        }
+    }
+
+    /** Add a WifiLock acqusition session */
+    public void addWifiLockAcqSession(int lockType, long duration) {
+        switch (lockType) {
+            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                mWifiLockHighPerfAcqDurationSecHistogram.increment((int) (duration / 1000));
+                break;
+
+            case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
+                mWifiLockLowLatencyAcqDurationSecHistogram.increment((int) (duration / 1000));
+                break;
+
+            default:
+                Log.e(TAG, "addWifiLockAcqSession: Invalid lock type: " + lockType);
+                break;
+        }
+    }
+
+    /** Add a WifiLock active session */
+    public void addWifiLockActiveSession(int lockType, long duration) {
+        switch (lockType) {
+            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                mWifiLockStats.highPerfActiveTimeMs += duration;
+                mWifiLockHighPerfActiveSessionDurationSecHistogram.increment(
+                        (int) (duration / 1000));
+                break;
+
+            case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
+                mWifiLockStats.lowLatencyActiveTimeMs += duration;
+                mWifiLockLowLatencyActiveSessionDurationSecHistogram.increment(
+                        (int) (duration / 1000));
+                break;
+
+            default:
+                Log.e(TAG, "addWifiLockActiveSession: Invalid lock type: " + lockType);
+                break;
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 43f673a..3e5dc3c 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -139,6 +139,8 @@
         public InterfaceCallback externalListener;
         /** Network observer registered for this interface */
         public NetworkObserverInternal networkObserver;
+        /** Interface feature set / capabilities */
+        public long featureSet;
 
         Iface(int id, @Iface.IfaceType int type) {
             this.id = id;
@@ -968,6 +970,8 @@
             onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
             initializeNwParamsForClientInterface(iface.name);
             Log.i(TAG, "Successfully setup " + iface);
+
+            iface.featureSet = getSupportedFeatureSetInternal(iface.name);
             return iface.name;
         }
     }
@@ -1019,6 +1023,8 @@
             // update the interface state before we exit.
             onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
             Log.i(TAG, "Successfully setup " + iface);
+
+            iface.featureSet = getSupportedFeatureSetInternal(iface.name);
             return iface.name;
         }
     }
@@ -1073,6 +1079,8 @@
             // update the interface state before we exit.
             onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
             Log.i(TAG, "Successfully setup " + iface);
+
+            iface.featureSet = getSupportedFeatureSetInternal(iface.name);
             return iface.name;
         }
     }
@@ -2618,6 +2626,24 @@
      * @return bitmask defined by WifiManager.WIFI_FEATURE_*
      */
     public long getSupportedFeatureSet(@NonNull String ifaceName) {
+        synchronized (mLock) {
+            Iface iface = mIfaceMgr.getIface(ifaceName);
+            if (iface == null) {
+                Log.e(TAG, "Could not get Iface object for interface " + ifaceName);
+                return 0;
+            }
+
+            return iface.featureSet;
+        }
+    }
+
+    /**
+     * Get the supported features
+     *
+     * @param ifaceName Name of the interface.
+     * @return bitmask defined by WifiManager.WIFI_FEATURE_*
+     */
+    private long getSupportedFeatureSetInternal(@NonNull String ifaceName) {
         return mSupplicantStaIfaceHal.getAdvancedKeyMgmtCapabilities(ifaceName)
                 | mWifiVendorHal.getSupportedFeatureSet(ifaceName);
     }
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index 174379e..168200b 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -56,6 +56,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.nano.WifiMetricsProto;
 import com.android.server.wifi.util.ExternalCallbackTracker;
 import com.android.server.wifi.util.ScanResultUtil;
 import com.android.server.wifi.util.WifiPermissionsUtil;
@@ -63,6 +64,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -136,6 +138,8 @@
     private boolean mPeriodicScanTimerSet = false;
     private boolean mConnectionTimeoutSet = false;
     private boolean mIsPeriodicScanPaused = false;
+    // We sent a new connection request and are waiting for connection success.
+    private boolean mPendingConnectionSuccess = false;
     private boolean mWifiEnabled = false;
     /**
      * Indicates that we have new data to serialize.
@@ -712,6 +716,9 @@
         // necessary checks when processing CONNECT_NETWORK.
         int networkId = addNetworkToWifiConfigManager(network);
 
+        mWifiMetrics.setNominatorForNetwork(networkId,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER);
+
         // Send the connect request to ClientModeImpl.
         // TODO(b/117601161): Refactor this.
         Message msg = Message.obtain();
@@ -733,12 +740,8 @@
         WifiConfiguration networkToConnect =
                 new WifiConfiguration(mActiveSpecificNetworkRequestSpecifier.wifiConfiguration);
         networkToConnect.SSID = network.SSID;
-        // If the request is for a specific SSID and BSSID, then set WifiConfiguration.BSSID field
-        // to prevent roaming.
-        if (isActiveRequestForSingleAccessPoint()) {
-            networkToConnect.BSSID =
-                    mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.first.toString();
-        }
+        // Set the WifiConfiguration.BSSID field to prevent roaming.
+        networkToConnect.BSSID = findBestBssidFromActiveMatchedScanResultsForNetwork(network);
         // Mark the network ephemeral so that it's automatically removed at the end of connection.
         networkToConnect.ephemeral = true;
         networkToConnect.fromWifiNetworkSpecifier = true;
@@ -748,6 +751,8 @@
 
         // Trigger connection to the network.
         connectToNetwork(networkToConnect);
+        // Triggered connection to network, now wait for the connection status.
+        mPendingConnectionSuccess = true;
     }
 
     private void handleConnectToNetworkUserSelection(WifiConfiguration network) {
@@ -796,7 +801,10 @@
      * Invoked by {@link ClientModeImpl} on successful connection to a network.
      */
     private void handleNetworkConnectionSuccess(@NonNull WifiConfiguration connectedNetwork) {
-        if (mUserSelectedNetwork == null || connectedNetwork == null) return;
+        if (mUserSelectedNetwork == null || connectedNetwork == null
+                || !mPendingConnectionSuccess) {
+            return;
+        }
         if (!isUserSelectedNetwork(connectedNetwork)) {
             Log.w(TAG, "Connected to unknown network " + connectedNetwork + ". Ignoring...");
             return;
@@ -819,7 +827,9 @@
      * Invoked by {@link ClientModeImpl} on failure to connect to a network.
      */
     private void handleNetworkConnectionFailure(@NonNull WifiConfiguration failedNetwork) {
-        if (mUserSelectedNetwork == null || failedNetwork == null) return;
+        if (mUserSelectedNetwork == null || failedNetwork == null || !mPendingConnectionSuccess) {
+            return;
+        }
         if (!isUserSelectedNetwork(failedNetwork)) {
             Log.w(TAG, "Connection failed to unknown network " + failedNetwork + ". Ignoring...");
             return;
@@ -904,6 +914,7 @@
         mUserSelectedNetworkConnectRetryCount = 0;
         mIsPeriodicScanPaused = false;
         mActiveMatchedScanResults = null;
+        mPendingConnectionSuccess = false;
         // Cancel periodic scan, connection timeout alarm.
         cancelPeriodicScans();
         cancelConnectionTimeout();
@@ -933,6 +944,7 @@
         mConnectedSpecificNetworkRequestSpecifier = mActiveSpecificNetworkRequestSpecifier;
         mActiveSpecificNetworkRequest = null;
         mActiveSpecificNetworkRequestSpecifier = null;
+        mPendingConnectionSuccess = false;
         // Cancel connection timeout alarm.
         cancelConnectionTimeout();
     }
@@ -940,7 +952,7 @@
     // Invoked at the termination of current connected request processing.
     private void teardownForConnectedNetwork() {
         Log.i(TAG, "Disconnecting from network on reset");
-        mWifiInjector.getClientModeImpl().disconnectCommandInternal();
+        mWifiInjector.getClientModeImpl().disconnectCommand();
         mConnectedSpecificNetworkRequest = null;
         mConnectedSpecificNetworkRequestSpecifier = null;
         // ensure there is no active request in progress.
@@ -1166,6 +1178,33 @@
         return false;
     }
 
+    // Will return the best bssid to use for the current request's connection.
+    //
+    // Note: This will never return null, unless there is some internal error.
+    // For ex:
+    // i) The latest scan results were empty.
+    // ii) The latest scan result did not contain any BSSID for the SSID user chose.
+    private @Nullable String findBestBssidFromActiveMatchedScanResultsForNetwork(
+            @NonNull WifiConfiguration network) {
+        if (mActiveSpecificNetworkRequestSpecifier == null
+                || mActiveMatchedScanResults == null) return null;
+        ScanResult selectedScanResult = mActiveMatchedScanResults
+                .stream()
+                .filter(scanResult -> Objects.equals(
+                        ScanResultMatchInfo.fromScanResult(scanResult),
+                        ScanResultMatchInfo.fromWifiConfiguration(network)))
+                .max(Comparator.comparing(scanResult -> scanResult.level))
+                .orElse(null);
+        if (selectedScanResult == null) { // Should never happen.
+            Log.wtf(TAG, "Expected to find at least one matching scan result");
+            return null;
+        }
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "Best bssid selected for the request " + selectedScanResult);
+        }
+        return selectedScanResult.BSSID;
+    }
+
     private @Nullable ScanResult
             findUserApprovedAccessPointForActiveRequestFromActiveMatchedScanResults() {
         if (mActiveSpecificNetworkRequestSpecifier == null
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index e7608ed..0b6f03e 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wifi;
 
+import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +30,7 @@
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Pair;
@@ -35,6 +38,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.wifi.nano.WifiMetricsProto;
 import com.android.server.wifi.util.ScanResultUtil;
 
 import java.lang.annotation.Retention;
@@ -77,6 +81,11 @@
     public static final int WIFI_POOR_SCORE = ConnectedScore.WIFI_TRANSITION_SCORE - 10;
 
     /**
+     * The identifier string of the CandidateScorer to use (in the absence of overrides).
+     */
+    public static final String PRESET_CANDIDATE_SCORER_NAME = null;
+
+    /**
      * Experiment ID for the legacy scorer.
      */
     public static final int LEGACY_CANDIDATE_SCORER_EXP_ID = 0;
@@ -96,8 +105,11 @@
     private final int mStayOnNetworkMinimumTxRate;
     private final int mStayOnNetworkMinimumRxRate;
     private final boolean mEnableAutoJoinWhenAssociated;
+    private final WifiNative mWifiNative;
 
     private final Map<String, WifiCandidates.CandidateScorer> mCandidateScorers = new ArrayMap<>();
+    private boolean mIsEasyConnectSupportedInitialized = false;
+    private boolean mIsEasyConnectSupported;
 
     /**
      * WiFi Network Selector supports various categories of networks. Each category
@@ -407,6 +419,17 @@
         return validScanDetails;
     }
 
+    private boolean isEnhancedOpenSupported() {
+        if (mIsEasyConnectSupportedInitialized) {
+            return mIsEasyConnectSupported;
+        }
+
+        mIsEasyConnectSupportedInitialized = true;
+        mIsEasyConnectSupported = (mWifiNative.getSupportedFeatureSet(
+                mWifiNative.getClientInterfaceName()) & WIFI_FEATURE_OWE) != 0;
+        return mIsEasyConnectSupported;
+    }
+
     /**
      * This returns a list of ScanDetails that were filtered in the process of network selection.
      * The list is further filtered for only open unsaved networks.
@@ -417,6 +440,7 @@
      */
     public List<ScanDetail> getFilteredScanDetailsForOpenUnsavedNetworks() {
         List<ScanDetail> openUnsavedNetworks = new ArrayList<>();
+        boolean enhancedOpenSupported = isEnhancedOpenSupported();
         for (ScanDetail scanDetail : mFilteredNetworks) {
             ScanResult scanResult = scanDetail.getScanResult();
 
@@ -424,6 +448,12 @@
                 continue;
             }
 
+            // Filter out Enhanced Open networks on devices that do not support it
+            if (ScanResultUtil.isScanResultForOweNetwork(scanResult)
+                    && !enhancedOpenSupported) {
+                continue;
+            }
+
             // Skip saved networks
             if (mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail) != null) {
                 continue;
@@ -575,6 +605,8 @@
             localLog("After user selection adjustment, the final candidate is:"
                     + WifiNetworkSelector.toNetworkString(candidate) + " : "
                     + scanResultCandidate.BSSID);
+            mWifiMetrics.setNominatorForNetwork(candidate.networkId,
+                    WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED_USER_CONNECT_CHOICE);
         }
         return candidate;
     }
@@ -628,6 +660,7 @@
         // Determine the weight for the last user selection
         final int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork();
         final double lastSelectionWeight = calculateLastSelectionWeight();
+        final ArraySet<Integer> mNetworkIds = new ArraySet<>();
 
         // Go through the registered network evaluators in order
         WifiConfiguration selectedNetwork = null;
@@ -643,6 +676,7 @@
                     (scanDetail, config, score) -> {
                         if (config != null) {
                             mConnectableNetworks.add(Pair.create(scanDetail, config));
+                            mNetworkIds.add(config.networkId);
                             if (config.networkId == lastUserSelectedNetworkId) {
                                 wifiCandidates.add(scanDetail, config,
                                         registeredEvaluator.getId(), score, lastSelectionWeight);
@@ -650,8 +684,14 @@
                                 wifiCandidates.add(scanDetail, config,
                                         registeredEvaluator.getId(), score);
                             }
+                            mWifiMetrics.setNominatorForNetwork(config.networkId,
+                                    evaluatorIdToNominatorId(registeredEvaluator.getId()));
                         }
                     });
+            if (choice != null && !mNetworkIds.contains(choice.networkId)) {
+                Log.wtf(TAG, registeredEvaluator.getName()
+                        + " failed to report choice with noConnectibleListener");
+            }
             if (selectedNetwork == null && choice != null) {
                 selectedNetwork = choice; // First one wins
                 localLog(registeredEvaluator.getName() + " selects "
@@ -690,75 +730,80 @@
             }
         }
 
+        ArrayMap<Integer, Integer> experimentNetworkSelections = new ArrayMap<>(); // for metrics
+
+        final int legacySelectedNetworkId = selectedNetwork == null
+                ? WifiConfiguration.INVALID_NETWORK_ID
+                : selectedNetwork.networkId;
+
+        int selectedNetworkId = legacySelectedNetworkId;
+
+        // Run all the CandidateScorers
         boolean legacyOverrideWanted = true;
-
-        // Run any (experimental) CandidateScorers we have
-        try {
-            int activeExperimentId = LEGACY_CANDIDATE_SCORER_EXP_ID; // default legacy
-            ArrayMap<Integer, WifiConfiguration> experimentNetworkSelections = new ArrayMap<>();
-            experimentNetworkSelections.put(activeExperimentId, selectedNetwork);
-
-            for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) {
-                String id = candidateScorer.getIdentifier();
-                int expid = experimentIdFromIdentifier(id);
-                WifiCandidates.ScoredCandidate choice = wifiCandidates.choose(candidateScorer);
-                if (choice.candidateKey != null) {
-                    boolean thisOne = (expid == mScoringParams.getExperimentIdentifier());
-                    localLog(id + (thisOne ? " chooses " : " would choose ")
-                            + choice.candidateKey.networkId
-                            + " score " + choice.value + "+/-" + choice.err
-                            + " expid " + expid);
-                    int networkId = choice.candidateKey.networkId;
-                    WifiConfiguration thisSelectedNetwork =
-                            mWifiConfigManager.getConfiguredNetwork(networkId);
-                    experimentNetworkSelections.put(expid, thisSelectedNetwork);
-                    if (thisOne) { // update selected network only if this experiment is active
-                        activeExperimentId = expid; // ensures that experiment id actually exists
-                        selectedNetwork = thisSelectedNetwork;
-                        legacyOverrideWanted = candidateScorer.userConnectChoiceOverrideWanted();
-                        Log.i(TAG, id + " chooses " + networkId);
-                    }
-                } else {
-                    localLog(candidateScorer.getIdentifier() + " found no candidates");
-                    experimentNetworkSelections.put(expid, null);
-                }
+        final WifiCandidates.CandidateScorer activeScorer = getActiveCandidateScorer();
+        for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) {
+            WifiCandidates.ScoredCandidate choice;
+            try {
+                choice = wifiCandidates.choose(candidateScorer);
+            } catch (RuntimeException e) {
+                Log.wtf(TAG, "Exception running a CandidateScorer", e);
+                continue;
             }
-
-            for (Map.Entry<Integer, WifiConfiguration> entry :
-                    experimentNetworkSelections.entrySet()) {
-                int experimentId = entry.getKey();
-                if (experimentId == activeExperimentId) continue;
-                WifiConfiguration thisSelectedNetwork = entry.getValue();
-                mWifiMetrics.logNetworkSelectionDecision(experimentId, activeExperimentId,
-                        isSameNetworkSelection(selectedNetwork, thisSelectedNetwork),
-                        groupedCandidates.size());
+            int networkId = choice.candidateKey == null
+                    ? WifiConfiguration.INVALID_NETWORK_ID
+                    : choice.candidateKey.networkId;
+            String chooses = " would choose ";
+            if (candidateScorer == activeScorer) {
+                chooses = " chooses ";
+                legacyOverrideWanted = candidateScorer.userConnectChoiceOverrideWanted();
+                selectedNetworkId = networkId;
             }
-        } catch (RuntimeException e) {
-            Log.wtf(TAG, "Exception running a CandidateScorer, disabling", e);
-            mCandidateScorers.clear();
+            String id = candidateScorer.getIdentifier();
+            int expid = experimentIdFromIdentifier(id);
+            localLog(id + chooses + networkId
+                    + " score " + choice.value + "+/-" + choice.err
+                    + " expid " + expid);
+            experimentNetworkSelections.put(expid, networkId);
         }
 
-        if (selectedNetwork != null) {
-            // Update the copy of WifiConfiguration to reflect the scan result candidate update
-            // above.
-            selectedNetwork = mWifiConfigManager.getConfiguredNetwork(selectedNetwork.networkId);
-            if (selectedNetwork != null && legacyOverrideWanted) {
-                selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
-            }
+        // Update metrics about differences in the selections made by various methods
+        final int activeExperimentId = activeScorer == null ? LEGACY_CANDIDATE_SCORER_EXP_ID
+                : experimentIdFromIdentifier(activeScorer.getIdentifier());
+        experimentNetworkSelections.put(LEGACY_CANDIDATE_SCORER_EXP_ID, legacySelectedNetworkId);
+        for (Map.Entry<Integer, Integer> entry :
+                experimentNetworkSelections.entrySet()) {
+            int experimentId = entry.getKey();
+            if (experimentId == activeExperimentId) continue;
+            int thisSelectedNetworkId = entry.getValue();
+            mWifiMetrics.logNetworkSelectionDecision(experimentId, activeExperimentId,
+                    selectedNetworkId == thisSelectedNetworkId,
+                    groupedCandidates.size());
+        }
+
+        // Get a fresh copy of WifiConfiguration reflecting any scan result updates
+        selectedNetwork = mWifiConfigManager.getConfiguredNetwork(selectedNetworkId);
+        if (selectedNetwork != null && legacyOverrideWanted) {
+            selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
             mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
         }
         return selectedNetwork;
     }
 
-    private static boolean isSameNetworkSelection(WifiConfiguration c1, WifiConfiguration c2) {
-        if (c1 == null && c2 == null) {
-            return true;
-        } else if (c1 == null && c2 != null) {
-            return false;
-        } else if (c1 != null && c2 == null) {
-            return false;
-        } else {
-            return c1.networkId == c2.networkId;
+    private static int evaluatorIdToNominatorId(@NetworkEvaluator.EvaluatorId int evaluatorId) {
+        switch (evaluatorId) {
+            case NetworkEvaluator.EVALUATOR_ID_SAVED:
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED;
+            case NetworkEvaluator.EVALUATOR_ID_SUGGESTION:
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_SUGGESTION;
+            case NetworkEvaluator.EVALUATOR_ID_PASSPOINT:
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_PASSPOINT;
+            case NetworkEvaluator.EVALUATOR_ID_CARRIER:
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER;
+            case NetworkEvaluator.EVALUATOR_ID_SCORED:
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED;
+            default:
+                Log.e(TAG, "UnrecognizedEvaluatorId" + evaluatorId);
+                return WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN;
         }
     }
 
@@ -774,6 +819,24 @@
         return lastSelectionWeight;
     }
 
+    private WifiCandidates.CandidateScorer getActiveCandidateScorer() {
+        WifiCandidates.CandidateScorer ans = mCandidateScorers.get(PRESET_CANDIDATE_SCORER_NAME);
+        int overrideExperimentId = mScoringParams.getExperimentIdentifier();
+        if (overrideExperimentId >= MIN_SCORER_EXP_ID) {
+            for (WifiCandidates.CandidateScorer candidateScorer : mCandidateScorers.values()) {
+                int expId = experimentIdFromIdentifier(candidateScorer.getIdentifier());
+                if (expId == overrideExperimentId) {
+                    ans = candidateScorer;
+                    break;
+                }
+            }
+        }
+        if (ans == null && PRESET_CANDIDATE_SCORER_NAME != null) {
+            Log.wtf(TAG, PRESET_CANDIDATE_SCORER_NAME + " is not registered!");
+        }
+        return ans;
+    }
+
     /**
      * Register a network evaluator
      *
@@ -807,7 +870,7 @@
     }
 
     /**
-     * Derives a numeric experiment identifer from a CandidateScorer's identifier.
+     * Derives a numeric experiment identifier from a CandidateScorer's identifier.
      *
      * @returns a positive number that starts with the decimal digits ID_PREFIX
      */
@@ -817,16 +880,18 @@
     }
     private static final int ID_SUFFIX_MOD = 1_000_000;
     private static final int ID_PREFIX = 42;
+    private static final int MIN_SCORER_EXP_ID = ID_PREFIX * ID_SUFFIX_MOD;
 
     WifiNetworkSelector(Context context, WifiScoreCard wifiScoreCard, ScoringParams scoringParams,
             WifiConfigManager configManager, Clock clock, LocalLog localLog,
-            WifiMetrics wifiMetrics) {
+            WifiMetrics wifiMetrics, WifiNative wifiNative) {
         mWifiConfigManager = configManager;
         mClock = clock;
         mWifiScoreCard = wifiScoreCard;
         mScoringParams = scoringParams;
         mLocalLog = localLog;
         mWifiMetrics = wifiMetrics;
+        mWifiNative = wifiNative;
 
         mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
                 R.bool.config_wifi_framework_enable_associated_network_selection);
diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
index ad09dce..c2aade9 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
@@ -504,7 +504,7 @@
             if (mActiveNetworkSuggestionsMatchingConnection.isEmpty()) {
                 Log.i(TAG, "Only network suggestion matching the connected network removed. "
                         + "Disconnecting...");
-                mWifiInjector.getClientModeImpl().disconnectCommandInternal();
+                mWifiInjector.getClientModeImpl().disconnectCommand();
             }
         }
     }
@@ -549,7 +549,7 @@
      * Add the provided list of network suggestions from the corresponding app's active list.
      */
     public @WifiManager.NetworkSuggestionsStatusCode int add(
-            List<WifiNetworkSuggestion> networkSuggestions, String packageName) {
+            List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName) {
         if (mVerboseLoggingEnabled) {
             Log.v(TAG, "Adding " + networkSuggestions.size() + " networks from " + packageName);
         }
@@ -561,6 +561,10 @@
         if (perAppInfo == null) {
             perAppInfo = new PerAppInfo(packageName);
             mActiveNetworkSuggestionsPerApp.put(packageName, perAppInfo);
+            if (mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(uid)) {
+                Log.i(TAG, "Setting the carrier provisioning app approved");
+                perAppInfo.hasUserApproved = true;
+            }
         }
         Set<ExtendedWifiNetworkSuggestion> extNetworkSuggestions =
                 convertToExtendedWnsSet(networkSuggestions, perAppInfo);
@@ -581,7 +585,7 @@
         }
         if (perAppInfo.extNetworkSuggestions.isEmpty()) {
             // Start tracking app-op changes from the app if they have active suggestions.
-            startTrackingAppOpsChange(packageName, networkSuggestions.get(0).suggestorUid);
+            startTrackingAppOpsChange(packageName, uid);
         }
         perAppInfo.extNetworkSuggestions.addAll(extNetworkSuggestions);
         // Update the max size for this app.
diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java
index a96ba7f..850a206 100644
--- a/service/java/com/android/server/wifi/WifiScoreCard.java
+++ b/service/java/com/android/server/wifi/WifiScoreCard.java
@@ -814,4 +814,21 @@
         return Base64.encodeToString(raw, Base64.DEFAULT);
     }
 
+    /**
+     * Clears the internal state.
+     *
+     * This is called in response to a factoryReset call from Settings.
+     * The memory store will be called after we are called, to wipe the stable
+     * storage as well. Since we will have just removed all of our networks,
+     * it is very unlikely that we're connected, or will connect immediately.
+     * Any in-flight reads will land in the objects we are dropping here, and
+     * the memory store should drop the in-flight writes. Ideally we would
+     * avoid issuing reads until we were sure that the memory store had
+     * received the factoryReset.
+     */
+    public void clear() {
+        mApForBssid.clear();
+        resetConnectionStateInternal(false);
+    }
+
 }
diff --git a/service/java/com/android/server/wifi/WifiService.java b/service/java/com/android/server/wifi/WifiService.java
index d298fbb..b934ea1 100644
--- a/service/java/com/android/server/wifi/WifiService.java
+++ b/service/java/com/android/server/wifi/WifiService.java
@@ -42,6 +42,8 @@
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mImpl.checkAndStartWifi();
+        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            mImpl.handleBootCompleted();
         }
     }
 
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 7a69eaa..17d7ef8 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -591,15 +591,23 @@
         }
     }
 
+    public void handleBootCompleted() {
+        Log.d(TAG, "Handle boot completed");
+        mClientModeImpl.handleBootCompleted();
+    }
+
     public void handleUserSwitch(int userId) {
+        Log.d(TAG, "Handle user switch " + userId);
         mClientModeImpl.handleUserSwitch(userId);
     }
 
     public void handleUserUnlock(int userId) {
+        Log.d(TAG, "Handle user unlock " + userId);
         mClientModeImpl.handleUserUnlock(userId);
     }
 
     public void handleUserStop(int userId) {
+        Log.d(TAG, "Handle user stop " + userId);
         mClientModeImpl.handleUserStop(userId);
     }
 
@@ -1644,7 +1652,7 @@
             return false;
         }
         mLog.info("disconnect uid=%").c(Binder.getCallingUid()).flush();
-        mClientModeImpl.disconnectCommandExternal();
+        mClientModeImpl.disconnectCommand();
         return true;
     }
 
@@ -2907,6 +2915,7 @@
                 mWifiInjector.getWifiConfigManager().clearDeletedEphemeralNetworks();
                 mClientModeImpl.clearNetworkRequestUserApprovedAccessPoints();
                 mWifiNetworkSuggestionsManager.clear();
+                mWifiInjector.getWifiScoreCard().clear();
             });
         }
     }
@@ -3220,11 +3229,12 @@
         if (mVerboseLoggingEnabled) {
             mLog.info("addNetworkSuggestions uid=%").c(Binder.getCallingUid()).flush();
         }
+        int callingUid = Binder.getCallingUid();
         Mutable<Integer> success = new Mutable<>();
         boolean runWithScissorsSuccess = mWifiInjector.getClientModeImplHandler().runWithScissors(
                 () -> {
                     success.value = mWifiNetworkSuggestionsManager.add(
-                            networkSuggestions, callingPackageName);
+                            networkSuggestions, callingUid, callingPackageName);
                 }, RUN_WITH_SCISSORS_TIMEOUT_MILLIS);
         if (!runWithScissorsSuccess) {
             Log.e(TAG, "Failed to post runnable to add network suggestions");
diff --git a/service/java/com/android/server/wifi/WifiSettingsStore.java b/service/java/com/android/server/wifi/WifiSettingsStore.java
index 40ae05c..71b4deb 100644
--- a/service/java/com/android/server/wifi/WifiSettingsStore.java
+++ b/service/java/com/android/server/wifi/WifiSettingsStore.java
@@ -117,7 +117,8 @@
         } else {
             /* On airplane mode disable, restore wifi state if necessary */
             if (testAndClearWifiSavedState() ||
-                    mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
+                    mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE
+                    || mPersistWifiState == WIFI_DISABLED_AIRPLANE_ON) {
                 persistWifiState(WIFI_ENABLED);
             }
         }
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index e641106..1ac73ae 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -42,17 +42,18 @@
 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
 import android.net.wifi.aware.WifiAwareUtils;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
 
@@ -60,6 +61,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.net.DatagramSocket;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
@@ -97,7 +99,13 @@
     private static final int NETWORK_FACTORY_BANDWIDTH_AVAIL = 1;
     private static final int NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL = 1;
 
+    @VisibleForTesting
+    public static final int ADDRESS_VALIDATION_RETRY_INTERVAL_MS = 1_000; // 1 second
+    @VisibleForTesting
+    public static final int ADDRESS_VALIDATION_TIMEOUT_MS = 5_000; // 5 seconds
+
     private final WifiAwareStateManager mMgr;
+    private final Clock mClock;
     public NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper();
     private static final NetworkCapabilities sNetworkCapabilitiesFilter = new NetworkCapabilities();
     private final Set<String> mInterfaces = new HashSet<>();
@@ -108,14 +116,16 @@
     private WifiPermissionsUtil mWifiPermissionsUtil;
     private WifiPermissionsWrapper mPermissionsWrapper;
     private Looper mLooper;
+    private Handler mHandler;
     private WifiAwareNetworkFactory mNetworkFactory;
     public INetworkManagementService mNwService;
 
     // internal debug flag to override API check
     /* package */ boolean mAllowNdpResponderFromAnyOverride = false;
 
-    public WifiAwareDataPathStateManager(WifiAwareStateManager mgr) {
+    public WifiAwareDataPathStateManager(WifiAwareStateManager mgr, Clock clock) {
         mMgr = mgr;
+        mClock = clock;
     }
 
     /**
@@ -131,6 +141,7 @@
         mWifiPermissionsUtil = wifiPermissionsUtil;
         mPermissionsWrapper = permissionsWrapper;
         mLooper = looper;
+        mHandler = new Handler(mLooper);
 
         sNetworkCapabilitiesFilter.clearAll();
         sNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
@@ -282,8 +293,7 @@
             Log.w(TAG, "onDataPathInitiateSuccess: network request in incorrect state: state="
                     + nnri.state);
             mNetworkRequestsCache.remove(networkSpecifier);
-            mNetworkFactory.letAppKnowThatRequestsAreUnavailable(nnri);
-            mMgr.endDataPath(ndpId);
+            declareUnfullfillableAndEndDp(nnri, ndpId);
             return;
         }
 
@@ -428,7 +438,7 @@
 
         nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE;
         nnri.ndpId = ndpId;
-        nnri.startTimestamp = SystemClock.elapsedRealtime();
+        nnri.startTimestamp = mClock.getElapsedSinceBootMillis();
         mMgr.respondToDataPathRequest(true, ndpId, nnri.interfaceName, nnri.networkSpecifier.pmk,
                 nnri.networkSpecifier.passphrase,
                 NetworkInformationData.buildTlv(nnri.networkSpecifier.port,
@@ -565,8 +575,7 @@
                     Log.e(TAG, "onDataPathConfirm: ACCEPT nnri=" + nnri
                             + ": can't configure network - "
                             + e);
-                    mMgr.endDataPath(ndpId);
-                    nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING;
+                    declareUnfullfillableAndEndDp(nnri, ndpId);
                     return networkSpecifier;
                 }
             } else {
@@ -633,6 +642,7 @@
 
             if (!mNiWrapper.configureAgentProperties(nnri, nnri.equivalentRequests, ndpId,
                     networkInfo, networkCapabilities, linkProperties)) {
+                declareUnfullfillableAndEndDp(nnri, ndpId);
                 return networkSpecifier;
             }
 
@@ -641,12 +651,9 @@
                     new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TAG, ""),
                     networkCapabilities, linkProperties, NETWORK_FACTORY_SCORE_AVAIL,
                     nnri);
-            nnri.networkAgent.sendNetworkInfo(networkInfo);
-
-            mAwareMetrics.recordNdpStatus(NanStatusType.SUCCESS, networkSpecifier.isOutOfBand(),
-                    nnri.startTimestamp);
-            nnri.startTimestamp = SystemClock.elapsedRealtime(); // update time-stamp for duration
-            mAwareMetrics.recordNdpCreation(nnri.uid, mNetworkRequestsCache);
+            nnri.startValidationTimestamp = mClock.getElapsedSinceBootMillis();
+            handleAddressValidation(nnri, linkProperties, networkInfo, ndpId,
+                    networkSpecifier.isOutOfBand());
         } else {
             if (VDBG) {
                 Log.v(TAG, "onDataPathConfirm: data-path for networkSpecifier=" + networkSpecifier
@@ -661,6 +668,38 @@
         return networkSpecifier;
     }
 
+    private void handleAddressValidation(AwareNetworkRequestInformation nnri,
+            LinkProperties linkProperties, NetworkInfo networkInfo, int ndpId,
+            boolean isOutOfBand) {
+        if (mNiWrapper.isAddressUsable(linkProperties)) {
+            mNiWrapper.sendAgentNetworkInfo(nnri.networkAgent, networkInfo);
+
+            mAwareMetrics.recordNdpStatus(NanStatusType.SUCCESS, isOutOfBand, nnri.startTimestamp);
+            nnri.startTimestamp = mClock.getElapsedSinceBootMillis(); // update time-stamp
+            mAwareMetrics.recordNdpCreation(nnri.uid, mNetworkRequestsCache);
+        } else {
+            if (mClock.getElapsedSinceBootMillis() - nnri.startValidationTimestamp
+                    > ADDRESS_VALIDATION_TIMEOUT_MS) {
+                Log.e(TAG, "Timed-out while waiting for IPv6 address to be usable");
+
+                declareUnfullfillableAndEndDp(nnri, ndpId);
+                return;
+            }
+            if (mDbg) {
+                Log.d(TAG, "Failed address validation");
+            }
+            mHandler.postDelayed(() -> {
+                handleAddressValidation(nnri, linkProperties, networkInfo, ndpId, isOutOfBand);
+            }, ADDRESS_VALIDATION_RETRY_INTERVAL_MS);
+        }
+    }
+
+    private void declareUnfullfillableAndEndDp(AwareNetworkRequestInformation nnri, int ndpId) {
+        mNetworkFactory.letAppKnowThatRequestsAreUnavailable(nnri);
+        mMgr.endDataPath(ndpId);
+        nnri.state = AwareNetworkRequestInformation.STATE_TERMINATING;
+    }
+
     /**
      * Notification (unsolicited/asynchronous) from the firmware that the specified data-path has
      * been terminated.
@@ -906,7 +945,7 @@
                         null);
                 nnri.state =
                         AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE;
-                nnri.startTimestamp = SystemClock.elapsedRealtime();
+                nnri.startTimestamp = mClock.getElapsedSinceBootMillis();
             } else {
                 nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST;
             }
@@ -979,7 +1018,11 @@
         }
     }
 
-    private class WifiAwareNetworkAgent extends NetworkAgent {
+    /**
+     * Network agent for Wi-Fi Aware.
+     */
+    @VisibleForTesting
+    public class WifiAwareNetworkAgent extends NetworkAgent {
         private NetworkInfo mNetworkInfo;
         private AwareNetworkRequestInformation mAwareNetworkRequestInfo;
 
@@ -1140,6 +1183,7 @@
         public WifiAwareNetworkSpecifier networkSpecifier;
         public List<NanDataPathChannelInfo> channelInfo;
         public long startTimestamp = 0; // request is made (initiator) / get request (responder)
+        public long startValidationTimestamp = 0; // NDP created and starting to validate IPv6 addr
 
         public WifiAwareNetworkAgent networkAgent;
 
@@ -1492,6 +1536,36 @@
 
             return true;
         }
+
+        /**
+         * Tries binding to the input address to check whether it is configured (and therefore
+         * usable).
+         */
+        public boolean isAddressUsable(LinkProperties linkProperties) {
+            InetAddress address = linkProperties.getLinkAddresses().get(0).getAddress();
+            DatagramSocket testDatagramSocket = null;
+            try {
+                testDatagramSocket = new DatagramSocket(0, address);
+            } catch (SocketException e) {
+                if (mDbg) {
+                    Log.d(TAG, "Can't create socket on address " + address + " -- " + e);
+                }
+                return false;
+            } finally {
+                if (testDatagramSocket != null) {
+                    testDatagramSocket.close();
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Send updated network information to the agent.
+         */
+        public void sendAgentNetworkInfo(WifiAwareNetworkAgent networkAgent,
+                NetworkInfo networkInfo) {
+            networkAgent.sendNetworkInfo(networkInfo);
+        }
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index 23ecc75..e6e961d 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -43,6 +43,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
+import com.android.server.wifi.Clock;
 import com.android.server.wifi.FrameworkFacade;
 import com.android.server.wifi.WifiInjector;
 import com.android.server.wifi.util.WifiPermissionsUtil;
@@ -104,7 +105,7 @@
         mStateManager = awareStateManager;
         mShellCommand = awareShellCommand;
         mStateManager.start(mContext, handlerThread.getLooper(), awareMetrics, wifiPermissionsUtil,
-                permissionsWrapper);
+                permissionsWrapper, new Clock());
 
         frameworkFacade.registerContentObserver(mContext,
                 Settings.Global.getUriFor(Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED), true,
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
index ac1ed6a..36cabd6 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -52,6 +52,7 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.WakeupMessage;
+import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
 
@@ -378,7 +379,8 @@
      * @param looper Thread looper on which to run the handler.
      */
     public void start(Context context, Looper looper, WifiAwareMetrics awareMetrics,
-            WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper) {
+            WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper,
+            Clock clock) {
         Log.i(TAG, "start()");
 
         mContext = context;
@@ -388,7 +390,7 @@
         mSm.setDbg(VVDBG);
         mSm.start();
 
-        mDataPathMgr = new WifiAwareDataPathStateManager(this);
+        mDataPathMgr = new WifiAwareDataPathStateManager(this, clock);
         mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics,
                 wifiPermissionsUtil, permissionsWrapper);
 
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index d239057..e874ed1 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -338,7 +338,7 @@
             return -1;
         }
 
-        String mccMnc = mTelephonyManager.getNetworkOperator();
+        String mccMnc = mTelephonyManager.getSimOperator();
         if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) {
             return -1;
         }
@@ -399,7 +399,7 @@
      * {@code null} otherwise.
      */
     public PasspointConfiguration createEphemeralPasspointConfigForCarrier(int eapMethod) {
-        String mccMnc = mTelephonyManager.getNetworkOperator();
+        String mccMnc = mTelephonyManager.getSimOperator();
         if (mccMnc == null || mccMnc.length() < IMSIParameter.MCC_MNC_LENGTH - 1) {
             Log.e(TAG, "invalid length of mccmnc");
             return null;
@@ -418,7 +418,7 @@
         PasspointConfiguration config = new PasspointConfiguration();
         HomeSp homeSp = new HomeSp();
         homeSp.setFqdn(domain);
-        homeSp.setFriendlyName(mTelephonyManager.getNetworkOperatorName());
+        homeSp.setFriendlyName(mTelephonyManager.getSimOperatorName());
         config.setHomeSp(homeSp);
 
         Credential credential = new Credential();
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
index df17389..fe882f6 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
@@ -33,6 +33,7 @@
 import com.android.server.wifi.WifiConfigManager;
 import com.android.server.wifi.WifiNetworkSelector;
 import com.android.server.wifi.util.ScanResultUtil;
+import com.android.server.wifi.util.TelephonyUtil;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -96,11 +97,11 @@
         mPasspointManager.sweepCache();
 
         // Creates an ephemeral Passpoint profile if it finds a matching Passpoint AP for MCC/MNC
-        // of the current carrier on the device.
-        if (mWifiConfigManager.isSimPresent()
+        // of the current MNO carrier on the device.
+        if ((TelephonyUtil.getCarrierType(mTelephonyManager) == TelephonyUtil.CARRIER_MNO_TYPE)
                 && mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()
                 && !mPasspointManager.hasCarrierProvider(
-                mTelephonyManager.getNetworkOperator())) {
+                mTelephonyManager.getSimOperator())) {
             int eapMethod = mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier(
                     scanDetails);
             if (isCarrierEapMethod(eapMethod)) {
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
index 664824a..bf9e6d3 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
@@ -29,6 +29,7 @@
 import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.net.wifi.hotspot2.omadm.PpsMoParser;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -81,12 +82,12 @@
     private final WfaKeyStore mWfaKeyStore;
     private final PasspointObjectFactory mObjectFactory;
     private final SystemInfo mSystemInfo;
-    private RedirectListener mRedirectListener;
     private int mCurrentSessionId = 0;
     private int mCallingUid;
     private boolean mVerboseLoggingEnabled = false;
     private WifiManager mWifiManager;
     private PasspointManager mPasspointManager;
+    private Looper mLooper;
 
     PasspointProvisioner(Context context, WifiNative wifiNative,
             PasspointObjectFactory objectFactory, PasspointManager passpointManager) {
@@ -107,12 +108,12 @@
      * @param looper Looper on which the Provisioning state machine will run
      */
     public void init(Looper looper) {
+        mLooper = looper;
         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
-        mProvisioningStateMachine.start(new Handler(looper));
+        mProvisioningStateMachine.start(new Handler(mLooper));
         mOsuNetworkConnection.init(mProvisioningStateMachine.getHandler());
         // Offload the heavy load job to another thread
         mProvisioningStateMachine.getHandler().post(() -> {
-            mRedirectListener = RedirectListener.createInstance(looper);
             mWfaKeyStore.load();
             mOsuServerConnection.init(mObjectFactory.getSSLContext(TLS_VERSION),
                     mObjectFactory.getTrustManagerImpl(mWfaKeyStore.get()));
@@ -142,10 +143,6 @@
      */
     public boolean startSubscriptionProvisioning(int callingUid, OsuProvider provider,
             IProvisioningCallback callback) {
-        if (mRedirectListener == null) {
-            Log.e(TAG, "RedirectListener is not possible to run");
-            return false;
-        }
         mCallingUid = callingUid;
 
         Log.v(TAG, "Provisioning started with " + provider.toString());
@@ -181,12 +178,20 @@
         private String mSessionId;
         private String mWebUrl;
         private PasspointConfiguration mPasspointConfiguration;
+        private RedirectListener mRedirectListener;
+        private HandlerThread mRedirectHandlerThread;
+        private Handler mRedirectStartStopHandler;
 
         /**
          * Initializes and starts the state machine with a handler to handle incoming events
          */
         public void start(Handler handler) {
             mHandler = handler;
+            if (mRedirectHandlerThread == null) {
+                mRedirectHandlerThread = new HandlerThread("RedirectListenerHandler");
+                mRedirectHandlerThread.start();
+                mRedirectStartStopHandler = new Handler(mRedirectHandlerThread.getLooper());
+            }
         }
 
         /**
@@ -216,9 +221,17 @@
                 }
                 resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_PROVISIONING_ABORTED);
             }
+            mProvisioningCallback = callback;
+            mRedirectListener = RedirectListener.createInstance(mLooper);
+
+            if (mRedirectListener == null) {
+                resetStateMachineForFailure(
+                        ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER);
+                return;
+            }
+
             if (!mOsuServerConnection.canValidateServer()) {
                 Log.w(TAG, "Provisioning is not possible");
-                mProvisioningCallback = callback;
                 resetStateMachineForFailure(
                         ProvisioningCallback.OSU_FAILURE_PROVISIONING_NOT_AVAILABLE);
                 return;
@@ -228,12 +241,10 @@
                 serverUrl = new URL(provider.getServerUri().toString());
             } catch (MalformedURLException e) {
                 Log.e(TAG, "Invalid Server URL");
-                mProvisioningCallback = callback;
                 resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_SERVER_URL_INVALID);
                 return;
             }
             mServerUrl = serverUrl;
-            mProvisioningCallback = callback;
             mOsuProvider = provider;
             if (mOsuProvider.getOsuSsid() == null) {
                 // Find a best matching OsuProvider that has an OSU SSID from current scanResults
@@ -376,7 +387,7 @@
 
             invokeProvisioningCallback(PROVISIONING_STATUS,
                     ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED);
-            mRedirectListener.stopServer();
+            mRedirectListener.stopServer(mRedirectStartStopHandler);
             secondSoapExchange();
         }
 
@@ -395,7 +406,7 @@
                 resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_PROVISIONING_ABORTED);
                 return;
             }
-            mRedirectListener.stopServer();
+            mRedirectListener.stopServer(mRedirectStartStopHandler);
             resetStateMachineForFailure(
                     ProvisioningCallback.OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER);
         }
@@ -754,7 +765,7 @@
                     }
                     mProvisioningStateMachine.handleTimeOutForRedirectResponse();
                 }
-            })) {
+            }, mRedirectStartStopHandler)) {
                 Log.e(TAG, "fails to start redirect listener");
                 resetStateMachineForFailure(
                         ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER);
@@ -944,12 +955,15 @@
         }
 
         private void resetStateMachine() {
-            mRedirectListener.stopServer();
+            if (mRedirectListener != null) {
+                mRedirectListener.stopServer(mRedirectStartStopHandler);
+            }
             mOsuNetworkConnection.setEventCallback(null);
             mOsuNetworkConnection.disconnectIfNeeded();
             mOsuServerConnection.setEventCallback(null);
             mOsuServerConnection.cleanup();
             mPasspointConfiguration = null;
+            mProvisioningCallback = null;
             changeState(STATE_INIT);
         }
 
diff --git a/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java b/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java
index 03c3ebe..103aecb 100644
--- a/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java
+++ b/service/java/com/android/server/wifi/hotspot2/soap/RedirectListener.java
@@ -17,9 +17,7 @@
 package com.android.server.wifi.hotspot2.soap;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.util.Log;
 
@@ -42,15 +40,13 @@
  * redirect server no longer needed.
  */
 public class RedirectListener extends NanoHTTPD {
-    // 4 minutes for the maximum wait time.
+    // 10 minutes for the maximum wait time.
     @VisibleForTesting
-    static final int USER_TIMEOUT_MILLIS = 4 * 60 * 1000;
+    static final int USER_TIMEOUT_MILLIS = 10 * 60 * 1000;
 
     private static final String TAG = "PasspointRedirectListener";
-
     private final String mPath;
     private final URL mServerUrl;
-    private final Handler mStartStopHandler;
     private final Handler mHandler;
     private Runnable mTimeOutTask;
     private RedirectCallback mRedirectCallback;
@@ -72,7 +68,7 @@
     }
 
     @VisibleForTesting
-    /* package */ RedirectListener(Looper looper, @Nullable Looper startStopLooper, int port)
+    /* package */ RedirectListener(Looper looper, int port)
             throws IOException {
         super(InetAddress.getLocalHost().getHostAddress(), port);
 
@@ -82,12 +78,6 @@
         mServerUrl = new URL("http", getHostname(), port, mPath);
         mHandler = new Handler(looper);
         mTimeOutTask = () -> mRedirectCallback.onRedirectTimedOut();
-        if (startStopLooper == null) {
-            HandlerThread redirectHandlerThread = new HandlerThread("RedirectListenerHandler");
-            redirectHandlerThread.start();
-            startStopLooper = redirectHandlerThread.getLooper();
-        }
-        mStartStopHandler = new Handler(startStopLooper);
     }
 
     /**
@@ -99,8 +89,14 @@
     public static RedirectListener createInstance(@NonNull Looper looper) {
         RedirectListener redirectListener;
         try {
-            redirectListener = new RedirectListener(looper, null,
-                    new ServerSocket(0, 1, InetAddress.getLocalHost()).getLocalPort());
+            ServerSocket serverSocket = new ServerSocket(0, 1, InetAddress.getLocalHost());
+            redirectListener = new RedirectListener(looper, serverSocket.getLocalPort());
+            redirectListener.setServerSocketFactory(() -> {
+                // Close current server socket so that new server socket is able to bind the port
+                // in the start() of NanoHTTPD.
+                serverSocket.close();
+                return new ServerSocket();
+            });
         } catch (IOException e) {
             Log.e(TAG, "fails to create an instance: " + e);
             return null;
@@ -112,21 +108,26 @@
      * Start redirect listener
      *
      * @param callback to be notified when the redirect request is received or timed out.
-     * @return {@code true} in success, {@code false} if the {@code callback} is {@code null} or the
-     * server is already running.
+     * @param startHandler handler on which the start code is executed.
+     * @return {@code true} in success, {@code false} if the {@code callback} and {@code
+     * startHandler} are {@code null} or the server is already running.
      */
-    public boolean startServer(@NonNull RedirectCallback callback) {
+    public boolean startServer(@NonNull RedirectCallback callback, @NonNull Handler startHandler) {
         if (callback == null) {
             return false;
         }
 
+        if (startHandler == null) {
+            return false;
+        }
+
         if (isAlive()) {
             Log.e(TAG, "redirect listener is already running");
             return false;
         }
         mRedirectCallback = callback;
 
-        mStartStopHandler.post(() -> {
+        startHandler.post(() -> {
             try {
                 start();
             } catch (IOException e) {
@@ -139,13 +140,18 @@
 
     /**
      * Stop redirect listener
+     *
+     * @param stopHandler handler on which the stop code is executed.
      */
-    public void stopServer() {
+    public void stopServer(@NonNull Handler stopHandler) {
         if (mHandler.hasCallbacks(mTimeOutTask)) {
             mHandler.removeCallbacks(mTimeOutTask);
         }
+        if (stopHandler == null) {
+            return;
+        }
         if (isServerAlive()) {
-            mStartStopHandler.post(() -> stop());
+            stopHandler.post(() -> stop());
         }
     }
 
diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
index 7e0eaee..12adea1 100644
--- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
+++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -55,6 +55,7 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.WakeupMessage;
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.FrameworkFacade;
@@ -89,8 +90,6 @@
     private WifiPermissionsUtil mWifiPermissionsUtil;
     private ActivityManager mActivityManager;
     private PowerManager mPowerManager;
-    private LocationManager mLocationManager;
-    private FrameworkFacade mFrameworkFacade;
     private long mBackgroundProcessExecGapMs;
 
     private RttServiceSynchronized mRttServiceSynchronized;
@@ -99,7 +98,10 @@
 
     /* package */ static final String HAL_RANGING_TIMEOUT_TAG = TAG + " HAL Ranging Timeout";
 
-    private static final long HAL_RANGING_TIMEOUT_MS = 5_000; // 5 sec
+    @VisibleForTesting
+    public static final long HAL_RANGING_TIMEOUT_MS = 5_000; // 5 sec
+    @VisibleForTesting
+    public static final long HAL_AWARE_RANGING_TIMEOUT_MS = 10_000; // 10 sec
 
     // Default value for RTT background throttling interval.
     private static final long DEFAULT_BACKGROUND_PROCESS_EXEC_GAP_MS = 1_800_000; // 30 min
@@ -230,12 +232,10 @@
         mRttNative = rttNative;
         mRttMetrics = rttMetrics;
         mWifiPermissionsUtil = wifiPermissionsUtil;
-        mFrameworkFacade = frameworkFacade;
         mRttServiceSynchronized = new RttServiceSynchronized(looper, rttNative);
 
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mPowerManager = mContext.getSystemService(PowerManager.class);
-        mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         mContext.registerReceiver(new BroadcastReceiver() {
@@ -829,8 +829,14 @@
             nextRequest.cmdId = mNextCommandId++;
             if (mRttNative.rangeRequest(nextRequest.cmdId, nextRequest.request,
                     nextRequest.isCalledFromPrivilegedContext)) {
-                mRangingTimeoutMessage.schedule(
-                        mClock.getElapsedSinceBootMillis() + HAL_RANGING_TIMEOUT_MS);
+                long timeout = HAL_RANGING_TIMEOUT_MS;
+                for (ResponderConfig responderConfig : nextRequest.request.mRttPeers) {
+                    if (responderConfig.responderType == ResponderConfig.RESPONDER_AWARE) {
+                        timeout = HAL_AWARE_RANGING_TIMEOUT_MS;
+                        break;
+                    }
+                }
+                mRangingTimeoutMessage.schedule(mClock.getElapsedSinceBootMillis() + timeout);
             } else {
                 Log.w(TAG, "RttServiceSynchronized.startRanging: native rangeRequest call failed");
                 try {
diff --git a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java b/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
index b005529..38ecb07 100644
--- a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
+++ b/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
@@ -17,8 +17,10 @@
 package com.android.server.wifi.util;
 
 import android.annotation.NonNull;
+import android.os.SystemProperties;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
@@ -61,7 +63,13 @@
     private static final int GCM_TAG_LENGTH = 128;
     private static final String KEY_STORE = "AndroidKeyStore";
 
-    private File mIntegrityFile;
+    /**
+     * When KEYSTORE_FAILURE_RETURN_VALUE is true, all cryptographic operation failures will not
+     * enforce security and {@link #isOk(byte[])} always return true.
+     */
+    private static final boolean KEYSTORE_FAILURE_RETURN_VALUE = true;
+
+    private final File mIntegrityFile;
 
     /**
      * Construct a new integrity checker to update and check if/when a data file was altered
@@ -73,34 +81,37 @@
      *                          path as the file for which integrity is performed on.
      * @throws NullPointerException When integrity file is null or the empty string.
      */
-    public DataIntegrityChecker(@NonNull String integrityFilename) throws NullPointerException {
-        if (integrityFilename == null || integrityFilename.equals("")) {
+    public DataIntegrityChecker(@NonNull String integrityFilename) {
+        if (TextUtils.isEmpty(integrityFilename)) {
             throw new NullPointerException("integrityFilename must not be null or the empty "
                     + "string");
-        } else {
-            mIntegrityFile = new File(integrityFilename + FILE_SUFFIX);
         }
+        mIntegrityFile = new File(integrityFilename + FILE_SUFFIX);
     }
 
     /**
-     * Compute a digest of a byte array, encrypt it, and store the result
+     * Computes a digest of a byte array, encrypt it, and store the result
      *
      * Call this method immediately before storing the byte array
      *
      * @param data The data desired to ensure integrity
      */
     public void update(byte[] data) {
-        if (data == null || mIntegrityFile == null) {
+        if (data == null || data.length < 1) {
+            Log.e(TAG, "No data to update.");
+            reportException(new Exception("No data to update"));
             return;
         }
         byte[] digest = getDigest(data);
-        if (digest == null) {
+        if (digest == null || digest.length < 1) {
             return;
         }
         String alias = mIntegrityFile.getName() + ALIAS_SUFFIX;
         EncryptedData integrityData = encrypt(digest, alias);
         if (integrityData != null) {
             writeIntegrityData(integrityData, mIntegrityFile);
+        } else {
+            reportException(new Exception("integrityData null upon update"));
         }
     }
 
@@ -119,12 +130,12 @@
      * @return true if the data was not altered since {@link #update(byte[])} was last called
      */
     public boolean isOk(byte[] data) throws DigestException {
-        if (data == null || mIntegrityFile == null) {
-            return false;
+        if (data == null || data.length < 1) {
+            return KEYSTORE_FAILURE_RETURN_VALUE;
         }
         byte[] currentDigest = getDigest(data);
-        if (currentDigest == null) {
-            return false;
+        if (currentDigest == null || currentDigest.length < 1) {
+            return KEYSTORE_FAILURE_RETURN_VALUE;
         }
         EncryptedData encryptedData = readIntegrityData(mIntegrityFile);
         if (encryptedData == null) {
@@ -132,16 +143,17 @@
         }
         byte[] storedDigest = decrypt(encryptedData);
         if (storedDigest == null) {
-            return false;
+            return KEYSTORE_FAILURE_RETURN_VALUE;
         }
         return constantTimeEquals(storedDigest, currentDigest);
     }
 
-    private static byte[] getDigest(byte[] data) {
+    private byte[] getDigest(byte[] data) {
         try {
             return MessageDigest.getInstance(DIGEST_ALGORITHM).digest(data);
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "getDigest could not find algorithm: " + DIGEST_ALGORITHM);
+            reportException(e);
             return null;
         }
     }
@@ -154,22 +166,29 @@
             if (secretKeyReference != null) {
                 cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
                 encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV(), keyAlias);
+            } else {
+                reportException(new Exception("secretKeyReference is null."));
             }
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "encrypt could not find the algorithm: " + CIPHER_ALGORITHM);
+            reportException(e);
         } catch (NoSuchPaddingException e) {
             Log.e(TAG, "encrypt had a padding exception");
+            reportException(e);
         } catch (InvalidKeyException e) {
             Log.e(TAG, "encrypt received an invalid key");
+            reportException(e);
         } catch (BadPaddingException e) {
             Log.e(TAG, "encrypt had a padding problem");
+            reportException(e);
         } catch (IllegalBlockSizeException e) {
             Log.e(TAG, "encrypt had an illegal block size");
+            reportException(e);
         }
         return encryptedData;
     }
 
-    private static byte[] decrypt(EncryptedData encryptedData) {
+    private byte[] decrypt(EncryptedData encryptedData) {
         byte[] decryptedData = null;
         try {
             Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
@@ -181,21 +200,27 @@
             }
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "decrypt could not find cipher algorithm " + CIPHER_ALGORITHM);
+            reportException(e);
         } catch (NoSuchPaddingException e) {
             Log.e(TAG, "decrypt could not find padding algorithm");
+            reportException(e);
         } catch (IllegalBlockSizeException e) {
             Log.e(TAG, "decrypt had a illegal block size");
+            reportException(e);
         } catch (BadPaddingException e) {
             Log.e(TAG, "decrypt had bad padding");
+            reportException(e);
         } catch (InvalidKeyException e) {
             Log.e(TAG, "decrypt had an invalid key");
+            reportException(e);
         } catch (InvalidAlgorithmParameterException e) {
             Log.e(TAG, "decrypt had an invalid algorithm parameter");
+            reportException(e);
         }
         return decryptedData;
     }
 
-    private static SecretKey getOrCreateSecretKey(String keyAlias) {
+    private SecretKey getOrCreateSecretKey(String keyAlias) {
         SecretKey secretKey = null;
         try {
             KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
@@ -205,6 +230,9 @@
                         .getEntry(keyAlias, null);
                 if (secretKeyEntry != null) {
                     secretKey = secretKeyEntry.getSecretKey();
+                } else {
+                    reportException(new Exception("keystore contains the alias and the secret key "
+                            + "entry was null"));
                 }
             } else { // The key does not exist in key store. Create the key and store it.
                 KeyGenerator keyGenerator = KeyGenerator
@@ -221,49 +249,57 @@
             }
         } catch (CertificateException e) {
             Log.e(TAG, "getOrCreateSecretKey had a certificate exception.");
+            reportException(e);
         } catch (InvalidAlgorithmParameterException e) {
             Log.e(TAG, "getOrCreateSecretKey had an invalid algorithm parameter");
+            reportException(e);
         } catch (IOException e) {
             Log.e(TAG, "getOrCreateSecretKey had an IO exception.");
+            reportException(e);
         } catch (KeyStoreException e) {
             Log.e(TAG, "getOrCreateSecretKey cannot find the keystore: " + KEY_STORE);
+            reportException(e);
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "getOrCreateSecretKey cannot find algorithm");
+            reportException(e);
         } catch (NoSuchProviderException e) {
             Log.e(TAG, "getOrCreateSecretKey cannot find crypto provider");
+            reportException(e);
         } catch (UnrecoverableEntryException e) {
             Log.e(TAG, "getOrCreateSecretKey had an unrecoverable entry exception.");
+            reportException(e);
         }
         return secretKey;
     }
 
-    private static void writeIntegrityData(EncryptedData encryptedData, File file) {
-        try {
-            FileOutputStream fos = new FileOutputStream(file);
-            ObjectOutputStream oos = new ObjectOutputStream(fos);
+    private void writeIntegrityData(EncryptedData encryptedData, File file) {
+        try (FileOutputStream fos = new FileOutputStream(file);
+             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
             oos.writeObject(encryptedData);
-            oos.close();
         } catch (FileNotFoundException e) {
             Log.e(TAG, "writeIntegrityData could not find the integrity file");
+            reportException(e);
         } catch (IOException e) {
             Log.e(TAG, "writeIntegrityData had an IO exception");
+            reportException(e);
         }
     }
 
-    private static EncryptedData readIntegrityData(File file) {
-        EncryptedData encryptedData = null;
-        try {
-            FileInputStream fis = new FileInputStream(file);
-            ObjectInputStream ois = new ObjectInputStream(fis);
-            encryptedData = (EncryptedData) ois.readObject();
+    private EncryptedData readIntegrityData(File file) {
+        try (FileInputStream fis = new FileInputStream(file);
+             ObjectInputStream ois = new ObjectInputStream(fis)) {
+            return (EncryptedData) ois.readObject();
         } catch (FileNotFoundException e) {
             Log.e(TAG, "readIntegrityData could not find integrity file");
+            reportException(e);
         } catch (IOException e) {
             Log.e(TAG, "readIntegrityData had an IO exception");
+            reportException(e);
         } catch (ClassNotFoundException e) {
             Log.e(TAG, "readIntegrityData could not find the class EncryptedData");
+            reportException(e);
         }
-        return encryptedData;
+        return null;
     }
 
     private boolean constantTimeEquals(byte[] a, byte[] b) {
@@ -281,4 +317,15 @@
         }
         return (differenceAccumulator == 0);
     }
+
+    /* TODO(b/128526030): Remove this error reporting code upon resolving the bug. */
+    private static final boolean REQUEST_BUG_REPORT = true;
+    private void reportException(Exception exception) {
+        Log.wtf(TAG, "An irrecoverable key store error was encountered. "
+                + "KEYSTORE_FAILURE_RETURN_VALUE is set to " + KEYSTORE_FAILURE_RETURN_VALUE);
+        if (REQUEST_BUG_REPORT) {
+            SystemProperties.set("dumpstate.options", "bugreportwifi");
+            SystemProperties.set("ctl.start", "bugreport");
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java
index 597aa71..09b790a 100644
--- a/service/java/com/android/server/wifi/util/TelephonyUtil.java
+++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi.util;
 
+import android.annotation.NonNull;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.telephony.ImsiEncryptionInfo;
@@ -25,6 +26,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.CarrierNetworkConfig;
 import com.android.server.wifi.WifiNative;
 
 import java.security.InvalidKeyException;
@@ -45,6 +47,10 @@
 
     public static final String DEFAULT_EAP_PREFIX = "\0";
 
+    public static final int CARRIER_INVALID_TYPE = -1;
+    public static final int CARRIER_MNO_TYPE = 0; // Mobile Network Operator
+    public static final int CARRIER_MVNO_TYPE = 1; // Mobile Virtual Network Operator
+
     private static final String THREE_GPP_NAI_REALM_FORMAT = "wlan.mnc%s.mcc%s.3gppnetwork.org";
 
     // IMSI encryption method: RSA-OAEP with SHA-256 hash function
@@ -73,16 +79,22 @@
      *
      * @param tm TelephonyManager instance
      * @param config WifiConfiguration that indicates what sort of authentication is necessary
+     * @param telephonyUtil TelephonyUtil instance
+     * @param carrierNetworkConfig CarrierNetworkConfig instance
      * @return Pair<identify, encrypted identity> or null if the SIM is not available
      * or config is invalid
      */
     public static Pair<String, String> getSimIdentity(TelephonyManager tm,
-                                                      TelephonyUtil telephonyUtil,
-                                                      WifiConfiguration config) {
+            TelephonyUtil telephonyUtil,
+            WifiConfiguration config, CarrierNetworkConfig carrierNetworkConfig) {
         if (tm == null) {
             Log.e(TAG, "No valid TelephonyManager");
             return null;
         }
+        if (carrierNetworkConfig == null) {
+            Log.e(TAG, "No valid CarrierNetworkConfig");
+            return null;
+        }
         String imsi = tm.getSubscriberId();
         String mccMnc = "";
 
@@ -104,8 +116,12 @@
             return null;
         }
 
+        int base64EncodingFlag = carrierNetworkConfig.getBase64EncodingFlag();
+
         String encryptedIdentity = buildEncryptedIdentity(telephonyUtil,
-                getSimMethodForConfig(config), imsi, mccMnc, imsiEncryptionInfo);
+                getSimMethodForConfig(config), imsi, mccMnc, imsiEncryptionInfo,
+                base64EncodingFlag);
+
         // In case of failure for encryption, set empty string
         if (encryptedIdentity == null) encryptedIdentity = "";
         return Pair.create(identity, encryptedIdentity);
@@ -116,15 +132,17 @@
      * a Base64 encoded string.
      *
      * @param key The public key to use for encryption
+     * @param encodingFlag base64 encoding flag
      * @return Base64 encoded string, or null if encryption failed
      */
     @VisibleForTesting
-    public String encryptDataUsingPublicKey(PublicKey key, byte[] data) {
+    public String encryptDataUsingPublicKey(PublicKey key, byte[] data, int encodingFlag) {
         try {
             Cipher cipher = Cipher.getInstance(IMSI_CIPHER_TRANSFORMATION);
             cipher.init(Cipher.ENCRYPT_MODE, key);
             byte[] encryptedBytes = cipher.doFinal(data);
-            return Base64.encodeToString(encryptedBytes, 0, encryptedBytes.length, Base64.DEFAULT);
+
+            return Base64.encodeToString(encryptedBytes, 0, encryptedBytes.length, encodingFlag);
         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                 | IllegalBlockSizeException | BadPaddingException e) {
             Log.e(TAG, "Encryption failed: " + e.getMessage());
@@ -138,14 +156,16 @@
      * "0" - EAP-AKA Identity
      * "1" - EAP-SIM Identity
      * "6" - EAP-AKA' Identity
+     *
      * @param eapMethod EAP authentication method: EAP-SIM, EAP-AKA, EAP-AKA'
      * @param imsi The IMSI retrieved from the SIM
      * @param mccMnc The MCC MNC identifier retrieved from the SIM
      * @param imsiEncryptionInfo The IMSI encryption info retrieved from the SIM
+     * @param base64EncodingFlag base64 encoding flag
      */
     private static String buildEncryptedIdentity(TelephonyUtil telephonyUtil, int eapMethod,
-                                                 String imsi, String mccMnc,
-                                                 ImsiEncryptionInfo imsiEncryptionInfo) {
+            String imsi, String mccMnc,
+            ImsiEncryptionInfo imsiEncryptionInfo, int base64EncodingFlag) {
         if (imsiEncryptionInfo == null) {
             return null;
         }
@@ -155,9 +175,10 @@
             return null;
         }
         imsi = prefix + imsi;
+
         // Build and return the encrypted identity.
         String encryptedImsi = telephonyUtil.encryptDataUsingPublicKey(
-                imsiEncryptionInfo.getPublicKey(), imsi.getBytes());
+                imsiEncryptionInfo.getPublicKey(), imsi.getBytes(), base64EncodingFlag);
         if (encryptedImsi == null) {
             Log.e(TAG, "Failed to encrypt IMSI");
             return null;
@@ -621,4 +642,23 @@
             return null;
         }
     }
+
+    /**
+     * Get the carrier type of current SIM.
+     *
+     * @param tm {@link TelephonyManager} instance
+     * @return carrier type of current active sim, {{@link #CARRIER_INVALID_TYPE}} if sim is not
+     * ready or {@code tm} is {@code null}
+     */
+    public static int getCarrierType(@NonNull TelephonyManager tm) {
+        if (tm == null || tm.getSimState() != TelephonyManager.SIM_STATE_READY) {
+            return CARRIER_INVALID_TYPE;
+        }
+
+        // If two APIs return the same carrier ID, then is considered as MNO, otherwise MVNO
+        if (tm.getCarrierIdFromSimMccMnc() == tm.getSimCarrierId()) {
+            return CARRIER_MNO_TYPE;
+        }
+        return CARRIER_MVNO_TYPE;
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
index f68654f..eeacac0 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -482,4 +482,13 @@
                 android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, uid)
                 == PackageManager.PERMISSION_GRANTED;
     }
+
+    /**
+     * Returns true if the |uid| holds NETWORK_CARRIER_PROVISIONING permission.
+     */
+    public boolean checkNetworkCarrierProvisioningPermission(int uid) {
+        return mWifiPermissionsWrapper.getUidPermission(
+                android.Manifest.permission.NETWORK_CARRIER_PROVISIONING, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
 }
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index d14f303..24213cd 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -79,6 +79,8 @@
 	libbinder \
 	libbinderthreadstate \
 	libc++ \
+	ld-android \
+	libdl_android \
 	libcamera_client \
 	libcamera_metadata \
 	libcutils \
diff --git a/tests/wifitests/coverage.sh b/tests/wifitests/coverage.sh
index 33b9f30..f9ea1fb 100755
--- a/tests/wifitests/coverage.sh
+++ b/tests/wifitests/coverage.sh
@@ -40,12 +40,14 @@
   MODULES-IN-external-jacoco \
   FrameworksWifiTests
 
+APK_NAME="$(ls -t $(find $OUT -name FrameworksWifiTests.apk) | head -n 1)"
+
 adb root
 adb wait-for-device
 
 adb shell rm -f $REMOTE_COVERAGE_OUTPUT_FILE
 
-adb install -r -g "$OUT/data/app/FrameworksWifiTests/FrameworksWifiTests.apk"
+adb install -r -g "$APK_NAME"
 
 adb shell am instrument -e coverage true --no-hidden-api-checks -w 'com.android.server.wifi.test/com.android.server.wifi.CustomTestRunner'
 
diff --git a/tests/wifitests/runtests.sh b/tests/wifitests/runtests.sh
index 7b25183..6e83199 100755
--- a/tests/wifitests/runtests.sh
+++ b/tests/wifitests/runtests.sh
@@ -32,13 +32,14 @@
 # NOTE Don't actually run the command above since this shell doesn't inherit functions from the
 #      caller.
 $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode MODULES-IN-frameworks-opt-net-wifi-tests
+APK_NAME="$(ls -t $(find $OUT -name FrameworksWifiTests.apk) | head -n 1)"
 
 set -x # print commands
 
 adb root
 adb wait-for-device
 
-adb install -r -g "$OUT/testcases/FrameworksWifiTests/arm64/FrameworksWifiTests.apk"
+adb install -r -g "$APK_NAME"
 
 adb shell am instrument --no-hidden-api-checks -w "$@" \
   'com.android.server.wifi.test/com.android.server.wifi.CustomTestRunner'
diff --git a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
index 3f1478f..f52229c 100644
--- a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
@@ -438,6 +438,22 @@
     }
 
     /**
+     * Verifies that SoftApStateChanged event isn't passed to WifiServiceImpl for LOHS,
+     * so the state change for LOHS doesn't affect Wifi Tethering indication.
+     */
+    @Test
+    public void doesntCallWifiServiceCallbackOnLOHSStateChanged() throws Exception {
+        enterSoftApActiveMode(new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_LOCAL_ONLY, null));
+
+        mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
+        mLooper.dispatchAll();
+
+        verify(mSoftApStateMachineCallback, never()).onStateChanged(anyInt(), anyInt());
+        verify(mSoftApStateMachineCallback, never()).onNumClientsChanged(anyInt());
+    }
+
+    /**
      * Verifies that triggering a state change update will not crash if the callback to
      * WifiServiceImpl is null.
      */
diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
index 6e731e6..505e691 100644
--- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkConfigTest.java
@@ -80,14 +80,17 @@
      *
      * @param ssid The SSID of the carrier network
      * @param eapType The EAP type of the carrier network
+     * @param encodingMethod base64 encoding method
      * @return {@link PersistableBundle} containing carrier config
      */
-    private PersistableBundle generateTestConfig(String ssid, int eapType) {
+    private PersistableBundle generateTestConfig(String ssid, int eapType, int encodingMethod) {
         PersistableBundle bundle = new PersistableBundle();
         String networkConfig =
                 new String(Base64.encode(ssid.getBytes(), Base64.DEFAULT)) + "," + eapType;
         bundle.putStringArray(CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY,
                 new String[] {networkConfig});
+        bundle.putInt(CarrierConfigManager.KEY_IMSI_ENCODING_METHOD_INT, encodingMethod);
+
         return bundle;
     }
 
@@ -103,7 +106,8 @@
                 .thenReturn(mSubscriptionManager);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
         when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID))
-                .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE));
+                .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE,
+                        CarrierNetworkConfig.ENCODING_METHOD_RFC_2045));
         when(mSubscriptionManager.getActiveSubscriptionInfoList())
                 .thenReturn(Arrays.asList(new SubscriptionInfo[] {TEST_SUBSCRIPTION_INFO}));
         when(mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN))
@@ -135,6 +139,7 @@
         assertTrue(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID));
         assertEquals(TEST_INTERNAL_EAP_TYPE, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID));
         assertEquals(TEST_CARRIER_NAME, mCarrierNetworkConfig.getCarrierName(TEST_SSID));
+        assertEquals(Base64.DEFAULT, mCarrierNetworkConfig.getBase64EncodingFlag());
     }
 
     /**
@@ -198,7 +203,8 @@
         when(mSubscriptionManager.getActiveSubscriptionInfoList())
                 .thenReturn(Arrays.asList(new SubscriptionInfo[] {updatedSubscriptionInfo}));
         when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID))
-                .thenReturn(generateTestConfig(updatedSsid, updatedStandardEapType));
+                .thenReturn(generateTestConfig(updatedSsid, updatedStandardEapType,
+                        CarrierNetworkConfig.ENCODING_METHOD_RFC_2045));
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
 
@@ -206,11 +212,13 @@
         assertFalse(mCarrierNetworkConfig.isCarrierNetwork(TEST_SSID));
         assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(TEST_SSID));
         assertEquals(null, mCarrierNetworkConfig.getCarrierName(TEST_SSID));
+        assertEquals(Base64.DEFAULT, mCarrierNetworkConfig.getBase64EncodingFlag());
 
         // Verify that updated SSID is associated with a carrier network.
         assertTrue(mCarrierNetworkConfig.isCarrierNetwork(updatedSsid));
         assertEquals(updatedInternalEapType, mCarrierNetworkConfig.getNetworkEapType(updatedSsid));
         assertEquals(updatedCarrierName, mCarrierNetworkConfig.getCarrierName(updatedSsid));
+        assertEquals(Base64.DEFAULT, mCarrierNetworkConfig.getBase64EncodingFlag());
     }
 
     /**
@@ -242,4 +250,36 @@
         mContentObserver.onChange(false);
         assertTrue(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable());
     }
+
+    /**
+     * Verify that base64Encoding type should be {@link Base64#NO_WRAP} when carrier configuration
+     * defines RFC4648 for encoding method.
+     */
+    @Test
+    public void verifyBase64EncodingTypeWithRfc4648() {
+        when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID))
+                .thenReturn(generateTestConfig(TEST_SSID, TEST_STANDARD_EAP_TYPE,
+                        CarrierNetworkConfig.ENCODING_METHOD_RFC_4648));
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        assertEquals(Base64.NO_WRAP, mCarrierNetworkConfig.getBase64EncodingFlag());
+    }
+
+    /**
+     * Verify that carrier network config is not generated when carrier configuration defines
+     * unsupported encoding method.
+     */
+    @Test
+    public void verifyBase64EncodingTypeWithUnsupportedEncodingMethod() {
+        String ssid = "invalid carrier AP";
+        when(mCarrierConfigManager.getConfigForSubId(TEST_SUBSCRIPTION_ID))
+                .thenReturn(generateTestConfig(ssid, TEST_STANDARD_EAP_TYPE, 123));
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+        assertFalse(mCarrierNetworkConfig.isCarrierNetwork(ssid));
+        assertEquals(-1, mCarrierNetworkConfig.getNetworkEapType(ssid));
+        assertEquals(null, mCarrierNetworkConfig.getCarrierName(ssid));
+        assertEquals(Base64.DEFAULT, mCarrierNetworkConfig.getBase64EncodingFlag());
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java
index ef8ceb1..0c61e17 100644
--- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkEvaluatorTest.java
@@ -122,8 +122,8 @@
 
     private AddOrUpdateNetworkAnswer mAddOrUpdateNetworkAnswer;
 
-    private void configureNewSsid(int networkId, ScanDetail scanDetail, boolean isEphemeral,
-            boolean isSaved) {
+    private WifiConfiguration configureNewSsid(int networkId, ScanDetail scanDetail,
+            boolean isEphemeral, boolean isSaved) {
         WifiConfiguration newConfig = ScanResultUtil.createNetworkFromScanResult(
                 scanDetail.getScanResult());
         assertTrue("" + newConfig, WifiConfigurationUtil.validate(newConfig, true));
@@ -140,6 +140,7 @@
                 anyInt())).thenReturn(true);
         when(mWifiConfigManager.getConfiguredNetwork(networkId)).thenReturn(newConfig);
         mAddOrUpdateNetworkAnswer.addConfig(newConfig, networkId);
+        return newConfig;
     }
 
     /** Sets up test. */
@@ -334,4 +335,41 @@
         verify(mConnectableListener, never()).onConnectable(any(), any(), anyInt());
         assertNull(selected);
     }
+
+    /**
+     * One carrier Wi-Fi networks visible and cert installed but ssid is blacklisted.
+     *
+     * Desired behavior: no networks connectable or selected
+     */
+    @Test
+    public void testAvailableButBlacklisted() {
+        String[] ssids = {CARRIER1_SSID};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+        int[] freqs = {2470};
+        String[] caps = {"[WPA2-EAP-CCMP]"};
+        int[] levels = {10};
+
+        when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
+
+        List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(ssids, bssids,
+                freqs, caps, levels, mClock);
+        WifiConfiguration blacklisted =
+                configureNewSsid(CARRIER1_NET_ID, scanDetails.get(0), true, false);
+        blacklisted.getNetworkSelectionStatus()
+                .setNetworkSelectionStatus(
+                        WifiConfiguration.NetworkSelectionStatus
+                                .NETWORK_SELECTION_PERMANENTLY_DISABLED);
+        when(mWifiConfigManager.getConfiguredNetwork(eq(blacklisted.configKey())))
+                .thenReturn(blacklisted);
+        when(mWifiConfigManager.tryEnableNetwork(CARRIER1_NET_ID))
+                .thenReturn(false);
+
+        WifiConfiguration selected = mDut.evaluateNetworks(scanDetails, null, null, false, false,
+                mConnectableListener);
+        verify(mWifiConfigManager).getConfiguredNetwork(eq(blacklisted.configKey()));
+
+        verify(mConnectableListener, never()).onConnectable(any(), any(), anyInt());
+        assertNull(selected);
+    }
+
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java
index 34995a7..91a9a5d 100644
--- a/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/CarrierNetworkNotifierTest.java
@@ -51,6 +51,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wifi.nano.WifiMetricsProto;
 import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
 
 import org.junit.Before;
@@ -72,6 +73,7 @@
     private static final String TEST_SSID_2 = "Test SSID 2";
     private static final int MIN_RSSI_LEVEL = -127;
     private static final String CARRIER_NET_NOTIFIER_TAG = CarrierNetworkNotifier.TAG;
+    private static final int TEST_NETWORK_ID = 42;
 
     @Mock private Context mContext;
     @Mock private Resources mResources;
@@ -127,6 +129,8 @@
                 observerCaptor.capture());
         mContentObserver = observerCaptor.getValue();
         mNotificationController.handleScreenStateChanged(true);
+        when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt()))
+                .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
     }
 
     /**
@@ -686,6 +690,9 @@
 
         mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK));
 
+        verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER);
+
         ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mClientModeImpl).sendMessage(connectMessageCaptor.capture());
         Message connectMessage = connectMessageCaptor.getValue();
diff --git a/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java b/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java
new file mode 100644
index 0000000..8b1647f
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsCollectorTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static android.telephony.TelephonyManager.NETWORK_TYPE_CDMA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_GSM;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.validateMockitoUsage;
+
+import android.content.Context;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoTdscdma;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
+import android.telephony.CellSignalStrengthTdscdma;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.NetworkType;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.CellularLinkLayerStatsCollector}.
+ */
+@SmallTest
+public class CellularLinkLayerStatsCollectorTest {
+    private CellularLinkLayerStatsCollector mCollector;
+    private static final String TAG = "CellCollectorTest";
+    private static final int DBM_VAL = -110;
+    private static final int DB_VAL = -20;
+    private static final int DB_VAL_EVDO = 4;
+    private static final int SUBID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+    MockitoSession mMockingSession = null;
+    @Mock Context mContext;
+    @Mock TelephonyManager mTelephonyManager;
+    @Mock SubscriptionManager mSubscriptionManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE))
+                .thenReturn(mSubscriptionManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt()))
+                .thenReturn(mTelephonyManager);
+        mCollector = new CellularLinkLayerStatsCollector(mContext);
+        mMockingSession = mockitoSession().mockStatic(SubscriptionManager.class).startMocking();
+        when(SubscriptionManager.getDefaultDataSubscriptionId()).thenReturn(SUBID);
+        when(SubscriptionManager.getDefaultSubscriptionId()).thenReturn(SUBID);
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        validateMockitoUsage();
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private List<CellInfo> generateCellInfoList(@NetworkType int networkType) {
+        List<CellInfo> cil = new ArrayList<>();
+        int numCellInfo = 2;
+        for (int i = 0; i < numCellInfo; ++i) {
+            CellInfo ci;
+            if (networkType == NETWORK_TYPE_LTE) {
+                ci = new CellInfoLte();
+            } else if (networkType == NETWORK_TYPE_CDMA || networkType == NETWORK_TYPE_EVDO_0) {
+                ci = new CellInfoCdma();
+            } else if (networkType == NETWORK_TYPE_GSM) {
+                ci = new CellInfoGsm();
+            } else if (networkType == NETWORK_TYPE_TD_SCDMA) {
+                ci = new CellInfoTdscdma();
+            } else if (networkType == NETWORK_TYPE_UMTS) {
+                ci = new CellInfoWcdma();
+            } else if (networkType == NETWORK_TYPE_NR) {
+                // TODO: CellInfoNr() is not supported yet.
+                ci = new CellInfoLte();
+            } else {
+                ci = new CellInfoLte();
+            }
+            if (i == 0 && networkType != NETWORK_TYPE_UNKNOWN) {
+                ci.setRegistered(true);
+            } else {
+                ci.setRegistered(false);
+            }
+            cil.add(ci);
+        }
+        return cil;
+    }
+
+    private SignalStrength generateSignalStrength(int dBmVal, int dBVal,
+                @NetworkType int networkType) {
+        int dummy = 1000;
+        CellSignalStrengthLte mLte = new CellSignalStrengthLte();
+        CellSignalStrengthNr mNr = new CellSignalStrengthNr();
+        CellSignalStrengthGsm mGsm = new CellSignalStrengthGsm();
+        CellSignalStrengthCdma mCdma = new CellSignalStrengthCdma();
+        CellSignalStrengthTdscdma mTdscdma = new CellSignalStrengthTdscdma();
+        CellSignalStrengthWcdma mWcdma = new CellSignalStrengthWcdma();
+
+        if (networkType == NETWORK_TYPE_UNKNOWN) {
+            return new SignalStrength();
+        } else if (networkType == NETWORK_TYPE_LTE) {
+            mLte = new CellSignalStrengthLte(dummy, dBmVal, dBVal, dummy, dummy, dummy);
+        } else if (networkType == NETWORK_TYPE_CDMA) {
+            mCdma = new CellSignalStrengthCdma(dBmVal, dBVal, dBmVal, dummy,
+                    SignalStrength.INVALID);
+        } else if (networkType == NETWORK_TYPE_EVDO_0) {
+            mCdma = new CellSignalStrengthCdma(dBmVal, dummy, dBmVal, dummy, dBVal);
+        } else if (networkType == NETWORK_TYPE_TD_SCDMA) {
+            mTdscdma = new CellSignalStrengthTdscdma(dummy, dummy, dBmVal);
+        } else if (networkType == NETWORK_TYPE_UMTS) {
+            mWcdma = new CellSignalStrengthWcdma(dummy, dummy, dBmVal, dBVal);
+        } else if (networkType == NETWORK_TYPE_GSM) {
+            mGsm = new CellSignalStrengthGsm(dBmVal, dummy, dummy);
+        } else if (networkType == NETWORK_TYPE_NR) {
+            mNr = new CellSignalStrengthNr(dBmVal, dummy, dBVal, dummy, dummy, dummy);
+        } else {
+            return null;
+        }
+        return new SignalStrength(mCdma, mGsm, mWcdma, mTdscdma, mLte, mNr);
+    }
+
+    private void testCollectorUpdate(@NetworkType int networkType, boolean isSignalStrengthEmpty,
+                CellularLinkLayerStats trueStats) throws Exception {
+        int dBmVal = DBM_VAL;
+        int dBVal;
+        if (networkType == NETWORK_TYPE_EVDO_0) {
+            dBVal = DB_VAL_EVDO;
+        } else {
+            dBVal = DB_VAL;
+        }
+
+        SignalStrength ss = null;
+        if (!isSignalStrengthEmpty) ss = generateSignalStrength(dBmVal, dBVal, networkType);
+        List<CellInfo> allList = generateCellInfoList(networkType);
+        when(mTelephonyManager.getSignalStrength()).thenReturn(ss);
+        when(mTelephonyManager.getAllCellInfo()).thenReturn(allList);
+        when(mTelephonyManager.getDataNetworkType()).thenReturn(networkType);
+
+        CellularLinkLayerStats mStats = mCollector.update();
+
+        assertEquals(SUBID, SubscriptionManager.getDefaultDataSubscriptionId());
+        assertEquals(SUBID, SubscriptionManager.getDefaultSubscriptionId());
+
+        assertEquals(trueStats.getSignalStrengthDbm(), mStats.getSignalStrengthDbm());
+        assertEquals(trueStats.getSignalStrengthDb(), mStats.getSignalStrengthDb());
+        assertEquals(trueStats.getDataNetworkType(), mStats.getDataNetworkType());
+        assertEquals(trueStats.getIsSameRegisteredCell(), mStats.getIsSameRegisteredCell());
+    }
+
+    @Test
+    public void testEmptySignalStrengthLte() throws Exception {
+        @NetworkType int networkType;
+        CellularLinkLayerStats trueStats = new CellularLinkLayerStats();
+
+        networkType = NETWORK_TYPE_LTE;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(SignalStrength.INVALID);
+        trueStats.setSignalStrengthDbm(SignalStrength.INVALID);
+        trueStats.setDataNetworkType(NETWORK_TYPE_UNKNOWN);
+        testCollectorUpdate(networkType, true, trueStats);
+    }
+
+    @Test
+    public void testRepeatCellInfoTypeTwice() throws Exception {
+        @NetworkType int networkType;
+        CellularLinkLayerStats trueStats = new CellularLinkLayerStats();
+
+        networkType = NETWORK_TYPE_LTE;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_LTE;
+        trueStats.setIsSameRegisteredCell(true);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_EVDO_0;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL_EVDO);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_EVDO_0;
+        trueStats.setIsSameRegisteredCell(true);
+        trueStats.setSignalStrengthDb(DB_VAL_EVDO);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_NR;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_NR;
+        trueStats.setIsSameRegisteredCell(true);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+    }
+
+    @Test
+    public void testLoopOverAllNetworksWithoutRepeat() throws Exception {
+        @NetworkType int networkType;
+        CellularLinkLayerStats trueStats = new CellularLinkLayerStats();
+
+        networkType = NETWORK_TYPE_UNKNOWN;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(SignalStrength.INVALID);
+        trueStats.setSignalStrengthDbm(SignalStrength.INVALID);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_LTE;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_CDMA;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_EVDO_0;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL_EVDO);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_TD_SCDMA;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(SignalStrength.INVALID);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_UMTS;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_GSM;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(SignalStrength.INVALID);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+        networkType = NETWORK_TYPE_NR;
+        trueStats.setIsSameRegisteredCell(false);
+        trueStats.setSignalStrengthDb(DB_VAL);
+        trueStats.setSignalStrengthDbm(DBM_VAL);
+        trueStats.setDataNetworkType(networkType);
+        testCollectorUpdate(networkType, false, trueStats);
+
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java b/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java
new file mode 100644
index 0000000..712d653
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/CellularLinkLayerStatsTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.TelephonyManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.CellularLinkLayerStats}.
+ */
+@SmallTest
+public class CellularLinkLayerStatsTest {
+    private static final String TAG = "CellularStatsTest";
+
+    CellularLinkLayerStats mStats;
+
+    /**
+     * Sets up for unit test
+     */
+    @Before
+    public void setUp() throws Exception {
+        mStats = new CellularLinkLayerStats();
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+    }
+
+    /**
+     * Test all set and get methods by checking if the inputs of set() match the output of get()
+     */
+    @Test
+    public void testAllSetGetMethods() throws Exception {
+        int dataNetworkType = TelephonyManager.NETWORK_TYPE_GSM;
+        mStats.setDataNetworkType(dataNetworkType);
+        assertEquals(dataNetworkType, mStats.getDataNetworkType());
+        int dbmVal = -100;
+        mStats.setSignalStrengthDbm(dbmVal);
+        assertEquals(dbmVal, mStats.getSignalStrengthDbm());
+        int dbVal = -20;
+        mStats.setSignalStrengthDb(dbVal);
+        assertEquals(dbVal, mStats.getSignalStrengthDb());
+        boolean isSameCell = true;
+        mStats.setIsSameRegisteredCell(isSameCell);
+        assertEquals(isSameCell, mStats.getIsSameRegisteredCell());
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index 86fb45f..5908bbc 100644
--- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -140,6 +140,7 @@
                     ? ClientModeImpl.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
                     : ClientModeImpl.NUM_LOG_RECS_VERBOSE);
     private static final int FRAMEWORK_NETWORK_ID = 0;
+    private static final int PASSPOINT_NETWORK_ID = 1;
     private static final int TEST_RSSI = -54;
     private static final int TEST_NETWORK_ID = 54;
     private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
@@ -377,6 +378,7 @@
     @Mock LinkProbeManager mLinkProbeManager;
     @Mock PackageManager mPackageManager;
     @Mock WifiLockManager mWifiLockManager;
+    @Mock AsyncChannel mNullAsyncChannel;
 
     final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor =
             ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
@@ -490,6 +492,7 @@
 
         mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
         mConnectedNetwork = spy(WifiConfigurationTestUtil.createOpenNetwork());
+        when(mNullAsyncChannel.sendMessageSynchronously(any())).thenReturn(null);
     }
 
     private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger) {
@@ -530,7 +533,7 @@
         mBinderToken = Binder.clearCallingIdentity();
 
         /* Send the BOOT_COMPLETED message to setup some CMI state. */
-        mCmi.sendMessage(ClientModeImpl.CMD_BOOT_COMPLETED);
+        mCmi.handleBootCompleted();
         mLooper.dispatchAll();
 
         verify(mWifiNetworkFactory, atLeastOnce()).register();
@@ -560,7 +563,7 @@
     public void createNew() throws Exception {
         assertEquals("DefaultState", getCurrentState().getName());
 
-        mCmi.sendMessage(ClientModeImpl.CMD_BOOT_COMPLETED);
+        mCmi.handleBootCompleted();
         mLooper.dispatchAll();
         assertEquals("DefaultState", getCurrentState().getName());
     }
@@ -1027,6 +1030,39 @@
     }
 
     /**
+     * Tests that Passpoint fields in WifiInfo are reset when connecting to a non-Passpoint network
+     * during DisconnectedState.
+     * @throws Exception
+     */
+    @Test
+    public void testResetWifiInfoPasspointFields() throws Exception {
+        loadComponentsInStaMode();
+        WifiConfiguration config = spy(WifiConfigurationTestUtil.createPasspointNetwork());
+        config.SSID = sWifiSsid.toString();
+        config.BSSID = sBSSID;
+        config.networkId = PASSPOINT_NETWORK_ID;
+        when(config.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS);
+        config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+        setupAndStartConnectSequence(config);
+        validateSuccessfulConnectSequence(config);
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(PASSPOINT_NETWORK_ID, sWifiSsid, sBSSID,
+                        SupplicantState.ASSOCIATING));
+        mLooper.dispatchAll();
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID,
+                        SupplicantState.ASSOCIATING));
+        mLooper.dispatchAll();
+
+        WifiInfo wifiInfo = mCmi.getWifiInfo();
+        assertNotNull(wifiInfo);
+        assertNull(wifiInfo.getPasspointFqdn());
+        assertNull(wifiInfo.getPasspointProviderFriendlyName());
+    }
+
+    /**
      * Tests the OSU information is set in WifiInfo for OSU AP connection.
      */
     @Test
@@ -1056,6 +1092,40 @@
     }
 
     /**
+     * Tests that OSU fields in WifiInfo are reset when connecting to a non-OSU network during
+     * DisconnectedState.
+     * @throws Exception
+     */
+    @Test
+    public void testResetWifiInfoOsuFields() throws Exception {
+        loadComponentsInStaMode();
+        WifiConfiguration osuConfig = spy(WifiConfigurationTestUtil.createEphemeralNetwork());
+        osuConfig.SSID = sWifiSsid.toString();
+        osuConfig.BSSID = sBSSID;
+        osuConfig.osu = true;
+        osuConfig.networkId = PASSPOINT_NETWORK_ID;
+        osuConfig.providerFriendlyName = WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME;
+        when(osuConfig.getOrCreateRandomizedMacAddress()).thenReturn(TEST_LOCAL_MAC_ADDRESS);
+        osuConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+        setupAndStartConnectSequence(osuConfig);
+        validateSuccessfulConnectSequence(osuConfig);
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(PASSPOINT_NETWORK_ID, sWifiSsid, sBSSID,
+                        SupplicantState.ASSOCIATING));
+        mLooper.dispatchAll();
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(FRAMEWORK_NETWORK_ID, sWifiSsid, sBSSID,
+                        SupplicantState.ASSOCIATING));
+        mLooper.dispatchAll();
+
+        WifiInfo wifiInfo = mCmi.getWifiInfo();
+        assertNotNull(wifiInfo);
+        assertFalse(wifiInfo.isOsuAp());
+    }
+
+    /**
      * Verify that WifiStateTracker is called if wifi is disabled while connected.
      */
     @Test
@@ -1173,6 +1243,30 @@
         assertEquals(WifiManager.CONNECT_NETWORK_SUCCEEDED, reply.what);
     }
 
+    /**
+     * Tests that manual connection to a network (from settings app) logs the correct nominator ID.
+     */
+    @Test
+    public void testManualConnectNominator() throws Exception {
+        initializeAndAddNetworkAndVerifySuccess();
+        Message msg = Message.obtain();
+        msg.what = WifiManager.CONNECT_NETWORK;
+        msg.arg1 = TEST_NETWORK_ID;
+        msg.obj = null;
+        msg.sendingUid = Process.SYSTEM_UID;
+
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = TEST_NETWORK_ID;
+
+        when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config);
+
+        mCmi.sendMessage(msg);
+        mLooper.dispatchAll();
+
+        verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
+    }
+
     @Test
     public void testDhcpFailure() throws Exception {
         initializeAndAddNetworkAndVerifySuccess();
@@ -3311,38 +3405,56 @@
     }
 
     /**
-     * Verifies the handling of disconnect initiated from API surface when connected to a network.
+     * Verify that a WifiIsUnusableEvent is logged and the current list of usability stats entries
+     * are labeled and saved when receiving an IP reachability lost message.
+     * @throws Exception
      */
     @Test
-    public void testExternalDisconnectWhenConnected() throws Exception {
+    public void verifyIpReachabilityLostMsgUpdatesWifiUsabilityMetrics() throws Exception {
         connect();
 
-        mCmi.disconnectCommandExternal(); // Simulate settings invoking this.
+        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST);
         mLooper.dispatchAll();
-
-        verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
-        verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
-                StaEvent.DISCONNECT_API);
-        // verify that we temp blacklist the network.
-        verify(mWifiConfigManager).updateNetworkSelectionStatus(0,
-                WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT);
+        verify(mWifiMetrics).logWifiIsUnusableEvent(
+                WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
+        verify(mWifiMetrics).addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST);
     }
 
     /**
-     * Verifies the handling of disconnect initiated internally when connected to a network.
+     * Verify that syncGetAllMatchingFqdnsForScanResults does not return null from a null message.
      */
     @Test
-    public void testInternalDisconnectWhenConnected() throws Exception {
-        connect();
+    public void testSyncGetAllMatchingFqdnsForScanResult_doesNotReturnNull() {
+        assertNotNull(
+                mCmi.syncGetAllMatchingFqdnsForScanResults(null, mNullAsyncChannel));
+    }
 
-        mCmi.disconnectCommandInternal(); // Internal stack initiated disconnect.
-        mLooper.dispatchAll();
+    /**
+     * Verify that syncGetMatchingOsuProviders does not return null from a null message.
+     */
+    @Test
+    public void testSyncGetMatchingOsuProviders_doesNotReturnNull() {
+        assertNotNull(
+                mCmi.syncGetMatchingOsuProviders(null, mNullAsyncChannel));
+    }
 
-        verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
-        verify(mWifiMetrics).logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
-                StaEvent.DISCONNECT_GENERIC);
-        // verify that we don't temp blacklist the network.
-        verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(0,
-                WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER_DISCONNECT);
+    /**
+     * Verify that syncGetMatchingPasspointConfigsForOsuProviders does not return null from a null
+     * message.
+     */
+    @Test
+    public void testSyncGetMatchingPasspointConfigsForOsuProviders_doesNotReturnNull() {
+        assertNotNull(
+                mCmi.syncGetMatchingPasspointConfigsForOsuProviders(null, mNullAsyncChannel));
+    }
+
+    /**
+     * Verify that syncGetWifiConfigsForPasspointProfiles does not return null from a null message.
+     */
+    @Test
+    public void testSyncGetWifiConfigsForPasspointProfiles_doesNotReturnNull() {
+        assertNotNull(
+                mCmi.syncGetWifiConfigsForPasspointProfiles(null, mNullAsyncChannel));
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java
index 4f494c0..fadfc6b 100644
--- a/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionEvaluatorTest.java
@@ -125,7 +125,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -148,7 +148,7 @@
         assertNotNull(candidate);
         assertEquals(suggestionSsids[0] , candidate.SSID);
 
-        validateConnectableNetworks(connectableNetworks, new String[] {scanSsids[0]});
+        validateConnectableNetworks(connectableNetworks, scanSsids[0]);
 
         verifyAddToWifiConfigManager(suggestions[0].wifiConfiguration);
     }
@@ -170,7 +170,7 @@
         int[] securities = {SECURITY_PSK, SECURITY_PSK};
         boolean[] appInteractions = {true, true};
         boolean[] meteredness = {true, true};
-        int[] priorities = {0, 1};
+        int[] priorities = {-1, -1};
         int[] uids = {TEST_UID, TEST_UID};
         String[] packageNames = {TEST_PACKAGE, TEST_PACKAGE};
 
@@ -194,14 +194,60 @@
         assertNotNull(candidate);
         assertEquals(suggestionSsids[1] , candidate.SSID);
 
-        validateConnectableNetworks(connectableNetworks, scanSsids);
+        validateConnectableNetworks(connectableNetworks, scanSsids[0], scanSsids[1]);
 
-        verifyAddToWifiConfigManager(suggestions[0].wifiConfiguration,
+        verifyAddToWifiConfigManager(suggestions[1].wifiConfiguration,
                 suggestions[1].wifiConfiguration);
     }
 
     /**
      * Ensure that we select the network suggestion corresponding to the scan result with
+     * higest priority.
+     * Expected candidate: suggestionSsids[0]
+     * Expected connectable Networks: {suggestionSsids[0], suggestionSsids[1]}
+     */
+    @Test
+    public void testSelectNetworkSuggestionForMultipleMatchHighPriorityWins() {
+        String[] scanSsids = {"test1", "test2"};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+        int[] freqs = {2470, 2437};
+        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+        int[] levels = {-56, -45};
+        String[] suggestionSsids = {"\"" + scanSsids[0] + "\"", "\"" + scanSsids[1] + "\""};
+        int[] securities = {SECURITY_PSK, SECURITY_PSK};
+        boolean[] appInteractions = {true, true};
+        boolean[] meteredness = {true, true};
+        int[] priorities = {5, 1};
+        int[] uids = {TEST_UID, TEST_UID};
+        String[] packageNames = {TEST_PACKAGE, TEST_PACKAGE};
+
+        ScanDetail[] scanDetails =
+                buildScanDetails(scanSsids, bssids, freqs, caps, levels, mClock);
+        WifiNetworkSuggestion[] suggestions = buildNetworkSuggestions(suggestionSsids, securities,
+                appInteractions, meteredness, priorities, uids, packageNames);
+        // Link the scan result with suggestions.
+        linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions);
+        // setup config manager interactions.
+        setupAddToWifiConfigManager(suggestions[0].wifiConfiguration,
+                suggestions[1].wifiConfiguration);
+
+        List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+        WifiConfiguration candidate = mNetworkSuggestionEvaluator.evaluateNetworks(
+                Arrays.asList(scanDetails), null, null, true, false,
+                (ScanDetail scanDetail, WifiConfiguration configuration, int score) -> {
+                    connectableNetworks.add(Pair.create(scanDetail, configuration));
+                });
+
+        assertNotNull(candidate);
+        assertEquals(suggestionSsids[0] , candidate.SSID);
+
+        validateConnectableNetworks(connectableNetworks, scanSsids[0]);
+
+        verifyAddToWifiConfigManager(suggestions[0].wifiConfiguration);
+    }
+
+    /**
+     * Ensure that we select the network suggestion corresponding to the scan result with
      * highest RSSI. The lower RSSI scan result has multiple matching suggestions
      * (should pick any one in the connectable networks).
      *
@@ -221,7 +267,7 @@
         int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
         boolean[] appInteractions = {true, true, false};
         boolean[] meteredness = {true, true, false};
-        int[] priorities = {0, 1, 0};
+        int[] priorities = {-1, -1, -1};
         int[] uids = {TEST_UID, TEST_UID, TEST_UID_OTHER};
         String[] packageNames = {TEST_PACKAGE, TEST_PACKAGE, TEST_PACKAGE_OTHER};
 
@@ -252,6 +298,66 @@
     }
 
     /**
+     * Ensure that we select the network suggestion with the higest priority among network
+     * suggestions from the same package. Among different packages, pick the suggestion
+     * corresponding to the scan result with highest RSSI.
+     *
+     * The suggestion[1] has higher priority than suggestion[0] even though it has lower RSSI than
+     * suggestion[0].
+     *
+     * Expected candidate: suggestionSsids[1]
+     * Expected connectable Networks: {suggestionSsids[1],
+     *                                 (suggestionSsids[2],
+     *                                  suggestionSsids[3]}
+     */
+    @Test
+    public void
+            testSelectNetworkSuggestionForMultipleMatchWithMultipleSuggestionsHighPriorityWins() {
+        String[] scanSsids = {"test1", "test2", "test3", "test4"};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:fc:de:34:12",
+                "6c:fd:a1:11:11:98"};
+        int[] freqs = {2470, 2437, 2470, 2437};
+        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
+                "[WPA2-EAP-CCMP][ESS]"};
+        int[] levels = {-23, -45, -56, -65};
+        String[] suggestionSsids = {"\"" + scanSsids[0] + "\"", "\"" + scanSsids[1] + "\"",
+                "\"" + scanSsids[2] + "\"", "\"" + scanSsids[3] + "\""};
+        int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
+        boolean[] appInteractions = {true, true, false, false};
+        boolean[] meteredness = {true, true, false, false};
+        int[] priorities = {0, 5, -1, -1};
+        int[] uids = {TEST_UID, TEST_UID, TEST_UID_OTHER, TEST_UID_OTHER};
+        String[] packageNames = {TEST_PACKAGE, TEST_PACKAGE, TEST_PACKAGE_OTHER,
+                TEST_PACKAGE_OTHER};
+
+        ScanDetail[] scanDetails =
+                buildScanDetails(scanSsids, bssids, freqs, caps, levels, mClock);
+        WifiNetworkSuggestion[] suggestions = buildNetworkSuggestions(suggestionSsids, securities,
+                appInteractions, meteredness, priorities, uids, packageNames);
+        // Link the scan result with suggestions.
+        linkScanDetailsWithNetworkSuggestions(scanDetails, suggestions);
+        // setup config manager interactions.
+        setupAddToWifiConfigManager(suggestions[0].wifiConfiguration,
+                suggestions[1].wifiConfiguration, suggestions[2].wifiConfiguration,
+                suggestions[3].wifiConfiguration);
+
+        List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+        WifiConfiguration candidate = mNetworkSuggestionEvaluator.evaluateNetworks(
+                Arrays.asList(scanDetails), null, null, true, false,
+                (ScanDetail scanDetail, WifiConfiguration configuration, int score) -> {
+                    connectableNetworks.add(Pair.create(scanDetail, configuration));
+                });
+
+        assertNotNull(candidate);
+        assertEquals(suggestionSsids[1] , candidate.SSID);
+
+        validateConnectableNetworks(connectableNetworks, scanSsids[1], scanSsids[2], scanSsids[3]);
+
+        verifyAddToWifiConfigManager(suggestions[1].wifiConfiguration,
+                suggestions[2].wifiConfiguration, suggestions[3].wifiConfiguration);
+    }
+
+    /**
      * Ensure that we select the only matching network suggestion, but return null because
      * we failed the {@link WifiConfigManager} interactions.
      * Expected candidate: null.
@@ -268,7 +374,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -292,7 +398,7 @@
         assertNull(candidate);
         assertTrue(connectableNetworks.isEmpty());
 
-        verify(mWifiConfigManager, times(suggestionSsids.length))
+        verify(mWifiConfigManager, times(scanSsids.length))
                 .wasEphemeralNetworkDeleted(anyString());
         verify(mWifiConfigManager).getConfiguredNetwork(eq(
                 suggestions[0].wifiConfiguration.configKey()));
@@ -319,7 +425,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -346,7 +452,7 @@
         validateConnectableNetworks(connectableNetworks, new String[] {scanSsids[0]});
 
         // check for any saved networks.
-        verify(mWifiConfigManager, times(suggestionSsids.length))
+        verify(mWifiConfigManager, times(scanSsids.length))
                 .wasEphemeralNetworkDeleted(anyString());
         verify(mWifiConfigManager).getConfiguredNetwork(candidate.configKey());
         // Verify we did not try to add any new networks or other interactions with
@@ -371,7 +477,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -396,7 +502,7 @@
         assertNull(candidate);
         assertTrue(connectableNetworks.isEmpty());
 
-        verify(mWifiConfigManager, times(suggestionSsids.length))
+        verify(mWifiConfigManager, times(scanSsids.length))
                 .wasEphemeralNetworkDeleted(anyString());
         // Verify we did not try to add any new networks or other interactions with
         // WifiConfigManager.
@@ -421,7 +527,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -450,7 +556,7 @@
         assertNull(candidate);
         assertTrue(connectableNetworks.isEmpty());
 
-        verify(mWifiConfigManager, times(suggestionSsids.length))
+        verify(mWifiConfigManager, times(scanSsids.length))
                 .wasEphemeralNetworkDeleted(anyString());
         verify(mWifiConfigManager).getConfiguredNetwork(eq(
                 suggestions[0].wifiConfiguration.configKey()));
@@ -479,7 +585,7 @@
         int[] securities = {SECURITY_PSK};
         boolean[] appInteractions = {true};
         boolean[] meteredness = {true};
-        int[] priorities = {0};
+        int[] priorities = {-1};
         int[] uids = {TEST_UID};
         String[] packageNames = {TEST_PACKAGE};
 
@@ -512,7 +618,7 @@
 
         validateConnectableNetworks(connectableNetworks, new String[] {scanSsids[0]});
 
-        verify(mWifiConfigManager, times(suggestionSsids.length))
+        verify(mWifiConfigManager, times(scanSsids.length))
                 .wasEphemeralNetworkDeleted(anyString());
         verify(mWifiConfigManager).getConfiguredNetwork(eq(
                 suggestions[0].wifiConfiguration.configKey()));
@@ -555,7 +661,7 @@
 
     private void verifyAddToWifiConfigManager(WifiConfiguration...candidates) {
         // check for any saved networks.
-        verify(mWifiConfigManager, times(candidates.length)).getConfiguredNetwork(anyString());
+        verify(mWifiConfigManager, atLeast(candidates.length)).getConfiguredNetwork(anyString());
 
         ArgumentCaptor<WifiConfiguration> wifiConfigurationCaptor =
                 ArgumentCaptor.forClass(WifiConfiguration.class);
@@ -675,8 +781,8 @@
     }
 
     private void validateConnectableNetworks(List<Pair<ScanDetail, WifiConfiguration>> actual,
-                                             String[] expectedSsids) {
-        Set<String> expectedSsidSet = new HashSet<String>(Arrays.asList(expectedSsids));
+                                             String...expectedSsids) {
+        Set<String> expectedSsidSet = new HashSet<>(Arrays.asList(expectedSsids));
         assertEquals(expectedSsidSet.size(), actual.size());
 
         for (Pair<ScanDetail, WifiConfiguration> candidate : actual) {
diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
index e165dcf..5e1e728 100644
--- a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
@@ -51,6 +51,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wifi.nano.WifiMetricsProto;
 import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount;
 
 import org.junit.Before;
@@ -72,6 +73,7 @@
     private static final String TEST_SSID_2 = "Test SSID 2";
     private static final int MIN_RSSI_LEVEL = -127;
     private static final String OPEN_NET_NOTIFIER_TAG = OpenNetworkNotifier.TAG;
+    private static final int TEST_NETWORK_ID = 42;
 
     @Mock private Context mContext;
     @Mock private Resources mResources;
@@ -126,6 +128,8 @@
                 observerCaptor.capture());
         mContentObserver = observerCaptor.getValue();
         mNotificationController.handleScreenStateChanged(true);
+        when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt()))
+                .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
     }
 
     /**
@@ -681,6 +685,9 @@
 
         mBroadcastReceiver.onReceive(mContext, createIntent(ACTION_CONNECT_TO_NETWORK));
 
+        verify(mWifiMetrics).setNominatorForNetwork(TEST_NETWORK_ID,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_OPEN_NETWORK_AVAILABLE);
+
         ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mClientModeImpl).sendMessage(connectMessageCaptor.capture());
         Message connectMessage = connectMessageCaptor.getValue();
diff --git a/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
index 4643876..d1de03c 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
@@ -75,6 +75,7 @@
     @Mock private WifiPermissionsUtil mWifiPermissionsUtil;
     @Mock private OnConnectableListener mOnConnectableListener;
     @Captor private ArgumentCaptor<NetworkKey[]> mNetworkKeyArrayCaptor;
+    @Captor private ArgumentCaptor<WifiConfiguration> mWifiConfigCaptor;
 
     private WifiNetworkScoreCache mScoreCache;
     private ScoredNetworkEvaluator mScoredNetworkEvaluator;
@@ -272,6 +273,51 @@
     }
 
     /**
+     * When we have created a new ephemeral network, make sure that mOnConnectableListener
+     * is called.
+     */
+    @Test
+    public void testEvaluateNetworks_newEphemeralNetworkMustBeReportedAsConnectable() {
+        String[] ssids = {"\"test1\"", "\"test2\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+        int[] freqs = {2470, 2437};
+        String[] caps = {"[WPA2-PSK][ESS]", "[ESS]"};
+        int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+        Integer[] scores = {null, 120};
+        boolean[] meteredHints = {false, false};
+
+        List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+                ssids, bssids, freqs, caps, levels, mClock);
+        WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+                scanDetails, scores, meteredHints);
+
+        ScanResult scanResult = scanDetails.get(1).getScanResult();
+        WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil
+                .setupEphemeralNetwork(mWifiConfigManager, 1, scanDetails.get(1), meteredHints[1]);
+
+        // No saved networks.
+        when(mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(any(ScanDetail.class)))
+                .thenReturn(null);
+
+        // But when we create one, this is should be it.
+        when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt()))
+                .thenReturn(new NetworkUpdateResult(1));
+
+        // Untrusted networks allowed.
+        WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+                null, null, false, true, mOnConnectableListener);
+
+        WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate);
+        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+                scanResult, candidate);
+        assertEquals(meteredHints[1], candidate.meteredHint);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
+    }
+
+    /**
      * When no saved networks available, choose the available ephemeral networks
      * if untrusted networks are allowed.
      */
@@ -306,6 +352,10 @@
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanResult, candidate);
         assertEquals(meteredHints[1], candidate.meteredHint);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -346,6 +396,10 @@
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanResults[1], candidate);
         assertEquals(meteredHints[1], candidate.meteredHint);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -411,6 +465,10 @@
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanDetails.get(0).getScanResult(), candidate);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -443,6 +501,10 @@
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanDetails.get(1).getScanResult(), candidate);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -476,6 +538,10 @@
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanDetails.get(0).getScanResult(), candidate);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -529,6 +595,10 @@
         WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate);
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 ephemeralScanResult, candidate);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -601,6 +671,10 @@
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanResults[1], candidate);
         assertEquals(meteredHints[1], candidate.meteredHint);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
     }
 
     /**
@@ -633,5 +707,10 @@
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 scanDetails.get(1).getScanResult(), candidate);
+        verify(mOnConnectableListener, atLeastOnce())
+                .onConnectable(any(), mWifiConfigCaptor.capture(), anyInt());
+        assertTrue(mWifiConfigCaptor.getAllValues().stream()
+                .anyMatch(c -> c.networkId == candidate.networkId));
+
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 2fe8590..d218089 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX;
-
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
@@ -129,6 +127,7 @@
     @Mock private RandomizedMacStoreData mRandomizedMacStoreData;
     @Mock private WifiConfigManager.OnSavedNetworkUpdateListener mWcmListener;
     @Mock private FrameworkFacade mFrameworkFacade;
+    @Mock private CarrierNetworkConfig mCarrierNetworkConfig;
 
     private MockResources mResources;
     private InOrder mContextConfigStoreMockOrder;
@@ -207,6 +206,7 @@
         when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
         when(mWifiInjector.getWifiLastResortWatchdog().shouldIgnoreSsidUpdate())
                 .thenReturn(false);
+        when(mWifiInjector.getCarrierNetworkConfig()).thenReturn(mCarrierNetworkConfig);
         createWifiConfigManager();
         mWifiConfigManager.setOnSavedNetworkUpdateListener(mWcmListener);
         ArgumentCaptor<ContentObserver> observerCaptor =
@@ -358,7 +358,7 @@
         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
         Map<String, String> randomizedMacAddressMapping = new HashMap<>();
         final String randMac = "12:23:34:45:56:67";
-        randomizedMacAddressMapping.put(openNetwork.configKey(), randMac);
+        randomizedMacAddressMapping.put(openNetwork.getSsidAndSecurityTypeString(), randMac);
         assertNotEquals(randMac, openNetwork.getRandomizedMacAddress());
         when(mRandomizedMacStoreData.getMacMapping()).thenReturn(randomizedMacAddressMapping);
 
@@ -3318,6 +3318,81 @@
     }
 
     /**
+     * Verifies that the store read after bootup received after
+     * a previous user unlock and user switch via {@link WifiConfigManager#handleUserSwitch(int)}
+     * results in a user store read.
+     */
+    @Test
+    public void testHandleBootupAfterPreviousUserUnlockAndSwitch() throws Exception {
+        int user1 = TEST_DEFAULT_USER;
+        int user2 = TEST_DEFAULT_USER + 1;
+        setupUserProfiles(user2);
+
+        // Unlock the user1 (default user) for the first time and ensure that we don't read the data
+        // (need to wait for loadFromStore invocation).
+        mWifiConfigManager.handleUserUnlock(user1);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+                .switchUserStoresAndRead(any(List.class));
+
+        // Switch from user1 to user2 and ensure that we don't read or write any data
+        // (need to wait for loadFromStore invocation).
+        mWifiConfigManager.handleUserSwitch(user2);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+                .switchUserStoresAndRead(any(List.class));
+
+        // Now load from the store.
+        assertTrue(mWifiConfigManager.loadFromStore());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
+
+        // Unlock the user2 and ensure that we read from the user store.
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
+        mWifiConfigManager.handleUserUnlock(user2);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+                .switchUserStoresAndRead(any(List.class));
+    }
+
+    /**
+     * Verifies that the store read after bootup received after
+     * a user switch and unlock of a previous user via {@link WifiConfigManager#
+     * handleUserSwitch(int)} results in a user store read.
+     */
+    @Test
+    public void testHandleBootupAfterUserSwitchAndPreviousUserUnlock() throws Exception {
+        int user1 = TEST_DEFAULT_USER;
+        int user2 = TEST_DEFAULT_USER + 1;
+        setupUserProfiles(user2);
+
+        // Switch from user1 to user2 and ensure that we don't read or write any data
+        // (need to wait for loadFromStore invocation).
+        mWifiConfigManager.handleUserSwitch(user2);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+                .switchUserStoresAndRead(any(List.class));
+
+        // Unlock the user1 for the first time and ensure that we don't read the data
+        mWifiConfigManager.handleUserUnlock(user1);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+                .switchUserStoresAndRead(any(List.class));
+
+        // Now load from the store.
+        assertTrue(mWifiConfigManager.loadFromStore());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
+
+        // Unlock the user2 and ensure that we read from the user store.
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
+        mWifiConfigManager.handleUserUnlock(user2);
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+                .switchUserStoresAndRead(any(List.class));
+    }
+
+    /**
      * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)} does
      * not always result in a store read unless the user had switched or just booted up.
      */
@@ -4980,7 +5055,7 @@
                     NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP,
                     retrievedDisableTime);
             verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
-        } else if (reason < NETWORK_SELECTION_DISABLED_PERMANENT_STARTING_INDEX) {
+        } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
             // For temporarily disabled networks, we need to ensure that the current status remains
             // until the threshold is crossed.
             assertEquals(temporaryDisableReasonCounter, retrievedDisableReasonCounter);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
index 1cc0926..e0acd1f 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wifi;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyString;
@@ -31,6 +31,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.Locale;
 
 /**
@@ -196,4 +198,28 @@
         mWifiCountryCode.setCountryCode(telephonyCountryCodeLower);
         verify(mWifiNative).setCountryCode(any(), eq(telephonyCountryCodeUpper));
     }
+    /**
+     * Verifies that dump() does not fail
+     */
+    @Test
+    public void dumpDoesNotFail() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        mWifiCountryCode = new WifiCountryCode(
+                null,
+                null,
+                false /* config_wifi_revert_country_code_on_cellular_loss */);
+
+        mWifiCountryCode.dump(null, pw, null);
+        String dumpCountryCodeStr = sw.toString();
+
+        assertTrue(dumpCountryCodeStr.contains("mDriverCountryCode"));
+        assertTrue(dumpCountryCodeStr.contains("mTelephonyCountryCode"));
+        assertTrue(dumpCountryCodeStr.contains("mDefaultCountryCode"));
+        assertTrue(dumpCountryCodeStr.contains("mTelephonyCountryTimestamp"));
+        assertTrue(dumpCountryCodeStr.contains("mDriverCountryTimestamp"));
+        assertTrue(dumpCountryCodeStr.contains("mReadyTimestamp"));
+        assertTrue(dumpCountryCodeStr.contains("mReady"));
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java
new file mode 100644
index 0000000..7079a2d
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.WifiEnterpriseConfig;
+import android.os.Process;
+import android.security.Credentials;
+import android.security.KeyStore;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigManager}.
+ */
+@SmallTest
+public class WifiKeyStoreTest {
+    @Mock private WifiEnterpriseConfig mWifiEnterpriseConfig;
+    @Mock private KeyStore mKeyStore;
+
+    private WifiKeyStore mWifiKeyStore;
+    private static final String USER_CERT_ALIAS = "aabbccddee";
+    private static final String [] USER_CA_CERT_ALIAS = {"aacccddd", "bbbqqqqmmm"};
+
+    /**
+     * Setup the mocks and an instance of WifiConfigManager before each test.
+     */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mWifiKeyStore = new WifiKeyStore(mKeyStore);
+
+        when(mWifiEnterpriseConfig.getClientCertificateAlias()).thenReturn(USER_CERT_ALIAS);
+        when(mWifiEnterpriseConfig.getCaCertificateAliases())
+                .thenReturn(USER_CA_CERT_ALIAS);
+    }
+
+    /**
+     * Called after each test
+     */
+    @After
+    public void cleanup() {
+        validateMockitoUsage();
+    }
+
+    /**
+     * Verifies that keys and certs are removed when they were installed by an app.
+     */
+    @Test
+    public void testRemoveKeysForAppInstalledCerts() {
+        when(mWifiEnterpriseConfig.isAppInstalledDeviceKeyAndCert()).thenReturn(true);
+        when(mWifiEnterpriseConfig.isAppInstalledCaCert()).thenReturn(true);
+        mWifiKeyStore.removeKeys(mWifiEnterpriseConfig);
+
+        // Method calls the KeyStore#delete method 4 times, user key, user cert, and 2 CA cert
+        verify(mKeyStore).delete(Credentials.USER_PRIVATE_KEY + USER_CERT_ALIAS, Process.WIFI_UID);
+        verify(mKeyStore).delete(Credentials.USER_CERTIFICATE + USER_CERT_ALIAS, Process.WIFI_UID);
+        verify(mKeyStore).delete(Credentials.CA_CERTIFICATE + USER_CA_CERT_ALIAS[0],
+                Process.WIFI_UID);
+        verify(mKeyStore).delete(Credentials.CA_CERTIFICATE + USER_CA_CERT_ALIAS[1],
+                Process.WIFI_UID);
+    }
+
+    /**
+     * Verifies that keys and certs are removed when they were installed by an app and not removed
+     * when CA certs are installed by the user.
+     */
+    @Test
+    public void testRemoveKeysForMixedInstalledCerts1() {
+        when(mWifiEnterpriseConfig.isAppInstalledDeviceKeyAndCert()).thenReturn(true);
+        when(mWifiEnterpriseConfig.isAppInstalledCaCert()).thenReturn(false);
+        mWifiKeyStore.removeKeys(mWifiEnterpriseConfig);
+
+        // Method calls the KeyStore#delete method 2 times: user key and user cert
+        verify(mKeyStore).delete(Credentials.USER_PRIVATE_KEY + USER_CERT_ALIAS, Process.WIFI_UID);
+        verify(mKeyStore).delete(Credentials.USER_CERTIFICATE + USER_CERT_ALIAS, Process.WIFI_UID);
+        verifyNoMoreInteractions(mKeyStore);
+    }
+
+    /**
+     * Verifies that keys and certs are not removed when they were installed by the user and
+     * removed when CA certs are installed by the app.
+     */
+    @Test
+    public void testRemoveKeysForMixedInstalledCerts2() {
+        when(mWifiEnterpriseConfig.isAppInstalledDeviceKeyAndCert()).thenReturn(false);
+        when(mWifiEnterpriseConfig.isAppInstalledCaCert()).thenReturn(true);
+        mWifiKeyStore.removeKeys(mWifiEnterpriseConfig);
+
+        // Method calls the KeyStore#delete method 2 times: 2 CA certs
+        verify(mKeyStore).delete(Credentials.CA_CERTIFICATE + USER_CA_CERT_ALIAS[0],
+                Process.WIFI_UID);
+        verify(mKeyStore).delete(Credentials.CA_CERTIFICATE + USER_CA_CERT_ALIAS[1],
+                Process.WIFI_UID);
+        verifyNoMoreInteractions(mKeyStore);
+    }
+
+    /**
+     * Verifies that keys and certs are not removed when they were installed by the user.
+     */
+    @Test
+    public void testRemoveKeysForUserInstalledCerts() {
+        when(mWifiEnterpriseConfig.isAppInstalledDeviceKeyAndCert()).thenReturn(false);
+        when(mWifiEnterpriseConfig.isAppInstalledCaCert()).thenReturn(false);
+        mWifiKeyStore.removeKeys(mWifiEnterpriseConfig);
+        verifyNoMoreInteractions(mKeyStore);
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
index 9284360..a51a7a8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
@@ -61,6 +61,7 @@
     private ActivityManager.OnUidImportanceListener mUidImportanceListener;
 
     WifiLockManager mWifiLockManager;
+    @Mock Clock mClock;
     @Mock IBatteryStats mBatteryStats;
     @Mock IBinder mBinder;
     @Mock IBinder mBinder2;
@@ -72,6 +73,7 @@
     @Mock ActivityManager mActivityManager;
     @Mock WifiAsyncChannel mChannel;
     @Mock WifiHandler mCmiHandler;
+    @Mock WifiMetrics mWifiMetrics;
     TestLooper mLooper;
 
     /**
@@ -93,7 +95,7 @@
         when(mClientModeImpl.getHandler()).thenReturn(mCmiHandler);
 
         mWifiLockManager = new WifiLockManager(mContext, mBatteryStats,
-                mClientModeImpl, mFrameworkFacade, mLooper.getLooper());
+                mClientModeImpl, mFrameworkFacade, mLooper.getLooper(), mClock, mWifiMetrics);
         connectAsyncChannel();
     }
 
@@ -224,6 +226,8 @@
     public void releaseWifiLockShouldSucceed() throws Exception {
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", mBinder, mWorkSource);
         releaseWifiLockSuccessful(mBinder);
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode());
     }
 
@@ -420,6 +424,8 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
     }
 
     /**
@@ -447,15 +453,22 @@
 
         // Release the first lock
         releaseWifiLockSuccessful(mBinder);
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
+
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         // Release the second lock
         releaseWifiLockSuccessful(mBinder2);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
     }
 
     /**
@@ -546,9 +559,12 @@
         inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         releaseWifiLockSuccessful(mBinder);
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics, never()).addWifiLockActiveSession(anyInt(), anyLong());
 
         // Now attempting adding some other lock, WifiLockManager should retry setPowerSave()
         when(mClientModeImpl.setPowerSave(true)).thenReturn(true);
@@ -557,6 +573,8 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
     }
 
     /**
@@ -580,6 +598,8 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
     }
 
     /**
@@ -602,6 +622,8 @@
         inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         releaseWifiLockSuccessful(mBinder);
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
@@ -610,6 +632,8 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
     }
 
     /**
@@ -711,6 +735,7 @@
         // Set screen on, and app foreground
         mWifiLockManager.handleScreenStateChanged(true);
         when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+        when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
         when(mClientModeImpl.syncGetSupportedFeatures(any()))
                 .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY);
 
@@ -718,6 +743,7 @@
                 mBinder, mWorkSource);
 
         verify(mClientModeImpl).setLowLatencyMode(true);
+        verify(mClientModeImpl).setPowerSave(false);
     }
 
     /**
@@ -736,6 +762,7 @@
                 mBinder, mWorkSource);
 
         verify(mClientModeImpl).setPowerSave(false);
+        verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
     }
 
     /**
@@ -752,17 +779,22 @@
 
         // Make sure setLowLatencyMode() is successful
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
 
         InOrder inOrder = inOrder(mClientModeImpl);
 
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
                 mBinder, mWorkSource);
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         releaseLowLatencyWifiLockSuccessful(mBinder);
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
     }
 
     /**
@@ -795,6 +827,34 @@
     }
 
     /**
+     * Test when acquire of low-latency lock succeeds in enable low latency
+     * but fails to disable power save, then low latency mode is reverted
+     */
+    @Test
+    public void testLatencyfail2DisablePowerSave() throws Exception {
+        InOrder inOrder = inOrder(mClientModeImpl);
+
+        // Set screen on, and app is foreground
+        mWifiLockManager.handleScreenStateChanged(true);
+        when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+        when(mClientModeImpl.syncGetSupportedFeatures(any()))
+                .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY);
+
+        // Succeed to setLowLatencyMode()
+        when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        // Fail to setPowerSave()
+        when(mClientModeImpl.setPowerSave(false)).thenReturn(false);
+
+        acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
+                mBinder, mWorkSource);
+        assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                mWifiLockManager.getStrongestLockMode());
+        inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
+        inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+    }
+
+    /**
      * Test when a low-latency lock is acquired (foreground app, screen-on),
      * then, screen go off, then low-latency mode is turned off.
      */
@@ -808,6 +868,7 @@
 
         // Make sure setLowLatencyMode() is successful
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
 
         InOrder inOrder = inOrder(mClientModeImpl);
 
@@ -816,11 +877,15 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         mWifiLockManager.handleScreenStateChanged(false);
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
     }
 
     /**
@@ -837,6 +902,7 @@
 
         // Make sure setLowLatencyMode() is successful
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
 
         InOrder inOrder = inOrder(mClientModeImpl);
 
@@ -847,6 +913,7 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         /* App going to background */
         mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
@@ -855,6 +922,9 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
     }
 
     /**
@@ -882,6 +952,7 @@
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         /* App going to foreground */
         mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
@@ -891,6 +962,7 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
     }
 
     /**
@@ -933,7 +1005,10 @@
                 mWifiLockManager.getStrongestLockMode());
 
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
     }
 
     /**
@@ -957,10 +1032,14 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
         assertTrue(mWifiLockManager.forceLowLatencyMode(false));
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
     }
 
     /**
@@ -969,6 +1048,7 @@
     @Test
     public void testForceLowLatencyScreenOff() throws Exception {
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
         mWifiLockManager.handleScreenStateChanged(false);
         when(mClientModeImpl.syncGetSupportedFeatures(any()))
                 .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY);
@@ -982,16 +1062,19 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         mWifiLockManager.handleScreenStateChanged(true);
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         mWifiLockManager.handleScreenStateChanged(false);
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
     }
 
     /**
@@ -1000,6 +1083,7 @@
     @Test
     public void testForceLowLatencyAcqRelLowLatency() throws Exception {
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
         mWifiLockManager.handleScreenStateChanged(true);
         when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
         when(mClientModeImpl.syncGetSupportedFeatures(any()))
@@ -1012,21 +1096,27 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         assertTrue(mWifiLockManager.forceLowLatencyMode(true));
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         releaseLowLatencyWifiLockSuccessful(mBinder);
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
 
         assertTrue(mWifiLockManager.forceLowLatencyMode(false));
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
     }
 
     /**
@@ -1035,6 +1125,7 @@
     @Test
     public void testForceLowLatencyTwice() throws Exception {
         when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
         when(mClientModeImpl.syncGetSupportedFeatures(any()))
                 .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY);
         mWifiLockManager.updateWifiClientConnected(true);
@@ -1045,11 +1136,13 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         assertTrue(mWifiLockManager.forceLowLatencyMode(true));
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl, never()).setLowLatencyMode(anyBoolean());
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
     }
 
     /**
@@ -1074,7 +1167,10 @@
                 mWifiLockManager.getStrongestLockMode());
 
         inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
     }
 
     /**
@@ -1094,11 +1190,15 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        inOrder.verify(mClientModeImpl).setPowerSave(false);
 
         assertTrue(mWifiLockManager.forceHiPerfMode(true));
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(false);
+        inOrder.verify(mClientModeImpl).setPowerSave(true);
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                anyLong());
         inOrder.verify(mClientModeImpl).setPowerSave(false);
     }
 
@@ -1122,6 +1222,8 @@
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeImpl).setLowLatencyMode(true);
+        // Since setLowLatencyMode() failed, no call to setPowerSave()
+        inOrder.verify(mClientModeImpl, never()).setPowerSave(anyBoolean());
     }
 
     /**
@@ -1169,6 +1271,97 @@
     }
 
     /**
+     * Test that reporting of metrics for hi-perf lock acquistion is correct for both acquisition
+     * time and active time.
+     */
+    @Test
+    public void testHighPerfLockMetrics() throws Exception {
+        long acquireTime      = 1000;
+        long activationTime   = 2000;
+        long deactivationTime = 3000;
+        long releaseTime      = 4000;
+
+        // Make sure setPowerSave() is successful
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
+
+        InOrder inOrder = inOrder(mWifiMetrics);
+
+        // Acquire the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(acquireTime);
+        assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "",
+                mBinder, mWorkSource));
+
+        // Activate the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(activationTime);
+        mWifiLockManager.updateWifiClientConnected(true);
+
+        // Deactivate the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(deactivationTime);
+        mWifiLockManager.updateWifiClientConnected(false);
+
+        inOrder.verify(mWifiMetrics).addWifiLockActiveSession(
+                eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                eq(deactivationTime - activationTime));
+
+        // Release the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(releaseTime);
+        releaseWifiLockSuccessful_noBatteryStats(mBinder);
+
+        inOrder.verify(mWifiMetrics).addWifiLockAcqSession(
+                eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                eq(releaseTime - acquireTime));
+    }
+
+    /**
+     * Test that reporting of metrics for low-latency lock acquistion is correct for
+     * both acquisition time and active time.
+     */
+    @Test
+    public void testLowLatencyLockMetrics() throws Exception {
+        long acquireTime      = 1000;
+        long activationTime   = 2000;
+        long deactivationTime = 3000;
+        long releaseTime      = 4000;
+
+        // Make sure setLowLatencyMode()/setPowerSave() is successful
+        when(mClientModeImpl.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeImpl.setPowerSave(anyBoolean())).thenReturn(true);
+
+        // Set condition for activation of low-latency (except connection to AP)
+        mWifiLockManager.handleScreenStateChanged(true);
+        when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+        when(mClientModeImpl.syncGetSupportedFeatures(any()))
+                .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY);
+
+        InOrder inOrder = inOrder(mWifiMetrics);
+
+        // Acquire the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(acquireTime);
+        assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
+                mBinder, mWorkSource));
+
+        // Activate the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(activationTime);
+        mWifiLockManager.updateWifiClientConnected(true);
+
+        // Deactivate the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(deactivationTime);
+        mWifiLockManager.updateWifiClientConnected(false);
+
+        inOrder.verify(mWifiMetrics).addWifiLockActiveSession(
+                eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                eq(deactivationTime - activationTime));
+
+        // Release the lock
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(releaseTime);
+        releaseWifiLockSuccessful_noBatteryStats(mBinder);
+
+        inOrder.verify(mWifiMetrics).addWifiLockAcqSession(
+                eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                eq(releaseTime - acquireTime));
+    }
+
+    /**
      * Verfies that dump() does not fail when no locks are held.
      */
     @Test
@@ -1194,7 +1387,8 @@
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TEST_WIFI_LOCK_TAG,
                 mBinder, mWorkSource);
         releaseWifiLockSuccessful(mBinder);
-
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
+                anyLong());
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TEST_WIFI_LOCK_TAG,
                 mBinder, mWorkSource);
 
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 4dcaea1..33da966 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -62,6 +62,7 @@
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.Base64;
 import android.util.Pair;
 import android.util.SparseIntArray;
@@ -125,6 +126,7 @@
     TestLooper mTestLooper;
     Random mRandom = new Random();
     private static final int TEST_WIFI_USABILITY_STATS_LISTENER_IDENTIFIER = 2;
+    private static final int TEST_NETWORK_ID = 42;
     @Mock Context mContext;
     @Mock FrameworkFacade mFacade;
     @Mock Clock mClock;
@@ -139,16 +141,19 @@
     @Mock ExternalCallbackTracker<IOnWifiUsabilityStatsListener> mListenerTracker;
     @Mock WifiP2pMetrics mWifiP2pMetrics;
     @Mock DppMetrics mDppMetrics;
+    @Mock CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
+    @Mock CellularLinkLayerStats mCellularLinkLayerStats;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mDecodedProto = null;
         when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
+        when(mCellularLinkLayerStatsCollector.update()).thenReturn(mCellularLinkLayerStats);
         mTestLooper = new TestLooper();
         mWifiMetrics = new WifiMetrics(mContext, mFacade, mClock, mTestLooper.getLooper(),
                 new WifiAwareMetrics(mClock), new RttMetrics(mClock), mWifiPowerMetrics,
-                mWifiP2pMetrics, mDppMetrics);
+                mWifiP2pMetrics, mDppMetrics, mCellularLinkLayerStatsCollector);
         mWifiMetrics.setWifiConfigManager(mWcm);
         mWifiMetrics.setPasspointManager(mPpm);
         mWifiMetrics.setScoringParams(mScoringParams);
@@ -1340,6 +1345,10 @@
         when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
         when(scanDetail.getScanResult()).thenReturn(scanResult);
 
+        config.networkId = TEST_NETWORK_ID;
+        mWifiMetrics.setNominatorForNetwork(TEST_NETWORK_ID,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
+
         //Create a connection event using only the config
         mWifiMetrics.startConnectionEvent(config, "Red",
                 WifiMetricsProto.ConnectionEvent.ROAM_NONE);
@@ -1371,6 +1380,54 @@
                 mDecodedProto.connectionEvent[1].routerFingerprint.routerTechnology);
         assertTrue(mDecodedProto.connectionEvent[0].useRandomizedMac);
         assertFalse(mDecodedProto.connectionEvent[1].useRandomizedMac);
+        assertEquals(WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL,
+                mDecodedProto.connectionEvent[0].connectionNominator);
+    }
+
+    /**
+     * Tests that the mapping from networkId to nominatorId is not cleared.
+     */
+    @Test
+    public void testNetworkToNominatorNotCleared() throws Exception {
+        //Setup mock configs and scan details
+        NetworkDetail networkDetail = mock(NetworkDetail.class);
+        when(networkDetail.getWifiMode()).thenReturn(NETWORK_DETAIL_WIFIMODE);
+        when(networkDetail.getSSID()).thenReturn(SSID);
+        when(networkDetail.getDtimInterval()).thenReturn(NETWORK_DETAIL_DTIM);
+        ScanResult scanResult = mock(ScanResult.class);
+        scanResult.level = SCAN_RESULT_LEVEL;
+        WifiConfiguration config = mock(WifiConfiguration.class);
+        config.SSID = "\"" + SSID + "\"";
+        config.dtimInterval = CONFIG_DTIM;
+        config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+        WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
+                mock(WifiConfiguration.NetworkSelectionStatus.class);
+        when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
+        when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStat);
+        ScanDetail scanDetail = mock(ScanDetail.class);
+        when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+        when(scanDetail.getScanResult()).thenReturn(scanResult);
+
+        config.networkId = TEST_NETWORK_ID;
+        mWifiMetrics.setNominatorForNetwork(TEST_NETWORK_ID,
+                WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER);
+
+        // dump() calls clear() internally
+        mWifiMetrics.dump(null, new PrintWriter(new StringWriter()),
+                new String[]{WifiMetrics.PROTO_DUMP_ARG});
+
+        // Create a connection event using only the config
+        mWifiMetrics.startConnectionEvent(config, "Red",
+                WifiMetricsProto.ConnectionEvent.ROAM_NONE);
+        mWifiMetrics.endConnectionEvent(
+                WifiMetrics.ConnectionEvent.FAILURE_NONE,
+                WifiMetricsProto.ConnectionEvent.HLF_NONE,
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+
+        dumpProtoAndDeserialize();
+
+        assertEquals(WifiMetricsProto.ConnectionEvent.NOMINATOR_CARRIER,
+                mDecodedProto.connectionEvent[0].connectionNominator);
     }
 
     /**
@@ -2274,7 +2331,7 @@
         return bitSet;
     }
 
-    private static final int NUM_UNUSABLE_EVENT = 4;
+    private static final int NUM_UNUSABLE_EVENT = 5;
     private static final int NUM_UNUSABLE_EVENT_TIME_THROTTLE = 3;
 
     /**
@@ -2287,7 +2344,8 @@
         {WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX,        60,  60,  50,  40,  30,  1000,  -1, 51},
         {WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX, 55,  40,  30,  0,   0,   500,   -1, 52},
         {WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH,          60,  90,  30,  30,  0,   1000,  -1, 53},
-        {WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT,           55,  55,  30,  15,  10,  1000,   4, 54}
+        {WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT,           55,  55,  30,  15,  10,  1000,   4, 54},
+        {WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST,     50,  56,  28,  17,  12,  1000,  -1, 45}
     };
 
     /**
@@ -2320,6 +2378,9 @@
             case WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT:
                 mWifiMetrics.logWifiIsUnusableEvent(trigger[0], trigger[7]);
                 break;
+            case WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST:
+                mWifiMetrics.logWifiIsUnusableEvent(trigger[0]);
+                break;
             default:
                 break;
         }
@@ -2690,6 +2751,16 @@
         when(info.getBSSID()).thenReturn("Wifi");
         when(info.getFrequency()).thenReturn(5745);
 
+        int signalStrengthDbm = -50;
+        int signalStrengthDb = -10;
+        boolean isSameRegisteredCell = true;
+        CellularLinkLayerStats cellularStats =  new CellularLinkLayerStats();
+        cellularStats.setIsSameRegisteredCell(isSameRegisteredCell);
+        cellularStats.setDataNetworkType(TelephonyManager.NETWORK_TYPE_LTE);
+        cellularStats.setSignalStrengthDbm(signalStrengthDbm);
+        cellularStats.setSignalStrengthDb(signalStrengthDb);
+        when(mCellularLinkLayerStatsCollector.update()).thenReturn(cellularStats);
+
         WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats());
         WifiLinkLayerStats stats2 = nextRandomStats(stats1);
         mWifiMetrics.incrementWifiScoreCount(60);
@@ -2701,6 +2772,8 @@
         mWifiMetrics.incrementWifiUsabilityScoreCount(3, 56, 15);
         mWifiMetrics.logLinkProbeFailure(nextRandInt(), nextRandInt(), nextRandInt(),
                 nextRandInt(), nextRandInt());
+        mWifiMetrics.enterDeviceMobilityState(DEVICE_MOBILITY_STATE_HIGH_MVMT);
+
         mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats2);
         mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
                 WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX);
@@ -2741,6 +2814,17 @@
                 .stats[1].probeElapsedTimeSinceLastUpdateMs);
         assertEquals(-1, mDecodedProto.wifiUsabilityStatsList[0]
                 .stats[0].probeElapsedTimeSinceLastUpdateMs);
+        assertEquals(WifiUsabilityStatsEntry.NETWORK_TYPE_LTE,
+                mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularDataNetworkType);
+        assertEquals(signalStrengthDbm,
+                mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularSignalStrengthDbm);
+        assertEquals(signalStrengthDb,
+                mDecodedProto.wifiUsabilityStatsList[0].stats[0].cellularSignalStrengthDb);
+        assertEquals(isSameRegisteredCell,
+                mDecodedProto.wifiUsabilityStatsList[0].stats[0].isSameRegisteredCell);
+        assertEquals(DEVICE_MOBILITY_STATE_HIGH_MVMT, mDecodedProto.wifiUsabilityStatsList[1]
+                .stats[mDecodedProto.wifiUsabilityStatsList[1].stats.length - 1]
+                .deviceMobilityState);
     }
 
     /**
@@ -2840,17 +2924,21 @@
 
         WifiLinkLayerStats stats3 = new WifiLinkLayerStats();
         WifiLinkLayerStats stats4 = new WifiLinkLayerStats();
-        stats4.timeStampInMs = stats3.timeStampInMs - 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS;
-        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 2; i++) {
+        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) {
             mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats3);
             stats3 = nextRandomStats(stats3);
         }
-        addBadWifiUsabilityStats(stats3);
-        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 2; i++) {
+        mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats3);
+        mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX);
+        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) {
             mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats4);
             stats4 = nextRandomStats(stats4);
         }
-        addBadWifiUsabilityStats(stats4);
+        stats4.timeStampInMs = stats3.timeStampInMs - 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS;
+        mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats4);
+        mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX);
         dumpProtoAndDeserialize();
         assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length);
     }
@@ -2875,17 +2963,21 @@
 
         WifiLinkLayerStats stats3 = new WifiLinkLayerStats();
         WifiLinkLayerStats stats4 = new WifiLinkLayerStats();
-        stats4.timeStampInMs = stats3.timeStampInMs + 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS;
-        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 2; i++) {
+        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) {
             mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats3);
             stats3 = nextRandomStats(stats3);
         }
-        addBadWifiUsabilityStats(stats3);
-        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 2; i++) {
+        mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats3);
+        mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX);
+        for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) {
             mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats4);
             stats4 = nextRandomStats(stats4);
         }
-        addBadWifiUsabilityStats(stats4);
+        stats4.timeStampInMs = stats3.timeStampInMs + 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS;
+        mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats4);
+        mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX);
         dumpProtoAndDeserialize();
         assertEquals(4, mDecodedProto.wifiUsabilityStatsList.length);
     }
@@ -3026,6 +3118,14 @@
         WifiInfo info = mock(WifiInfo.class);
         when(info.getRssi()).thenReturn(nextRandInt());
         when(info.getLinkSpeed()).thenReturn(nextRandInt());
+
+        CellularLinkLayerStats cellularStats = new CellularLinkLayerStats();
+        cellularStats.setIsSameRegisteredCell(false);
+        cellularStats.setDataNetworkType(TelephonyManager.NETWORK_TYPE_UMTS);
+        cellularStats.setSignalStrengthDbm(-100);
+        cellularStats.setSignalStrengthDb(-20);
+        when(mCellularLinkLayerStatsCollector.update()).thenReturn(cellularStats);
+
         WifiLinkLayerStats linkLayerStats = nextRandomStats(new WifiLinkLayerStats());
         mWifiMetrics.updateWifiUsabilityStatsEntries(info, linkLayerStats);
 
@@ -3039,6 +3139,10 @@
         assertEquals(usabilityStats.getValue().getTimeStampMillis(), linkLayerStats.timeStampInMs);
         assertEquals(usabilityStats.getValue().getTotalRoamScanTimeMillis(),
                 linkLayerStats.on_time_roam_scan);
+        assertEquals(usabilityStats.getValue().getCellularDataNetworkType(),
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertEquals(usabilityStats.getValue().getCellularSignalStrengthDbm(), -100);
+        assertEquals(usabilityStats.getValue().getCellularSignalStrengthDb(), -20);
     }
 
     /**
@@ -3445,4 +3549,109 @@
         assertNotNull("not found!", result);
         return result;
     }
+
+    /**
+     * Verify that the label and the triggerType of Wifi usability stats are saved correctly
+     * during IP reachability lost message is received.
+     * @throws Exception
+     */
+    @Test
+    public void verifyIpReachabilityLostUpdatesWifiUsabilityMetrics() throws Exception {
+        WifiInfo info = mock(WifiInfo.class);
+        when(info.getRssi()).thenReturn(nextRandInt());
+        when(info.getLinkSpeed()).thenReturn(nextRandInt());
+        WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats());
+        mWifiMetrics.updateWifiUsabilityStatsEntries(info, stats1);
+
+        // Add 1 LABEL_GOOD
+        WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats1));
+        // IP reachability lost occurs
+        mWifiMetrics.addToWifiUsabilityStatsList(WifiUsabilityStats.LABEL_BAD,
+                WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST);
+
+        dumpProtoAndDeserialize();
+        assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length);
+        WifiUsabilityStats[] statsList = mDecodedProto.wifiUsabilityStatsList;
+        assertEquals(WifiUsabilityStats.LABEL_BAD, statsList[1].label);
+        assertEquals(WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, statsList[1].triggerType);
+    }
+
+    /**
+     * Test the WifiLock active session statistics
+     */
+    @Test
+    public void testWifiLockActiveSession() throws Exception {
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 100000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 1000);
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 90000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 900000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 9000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 20000000);
+
+        dumpProtoAndDeserialize();
+
+        assertEquals(10111000, mDecodedProto.wifiLockStats.highPerfActiveTimeMs);
+        assertEquals(20999000, mDecodedProto.wifiLockStats.lowLatencyActiveTimeMs);
+
+        HistogramBucketInt32[] expectedHighPerfHistogram = {
+                buildHistogramBucketInt32(1, 10, 1),
+                buildHistogramBucketInt32(10, 60, 1),
+                buildHistogramBucketInt32(60, 600, 1),
+                buildHistogramBucketInt32(3600, Integer.MAX_VALUE, 1),
+        };
+
+        HistogramBucketInt32[] expectedLowLatencyHistogram = {
+                buildHistogramBucketInt32(1, 10, 1),
+                buildHistogramBucketInt32(60, 600, 1),
+                buildHistogramBucketInt32(600, 3600, 1),
+                buildHistogramBucketInt32(3600, Integer.MAX_VALUE, 1),
+        };
+
+        assertHistogramBucketsEqual(expectedHighPerfHistogram,
+                mDecodedProto.wifiLockStats.highPerfActiveSessionDurationSecHistogram);
+
+        assertHistogramBucketsEqual(expectedLowLatencyHistogram,
+                mDecodedProto.wifiLockStats.lowLatencyActiveSessionDurationSecHistogram);
+    }
+
+    /**
+     * Test the WifiLock acquisition session statistics
+     */
+    @Test
+    public void testWifiLockAcqSession() throws Exception {
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 100000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 1000);
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 90000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 900000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 9000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 20000000);
+
+        dumpProtoAndDeserialize();
+
+        HistogramBucketInt32[] expectedHighPerfHistogram = {
+                buildHistogramBucketInt32(1, 10, 1),
+                buildHistogramBucketInt32(10, 60, 1),
+                buildHistogramBucketInt32(60, 600, 1),
+                buildHistogramBucketInt32(3600, Integer.MAX_VALUE, 1),
+        };
+
+        HistogramBucketInt32[] expectedLowLatencyHistogram = {
+                buildHistogramBucketInt32(1, 10, 1),
+                buildHistogramBucketInt32(60, 600, 1),
+                buildHistogramBucketInt32(600, 3600, 1),
+                buildHistogramBucketInt32(3600, Integer.MAX_VALUE, 1),
+        };
+
+        assertHistogramBucketsEqual(expectedHighPerfHistogram,
+                mDecodedProto.wifiLockStats.highPerfLockAcqDurationSecHistogram);
+
+        assertHistogramBucketsEqual(expectedLowLatencyHistogram,
+                mDecodedProto.wifiLockStats.lowLatencyLockAcqDurationSecHistogram);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
index a5e28a2..c7a66e3 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
@@ -447,6 +447,8 @@
         mInOrder.verify(mWificondControl).setupInterfaceForSoftApMode(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).registerObserver(mNetworkObserverCaptor1.capture());
         mInOrder.verify(mNwManagementService).getInterfaceConfig(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
 
         // Execute a teardown of the interface to ensure that the new iface removal works.
         executeAndValidateTeardownSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback1,
@@ -497,6 +499,8 @@
         mInOrder.verify(mNwManagementService).clearInterfaceAddresses(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).setInterfaceIpv6PrivacyExtensions(IFACE_NAME_0, true);
         mInOrder.verify(mNwManagementService).disableIpv6(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
 
         // Execute a teardown of the interface to ensure that the new iface removal works.
         executeAndValidateTeardownClientInterface(false, false, IFACE_NAME_0, mIfaceCallback1,
@@ -706,6 +710,8 @@
         mInOrder.verify(mWificondControl).setupInterfaceForSoftApMode(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).registerObserver(mNetworkObserverCaptor1.capture());
         mInOrder.verify(mNwManagementService).getInterfaceConfig(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
 
         // Step (c) - Iface up on old iface, ignored!
         mNetworkObserverCaptor0.getValue().interfaceLinkStateChanged(IFACE_NAME_0, true);
@@ -1163,6 +1169,8 @@
         mInOrder.verify(mNwManagementService).clearInterfaceAddresses(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).setInterfaceIpv6PrivacyExtensions(IFACE_NAME_0, true);
         mInOrder.verify(mNwManagementService).disableIpv6(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
 
         // Now setup an AP interface.
         assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1));
@@ -1186,6 +1194,8 @@
         mInOrder.verify(mWificondControl).setupInterfaceForSoftApMode(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).registerObserver(mNetworkObserverCaptor1.capture());
         mInOrder.verify(mNwManagementService).getInterfaceConfig(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
     }
 
     /**
@@ -1212,6 +1222,8 @@
         mInOrder.verify(mWificondControl).setupInterfaceForSoftApMode(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).registerObserver(mNetworkObserverCaptor0.capture());
         mInOrder.verify(mNwManagementService).getInterfaceConfig(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
 
         // Now setup a STA interface.
         assertEquals(IFACE_NAME_0,
@@ -1240,6 +1252,8 @@
         mInOrder.verify(mNwManagementService).clearInterfaceAddresses(IFACE_NAME_0);
         mInOrder.verify(mNwManagementService).setInterfaceIpv6PrivacyExtensions(IFACE_NAME_0, true);
         mInOrder.verify(mNwManagementService).disableIpv6(IFACE_NAME_0);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(IFACE_NAME_0);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(IFACE_NAME_0);
     }
 
     /**
@@ -1302,6 +1316,8 @@
         mInOrder.verify(mNwManagementService).clearInterfaceAddresses(ifaceName);
         mInOrder.verify(mNwManagementService).setInterfaceIpv6PrivacyExtensions(ifaceName, true);
         mInOrder.verify(mNwManagementService).disableIpv6(ifaceName);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(ifaceName);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName);
     }
 
     private void executeAndValidateTeardownClientInterface(
@@ -1372,6 +1388,8 @@
         mInOrder.verify(mNwManagementService).registerObserver(networkObserverCaptor.capture());
         mInOrder.verify(mWifiMonitor).startMonitoring(ifaceName);
         mInOrder.verify(mNwManagementService).getInterfaceConfig(ifaceName);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(ifaceName);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName);
     }
 
     private void executeAndValidateTeardownClientInterfaceForScan(
@@ -1442,6 +1460,8 @@
         mInOrder.verify(mWificondControl).setupInterfaceForSoftApMode(ifaceName);
         mInOrder.verify(mNwManagementService).registerObserver(networkObserverCaptor.capture());
         mInOrder.verify(mNwManagementService).getInterfaceConfig(ifaceName);
+        mInOrder.verify(mSupplicantStaIfaceHal).getAdvancedKeyMgmtCapabilities(ifaceName);
+        mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName);
     }
 
     private void executeAndValidateTeardownSoftApInterface(
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
index 0091ae1..caecdf0 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -64,6 +64,7 @@
 
 import com.android.internal.util.AsyncChannel;
 import com.android.server.wifi.WifiNetworkFactory.AccessPoint;
+import com.android.server.wifi.nano.WifiMetricsProto;
 import com.android.server.wifi.util.ScanResultUtil;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
@@ -567,7 +568,7 @@
         // Release the network request.
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
         // Verify that we did not trigger a disconnect because we've not yet connected.
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
         // Re-enable connectivity manager .
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
 
@@ -1022,6 +1023,7 @@
         assertNotNull(networkRequestUserSelectionCallback);
 
         // Now trigger user selection to one of the network.
+        ScanResult matchingScanResult = mTestScanDatas[0].getResults()[0];
         mSelectedNetwork = WifiConfigurationTestUtil.createPskNetwork();
         mSelectedNetwork.SSID = "\"" + mTestScanDatas[0].getResults()[0].SSID + "\"";
         networkRequestUserSelectionCallback.select(mSelectedNetwork);
@@ -1032,20 +1034,9 @@
         // Disable connectivity manager
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(true);
 
-        ArgumentCaptor<WifiConfiguration> wifiConfigurationCaptor =
-                ArgumentCaptor.forClass(WifiConfiguration.class);
-        verify(mWifiConfigManager).addOrUpdateNetwork(
-                wifiConfigurationCaptor.capture(), eq(TEST_UID_1), eq(TEST_PACKAGE_NAME_1));
-        WifiConfiguration network =  wifiConfigurationCaptor.getValue();
-        assertNotNull(network);
-        WifiConfiguration expectedWifiConfiguration =
-                new WifiConfiguration(((WifiNetworkSpecifier) mNetworkRequest.networkCapabilities
-                        .getNetworkSpecifier()).wifiConfiguration);
-        expectedWifiConfiguration.SSID = mSelectedNetwork.SSID;
-        expectedWifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
-        expectedWifiConfiguration.ephemeral = true;
-        expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
-        WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
+        validateConnectParams(mSelectedNetwork.SSID, matchingScanResult.BSSID);
+        verify(mWifiMetrics).setNominatorForNetwork(anyInt(),
+                eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER));
 
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mClientModeImpl).sendMessage(messageCaptor.capture());
@@ -1102,7 +1093,7 @@
 
     /**
      * Verify handling of user selection to trigger connection to a network. Ensure we fill
-     * up the BSSID field for connection to specific access points.
+     * up the BSSID field.
      */
     @Test
     public void
@@ -1142,21 +1133,67 @@
         mLooper.dispatchAll();
 
         // Verify WifiConfiguration params.
-        ArgumentCaptor<WifiConfiguration> wifiConfigurationCaptor =
-                ArgumentCaptor.forClass(WifiConfiguration.class);
-        verify(mWifiConfigManager).addOrUpdateNetwork(
-                wifiConfigurationCaptor.capture(), eq(TEST_UID_1), eq(TEST_PACKAGE_NAME_1));
-        WifiConfiguration network =  wifiConfigurationCaptor.getValue();
-        assertNotNull(network);
-        WifiConfiguration expectedWifiConfiguration =
-                new WifiConfiguration(((WifiNetworkSpecifier) mNetworkRequest.networkCapabilities
-                        .getNetworkSpecifier()).wifiConfiguration);
-        expectedWifiConfiguration.SSID = mSelectedNetwork.SSID;
-        expectedWifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
-        expectedWifiConfiguration.BSSID = matchingScanResult.BSSID;
-        expectedWifiConfiguration.ephemeral = true;
-        expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
-        WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
+        validateConnectParams(mSelectedNetwork.SSID, matchingScanResult.BSSID);
+        verify(mWifiMetrics).setNominatorForNetwork(anyInt(),
+                eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER));
+
+        // Verify connection message.
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mClientModeImpl).sendMessage(messageCaptor.capture());
+
+        Message message = messageCaptor.getValue();
+        assertNotNull(message);
+
+        assertEquals(WifiManager.CONNECT_NETWORK, message.what);
+        assertEquals(TEST_NETWORK_ID_1, message.arg1);
+    }
+
+    /**
+     * Verify handling of user selection to trigger connection to a network. Ensure we fill
+     * up the BSSID field with scan result for highest RSSI.
+     */
+    @Test
+    public void
+            testNetworkSpecifierHandleUserSelectionConnectToNetworkMultipleBssidMatches()
+            throws Exception {
+        setupScanData(SCAN_RESULT_TYPE_WPA_PSK,
+                TEST_SSID_1, TEST_SSID_1, TEST_SSID_1, TEST_SSID_4);
+
+        // Make a ssid pattern request which matches 3 scan results - 0, 1, 2.
+        PatternMatcher ssidPatternMatch =
+                new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
+        Pair<MacAddress, MacAddress> bssidPatternMatch =
+                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
+        WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
+                ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1,
+                TEST_PACKAGE_NAME_1);
+        mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier);
+        mWifiNetworkFactory.needNetworkFor(mNetworkRequest, 0);
+        mWifiNetworkFactory.addCallback(mAppBinder, mNetworkRequestMatchCallback,
+                TEST_CALLBACK_IDENTIFIER);
+        verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
+                mNetworkRequestUserSelectionCallback.capture());
+        verifyPeriodicScans(0, PERIODIC_SCAN_INTERVAL_MS);
+
+        // Scan result 2 has the highest RSSI, so that should be picked.
+        ScanResult matchingScanResult = mTestScanDatas[0].getResults()[2];
+
+        // Now trigger user selection to the network.
+        mSelectedNetwork = ScanResultUtil.createNetworkFromScanResult(matchingScanResult);
+        mSelectedNetwork.SSID = "\"" + matchingScanResult.SSID + "\"";
+        INetworkRequestUserSelectionCallback networkRequestUserSelectionCallback =
+                mNetworkRequestUserSelectionCallback.getValue();
+        assertNotNull(networkRequestUserSelectionCallback);
+        networkRequestUserSelectionCallback.select(mSelectedNetwork);
+        mLooper.dispatchAll();
+
+        // Verify WifiConfiguration params.
+        validateConnectParams(mSelectedNetwork.SSID, matchingScanResult.BSSID);
+        verify(mWifiMetrics).setNominatorForNetwork(anyInt(),
+                eq(WifiMetricsProto.ConnectionEvent.NOMINATOR_SPECIFIER));
 
         // Verify connection message.
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
@@ -1355,6 +1392,66 @@
     }
 
     /**
+     * Verify that we ignore connection success events after the first one (may be triggered by a
+     * roam event)
+     */
+    @Test
+    public void testNetworkSpecifierDuplicateHandleConnectionSuccess() throws Exception {
+        sendNetworkRequestAndSetupForConnectionStatus();
+
+        // Send network connection success indication.
+        assertNotNull(mSelectedNetwork);
+        mWifiNetworkFactory.handleConnectionAttemptEnded(
+                WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
+
+        // Verify that we sent the connection success callback.
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
+        // verify we canceled the timeout alarm.
+        verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
+
+        verify(mWifiMetrics).incrementNetworkRequestApiNumConnectSuccess();
+
+        // Send second network connection success indication which should be ignored.
+        mWifiNetworkFactory.handleConnectionAttemptEnded(
+                WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
+        verifyNoMoreInteractions(mNetworkRequestMatchCallback);
+    }
+
+    /**
+     * Verify that we ignore any connection failure events after the first connection success (may
+     * be triggered by a disconnect).
+     * Note: The disconnect handling will be done via the NetworkAgent.
+     */
+    @Test
+    public void testNetworkSpecifierHandleConnectionFailureAfterSuccess() throws Exception {
+        sendNetworkRequestAndSetupForConnectionStatus();
+
+        // Send network connection success indication.
+        assertNotNull(mSelectedNetwork);
+        mWifiNetworkFactory.handleConnectionAttemptEnded(
+                WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
+
+        // Verify that we sent the connection success callback.
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
+        // verify we canceled the timeout alarm.
+        verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
+
+        verify(mWifiMetrics).incrementNetworkRequestApiNumConnectSuccess();
+
+        // Send a network connection failure indication which should be ignored (beyond the retry
+        // limit to trigger the failure handling).
+        for (int i = 0; i <= WifiNetworkFactory.USER_SELECTED_NETWORK_CONNECT_RETRY_MAX; i++) {
+            mWifiNetworkFactory.handleConnectionAttemptEnded(
+                    WifiMetrics.ConnectionEvent.FAILURE_DHCP, mSelectedNetwork);
+            mLooper.dispatchAll();
+        }
+        // Verify that we ignore the second connection failure callback.
+        verifyNoMoreInteractions(mNetworkRequestMatchCallback);
+    }
+
+    /**
      * Verify handling of connection success to a different network.
      */
     @Test
@@ -1405,7 +1502,7 @@
         // Now release the network request.
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
         // Verify that we triggered a disconnect.
-        verify(mClientModeImpl).disconnectCommandInternal();
+        verify(mClientModeImpl).disconnectCommand();
         // Re-enable connectivity manager .
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
     }
@@ -1580,12 +1677,12 @@
         verify(mWifiConnectivityManager, times(1)).setSpecificNetworkRequestInProgress(true);
         verify(mWifiScanner, times(2)).startScan(any(), any(), any());
         // we shouldn't disconnect until the user accepts the next request.
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
 
         // Remove the connected request1 & ensure we disconnect.
         mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
-        verify(mClientModeImpl).disconnectCommandInternal();
+        verify(mClientModeImpl).disconnectCommand();
 
         verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl,
                 mAlarmManager);
@@ -1630,7 +1727,7 @@
 
         // We shouldn't explicitly disconnect, the new connection attempt will implicitly disconnect
         // from the connected network.
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
 
         // Remove the stale request1 & ensure nothing happens (because it was replaced by the
         // second request)
@@ -1643,7 +1740,7 @@
         // Now remove the rejected request2, ensure we disconnect & re-enable auto-join.
         mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier2);
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
-        verify(mClientModeImpl).disconnectCommandInternal();
+        verify(mClientModeImpl).disconnectCommand();
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
 
         verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl,
@@ -1679,12 +1776,12 @@
 
         // we shouldn't disconnect/re-enable auto-join until the connected request is released.
         verify(mWifiConnectivityManager, never()).setSpecificNetworkRequestInProgress(false);
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
 
         // Remove the connected request1 & ensure we disconnect & ensure auto-join is re-enabled.
         mNetworkRequest.networkCapabilities.setNetworkSpecifier(specifier1);
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
-        verify(mClientModeImpl).disconnectCommandInternal();
+        verify(mClientModeImpl).disconnectCommand();
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
 
         verifyNoMoreInteractions(mWifiConnectivityManager, mWifiScanner, mClientModeImpl,
@@ -2358,18 +2455,23 @@
 
         String caps = getScanResultCapsForType(scanResultType);
 
+        // Scan results have increasing RSSI.
         scanResults[0].SSID = ssid1;
         scanResults[0].BSSID = TEST_BSSID_1;
         scanResults[0].capabilities = caps;
+        scanResults[0].level = -45;
         scanResults[1].SSID = ssid2;
         scanResults[1].BSSID = TEST_BSSID_2;
         scanResults[1].capabilities = caps;
+        scanResults[1].level = -35;
         scanResults[2].SSID = ssid3;
         scanResults[2].BSSID = TEST_BSSID_3;
         scanResults[2].capabilities = caps;
+        scanResults[2].level = -25;
         scanResults[3].SSID = ssid4;
         scanResults[3].BSSID = TEST_BSSID_4;
         scanResults[3].capabilities = caps;
+        scanResults[3].level = -15;
     }
 
     private void validateScanResults(
@@ -2463,4 +2565,22 @@
         assertTrue((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0);
         assertTrue((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
     }
+
+    private void validateConnectParams(String ssid, String bssid) {
+        ArgumentCaptor<WifiConfiguration> wifiConfigurationCaptor =
+                ArgumentCaptor.forClass(WifiConfiguration.class);
+        verify(mWifiConfigManager).addOrUpdateNetwork(
+                wifiConfigurationCaptor.capture(), eq(TEST_UID_1), eq(TEST_PACKAGE_NAME_1));
+        WifiConfiguration network =  wifiConfigurationCaptor.getValue();
+        assertNotNull(network);
+        WifiConfiguration expectedWifiConfiguration =
+                new WifiConfiguration(((WifiNetworkSpecifier) mNetworkRequest.networkCapabilities
+                        .getNetworkSpecifier()).wifiConfiguration);
+        expectedWifiConfiguration.SSID = ssid;
+        expectedWifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
+        expectedWifiConfiguration.BSSID = bssid;
+        expectedWifiConfiguration.ephemeral = true;
+        expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
+        WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 9cefc2e..ec79e57 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -16,11 +16,14 @@
 
 package com.android.server.wifi;
 
+import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
+
 import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_EAP;
 import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE;
 import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
 import static com.android.server.wifi.WifiNetworkSelector.experimentIdFromIdentifier;
 
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
@@ -38,11 +41,12 @@
 
 import com.android.internal.R;
 import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+import com.android.server.wifi.nano.WifiMetricsProto;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -61,6 +65,7 @@
     private static final int RSSI_BUMP = 1;
     private static final int DUMMY_EVALUATOR_ID_1 = -2; // lowest index
     private static final int DUMMY_EVALUATOR_ID_2 = -1;
+    private static final HashSet<String> EMPTY_BLACKLIST = new HashSet<>();
 
     /** Sets up test. */
     @Before
@@ -81,7 +86,8 @@
                 mScoringParams,
                 mWifiConfigManager, mClock,
                 mLocalLog,
-                mWifiMetrics);
+                mWifiMetrics,
+                mWifiNative);
         mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator);
         mDummyEvaluator.setEvaluatorToSelectCandidate(true);
         when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
@@ -89,6 +95,7 @@
         when(mWifiScoreCard.lookupBssid(any(), any())).thenReturn(mPerBssid);
         mCompatibilityScorer = new CompatibilityScorer(mScoringParams);
         mScoreCardBasedScorer = new ScoreCardBasedScorer(mScoringParams);
+        when(mWifiNative.getClientInterfaceName()).thenReturn("wlan0");
     }
 
     /** Cleans up test. */
@@ -179,6 +186,7 @@
     @Mock private WifiScoreCard.PerBssid mPerBssid;
     @Mock private WifiCandidates.CandidateScorer mCandidateScorer;
     @Mock private WifiMetrics mWifiMetrics;
+    @Mock private WifiNative mWifiNative;
 
     // For simulating the resources, we use a Spy on a MockResource
     // (which is really more of a stub than a mock, in spite if its name).
@@ -699,6 +707,14 @@
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
                 blacklist, mWifiInfo, false, true, false);
 
+        ArgumentCaptor<Integer> nominatorIdCaptor = ArgumentCaptor.forClass(int.class);
+        verify(mWifiMetrics, atLeastOnce()).setNominatorForNetwork(eq(candidate.networkId),
+                nominatorIdCaptor.capture());
+        // unknown because DummyEvaluator does not have a nominator ID
+        // getValue() returns the argument from the *last* call
+        assertEquals(WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN,
+                nominatorIdCaptor.getValue().intValue());
+
         WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate);
 
         when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
@@ -710,10 +726,66 @@
         candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
                 blacklist, mWifiInfo, false, true, false);
 
+        verify(mWifiMetrics, atLeastOnce()).setNominatorForNetwork(eq(candidate.networkId),
+                nominatorIdCaptor.capture());
+        // getValue() returns the argument from the *last* call
+        assertEquals(WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED_USER_CONNECT_CHOICE,
+                nominatorIdCaptor.getValue().intValue());
         WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
     }
 
     /**
+     * Tests when multiple evaluators nominate the same candidate, any one of the nominator IDs is
+     * acceptable.
+     */
+    @Test
+    public void testMultipleEvaluatorsSetsNominatorIdCorrectly() {
+        // first dummy evaluator is registered in setup, returns index 0
+        // register a second network evaluator that also returns index 0, but with a different ID
+        mWifiNetworkSelector.registerNetworkEvaluator(new DummyNetworkEvaluator(0,
+                WifiNetworkSelector.NetworkEvaluator.EVALUATOR_ID_SCORED));
+        // register a third network evaluator that also returns index 0, but with a different ID
+        mWifiNetworkSelector.registerNetworkEvaluator(new DummyNetworkEvaluator(0,
+                WifiNetworkSelector.NetworkEvaluator.EVALUATOR_ID_SAVED));
+
+        String[] ssids = {"\"test1\"", "\"test2\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+        int[] freqs = {2437, 5180};
+        String[] caps = {"[WPA2-PSK][ESS]", "[WPA2-PSK][ESS]"};
+        int[] levels = {mThresholdMinimumRssi2G + RSSI_BUMP, mThresholdMinimumRssi5G + RSSI_BUMP};
+        int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+                        freqs, caps, levels, securities, mWifiConfigManager, mClock);
+        List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+        HashSet<String> blacklist = new HashSet<>();
+
+        // DummyEvaluator always selects the first network in the list.
+        WifiConfiguration networkSelectorChoice = scanDetailsAndConfigs.getWifiConfigs()[0];
+        networkSelectorChoice.getNetworkSelectionStatus()
+                .setSeenInLastQualifiedNetworkSelection(true);
+
+        WifiConfiguration userChoice = scanDetailsAndConfigs.getWifiConfigs()[1];
+        userChoice.getNetworkSelectionStatus()
+                .setCandidate(scanDetailsAndConfigs.getScanDetails().get(1).getScanResult());
+
+        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+                blacklist, mWifiInfo, false, true, false);
+
+        ArgumentCaptor<Integer> nominatorIdCaptor = ArgumentCaptor.forClass(int.class);
+        verify(mWifiMetrics, atLeastOnce()).setNominatorForNetwork(eq(candidate.networkId),
+                nominatorIdCaptor.capture());
+
+        for (int nominatorId : nominatorIdCaptor.getAllValues()) {
+            assertThat(nominatorId, is(oneOf(
+                    WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN,
+                    WifiMetricsProto.ConnectionEvent.NOMINATOR_EXTERNAL_SCORED,
+                    WifiMetricsProto.ConnectionEvent.NOMINATOR_SAVED)));
+        }
+    }
+
+    /**
      * Wifi network selector doesn't recommend any network if the currently connected 2.4Ghz
      * network is high quality and no 5GHz networks are available
      *
@@ -1172,6 +1244,69 @@
     }
 
     /**
+     * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} for device that
+     * supports enhanced open networks, should filter out networks that are not open and not
+     * enhanced open after network selection is made.
+     *
+     * Expected behavior: return open and enhanced open networks only
+     */
+    @Test
+    public void getfilterOpenUnsavedNetworks_filtersForOpenAndOweNetworksOweSupported() {
+        String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
+        int[] freqs = {2437, 5180, 2414};
+        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]", "[RSN-OWE-CCMP][ESS]"};
+        int[] levels = {mThresholdMinimumRssi2G, mThresholdMinimumRssi5G + RSSI_BUMP,
+                mThresholdMinimumRssi2G + RSSI_BUMP};
+        mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+        when(mWifiNative.getSupportedFeatureSet(anyString()))
+                .thenReturn(new Long(WIFI_FEATURE_OWE));
+
+        List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+                ssids, bssids, freqs, caps, levels, mClock);
+        HashSet<String> blacklist = new HashSet<>();
+
+        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+        List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
+        expectedOpenUnsavedNetworks.add(scanDetails.get(1));
+        expectedOpenUnsavedNetworks.add(scanDetails.get(2));
+        assertEquals("Expect open unsaved networks",
+                expectedOpenUnsavedNetworks,
+                mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+    }
+
+    /**
+     * {@link WifiNetworkSelector#getFilteredScanDetailsForOpenUnsavedNetworks()} for device that
+     * does not support enhanced open networks, should filter out both networks that are not open
+     * and enhanced open after network selection is made.
+     *
+     * Expected behavior: return open networks only
+     */
+    @Test
+    public void getfilterOpenUnsavedNetworks_filtersForOpenAndOweNetworksOweNotSupported() {
+        String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
+        int[] freqs = {2437, 5180, 2414};
+        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]", "[RSN-OWE-CCMP][ESS]"};
+        int[] levels = {mThresholdMinimumRssi2G, mThresholdMinimumRssi5G + RSSI_BUMP,
+                mThresholdMinimumRssi2G + RSSI_BUMP};
+        mDummyEvaluator.setEvaluatorToSelectCandidate(false);
+        when(mWifiNative.getSupportedFeatureSet(anyString()))
+                .thenReturn(new Long(~WIFI_FEATURE_OWE));
+
+        List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+                ssids, bssids, freqs, caps, levels, mClock);
+        HashSet<String> blacklist = new HashSet<>();
+
+        mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+        List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
+        expectedOpenUnsavedNetworks.add(scanDetails.get(1));
+        assertEquals("Expect open unsaved networks",
+                expectedOpenUnsavedNetworks,
+                mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
+    }
+
+    /**
      * {@link WifiNetworkSelector#getFilteredScanDetailsForCarrierUnsavedNetworks()} should filter
      * out networks that are not EAP after network selection is made.
      *
@@ -1359,8 +1494,12 @@
         assertEquals(experimentId, mScoringParams.getExperimentIdentifier());
 
         mWifiNetworkSelector.registerCandidateScorer(mCandidateScorer);
-        test2GhzHighQuality5GhzAvailable(); // calls selectNetwork twice
-        verify(mCandidateScorer, times(2)).scoreCandidates(any());
+
+        WifiConfiguration selected = mWifiNetworkSelector.selectNetwork(
+                setUpTwoNetworks(-35, -40),
+                EMPTY_BLACKLIST, mWifiInfo, false, true, true);
+
+        verify(mCandidateScorer).scoreCandidates(any());
     }
 
     /**
@@ -1370,15 +1509,16 @@
     public void testCandidateScorerMetrics_onlyOneScorer() {
         test2GhzHighQuality5GhzAvailable();
 
-        verifyNoMoreInteractions(mWifiMetrics);
+        verify(mWifiMetrics, never()).logNetworkSelectionDecision(
+                anyInt(), anyInt(), anyBoolean(), anyInt());
     }
 
     /**
-     * Tests that metrics are recorded for 2 scorers (legacy and legacy compatibility).
+     * Tests that metrics are recorded for 2 scorers (legacy and another).
      */
     @Test
     public void testCandidateScorerMetrics_twoScorers() {
-        mWifiNetworkSelector.registerCandidateScorer(mCompatibilityScorer);
+        mWifiNetworkSelector.registerCandidateScorer(mScoreCardBasedScorer);
 
         // add a second NetworkEvaluator that returns the second network in the scan list
         mWifiNetworkSelector.registerNetworkEvaluator(
@@ -1386,14 +1526,12 @@
 
         test2GhzHighQuality5GhzAvailable();
 
-        int compatibilityExpId = experimentIdFromIdentifier(mCompatibilityScorer.getIdentifier());
+        int registeredExpId = experimentIdFromIdentifier(mScoreCardBasedScorer.getIdentifier());
 
         // Wanted 2 times since test2GhzHighQuality5GhzAvailable() calls
         // WifiNetworkSelector.selectNetwork() twice
-        verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(compatibilityExpId,
+        verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(registeredExpId,
                 WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, true, 2);
-
-        verifyNoMoreInteractions(mWifiMetrics);
     }
 
     /**
@@ -1418,8 +1556,6 @@
         // WifiNetworkSelector.selectNetwork() twice
         verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(
                 WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, compatibilityExpId, true, 2);
-
-        verifyNoMoreInteractions(mWifiMetrics);
     }
 
     private static final WifiCandidates.CandidateScorer NULL_SCORER =
@@ -1462,16 +1598,13 @@
         // WifiNetworkSelector.selectNetwork() twice
         verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(nullScorerId,
                 WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, false, 2);
-
-        verifyNoMoreInteractions(mWifiMetrics);
     }
 
     /**
      * Tests that metrics are recorded for 2 scorers (legacy and null) when the active
-     * candidate scorer returns null.
+     * candidate scorer returns NONE.
      */
     @Test
-    @Ignore("TODO Until b/126273496 is resolved")
     public void testCandidateScorerMetrics_twoScorers_nullActive() {
         int nullScorerId = experimentIdFromIdentifier(NULL_SCORER.getIdentifier());
 
@@ -1485,16 +1618,31 @@
         mWifiNetworkSelector.registerNetworkEvaluator(
                 new DummyNetworkEvaluator(1, DUMMY_EVALUATOR_ID_2));
 
-        test2GhzHighQuality5GhzAvailable();
+        WifiConfiguration selected = mWifiNetworkSelector.selectNetwork(
+                setUpTwoNetworks(-35, -40),
+                EMPTY_BLACKLIST, mWifiInfo, false, true, true);
 
-        // Wanted 2 times since test2GhzHighQuality5GhzAvailable() calls
-        // WifiNetworkSelector.selectNetwork() twice
-        verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(
+        assertNull(selected);
+
+        verify(mWifiMetrics).logNetworkSelectionDecision(
                 WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, nullScorerId, false, 2);
-
+        verify(mWifiMetrics, atLeastOnce()).setNominatorForNetwork(anyInt(), anyInt());
         verifyNoMoreInteractions(mWifiMetrics);
     }
 
+    private List<ScanDetail> setUpTwoNetworks(int rssiNetwork1, int rssiNetwork2) {
+        String[] ssids = {"\"test1\"", "\"test2\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+        int[] freqs = {5180, 2437};
+        String[] caps = {"[ESS]", "[ESS]"};
+        int[] levels = {rssiNetwork1, rssiNetwork2};
+        int[] securities = {SECURITY_NONE, SECURITY_NONE};
+        ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+                WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+                        freqs, caps, levels, securities, mWifiConfigManager, mClock);
+        return scanDetailsAndConfigs.getScanDetails();
+    }
+
     /**
      * Tests that metrics are recorded for 3 scorers (legacy, compat, and null scorer).
      */
@@ -1522,8 +1670,6 @@
 
         verify(mWifiMetrics, times(2)).logNetworkSelectionDecision(
                 WifiNetworkSelector.LEGACY_CANDIDATE_SCORER_EXP_ID, compatibilityExpId, true, 2);
-
-        verifyNoMoreInteractions(mWifiMetrics);
     }
 }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
index dd78012..ca7c13e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
@@ -21,7 +21,6 @@
 import static android.app.AppOpsManager.OPSTR_CHANGE_WIFI_STATE;
 import static android.app.AppOpsManager.OP_CHANGE_WIFI_STATE;
 import static android.app.Notification.EXTRA_TEXT;
-import static android.app.Notification.EXTRA_TITLE;
 
 import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION;
 import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION;
@@ -198,9 +197,11 @@
             }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
 
         Set<WifiNetworkSuggestion> allNetworkSuggestions =
                 mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
@@ -241,9 +242,11 @@
             }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
 
         // Now remove all of them.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -283,9 +286,11 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
 
         // Now remove all of them by sending an empty list.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -311,11 +316,13 @@
             }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiNetworkSuggestionsManager.remove(networkSuggestionList1, TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         Set<WifiNetworkSuggestion> allNetworkSuggestions =
                 mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
@@ -340,7 +347,8 @@
             }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Modify the original suggestion.
         networkSuggestion.wifiConfiguration.meteredOverride =
@@ -348,7 +356,8 @@
 
         // Replace attempt should fail.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
     }
 
     /**
@@ -365,7 +374,8 @@
         }
         // The first add should succeed.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         List<WifiNetworkSuggestion> originalNetworkSuggestionsList = networkSuggestionList;
 
         // Now add 3 more.
@@ -377,7 +387,8 @@
         }
         // The second add should fail.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Now remove 3 of the initially added ones.
         networkSuggestionList = new ArrayList<>();
@@ -397,7 +408,8 @@
         }
         // This add should now succeed.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
     }
 
     /**
@@ -421,7 +433,8 @@
                 add(networkSuggestion2);
             }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         // Remove should fail because the network list is different.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
                 mWifiNetworkSuggestionsManager.remove(networkSuggestionList2, TEST_PACKAGE_1));
@@ -431,6 +444,38 @@
      * Verify a successful lookup of a single network suggestion matching the provided scan detail.
      */
     @Test
+    public void
+            testGetNetworkSuggestionsForScanDetailSuccessWithOneMatchForCarrierProvisioningApp() {
+        WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+                WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1,
+                TEST_PACKAGE_1);
+        List<WifiNetworkSuggestion> networkSuggestionList1 =
+                new ArrayList<WifiNetworkSuggestion>() {{
+                    add(networkSuggestion);
+                }};
+        // This app should be pre-approved. No need to explicitly call
+        // |setHasUserApprovedForApp(true, TEST_PACKAGE_1)|
+        when(mWifiPermissionsUtil.checkNetworkCarrierProvisioningPermission(TEST_UID_1))
+                .thenReturn(true);
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
+
+        ScanDetail scanDetail = createScanDetailForNetwork(networkSuggestion.wifiConfiguration);
+
+        Set<WifiNetworkSuggestion> matchingNetworkSuggestions =
+                mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(scanDetail);
+        Set<WifiNetworkSuggestion> expectedMatchingNetworkSuggestions =
+                new HashSet<WifiNetworkSuggestion>() {{
+                    add(networkSuggestion);
+                }};
+        assertEquals(expectedMatchingNetworkSuggestions, matchingNetworkSuggestions);
+    }
+
+    /**
+     * Verify a successful lookup of a single network suggestion matching the provided scan detail.
+     */
+    @Test
     public void testGetNetworkSuggestionsForScanDetailSuccessWithOneMatch() {
         WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
                 WifiConfigurationTestUtil.createOpenNetwork(), false, false, TEST_UID_1,
@@ -440,7 +485,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         ScanDetail scanDetail = createScanDetailForNetwork(networkSuggestion.wifiConfiguration);
@@ -478,9 +524,11 @@
             }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -513,7 +561,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         Set<WifiNetworkSuggestion> matchingNetworkSuggestions =
@@ -552,9 +601,11 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -594,7 +645,8 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         Set<WifiNetworkSuggestion> matchingNetworkSuggestions =
@@ -636,9 +688,11 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -678,7 +732,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertFalse(mWifiNetworkSuggestionsManager.hasUserApprovedForApp(TEST_PACKAGE_1));
 
         ScanDetail scanDetail = createScanDetailForNetwork(networkSuggestion.wifiConfiguration);
@@ -693,7 +748,8 @@
     public void testGetNetworkSuggestionsForScanDetailFailureOnSuggestionRemoval() {
         WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork();
         WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
-                wifiConfiguration, false, false, TEST_UID_1, TEST_PACKAGE_1);
+                wifiConfiguration, false, false, TEST_UID_1,
+                        TEST_PACKAGE_1);
         ScanDetail scanDetail = createScanDetailForNetwork(wifiConfiguration);
         List<WifiNetworkSuggestion> networkSuggestionList1 =
                 new ArrayList<WifiNetworkSuggestion>() {{
@@ -702,7 +758,8 @@
 
         // add the suggestion & ensure lookup works.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         assertNotNull(mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
                 scanDetail));
@@ -726,7 +783,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Create a scan result corresponding to a different network.
@@ -753,7 +811,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate connecting to the network.
@@ -786,7 +845,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate connecting to the network.
@@ -811,22 +871,26 @@
     public void testOnNetworkConnectionSuccessWithMultipleMatch() {
         WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork();
         WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_1, TEST_PACKAGE_1);
+                wifiConfiguration, true, false, TEST_UID_1,
+                        TEST_PACKAGE_1);
         List<WifiNetworkSuggestion> networkSuggestionList1 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion1);
                 }};
         WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_2, TEST_PACKAGE_2);
+                wifiConfiguration, true, false, TEST_UID_2,
+                        TEST_PACKAGE_2);
         List<WifiNetworkSuggestion> networkSuggestionList2 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion2);
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -869,22 +933,26 @@
         WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork();
         wifiConfiguration.BSSID = TEST_BSSID;
         WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_1, TEST_PACKAGE_1);
+                wifiConfiguration, true, false, TEST_UID_1,
+                        TEST_PACKAGE_1);
         List<WifiNetworkSuggestion> networkSuggestionList1 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion1);
                 }};
         WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_2, TEST_PACKAGE_2);
+                wifiConfiguration, true, false, TEST_UID_2,
+                        TEST_PACKAGE_2);
         List<WifiNetworkSuggestion> networkSuggestionList2 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion2);
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -928,22 +996,26 @@
         WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
         wifiConfiguration2.BSSID = TEST_BSSID;
         WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion(
-                wifiConfiguration1, true, false, TEST_UID_1, TEST_PACKAGE_1);
+                wifiConfiguration1, true, false, TEST_UID_1,
+                        TEST_PACKAGE_1);
         List<WifiNetworkSuggestion> networkSuggestionList1 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion1);
                 }};
         WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion(
-                wifiConfiguration2, true, false, TEST_UID_2, TEST_PACKAGE_2);
+                wifiConfiguration2, true, false, TEST_UID_2,
+                        TEST_PACKAGE_2);
         List<WifiNetworkSuggestion> networkSuggestionList2 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion2);
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -992,7 +1064,9 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
+        verify(mWifiPermissionsUtil).checkNetworkCarrierProvisioningPermission(TEST_UID_1);
         assertFalse(mWifiNetworkSuggestionsManager.hasUserApprovedForApp(TEST_PACKAGE_1));
 
         // Simulate connecting to the network.
@@ -1021,7 +1095,9 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
+        verify(mWifiPermissionsUtil).checkNetworkCarrierProvisioningPermission(TEST_UID_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate connecting to the network.
@@ -1050,7 +1126,9 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
+        verify(mWifiPermissionsUtil).checkNetworkCarrierProvisioningPermission(TEST_UID_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         doThrow(new SecurityException())
@@ -1082,7 +1160,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Verify config store interactions.
         verify(mWifiConfigManager).saveToStore(true);
@@ -1123,7 +1202,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1));
 
@@ -1246,7 +1326,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate connecting to the network.
@@ -1257,7 +1338,7 @@
         // Now remove the network suggestion and ensure we did not trigger a disconnect.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiNetworkSuggestionsManager.remove(networkSuggestionList, TEST_PACKAGE_1));
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
     }
 
     /**
@@ -1268,22 +1349,26 @@
     public void testRemoveAppMatchingConnectionSuccessWithMultipleMatch() {
         WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createOpenNetwork();
         WifiNetworkSuggestion networkSuggestion1 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_1, TEST_PACKAGE_1);
+                wifiConfiguration, true, false, TEST_UID_1,
+                        TEST_PACKAGE_1);
         List<WifiNetworkSuggestion> networkSuggestionList1 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion1);
                 }};
         WifiNetworkSuggestion networkSuggestion2 = new WifiNetworkSuggestion(
-                wifiConfiguration, true, false, TEST_UID_2, TEST_PACKAGE_2);
+                wifiConfiguration, true, false, TEST_UID_2,
+                        TEST_PACKAGE_2);
         List<WifiNetworkSuggestion> networkSuggestionList2 =
                 new ArrayList<WifiNetworkSuggestion>() {{
                     add(networkSuggestion2);
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_2);
 
@@ -1293,11 +1378,11 @@
 
         // Now remove one of the apps and ensure we did not trigger a disconnect.
         mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1);
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
 
         // Now remove the other app and ensure we trigger a disconnect.
         mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_2);
-        verify(mClientModeImpl).disconnectCommandInternal();
+        verify(mClientModeImpl).disconnectCommand();
     }
 
     /**
@@ -1314,7 +1399,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate connecting to some other network.
@@ -1324,7 +1410,7 @@
 
         // Now remove the app and ensure we did not trigger a disconnect.
         mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1);
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
     }
 
     /**
@@ -1341,7 +1427,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_PACKAGE_1);
 
         // Simulate failing connection to the network.
@@ -1356,7 +1443,7 @@
 
         // Now remove the app and ensure we did not trigger a disconnect.
         mWifiNetworkSuggestionsManager.removeApp(TEST_PACKAGE_1);
-        verify(mClientModeImpl, never()).disconnectCommandInternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
     }
 
     /**
@@ -1385,13 +1472,15 @@
 
         // Watch app-ops changes on first add.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         mInorder.verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE),
                 eq(TEST_PACKAGE_1), mAppOpChangedListenerCaptor.capture());
 
         // Nothing happens on second add.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Now remove first add, nothing happens.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -1419,7 +1508,8 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         Set<WifiNetworkSuggestion> allNetworkSuggestions =
                 mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
@@ -1436,7 +1526,8 @@
 
         // allow change wifi state.
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_ALLOWED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1445,7 +1536,8 @@
 
         // disallow change wifi state & ensure we remove the app from database.
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_IGNORED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1483,7 +1575,8 @@
 
         // allow change wifi state.
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_ALLOWED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1492,7 +1585,8 @@
 
         // disallow change wifi state & ensure we remove all the suggestions for that app.
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_IGNORED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1513,7 +1607,8 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         Set<WifiNetworkSuggestion> allNetworkSuggestions =
                 mWifiNetworkSuggestionsManager.getAllNetworkSuggestions();
@@ -1532,7 +1627,8 @@
         doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(
                 eq(TEST_UID_1), eq(TEST_PACKAGE_1));
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_IGNORED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1562,9 +1658,11 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
 
         // Remove all suggestions from TEST_PACKAGE_1 & TEST_PACKAGE_2, we should continue to track.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -1624,9 +1722,11 @@
                 }};
 
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList1, TEST_UID_1,
+                        TEST_PACKAGE_1));
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_PACKAGE_2));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList2, TEST_UID_2,
+                        TEST_PACKAGE_2));
 
         // Remove all suggestions from TEST_PACKAGE_1 & TEST_PACKAGE_2, we should continue to track.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -1667,7 +1767,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Simulate finding the network in scan results.
         mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1701,7 +1802,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Simulate finding the network in scan results.
         mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1735,7 +1837,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
         verify(mAppOpsManager).startWatchingMode(eq(OPSTR_CHANGE_WIFI_STATE),
                 eq(TEST_PACKAGE_1), mAppOpChangedListenerCaptor.capture());
 
@@ -1750,7 +1853,8 @@
                 NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION, TEST_PACKAGE_1, TEST_UID_1);
         // Ensure we turn off CHANGE_WIFI_STATE app-ops.
         verify(mAppOpsManager).setMode(
-                OP_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1, MODE_IGNORED);
+                OP_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1, MODE_IGNORED);
         // Cancel the notification.
         verify(mNotificationManger).cancel(SystemMessage.NOTE_NETWORK_SUGGESTION_AVAILABLE);
 
@@ -1760,7 +1864,8 @@
         AppOpsManager.OnOpChangedListener listener = mAppOpChangedListenerCaptor.getValue();
         assertNotNull(listener);
         when(mAppOpsManager.unsafeCheckOpNoThrow(
-                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1, TEST_PACKAGE_1))
+                OPSTR_CHANGE_WIFI_STATE, TEST_UID_1,
+                        TEST_PACKAGE_1))
                 .thenReturn(MODE_IGNORED);
         listener.onOpChanged(OPSTR_CHANGE_WIFI_STATE, TEST_PACKAGE_1);
         mLooper.dispatchAll();
@@ -1768,7 +1873,8 @@
 
         // Assuming the user re-enabled the app again & added the same suggestions back.
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // We should resend the notification when the network is again found in scan results.
         mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
@@ -1791,7 +1897,8 @@
                     add(networkSuggestion);
                 }};
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
-                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_PACKAGE_1));
+                mWifiNetworkSuggestionsManager.add(networkSuggestionList, TEST_UID_1,
+                        TEST_PACKAGE_1));
 
         // Simulate finding the network in scan results.
         mWifiNetworkSuggestionsManager.getNetworkSuggestionsForScanDetail(
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
index d31b0a8..28aa180 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
@@ -452,7 +452,7 @@
     }
 
     /**
-     * Installing a MemoryStore after startup should issue reads
+     * Installing a MemoryStore after startup should issue reads.
      */
     @Test
     public void testReadAfterDelayedMemoryStoreInstallation() throws Exception {
@@ -461,4 +461,16 @@
         verify(mMemoryStore).read(any(), any());
     }
 
+    /**
+     * Calling clear should forget the state.
+     */
+    @Test
+    public void testClearReallyDoesClearTheState() throws Exception {
+        byte[] serialized = makeSerializedAccessPointExample();
+        assertNotEquals(0, serialized.length);
+        mWifiScoreCard.clear();
+        byte[] leftovers = mWifiScoreCard.getNetworkListByteArray(false);
+        assertEquals(0, leftovers.length);
+    }
+
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index ffc23a5..af6693e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -85,6 +85,7 @@
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.HomeSp;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -235,6 +236,7 @@
     @Mock IOnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener;
     @Mock WifiConfigManager mWifiConfigManager;
     @Mock WifiScoreReport mWifiScoreReport;
+    @Mock WifiScoreCard mWifiScoreCard;
 
     @Spy FakeWifiLog mLog;
 
@@ -360,6 +362,7 @@
         when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager);
         when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
         when(mClientModeImpl.getWifiScoreReport()).thenReturn(mWifiScoreReport);
+        when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard);
         when(mClientModeImpl.syncStartSubscriptionProvisioning(anyInt(),
                 any(OsuProvider.class), any(IProvisioningCallback.class), any())).thenReturn(true);
         when(mPackageManager.hasSystemFeature(
@@ -2812,7 +2815,7 @@
         doThrow(new SecurityException()).when(mAppOpsManager)
                 .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
         assertTrue(mWifiServiceImpl.disconnect(TEST_PACKAGE_NAME));
-        verify(mClientModeImpl).disconnectCommandExternal();
+        verify(mClientModeImpl).disconnectCommand();
     }
 
     /**
@@ -2824,7 +2827,7 @@
     public void testDisconnectWithChangeWifiStatePerm() throws Exception {
         assertFalse(mWifiServiceImpl.disconnect(TEST_PACKAGE_NAME));
         verifyCheckChangePermission(TEST_PACKAGE_NAME);
-        verify(mClientModeImpl, never()).disconnectCommandExternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
     }
 
     /**
@@ -2843,7 +2846,7 @@
 
         }
         verifyCheckChangePermission(TEST_PACKAGE_NAME);
-        verify(mClientModeImpl, never()).disconnectCommandExternal();
+        verify(mClientModeImpl, never()).disconnectCommand();
     }
 
     @Test
@@ -3259,6 +3262,7 @@
         verify(mWifiConfigManager).clearDeletedEphemeralNetworks();
         verify(mClientModeImpl).clearNetworkRequestUserApprovedAccessPoints();
         verify(mWifiNetworkSuggestionsManager).clear();
+        verify(mWifiScoreCard).clear();
     }
 
     /**
@@ -3452,12 +3456,12 @@
     public void testAddNetworkSuggestions() {
         setupClientModeImplHandlerForRunWithScissors();
 
-        when(mWifiNetworkSuggestionsManager.add(any(), anyString()))
+        when(mWifiNetworkSuggestionsManager.add(any(), anyInt(), anyString()))
                 .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
 
-        when(mWifiNetworkSuggestionsManager.add(any(), anyString()))
+        when(mWifiNetworkSuggestionsManager.add(any(), anyInt(), anyString()))
                 .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE);
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
                 mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
@@ -3467,7 +3471,8 @@
         assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
                 mWifiServiceImpl.addNetworkSuggestions(mock(List.class), TEST_PACKAGE_NAME));
 
-        verify(mWifiNetworkSuggestionsManager, times(2)).add(any(), eq(TEST_PACKAGE_NAME));
+        verify(mWifiNetworkSuggestionsManager, times(2)).add(
+                any(), eq(Binder.getCallingUid()),  eq(TEST_PACKAGE_NAME));
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index e7a61e5..f2cc45f 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -80,6 +80,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.AsyncChannel;
+import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
 
@@ -101,6 +102,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
+
 /**
  * Unit test harness for WifiAwareDataPathStateManager class.
  */
@@ -111,6 +113,7 @@
     private TestLooper mMockLooper;
     private Handler mMockLooperHandler;
     private WifiAwareStateManager mDut;
+    @Mock private Clock mClock;
     @Mock private WifiAwareNativeManager mMockNativeManager;
     @Spy private TestUtils.MonitoredWifiAwareNativeApi mMockNative =
             new TestUtils.MonitoredWifiAwareNativeApi();
@@ -164,12 +167,13 @@
         mDut = new WifiAwareStateManager();
         mDut.setNative(mMockNativeManager, mMockNative);
         mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
-                mWifiPermissionsUtil, mPermissionsWrapperMock);
+                mWifiPermissionsUtil, mPermissionsWrapperMock, mClock);
         mDut.startLate();
         mMockLooper.dispatchAll();
 
         when(mMockNetworkInterface.configureAgentProperties(any(), any(), anyInt(), any(), any(),
                 any())).thenReturn(true);
+        when(mMockNetworkInterface.isAddressUsable(any())).thenReturn(true);
 
         when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false);
         when(mMockPowerManager.isInteractive()).thenReturn(true);
@@ -867,7 +871,7 @@
 
         testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
                 buildTlv((1 << 16) - 1, (1 << 8) - 1, true), (1 << 16) - 1, (1 << 8) - 1,
-                linkLocalIpv6Address);
+                linkLocalIpv6Address, 0);
     }
 
     /**
@@ -880,7 +884,7 @@
                 peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
 
         testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
-                buildTlv(1 << 15, 1 << 7, true), 1 << 15, 1 << 7, linkLocalIpv6Address);
+                buildTlv(1 << 15, 1 << 7, true), 1 << 15, 1 << 7, linkLocalIpv6Address, 0);
     }
 
     /**
@@ -894,7 +898,7 @@
 
         testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
                 buildTlv((1 << 15) - 1, (1 << 7) - 1, true), (1 << 15) - 1, (1 << 7) - 1,
-                linkLocalIpv6Address);
+                linkLocalIpv6Address, 0);
     }
 
     /**
@@ -908,7 +912,38 @@
                         (byte) 0xfe, 0x7a, 0x2f, (byte) 0xa2};
 
         testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
-                testVector, 0, -1, "fe80::b3:e1ff:fe7a:2fa2");
+                testVector, 0, -1, "fe80::b3:e1ff:fe7a:2fa2", 0);
+    }
+
+    /**
+     * Verify that retrying address validation a 'small' number of times results in successful
+     * NDP setup.
+     */
+    @Test
+    public void testDataPathInitiatorAddressValidationRetrySuccess() throws Exception {
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                null, 0, -1, linkLocalIpv6Address,
+                WifiAwareDataPathStateManager.ADDRESS_VALIDATION_TIMEOUT_MS
+                        / WifiAwareDataPathStateManager.ADDRESS_VALIDATION_RETRY_INTERVAL_MS - 1);
+    }
+
+    /**
+     * Verify that retrying address validation a 'large' number of times results in failure.
+     */
+    @Test
+    public void testDataPathInitiatorAddressValidationRetryFail() throws Exception {
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                null, 0, -1, linkLocalIpv6Address,
+                WifiAwareDataPathStateManager.ADDRESS_VALIDATION_TIMEOUT_MS
+                        / WifiAwareDataPathStateManager.ADDRESS_VALIDATION_RETRY_INTERVAL_MS + 10);
     }
 
     /**
@@ -1254,13 +1289,13 @@
 
         testDataPathInitiatorUtilityMore(useDirect, provideMac, providePmk, providePassphrase,
                 getConfirmation, immediateHalFailure, peerDataPathMac, null, 0, -1,
-                linkLocalIpv6Address);
+                linkLocalIpv6Address, 0);
     }
 
     private void testDataPathInitiatorUtilityMore(boolean useDirect, boolean provideMac,
             boolean providePmk, boolean providePassphrase, boolean getConfirmation,
             boolean immediateHalFailure, byte[] peerDataPathMac, byte[] peerToken, int port,
-            int transportProtocol, String ipv6Address)
+            int transportProtocol, String ipv6Address, int numAddrValidationRetries)
             throws Exception {
         final int clientId = 123;
         final byte pubSubId = 58;
@@ -1275,7 +1310,7 @@
         ArgumentCaptor<NetworkCapabilities> netCapCaptor = ArgumentCaptor.forClass(
                 NetworkCapabilities.class);
         InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback,
-                mMockNwMgt);
+                mMockNwMgt, mMockNetworkInterface);
         InOrder inOrderM = inOrder(mAwareMetricsMock);
 
         if (!providePmk) {
@@ -1332,20 +1367,56 @@
 
         // (2) get confirmation OR timeout
         if (getConfirmation) {
+            if (numAddrValidationRetries > 0) {
+                when(mMockNetworkInterface.isAddressUsable(any())).thenReturn(false);
+            }
+            when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+
             mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0, peerToken, null);
             mMockLooper.dispatchAll();
             inOrder.verify(mMockNwMgt).setInterfaceUp(anyString());
             inOrder.verify(mMockNwMgt).enableIpv6(anyString());
+            inOrder.verify(mMockNetworkInterface).configureAgentProperties(any(), any(), anyInt(),
+                    any(), any(), any());
             inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(),
                     netCapCaptor.capture(), anyInt(), any(), anyInt());
-            inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS),
-                    eq(useDirect), anyLong());
-            inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
-            WifiAwareNetworkInfo netInfo =
-                    (WifiAwareNetworkInfo) netCapCaptor.getValue().getTransportInfo();
-            assertEquals(ipv6Address, netInfo.getPeerIpv6Addr().getHostAddress());
-            assertEquals(port, netInfo.getPort());
-            assertEquals(transportProtocol, netInfo.getTransportProtocol());
+
+            inOrder.verify(mMockNetworkInterface).isAddressUsable(any());
+            boolean timedout = false;
+            for (int i = 0; i < numAddrValidationRetries; ++i) {
+                if (i == numAddrValidationRetries - 1) {
+                    when(mMockNetworkInterface.isAddressUsable(any())).thenReturn(true);
+                }
+
+                long currentTime = (i + 1L)
+                        * WifiAwareDataPathStateManager.ADDRESS_VALIDATION_RETRY_INTERVAL_MS;
+                when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+                mMockLooper.moveTimeForward(
+                        WifiAwareDataPathStateManager.ADDRESS_VALIDATION_RETRY_INTERVAL_MS + 1);
+                mMockLooper.dispatchAll();
+                inOrder.verify(mMockNetworkInterface).isAddressUsable(any());
+
+                if (currentTime > WifiAwareDataPathStateManager.ADDRESS_VALIDATION_TIMEOUT_MS) {
+                    timedout = true;
+                    break;
+                }
+            }
+
+            if (timedout) {
+                verifyUnfullfillableDispatched(res.mReverseMessenger);
+                inOrder.verify(mMockNative).endDataPath(transactionId.capture(), eq(ndpId));
+                mDut.onEndDataPathResponse(transactionId.getValue(), true, 0);
+            } else {
+                inOrder.verify(mMockNetworkInterface).sendAgentNetworkInfo(any(), any());
+                inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS),
+                        eq(useDirect), anyLong());
+                inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
+                WifiAwareNetworkInfo netInfo =
+                        (WifiAwareNetworkInfo) netCapCaptor.getValue().getTransportInfo();
+                assertEquals(ipv6Address, netInfo.getPeerIpv6Addr().getHostAddress());
+                assertEquals(port, netInfo.getPort());
+                assertEquals(transportProtocol, netInfo.getTransportProtocol());
+            }
         } else {
             assertTrue(mAlarmManager.dispatch(
                     WifiAwareStateManager.HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG));
@@ -1377,7 +1448,8 @@
             inOrderM.verify(mAwareMetricsMock).recordNdpSessionDuration(anyLong());
         }
 
-        verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt);
+        verifyNoMoreInteractions(mMockNative, mMockCm, mAwareMetricsMock, mMockNwMgt,
+                mMockNetworkInterface);
     }
 
     private void testDataPathResponderUtility(boolean useDirect, boolean provideMac,
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
index 8fd38cc..1204c4f 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 
+
 /**
  * Unit test harness for WifiAwareStateManager.
  */
@@ -151,7 +152,7 @@
                 mock(WifiAwareNativeManager.class), mock(WifiAwareNativeApi.class),
                 mock(WifiAwareNativeCallback.class));
         verify(mAwareStateManagerMock).start(eq(mContextMock), any(), eq(mAwareMetricsMock),
-                eq(mWifiPermissionsUtil), eq(mPermissionsWrapperMock));
+                eq(mWifiPermissionsUtil), eq(mPermissionsWrapperMock), any());
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
index 920341d..b4f93c1 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
@@ -73,6 +73,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
 
@@ -170,7 +171,7 @@
         mDut = new WifiAwareStateManager();
         mDut.setNative(mMockNativeManager, mMockNative);
         mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
-                mWifiPermissionsUtil, mPermissionsWrapperMock);
+                mWifiPermissionsUtil, mPermissionsWrapperMock, new Clock());
         mDut.startLate();
         mMockLooper.dispatchAll();
         verify(mMockContext, times(3)).registerReceiver(bcastRxCaptor.capture(),
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index 2442751..8503c45 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -1586,7 +1586,7 @@
         try {
             TelephonyManager telephonyManager = mock(TelephonyManager.class);
             when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager);
-            when(telephonyManager.getNetworkOperator()).thenReturn("123456");
+            when(telephonyManager.getSimOperator()).thenReturn("123456");
             PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative,
                     mWifiKeyStore, mClock,
                     mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore,
@@ -1611,8 +1611,8 @@
             TelephonyManager telephonyManager = mock(TelephonyManager.class);
             String mccmnc = "123456";
             when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager);
-            when(telephonyManager.getNetworkOperator()).thenReturn(mccmnc);
-            when(telephonyManager.getNetworkOperatorName()).thenReturn("test");
+            when(telephonyManager.getSimOperator()).thenReturn(mccmnc);
+            when(telephonyManager.getSimOperatorName()).thenReturn("test");
 
             PasspointManager passpointManager = new PasspointManager(mContext, mWifiNative,
                     mWifiKeyStore, mClock,
@@ -1693,7 +1693,7 @@
         try {
             TelephonyManager telephonyManager = mock(TelephonyManager.class);
             when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager);
-            when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC);
+            when(telephonyManager.getSimOperator()).thenReturn(TEST_MCC_MNC);
             when(mWifiConfigManager.isSimPresent()).thenReturn(true);
             List<ScanDetail> scanDetails = new ArrayList<>();
             scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, TEST_HESSID,
@@ -1737,7 +1737,7 @@
         try {
             TelephonyManager telephonyManager = mock(TelephonyManager.class);
             when(TelephonyManager.from(any(Context.class))).thenReturn(telephonyManager);
-            when(telephonyManager.getNetworkOperator()).thenReturn(TEST_MCC_MNC);
+            when(telephonyManager.getSimOperator()).thenReturn(TEST_MCC_MNC);
             when(mWifiConfigManager.isSimPresent()).thenReturn(true);
             List<ScanDetail> scanDetails = new ArrayList<>();
             scanDetails.add(generateScanDetail(TEST_SSID, TEST_BSSID_STRING, 0, 0, false));
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
index 76a2e65..61d0c37 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
@@ -145,7 +145,7 @@
         mEvaluator = new PasspointNetworkEvaluator(mPasspointManager, mWifiConfigManager,
                 mLocalLog, mCarrierNetworkConfig, mTelephonyManager);
         when(mWifiConfigManager.isSimPresent()).thenReturn(true);
-        when(mTelephonyManager.getNetworkOperator()).thenReturn("123456");
+        when(mTelephonyManager.getSimOperator()).thenReturn("123456");
     }
 
     /**
@@ -412,6 +412,31 @@
     }
 
     /**
+     * Verify that it never creates an ephemeral Passpoint Configuration when the carrier is not
+     * MNO.
+     */
+    @Test
+    public void skipCreateEphemeralPasspointConfigurationForNonMNO() {
+        // Setup ScanDetail and match providers.
+        List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{
+                generateScanDetail(TEST_SSID1, TEST_BSSID1)});
+        when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+
+        // MVNO carrier is enabled.
+        when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1);
+        when(mTelephonyManager.getSimCarrierId()).thenReturn(20);
+        when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
+        when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false);
+        when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier(
+                any(List.class))).thenReturn(
+                EAPConstants.EAP_AKA);
+
+        assertEquals(null, mEvaluator.evaluateNetworks(
+                scanDetails, null, null, false, false, mOnConnectableListener));
+        verify(mPasspointManager, never()).createEphemeralPasspointConfigForCarrier(anyInt());
+    }
+
+    /**
      * Verify that it never creates an ephemeral Passpoint Configuration when the profile for the
      * carrier already exists.
      */
@@ -437,6 +462,11 @@
         // Setup ScanDetail
         List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{
                 generateScanDetail(TEST_SSID1, TEST_BSSID1)});
+        when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+
+        // MNO carrier is enabled.
+        when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1);
+        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
         when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
         when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false);
         when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier(
@@ -459,6 +489,11 @@
         // Setup ScanDetail
         List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[]{
                 generateScanDetail(TEST_SSID1, TEST_BSSID1)});
+        when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+
+        // MNO carrier is enabled.
+        when(mTelephonyManager.getCarrierIdFromSimMccMnc()).thenReturn(1);
+        when(mTelephonyManager.getSimCarrierId()).thenReturn(1);
         when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true);
         when(mPasspointManager.hasCarrierProvider(anyString())).thenReturn(false);
         when(mPasspointManager.findEapMethodFromNAIRealmMatchedWithCarrier(
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
index 0dcf522..c83023e 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
@@ -199,12 +199,10 @@
         mSession = ExtendedMockito.mockitoSession().mockStatic(
                 RedirectListener.class).mockStatic(PpsMoParser.class).mockStatic(
                 UpdateResponseMessage.class).startMocking();
-
-        when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn(
-                mRedirectListener);
+        when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn(mRedirectListener);
         when(mRedirectListener.getServerUrl()).thenReturn(new URL(TEST_REDIRECT_URL));
         when(mRedirectListener.startServer(
-                any(RedirectListener.RedirectCallback.class))).thenReturn(true);
+                any(RedirectListener.RedirectCallback.class), any(Handler.class))).thenReturn(true);
         when(mRedirectListener.isAlive()).thenReturn(true);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
         when(mObjectFactory.makeOsuNetworkConnection(any(Context.class)))
@@ -356,7 +354,8 @@
                 verify(mCallback).onProvisioningStatus(
                         ProvisioningCallback.OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE);
                 verify(mRedirectListener, atLeastOnce())
-                        .startServer(mOnRedirectReceivedArgumentCaptor.capture());
+                        .startServer(mOnRedirectReceivedArgumentCaptor.capture(),
+                                any(Handler.class));
                 mRedirectReceivedListener = mOnRedirectReceivedArgumentCaptor.getValue();
                 verifyNoMoreInteractions(mCallback);
             } else if (step == STEP_WAIT_FOR_SECOND_SOAP_RESPONSE) {
@@ -368,7 +367,7 @@
                 mRedirectReceivedListener.onRedirectReceived();
                 mLooper.dispatchAll();
 
-                verify(mRedirectListener, atLeastOnce()).stopServer();
+                verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class));
                 verify(mCallback).onProvisioningStatus(
                         ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED);
                 verify(mCallback).onProvisioningStatus(
@@ -520,6 +519,35 @@
     }
 
     /**
+     * Verifies existing provisioning flow is aborted when failing to create an instance of {@link
+     * RedirectListener}.
+     */
+    @Test
+    public void verifyRedirectStartFailure() throws RemoteException {
+        when(RedirectListener.createInstance(mLooper.getLooper())).thenReturn(null);
+        mPasspointProvisioner.init(mLooper.getLooper());
+        verify(mOsuNetworkConnection).init(mHandlerCaptor.capture());
+
+        mHandler = mHandlerCaptor.getValue();
+        assertEquals(mHandler.getLooper(), mLooper.getLooper());
+
+        mLooper.dispatchAll();
+
+        assertTrue(mPasspointProvisioner.startSubscriptionProvisioning(
+                TEST_UID, mOsuProvider, mCallback));
+
+        mLooper.dispatchAll();
+
+        // Since creating an instance of RedirectListener, directly move to FAILED_STATE
+        verify(mCallback).onProvisioningFailure(
+                ProvisioningCallback.OSU_FAILURE_START_REDIRECT_LISTENER);
+
+        // Failure case, no more runnable posted
+        verifyNoMoreInteractions(mCallback);
+
+    }
+
+    /**
      * Verifies that if connection attempt to OSU AP fails, corresponding error callback is invoked.
      */
     @Test
@@ -775,7 +803,7 @@
         mRedirectReceivedListener.onRedirectTimedOut();
         mLooper.dispatchAll();
 
-        verify(mRedirectListener, atLeastOnce()).stopServer();
+        verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class));
         verify(mCallback).onProvisioningFailure(
                 ProvisioningCallback.OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER);
         // No further runnable posted
@@ -794,7 +822,7 @@
         mRedirectReceivedListener.onRedirectReceived();
         mLooper.dispatchAll();
 
-        verify(mRedirectListener, atLeastOnce()).stopServer();
+        verify(mRedirectListener, atLeastOnce()).stopServer(any(Handler.class));
         verify(mCallback).onProvisioningStatus(
                 ProvisioningCallback.OSU_STATUS_REDIRECT_RESPONSE_RECEIVED);
         verify(mCallback).onProvisioningStatus(
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java
index 308bb47..d61a59d 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/soap/RedirectListenerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.os.Handler;
 import android.os.Looper;
 import android.os.test.TestLooper;
 
@@ -47,6 +48,7 @@
     private RedirectListenerSpy mRedirectListener;
     private URL mServerUrl;
     private TestLooper mLooper = new TestLooper();
+    private Handler mHandler = new Handler(mLooper.getLooper());
 
     @Mock RedirectListener.RedirectCallback mListener;
     @Mock NanoHTTPD.IHTTPSession mIHTTPSession;
@@ -55,7 +57,7 @@
     private class RedirectListenerSpy extends RedirectListener {
         boolean mIsStart = false;
         RedirectListenerSpy(Looper looper, int port) throws IOException {
-            super(looper, looper, port);
+            super(looper, port);
         }
 
         @Override
@@ -80,20 +82,19 @@
     @Before
     public void setUp() throws Exception {
         initMocks(this);
-
         mRedirectListener = new RedirectListenerSpy(mLooper.getLooper(), TEST_PORT);
         mServerUrl = mRedirectListener.getServerUrl();
     }
 
     private void verifyStartServer() {
-        mRedirectListener.startServer(mListener);
+        mRedirectListener.startServer(mListener, mHandler);
         mLooper.dispatchAll();
 
         assertTrue(mRedirectListener.mIsStart);
     }
 
     private void verifyStopServer() {
-        mRedirectListener.stopServer();
+        mRedirectListener.stopServer(mHandler);
         mLooper.dispatchAll();
 
         assertFalse(mRedirectListener.mIsStart);
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
index 19a5aa5..cd61aac 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
@@ -93,6 +93,7 @@
 import java.util.Map;
 import java.util.Set;
 
+
 /**
  * Unit test harness for the RttServiceImpl class.
  */
@@ -253,8 +254,12 @@
         RangingRequest[] requests = new RangingRequest[numIter];
         List<Pair<List<RttResult>, List<RangingResult>>> results = new ArrayList<>();
 
-        for (int i = 0; i < numIter; ++i) {
-            requests[i] = RttTestUtils.getDummyRangingRequest((byte) i);
+        for (int i = 0; i < numIter; ++i) { // even: MC, non-MC, Aware, odd: MC only
+            if (i % 2 == 0) {
+                requests[i] = RttTestUtils.getDummyRangingRequestMcOnly((byte) i);
+            } else {
+                requests[i] = RttTestUtils.getDummyRangingRequest((byte) i);
+            }
             results.add(RttTestUtils.getDummyRangingResults(requests[i]));
         }
 
@@ -267,7 +272,7 @@
         for (int i = 0; i < numIter; ++i) {
             // (2) verify that request issued to native
             verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(requests[i]), eq(true));
-            verifyWakeupSet();
+            verifyWakeupSet(i % 2 != 0, 0);
 
             // (3) native calls back with result
             mDut.onRangingResults(mIntCaptor.getValue(), results.get(i).first);
@@ -324,7 +329,7 @@
 
         // verify that requested with MAC address translated from the PeerHandle issued to Native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), mRequestCaptor.capture(), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         RangingRequest finalRequest = mRequestCaptor.getValue();
         assertNotEquals("Request to native is not null", null, finalRequest);
@@ -398,7 +403,7 @@
             if (i == 0) {
                 verify(mockCallback).onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
             } else {
-                verifyWakeupSet();
+                verifyWakeupSet(true, 0);
             }
 
             // (4) on failed HAL: even if native calls back with result we shouldn't dispatch
@@ -443,7 +448,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) native calls back with result - should get a FAILED callback
         when(mockPermissionUtil.checkCallersLocationPermission(eq(mPackageName),
@@ -494,7 +499,7 @@
             // (3) verify first request and all odd requests issued to HAL
             if (i == 0 || i % 2 == 1) {
                 verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(requests[i]), eq(true));
-                verifyWakeupSet();
+                verifyWakeupSet(true, 0);
             }
 
             // (4) trigger first death recipient (which will map to the even UID)
@@ -563,7 +568,7 @@
 
         verify(mockIbinder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (2) execute binder death
         mDeathRecipientCaptor.getValue().binderDied();
@@ -613,7 +618,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) cancel the request
         mDut.cancelRanging(worksourceCancel);
@@ -659,7 +664,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) cancel the request
         mDut.cancelRanging(worksourceCancel);
@@ -697,7 +702,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) native calls back with result - but wrong ID
         mDut.onRangingResults(mIntCaptor.getValue() + 1,
@@ -748,7 +753,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) return results with missing entries
         mDut.onRangingResults(mIntCaptor.getValue(), results.first);
@@ -791,7 +796,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) return results with ALL results missing
         mDut.onRangingResults(mIntCaptor.getValue(), new ArrayList<>());
@@ -844,7 +849,7 @@
 
         // (2) verify that request issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(false));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) return results with missing entries
         mDut.onRangingResults(mIntCaptor.getValue(), results.first);
@@ -886,7 +891,7 @@
         // verify that request 1 issued to native
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request1), eq(true));
         int cmdId1 = mIntCaptor.getValue();
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (2) time-out
         mAlarmManager.dispatch(RttServiceImpl.HAL_RANGING_TIMEOUT_TAG);
@@ -896,7 +901,7 @@
         verify(mockNative).rangeCancel(eq(cmdId1), any());
         verify(mockCallback).onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request2), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (3) send both result 1 and result 2
         mDut.onRangingResults(cmdId1, result1.first);
@@ -955,7 +960,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request1), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, clock.time);
 
         // (1.1) get result
         mDut.onRangingResults(mIntCaptor.getValue(), result1.first);
@@ -977,7 +982,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request3), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, clock.time);
 
         // (3.1) get result
         mDut.onRangingResults(mIntCaptor.getValue(), result3.first);
@@ -995,7 +1000,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request4), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, clock.time);
 
         // (4.1) get result
         mDut.onRangingResults(mIntCaptor.getValue(), result4.first);
@@ -1086,7 +1091,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request1), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, clock.time);
 
         // (1.1) get result
         mDut.onRangingResults(mIntCaptor.getValue(), result1.first);
@@ -1102,7 +1107,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request2), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, clock.time);
 
         // (2.1) get result
         mDut.onRangingResults(mIntCaptor.getValue(), result2.first);
@@ -1171,7 +1176,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // 2. issue FLOOD LEVEL requests + 10 at various UIDs - no failure expected
         for (int i = 0; i < RttServiceImpl.MAX_QUEUED_PER_UID + 10; ++i) {
@@ -1237,7 +1242,7 @@
         mMockLooper.dispatchAll();
 
         nativeInorder.verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // 2. issue FLOOD LEVEL requests + 10: should get 11 failures (10 extra + 1 original)
         for (int i = 0; i < RttServiceImpl.MAX_QUEUED_PER_UID + 10; ++i) {
@@ -1257,7 +1262,7 @@
         verifyWakeupCancelled();
 
         nativeInorder.verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // 4. issue a request: don't expect a failure
         mDut.startRanging(mockIbinder, mPackageName, useUids ? null : ws, request, mockCallback);
@@ -1337,7 +1342,7 @@
         mMockLooper.dispatchAll();
 
         verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request1), eq(true));
-        verifyWakeupSet();
+        verifyWakeupSet(true, 0);
 
         // (2) disable RTT: all requests should "fail"
         if (failureMode == FAILURE_MODE_DISABLE_WIFI) {
@@ -1416,10 +1421,15 @@
         mLocationModeReceiver.onReceive(mockContext, intent);
     }
 
-    private void verifyWakeupSet() {
-        mInOrder.verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+    private void verifyWakeupSet(boolean useAwareTimeout, long baseTime) {
+        ArgumentCaptor<Long> longCaptor = ArgumentCaptor.forClass(Long.class);
+
+        mInOrder.verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), longCaptor.capture(),
                 eq(RttServiceImpl.HAL_RANGING_TIMEOUT_TAG), any(AlarmManager.OnAlarmListener.class),
                 any(Handler.class));
+
+        assertEquals(baseTime + (useAwareTimeout ? RttServiceImpl.HAL_AWARE_RANGING_TIMEOUT_MS
+                : RttServiceImpl.HAL_RANGING_TIMEOUT_MS), longCaptor.getValue().longValue());
     }
 
     private void verifyWakeupCancelled() {
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
index 8d3a5cb..c5385eb 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
@@ -78,6 +78,23 @@
     }
 
     /**
+     * Returns a dummy ranging request with 2 requests:
+     * - First: 802.11mc capable
+     */
+    public static RangingRequest getDummyRangingRequestMcOnly(byte lastMacByte) {
+        RangingRequest.Builder builder = new RangingRequest.Builder();
+
+        ScanResult scan1 = new ScanResult();
+        scan1.BSSID = "00:01:02:03:04:" + String.format("%02d", lastMacByte);
+        scan1.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+        scan1.channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ;
+
+        builder.addAccessPoint(scan1);
+
+        return builder.build();
+    }
+
+    /**
      * Returns a dummy ranging request with 2 requests - neither of which support 802.11mc.
      */
     public static RangingRequest getDummyRangingRequestNo80211mcSupport(byte lastMacByte) {
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
index 356b50f..c2c9744 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -73,6 +73,7 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.test.BidirectionalAsyncChannel;
+import com.android.server.wifi.CellularLinkLayerStatsCollector;
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.DppMetrics;
 import com.android.server.wifi.FakeWifiLog;
@@ -132,7 +133,7 @@
     TestLooper mLooper;
     WifiScanningServiceImpl mWifiScanningServiceImpl;
     @Mock WifiP2pMetrics mWifiP2pMetrics;
-
+    @Mock CellularLinkLayerStatsCollector mCellularLinkLayerStatsCollector;
 
     @Before
     public void setUp() throws Exception {
@@ -152,7 +153,7 @@
         mLooper = new TestLooper();
         mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, mLooper.getLooper(),
                 new WifiAwareMetrics(mClock), new RttMetrics(mClock), new WifiPowerMetrics(),
-                mWifiP2pMetrics, mDppMetrics);
+                mWifiP2pMetrics, mDppMetrics, mCellularLinkLayerStatsCollector);
         when(mWifiScannerImplFactory
                 .create(any(), any(), any()))
                 .thenReturn(mWifiScannerImpl);
diff --git a/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java b/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
index 5e4f6cc..b707698 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/DataIntegrityCheckerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.*;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.io.File;
@@ -38,6 +39,7 @@
      * @throws Exception
      */
     @Test
+    @Ignore
     public void testIntegrityWithKnownDataAndKnownAlias() throws Exception {
         File integrityFile = File.createTempFile("testIntegrityWithKnownDataAndKnownAlias",
                 ".tmp");
@@ -56,6 +58,7 @@
      * @throws Exception
      */
     @Test
+    @Ignore
     public void testIntegrityWithUnknownDataAndKnownAlias() throws Exception {
         File integrityFile = File.createTempFile("testIntegrityWithUnknownDataAndKnownAlias",
                 ".tmp");
@@ -72,6 +75,7 @@
      * @throws Exception
      */
     @Test(expected = DigestException.class)
+    @Ignore
     public void testIntegrityWithoutUpdate() throws Exception {
         File tmpFile = File.createTempFile("testIntegrityWithoutUpdate", ".tmp");
 
diff --git a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java
index 112add8..8c9dcbd 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi.util;
 
 import static org.junit.Assert.*;
-import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Mockito.*;
 
 import android.net.wifi.WifiConfiguration;
@@ -29,23 +28,41 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.wifi.CarrierNetworkConfig;
 import com.android.server.wifi.WifiConfigurationTestUtil;
 import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
 import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
 
 import java.security.PublicKey;
 
+import javax.crypto.Cipher;
+
 /**
  * Unit tests for {@link com.android.server.wifi.util.TelephonyUtil}.
  */
 @SmallTest
 public class TelephonyUtilTest {
+    private TelephonyUtil mTelephonyUtil;
+
+    @Mock
+    CarrierNetworkConfig mCarrierNetworkConfig;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mCarrierNetworkConfig.getBase64EncodingFlag()).thenReturn(Base64.DEFAULT);
+        mTelephonyUtil = new TelephonyUtil();
+    }
     @Test
     public void getSimIdentityEapSim() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         final Pair<String, String> expectedIdentity = Pair.create(
                 "13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", "");
 
@@ -54,18 +71,17 @@
         when(tm.getSimOperator()).thenReturn("321456");
         when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM,
-                        WifiEnterpriseConfig.Phase2.NONE)));
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP,
-                        WifiEnterpriseConfig.Phase2.SIM)));
+                        WifiEnterpriseConfig.Phase2.SIM), mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentityEapAka() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         final Pair<String, String> expectedIdentity = Pair.create(
                 "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", "");
         when(tm.getSubscriberId()).thenReturn("3214561234567890");
@@ -74,18 +90,17 @@
         when(tm.getSimOperator()).thenReturn("321456");
         when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
-                        WifiEnterpriseConfig.Phase2.NONE)));
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP,
-                        WifiEnterpriseConfig.Phase2.AKA)));
+                        WifiEnterpriseConfig.Phase2.AKA), mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentityEapAkaPrime() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         final Pair<String, String> expectedIdentity = Pair.create(
                 "63214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", "");
 
@@ -94,76 +109,95 @@
         when(tm.getSimOperator()).thenReturn("321456");
         when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(
                         WifiEnterpriseConfig.Eap.AKA_PRIME,
-                        WifiEnterpriseConfig.Phase2.NONE)));
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.PEAP,
-                        WifiEnterpriseConfig.Phase2.AKA_PRIME)));
+                        WifiEnterpriseConfig.Phase2.AKA_PRIME), mCarrierNetworkConfig));
     }
 
     /**
-     * Verify that an expected identity is returned when using the encrypted IMSI.
-     *
-     * @throws Exception
+     * Verify that an expected identity is returned when using the encrypted IMSI encoded by
+     * RFC4648.
      */
     @Test
-    public void getEncryptedIdentityImsi() throws Exception {
+    public void getEncryptedIdentityImsiWithRfc4648() throws Exception {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
-        String encryptedImsi = "EncryptedIMSI";
+        Cipher cipher = mock(Cipher.class);
+        PublicKey key = null;
+        int flag = Base64.NO_WRAP;
+        String imsi = "3214561234567890";
+        String encryptedImsi = Base64.encodeToString(imsi.getBytes(), 0, imsi.getBytes().length,
+                flag);
         String encryptedIdentity = "\0" + encryptedImsi + "@wlan.mnc456.mcc321.3gppnetwork.org";
         final Pair<String, String> expectedIdentity = Pair.create(
                 "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", encryptedIdentity);
-        PublicKey key = null;
 
-        when(tm.getSubscriberId()).thenReturn("3214561234567890");
-        when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
-        when(tm.getSimOperator()).thenReturn("321456");
-        ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456",
-                TelephonyManager.KEY_TYPE_WLAN, null, key, null);
-        when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN)))
-                .thenReturn(info);
+        // static mocking
+        MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(
+                Cipher.class).startMocking();
+        try {
+            when(Cipher.getInstance(anyString())).thenReturn(cipher);
+            when(cipher.doFinal(any(byte[].class))).thenReturn(imsi.getBytes());
+            when(tm.getSubscriberId()).thenReturn(imsi);
+            when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+            when(tm.getSimOperator()).thenReturn("321456");
+            ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456",
+                    TelephonyManager.KEY_TYPE_WLAN, null, key, null);
+            when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN)))
+                    .thenReturn(info);
+            when(mCarrierNetworkConfig.getBase64EncodingFlag()).thenReturn(flag);
 
-        when(telephonyUtil.encryptDataUsingPublicKey(any(), any())).thenReturn(encryptedImsi);
-
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
-                WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
-                        WifiEnterpriseConfig.Phase2.NONE)));
+            assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
+                    WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
+                            WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
+        } finally {
+            session.finishMocking();
+        }
     }
 
     /**
-     * Verify that an expected identity is returned when using the encrypted IMSI with key
-     * identifier.
-     *
-     * @throws Exception
+     * Verify that an expected identity is returned when using the encrypted IMSI encoded by RFC2045
+     * with key identifier.
      */
     @Test
-    public void getEncryptedIdentityKeyIdentifier() throws Exception {
+    public void getEncryptedIdentityKeyIdentifierWithRfc2045() throws Exception {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
+        Cipher cipher = mock(Cipher.class);
+        int flag = Base64.DEFAULT;
         PublicKey key = null;
         String keyIdentifier = "key=testKey";
-        String encryptedImsi = "EncryptedIMSI";
+        String imsi = "3214561234567890";
+        String encryptedImsi = Base64.encodeToString(imsi.getBytes(), 0, imsi.getBytes().length,
+                flag);
         String encryptedIdentity = "\0" + encryptedImsi + "@wlan.mnc456.mcc321.3gppnetwork.org,"
                 + keyIdentifier;
         final Pair<String, String> expectedIdentity = Pair.create(
                 "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", encryptedIdentity);
 
-        when(tm.getSubscriberId()).thenReturn("3214561234567890");
-        when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
-        when(tm.getSimOperator()).thenReturn("321456");
-        ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456",
-                TelephonyManager.KEY_TYPE_WLAN, keyIdentifier, key, null);
-        when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN)))
-                .thenReturn(info);
+        // static mocking
+        MockitoSession session = ExtendedMockito.mockitoSession().mockStatic(
+                Cipher.class).startMocking();
+        try {
+            when(Cipher.getInstance(anyString())).thenReturn(cipher);
+            when(cipher.doFinal(any(byte[].class))).thenReturn(imsi.getBytes());
+            when(tm.getSubscriberId()).thenReturn(imsi);
+            when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+            when(tm.getSimOperator()).thenReturn("321456");
+            ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456",
+                    TelephonyManager.KEY_TYPE_WLAN, keyIdentifier, key, null);
+            when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN)))
+                    .thenReturn(info);
+            when(mCarrierNetworkConfig.getBase64EncodingFlag()).thenReturn(flag);
 
-        when(telephonyUtil.encryptDataUsingPublicKey(any(), any())).thenReturn(encryptedImsi);
-
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
-                WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
-                        WifiEnterpriseConfig.Phase2.NONE)));
+            assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
+                    WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
+                            WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
+        } finally {
+            session.finishMocking();
+        }
     }
 
     /**
@@ -174,8 +208,6 @@
     @Test
     public void getEncryptedIdentityFailed() throws Exception {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
-        PublicKey key = null;
         String imsi = "3214561234567890";
         final Pair<String, String> expectedIdentity = Pair.create(
                 "03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org", "");
@@ -183,22 +215,17 @@
         when(tm.getSubscriberId()).thenReturn("3214561234567890");
         when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
         when(tm.getSimOperator()).thenReturn("321456");
-        ImsiEncryptionInfo info = new ImsiEncryptionInfo("321", "456",
-                TelephonyManager.KEY_TYPE_WLAN, null, key, null);
         when(tm.getCarrierInfoForImsiEncryption(eq(TelephonyManager.KEY_TYPE_WLAN)))
-                .thenReturn(info);
-        when(telephonyUtil.encryptDataUsingPublicKey(any(), aryEq(imsi.getBytes())))
                 .thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.AKA,
-                        WifiEnterpriseConfig.Phase2.NONE)));
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentity2DigitMnc() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         final Pair<String, String> expectedIdentity = Pair.create(
                 "1321560123456789@wlan.mnc056.mcc321.3gppnetwork.org", "");
 
@@ -207,15 +234,14 @@
         when(tm.getSimOperator()).thenReturn("32156");
         when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM,
-                        WifiEnterpriseConfig.Phase2.NONE)));
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentityUnknownMccMnc() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         final Pair<String, String> expectedIdentity = Pair.create(
                 "13214560123456789@wlan.mnc456.mcc321.3gppnetwork.org", "");
 
@@ -224,36 +250,39 @@
         when(tm.getSimOperator()).thenReturn(null);
         when(tm.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(null);
 
-        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(expectedIdentity, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(WifiEnterpriseConfig.Eap.SIM,
-                        WifiEnterpriseConfig.Phase2.NONE)));
+                        WifiEnterpriseConfig.Phase2.NONE), mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentityWithNoTelephonyManager() {
         assertEquals(null, TelephonyUtil.getSimIdentity(null, null,
                 WifiConfigurationTestUtil.createEapNetwork(
-                        WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+                        WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE),
+                mCarrierNetworkConfig));
     }
 
     @Test
     public void getSimIdentityNonTelephonyConfig() {
         TelephonyManager tm = mock(TelephonyManager.class);
-        TelephonyUtil telephonyUtil = mock(TelephonyUtil.class);
         when(tm.getSubscriberId()).thenReturn("321560123456789");
         when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
         when(tm.getSimOperator()).thenReturn("32156");
-        assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+        assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(
-                        WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM)));
-        assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+                        WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM),
+                mCarrierNetworkConfig));
+        assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(
-                        WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2)));
-        assertEquals(null, TelephonyUtil.getSimIdentity(tm, telephonyUtil,
+                        WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2),
+                mCarrierNetworkConfig));
+        assertEquals(null, TelephonyUtil.getSimIdentity(tm, mTelephonyUtil,
                 WifiConfigurationTestUtil.createEapNetwork(
-                        WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)));
+                        WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE),
+                mCarrierNetworkConfig));
         assertEquals(null, TelephonyUtil.getSimIdentity(
-                tm, telephonyUtil, new WifiConfiguration()));
+                tm, mTelephonyUtil, new WifiConfiguration(), mCarrierNetworkConfig));
     }
 
     @Test