Merge "[WifiTrackerLib] Fix OWE security parsing for WifiEntry" into rvc-dev
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/OsuWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/OsuWifiEntry.java
index 9aaf346..b3b99e0 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/OsuWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/OsuWifiEntry.java
@@ -35,6 +35,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
@@ -50,6 +51,9 @@
 class OsuWifiEntry extends WifiEntry {
     static final String KEY_PREFIX = "OsuWifiEntry:";
 
+    private final Object mLock = new Object();
+    // Scan result list must be thread safe for generating the verbose scan summary
+    @GuardedBy("mLock")
     @NonNull private final List<ScanResult> mCurrentScanResults = new ArrayList<>();
 
     @NonNull private final String mKey;
@@ -265,10 +269,12 @@
             throws IllegalArgumentException {
         if (scanResults == null) scanResults = new ArrayList<>();
 
-        mCurrentScanResults.clear();
-        mCurrentScanResults.addAll(scanResults);
+        synchronized (mLock) {
+            mCurrentScanResults.clear();
+            mCurrentScanResults.addAll(scanResults);
+        }
 
-        final ScanResult bestScanResult = getBestScanResultByLevel(mCurrentScanResults);
+        final ScanResult bestScanResult = getBestScanResultByLevel(scanResults);
         if (bestScanResult == null) {
             mLevel = WIFI_LEVEL_UNREACHABLE;
         } else {
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
index b26d035..e7ed153 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
@@ -40,6 +40,7 @@
 import android.os.Handler;
 import android.text.TextUtils;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
@@ -57,7 +58,11 @@
 public class PasspointWifiEntry extends WifiEntry {
     static final String KEY_PREFIX = "PasspointWifiEntry:";
 
+    private final Object mLock = new Object();
+    // Scan result list must be thread safe for generating the verbose scan summary
+    @GuardedBy("mLock")
     private final List<ScanResult> mCurrentHomeScanResults = new ArrayList<>();
+    @GuardedBy("mLock")
     private final List<ScanResult> mCurrentRoamingScanResults = new ArrayList<>();
 
     @NonNull private final String mKey;
@@ -405,13 +410,15 @@
             throws IllegalArgumentException {
         mIsRoaming = false;
         mWifiConfig = wifiConfig;
-        mCurrentHomeScanResults.clear();
-        mCurrentRoamingScanResults.clear();
-        if (homeScanResults != null) {
-            mCurrentHomeScanResults.addAll(homeScanResults);
-        }
-        if (roamingScanResults != null) {
-            mCurrentRoamingScanResults.addAll(roamingScanResults);
+        synchronized (mLock) {
+            mCurrentHomeScanResults.clear();
+            mCurrentRoamingScanResults.clear();
+            if (homeScanResults != null) {
+                mCurrentHomeScanResults.addAll(homeScanResults);
+            }
+            if (roamingScanResults != null) {
+                mCurrentRoamingScanResults.addAll(roamingScanResults);
+            }
         }
         if (mWifiConfig != null) {
             mSecurity = getSecurityTypeFromWifiConfiguration(wifiConfig);
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java
index d30fcc4..68f58dd 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardNetworkDetailsTracker.java
@@ -45,7 +45,6 @@
 
 import java.time.Clock;
 import java.util.Collections;
-import java.util.Optional;
 
 /**
  * Implementation of NetworkDetailsTracker that tracks a single StandardWifiEntry.
@@ -107,7 +106,8 @@
         checkNotNull(intent, "Intent cannot be null!");
         final WifiConfiguration updatedConfig =
                 (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
-        if (updatedConfig != null && TextUtils.equals(
+        if (updatedConfig != null && !updatedConfig.isPasspoint()
+                && !updatedConfig.fromWifiNetworkSuggestion && TextUtils.equals(
                 wifiConfigToStandardWifiEntryKey(updatedConfig), mChosenEntry.getKey())) {
             final int changeReason = intent.getIntExtra(WifiManager.EXTRA_CHANGE_REASON,
                     -1 /* defaultValue*/);
@@ -172,11 +172,15 @@
      * null if it does not exist.
      */
     private void conditionallyUpdateConfig() {
-        Optional<WifiConfiguration> optionalConfig = mWifiManager.getConfiguredNetworks()
-                .stream().filter(config -> TextUtils.equals(
-                        wifiConfigToStandardWifiEntryKey(config), mChosenEntry.getKey()))
-                .findAny();
-        mChosenEntry.updateConfig(optionalConfig.orElse(null));
+        WifiConfiguration config = mWifiManager.getConfiguredNetworks().stream()
+                .filter(savedConfig -> TextUtils.equals(
+                        wifiConfigToStandardWifiEntryKey(savedConfig), mChosenEntry.getKey()))
+                .findAny().orElse(mWifiManager.getPrivilegedConfiguredNetworks().stream()
+                        .filter(suggestedConfig -> TextUtils.equals(
+                                wifiConfigToStandardWifiEntryKey(suggestedConfig),
+                                mChosenEntry.getKey()))
+                        .findAny().orElse(null));
+        mChosenEntry.updateConfig(config);
     }
 
     /**
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
index 642313a..5f8d44d 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
@@ -46,6 +46,7 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -98,7 +99,10 @@
     private static final int PSK_WPA_WPA2 = 2;
     private static final int PSK_UNKNOWN = 3;
 
-    private final List<ScanResult> mCurrentScanResults = new ArrayList<>();
+    private final Object mLock = new Object();
+    // Scan result list must be thread safe for generating the verbose scan summary
+    @GuardedBy("mLock")
+    @NonNull private final List<ScanResult> mCurrentScanResults = new ArrayList<>();
 
     @NonNull private final String mKey;
     @NonNull private final String mSsid;
@@ -107,6 +111,7 @@
     private @EapType int mEapType = EAP_UNKNOWN;
     private @PskType int mPskType = PSK_UNKNOWN;
     @Nullable private WifiConfiguration mWifiConfig;
+    private boolean mIsUserShareable = false;
     @Nullable private String mRecommendationServiceLabel;
 
     private boolean mShouldAutoOpenCaptivePortal = false;
@@ -225,14 +230,6 @@
     private String getConnectStateDescription() {
         if (getConnectedState() == CONNECTED_STATE_CONNECTED) {
             if (!isSaved()) {
-                // For ephemeral networks.
-                final String suggestionOrSpecifierPackageName = mWifiInfo != null
-                        ? mWifiInfo.getRequestingPackageName() : null;
-                if (!TextUtils.isEmpty(suggestionOrSpecifierPackageName)) {
-                    return mContext.getString(R.string.connected_via_app,
-                            getAppLabel(mContext, suggestionOrSpecifierPackageName));
-                }
-
                 // Special case for connected + ephemeral networks.
                 if (!TextUtils.isEmpty(mRecommendationServiceLabel)) {
                     return String.format(mContext.getString(R.string.connected_via_network_scorer),
@@ -241,6 +238,14 @@
                 return mContext.getString(R.string.connected_via_network_scorer_default);
             }
 
+            // For network suggestions
+            final String suggestionOrSpecifierPackageName = mWifiInfo != null
+                    ? mWifiInfo.getRequestingPackageName() : null;
+            if (!TextUtils.isEmpty(suggestionOrSpecifierPackageName)) {
+                return mContext.getString(R.string.connected_via_app,
+                        getAppLabel(mContext, suggestionOrSpecifierPackageName));
+            }
+
             String networkCapabilitiesinformation =
                     getCurrentNetworkCapabilitiesInformation(mContext, mNetworkCapabilities);
             if (!TextUtils.isEmpty(networkCapabilitiesinformation)) {
@@ -300,7 +305,10 @@
 
     @Override
     public WifiConfiguration getWifiConfiguration() {
-        return mWifiConfig;
+        if (mWifiConfig != null && !mWifiConfig.fromWifiNetworkSuggestion) {
+            return mWifiConfig;
+        }
+        return null;
     }
 
     @Override
@@ -373,12 +381,12 @@
 
     @Override
     public boolean canForget() {
-        return isSaved();
+        return getWifiConfiguration() != null;
     }
 
     @Override
     public void forget(@Nullable ForgetCallback callback) {
-        if (mWifiConfig != null) {
+        if (canForget()) {
             mForgetCallback = callback;
             mWifiManager.forget(mWifiConfig.networkId, new ForgetActionListener());
         }
@@ -407,7 +415,7 @@
      */
     @Override
     public boolean canShare() {
-        if (!isSaved()) {
+        if (getWifiConfiguration() == null) {
             return false;
         }
 
@@ -429,7 +437,7 @@
      */
     @Override
     public boolean canEasyConnect() {
-        if (!isSaved()) {
+        if (getWifiConfiguration() == null) {
             return false;
         }
 
@@ -467,8 +475,8 @@
     @Override
     @MeteredChoice
     public int getMeteredChoice() {
-        if (mWifiConfig != null) {
-            final int meteredOverride = mWifiConfig.meteredOverride;
+        if (getWifiConfiguration() != null) {
+            final int meteredOverride = getWifiConfiguration().meteredOverride;
             if (meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED) {
                 return METERED_CHOICE_METERED;
             } else if (meteredOverride == WifiConfiguration.METERED_OVERRIDE_NOT_METERED) {
@@ -480,12 +488,12 @@
 
     @Override
     public boolean canSetMeteredChoice() {
-        return isSaved();
+        return getWifiConfiguration() != null;
     }
 
     @Override
     public void setMeteredChoice(int meteredChoice) {
-        if (mWifiConfig == null) {
+        if (!canSetMeteredChoice()) {
             return;
         }
 
@@ -501,17 +509,14 @@
 
     @Override
     public boolean canSetPrivacy() {
-        return isSaved();
+        return getWifiConfiguration() != null;
     }
 
     @Override
     @Privacy
     public int getPrivacy() {
-        if (mWifiConfig == null) {
-            return PRIVACY_UNKNOWN;
-        }
-
-        if (mWifiConfig.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE) {
+        if (mWifiConfig != null
+                && mWifiConfig.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_NONE) {
             return PRIVACY_DEVICE_MAC;
         } else {
             return PRIVACY_RANDOMIZED_MAC;
@@ -531,7 +536,7 @@
 
     @Override
     public boolean isAutoJoinEnabled() {
-        if (mWifiConfig == null) {
+        if (!isSaved()) {
             return false;
         }
 
@@ -616,10 +621,12 @@
             }
         }
 
-        mCurrentScanResults.clear();
-        mCurrentScanResults.addAll(scanResults);
+        synchronized (mLock) {
+            mCurrentScanResults.clear();
+            mCurrentScanResults.addAll(scanResults);
+        }
 
-        final ScanResult bestScanResult = getBestScanResultByLevel(mCurrentScanResults);
+        final ScanResult bestScanResult = getBestScanResultByLevel(scanResults);
         if (bestScanResult == null) {
             mLevel = WIFI_LEVEL_UNREACHABLE;
         } else {
@@ -697,6 +704,22 @@
         notifyOnUpdated();
     }
 
+    /**
+     * Sets whether the suggested config for this entry is shareable to the user or not.
+     */
+    @WorkerThread
+    void setUserShareable(boolean isUserShareable) {
+        mIsUserShareable = isUserShareable;
+    }
+
+    /**
+     * Returns whether the suggested config for this entry is shareable to the user or not.
+     */
+    @WorkerThread
+    boolean isUserShareable() {
+        return mIsUserShareable;
+    }
+
     @WorkerThread
     protected boolean connectionInfoMatches(@NonNull WifiInfo wifiInfo,
             @NonNull NetworkInfo networkInfo) {
@@ -704,7 +727,16 @@
             return false;
         }
 
-        return mWifiConfig != null && mWifiConfig.networkId == wifiInfo.getNetworkId();
+        if (mWifiConfig != null) {
+            if (mWifiConfig.fromWifiNetworkSuggestion) {
+                // Match network suggestions with SSID since the net id is prone to change.
+                return TextUtils.equals(mSsid, sanitizeSsid(wifiInfo.getSSID()));
+            }
+            if (mWifiConfig.networkId == wifiInfo.getNetworkId()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private void updateRecommendationServiceLabel() {
@@ -731,8 +763,10 @@
 
     @Override
     String getScanResultDescription() {
-        if (mCurrentScanResults.size() == 0) {
-            return "";
+        synchronized (mLock) {
+            if (mCurrentScanResults.size() == 0) {
+                return "";
+            }
         }
 
         final StringBuilder description = new StringBuilder();
@@ -745,11 +779,14 @@
     }
 
     private String getScanResultDescription(int minFrequency, int maxFrequency) {
-        final List<ScanResult> scanResults = mCurrentScanResults.stream()
-                .filter(scanResult -> scanResult.frequency >= minFrequency
-                        && scanResult.frequency <= maxFrequency)
-                .sorted(Comparator.comparingInt(scanResult -> -1 * scanResult.level))
-                .collect(Collectors.toList());
+        final List<ScanResult> scanResults;
+        synchronized (mLock) {
+            scanResults = mCurrentScanResults.stream()
+                    .filter(scanResult -> scanResult.frequency >= minFrequency
+                            && scanResult.frequency <= maxFrequency)
+                    .sorted(Comparator.comparingInt(scanResult -> -1 * scanResult.level))
+                    .collect(Collectors.toList());
+        }
 
         final int scanResultCount = scanResults.size();
         if (scanResultCount == 0) {
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
index 0c6d7d6..c08b47c 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
@@ -44,6 +44,7 @@
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Handler;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -91,9 +92,14 @@
 
     // Cache containing saved WifiConfigurations mapped by StandardWifiEntry key
     private final Map<String, WifiConfiguration> mWifiConfigCache = new HashMap<>();
+
+    private final Map<String, WifiConfiguration> mSuggestedConfigCache = new HashMap<>();
     // Cache containing visible StandardWifiEntries. Must be accessed only by the worker thread.
     private final Map<String, StandardWifiEntry> mStandardWifiEntryCache = new HashMap<>();
-
+    // Cache containing available suggested StandardWifiEntries. These entries may be already
+    // represented in mStandardWifiEntryCache, so filtering must be done before they are returned in
+    // getWifiEntry() and getConnectedWifiEntry().
+    private final Map<String, StandardWifiEntry> mSuggestedWifiEntryCache = new HashMap<>();
     // Cache containing saved PasspointConfigurations mapped by PasspointWifiEntry key.
     private final Map<String, PasspointConfiguration> mPasspointConfigCache = new HashMap<>();
     // Cache containing visible PasspointWifiEntries. Must be accessed only by the worker thread.
@@ -173,6 +179,8 @@
     @Override
     protected void handleOnStart() {
         updateStandardWifiEntryConfigs(mWifiManager.getConfiguredNetworks());
+        updateSuggestedWifiEntryConfigs(mWifiManager.getPrivilegedConfiguredNetworks().stream()
+                .filter(config -> config.fromWifiNetworkSuggestion).collect(toList()));
         updatePasspointWifiEntryConfigs(mWifiManager.getPasspointConfigurations());
         mScanResultUpdater.update(mWifiManager.getScanResults());
         conditionallyUpdateScanResults(true /* lastScanSucceeded */);
@@ -181,6 +189,7 @@
         updateConnectionInfo(wifiInfo, networkInfo);
         // Create a StandardWifiEntry for the current connection if there are no scan results yet.
         conditionallyCreateConnectedStandardWifiEntry(wifiInfo, networkInfo);
+        conditionallyCreateConnectedSuggestedWifiEntry(wifiInfo, networkInfo);
         conditionallyCreateConnectedPasspointWifiEntry(wifiInfo, networkInfo);
         handleLinkPropertiesChanged(mConnectivityManager.getLinkProperties(
                 mWifiManager.getCurrentNetwork()));
@@ -213,11 +222,14 @@
 
         final WifiConfiguration config =
                 (WifiConfiguration) intent.getExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
-        if (config != null) {
+        if (config != null && !config.isPasspoint() && !config.fromWifiNetworkSuggestion) {
             updateStandardWifiEntryConfig(
                     config, (Integer) intent.getExtra(WifiManager.EXTRA_CHANGE_REASON));
         } else {
             updateStandardWifiEntryConfigs(mWifiManager.getConfiguredNetworks());
+            updateSuggestedWifiEntryConfigs(mWifiManager.getPrivilegedConfiguredNetworks().stream()
+                    .filter((privilegedConfig) -> privilegedConfig.fromWifiNetworkSuggestion)
+                    .collect(toList()));
         }
         updatePasspointWifiEntryConfigs(mWifiManager.getPasspointConfigurations());
         // Update scans since config changes may result in different entries being shown.
@@ -240,6 +252,7 @@
         updateConnectionInfo(wifiInfo, networkInfo);
         // Create a StandardWifiEntry for the current connection if there are no scan results yet.
         conditionallyCreateConnectedStandardWifiEntry(wifiInfo, networkInfo);
+        conditionallyCreateConnectedSuggestedWifiEntry(wifiInfo, networkInfo);
         conditionallyCreateConnectedPasspointWifiEntry(wifiInfo, networkInfo);
         updateWifiEntries();
     }
@@ -268,19 +281,19 @@
     @WorkerThread
     private void updateWifiEntries() {
         synchronized (mLock) {
-            mWifiEntries.clear();
-            mWifiEntries.addAll(mStandardWifiEntryCache.values().stream().filter(entry ->
-                    entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
-            mWifiEntries.addAll(mPasspointWifiEntryCache.values().stream().filter(entry ->
-                    entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
-            mWifiEntries.addAll(mOsuWifiEntryCache.values().stream().filter(entry ->
-                    entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
             mConnectedWifiEntry = mStandardWifiEntryCache.values().stream().filter(entry -> {
                 final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
                 return connectedState == CONNECTED_STATE_CONNECTED
                         || connectedState == CONNECTED_STATE_CONNECTING;
             }).findAny().orElse(null /* other */);
             if (mConnectedWifiEntry == null) {
+                mConnectedWifiEntry = mSuggestedWifiEntryCache.values().stream().filter(entry -> {
+                    final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
+                    return connectedState == CONNECTED_STATE_CONNECTED
+                            || connectedState == CONNECTED_STATE_CONNECTING;
+                }).findAny().orElse(null /* other */);
+            }
+            if (mConnectedWifiEntry == null) {
                 mConnectedWifiEntry = mPasspointWifiEntryCache.values().stream().filter(entry -> {
                     final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
                     return connectedState == CONNECTED_STATE_CONNECTED
@@ -294,6 +307,32 @@
                             || connectedState == CONNECTED_STATE_CONNECTING;
                 }).findAny().orElse(null /* other */);
             }
+            mWifiEntries.clear();
+            for (String key : mStandardWifiEntryCache.keySet()) {
+                // Continue if we're connected to this network with a non-user-shareable config.
+                if (mConnectedWifiEntry != null
+                        && TextUtils.equals(key, mConnectedWifiEntry.getKey())) {
+                    continue;
+                }
+                StandardWifiEntry entry = mStandardWifiEntryCache.get(key);
+                StandardWifiEntry suggestedEntry = mSuggestedWifiEntryCache.get(key);
+                // Return a user-shareable suggested network to the user if one exists
+                if (!entry.isSaved()
+                        && suggestedEntry != null && suggestedEntry.isUserShareable()) {
+                    if (suggestedEntry.getConnectedState() == CONNECTED_STATE_DISCONNECTED) {
+                        mWifiEntries.add(suggestedEntry);
+                    }
+                } else {
+                    if (entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED) {
+                        mWifiEntries.add(entry);
+                    }
+                }
+
+            }
+            mWifiEntries.addAll(mPasspointWifiEntryCache.values().stream().filter(entry ->
+                    entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
+            mWifiEntries.addAll(mOsuWifiEntryCache.values().stream().filter(entry ->
+                    entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
             Collections.sort(mWifiEntries);
             if (isVerboseLoggingEnabled()) {
                 Log.v(TAG, "Connected WifiEntry: " + mConnectedWifiEntry);
@@ -343,6 +382,59 @@
         }
     }
 
+    /**
+     * Updates or removes scan results for the corresponding StandardWifiEntries.
+     * New entries will be created for scan results without an existing entry.
+     * Unreachable entries will be removed.
+     *
+     * @param scanResults List of valid scan results to convey as StandardWifiEntries
+     */
+    @WorkerThread
+    private void updateSuggestedWifiEntryScans(@NonNull List<ScanResult> scanResults) {
+        checkNotNull(scanResults, "Scan Result list should not be null!");
+
+        // Group scans by StandardWifiEntry key
+        final Map<String, List<ScanResult>> scanResultsByKey = mapScanResultsToKey(
+                scanResults,
+                true /* chooseSingleSecurity */,
+                mWifiConfigCache,
+                mWifiManager.isWpa3SaeSupported(),
+                mWifiManager.isWpa3SuiteBSupported(),
+                mWifiManager.isEnhancedOpenSupported());
+
+        Map<String, WifiConfiguration> userSharedConfigsByKey =
+                mWifiManager.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults)
+                        .stream()
+                        .collect(Collectors.toMap(
+                                StandardWifiEntry::wifiConfigToStandardWifiEntryKey,
+                                Function.identity()));
+
+        Set<String> seenKeys = new TreeSet<>();
+        for (String key : userSharedConfigsByKey.keySet()) {
+            seenKeys.add(key);
+            if (!mSuggestedWifiEntryCache.containsKey(key)) {
+                mSuggestedWifiEntryCache.put(key, new StandardWifiEntry(mContext, mMainHandler, key,
+                        userSharedConfigsByKey.get(key), mWifiManager,
+                        false /* forSavedNetworksPage */));
+            }
+            final StandardWifiEntry entry = mSuggestedWifiEntryCache.get(key);
+            entry.setUserShareable(true);
+            entry.updateScanResultInfo(scanResultsByKey.get(key));
+        }
+
+        // Remove entries without configs
+        mSuggestedWifiEntryCache.entrySet()
+                .removeIf(entry -> {
+                    StandardWifiEntry wifiEntry = entry.getValue();
+                    String key = entry.getKey();
+                    if (!seenKeys.contains(key)) {
+                        wifiEntry.updateConfig(mSuggestedConfigCache.get(key));
+                        wifiEntry.setUserShareable(false);
+                    }
+                    return !wifiEntry.isSaved();
+                });
+    }
+
     @WorkerThread
     private void updatePasspointWifiEntryScans(@NonNull List<ScanResult> scanResults) {
         checkNotNull(scanResults, "Scan Result list should not be null!");
@@ -415,6 +507,7 @@
     private void conditionallyUpdateScanResults(boolean lastScanSucceeded) {
         if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED) {
             updateStandardWifiEntryScans(Collections.emptyList());
+            updateSuggestedWifiEntryScans(Collections.emptyList());
             updatePasspointWifiEntryScans(Collections.emptyList());
             updateOsuWifiEntryScans(Collections.emptyList());
             return;
@@ -432,6 +525,7 @@
 
         List<ScanResult> scanResults = mScanResultUpdater.getScanResults(scanAgeWindow);
         updateStandardWifiEntryScans(scanResults);
+        updateSuggestedWifiEntryScans(scanResults);
         updatePasspointWifiEntryScans(scanResults);
         updateOsuWifiEntryScans(scanResults);
     }
@@ -483,12 +577,33 @@
     }
 
     @WorkerThread
+    private void updateSuggestedWifiEntryConfigs(@NonNull List<WifiConfiguration> configs) {
+        checkNotNull(configs, "Config list should not be null!");
+        mSuggestedConfigCache.clear();
+        mSuggestedConfigCache.putAll(configs.stream().collect(Collectors.toMap(
+                StandardWifiEntry::wifiConfigToStandardWifiEntryKey,
+                Function.identity())));
+
+        // Iterate through current entries and update each entry's config
+        mSuggestedWifiEntryCache.entrySet().removeIf((entry) -> {
+            final StandardWifiEntry wifiEntry = entry.getValue();
+            final String key = wifiEntry.getKey();
+            final WifiConfiguration cachedConfig = mSuggestedConfigCache.get(key);
+            if (cachedConfig != null) {
+                wifiEntry.updateConfig(cachedConfig);
+                return false;
+            } else {
+                return true;
+            }
+        });
+    }
+
+    @WorkerThread
     private void updatePasspointWifiEntryConfigs(@NonNull List<PasspointConfiguration> configs) {
         checkNotNull(configs, "Config list should not be null!");
-
         mPasspointConfigCache.clear();
         mPasspointConfigCache.putAll(configs.stream().collect(
-                toMap((config) -> uniqueIdToPasspointWifiEntryKey(
+                toMap(config -> uniqueIdToPasspointWifiEntryKey(
                         config.getUniqueId()), Function.identity())));
 
         // Iterate through current entries and update each entry's config or remove if no config
@@ -517,6 +632,9 @@
         for (WifiEntry entry : mStandardWifiEntryCache.values()) {
             entry.updateConnectionInfo(wifiInfo, networkInfo);
         }
+        for (WifiEntry entry : mSuggestedWifiEntryCache.values()) {
+            entry.updateConnectionInfo(wifiInfo, networkInfo);
+        }
         for (WifiEntry entry : mPasspointWifiEntryCache.values()) {
             entry.updateConnectionInfo(wifiInfo, networkInfo);
         }
@@ -534,7 +652,7 @@
     @WorkerThread
     private void conditionallyCreateConnectedStandardWifiEntry(@Nullable WifiInfo wifiInfo,
             @Nullable NetworkInfo networkInfo) {
-        if (wifiInfo.isPasspointAp()) {
+        if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
             return;
         }
 
@@ -554,7 +672,37 @@
     }
 
     /**
-     * Creates and caches a StandardWifiEntry representing the current connection using the current
+     * Creates and caches a suggested StandardWifiEntry representing the current connection using
+     * the current WifiInfo and NetworkInfo if there are no scans results available for the network
+     * yet.
+     * @param wifiInfo WifiInfo of the current connection
+     * @param networkInfo NetworkInfo of the current connection
+     */
+    @WorkerThread
+    private void conditionallyCreateConnectedSuggestedWifiEntry(@Nullable WifiInfo wifiInfo,
+            @Nullable NetworkInfo networkInfo) {
+        if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
+            return;
+        }
+
+        mSuggestedConfigCache.values().stream()
+                .filter(config ->
+                        TextUtils.equals(config.SSID, wifiInfo.getSSID())
+                                && !mSuggestedWifiEntryCache.containsKey(
+                                        wifiConfigToStandardWifiEntryKey(config)))
+                .findAny().ifPresent(config -> {
+                    final StandardWifiEntry connectedEntry =
+                            new StandardWifiEntry(mContext, mMainHandler,
+                                    wifiConfigToStandardWifiEntryKey(config), config, mWifiManager,
+                                    false /* forSavedNetworksPage */);
+                    connectedEntry.updateConnectionInfo(wifiInfo, networkInfo);
+                    mSuggestedWifiEntryCache.put(connectedEntry.getKey(), connectedEntry);
+                });
+    }
+
+
+    /**
+     * Creates and caches a PasspointWifiEntry representing the current connection using the current
      * WifiInfo and NetworkInfo if there are no scans results available for the network yet.
      * @param wifiInfo WifiInfo of the current connection
      * @param networkInfo NetworkInfo of the current connection
diff --git a/service/Android.bp b/service/Android.bp
index 928f9c5..0fdc7fe 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -58,7 +58,7 @@
         "jsr305",
         "framework-annotations-lib",
         // load the resources from the resources APK.
-        "wifi-service-resources",
+        "ServiceWifiResources",
         // need pre-jarjar symbols so that wifi-service can reference the original class names at
         // compile time
         "framework-wifi-pre-jarjar",
@@ -143,11 +143,9 @@
 }
 
 // APK to hold all the wifi overlayable resources.
-// TODO: This should be signed by a wifi specific certificate.
 // ============================================================
 android_app {
-    // TODO: Rename to service-wifi-resources? What about b/151678653?
-    name: "wifi-service-resources",
+    name: "ServiceWifiResources",
     defaults: ["wifi-service-common"],
     resource_dirs: [
         "res",
@@ -156,6 +154,16 @@
     sdk_version: "system_current",
     export_package_resources: true,
     manifest: "AndroidManifest_Resources.xml",
+    apex_available: [
+        "com.android.wifi",
+        "test_com.android.wifi",
+    ],
+    certificate: ":com.android.wifi.resources.certificate",
+}
+
+android_app_certificate {
+    name: "com.android.wifi.resources.certificate",
+    certificate: "resources-certs/com.android.wifi.resources"
 }
 
 // Prebuilt for the wifi.rc file.
diff --git a/service/AndroidManifest_Resources.xml b/service/AndroidManifest_Resources.xml
index 4585721..a6b2a58 100644
--- a/service/AndroidManifest_Resources.xml
+++ b/service/AndroidManifest_Resources.xml
@@ -26,5 +26,12 @@
         android:defaultToDeviceProtectedStorage="true"
         android:directBootAware="true"
         android:usesCleartextTraffic="true">
+        <!-- This is only used to identify this app by resolving the action.
+             The activity is never actually triggered. -->
+        <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+            <intent-filter>
+                <action android:name="com.android.server.wifi.intent.action.SERVICE_WIFI_RESOURCES_APK" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/service/apex/Android.bp b/service/apex/Android.bp
index 65cd6d5..e23d9cc 100644
--- a/service/apex/Android.bp
+++ b/service/apex/Android.bp
@@ -30,8 +30,8 @@
     key: "com.android.wifi.key",
     certificate: ":com.android.wifi.certificate",
     apps: [
-        "wifi-service-resources",
         "OsuLogin",
+        "ServiceWifiResources",
     ],
 }
 
diff --git a/service/java/com/android/server/wifi/BssidBlocklistMonitor.java b/service/java/com/android/server/wifi/BssidBlocklistMonitor.java
index 897090c..70c9193 100644
--- a/service/java/com/android/server/wifi/BssidBlocklistMonitor.java
+++ b/service/java/com/android/server/wifi/BssidBlocklistMonitor.java
@@ -154,13 +154,10 @@
         pw.println("BssidBlocklistMonitor - Bssid blocklist End ----");
     }
 
-    private void addToBlocklist(@NonNull BssidStatus entry, long durationMs,
-            boolean doNotClearUntilTimeout, String reasonString) {
+    private void addToBlocklist(@NonNull BssidStatus entry, long durationMs, String reasonString) {
         entry.addToBlocklist(durationMs);
-        entry.doNotClearUntilTimeout = doNotClearUntilTimeout;
         localLog(TAG + " addToBlocklist: bssid=" + entry.bssid + ", ssid=" + entry.ssid
-                + ", durationMs=" + durationMs + ", doNotClearUntilTimeout="
-                + doNotClearUntilTimeout + ", reason=" + reasonString);
+                + ", durationMs=" + durationMs + ", reason=" + reasonString);
     }
 
     /**
@@ -223,8 +220,7 @@
             // Return because this BSSID is already being blocked for a longer time.
             return;
         }
-        addToBlocklist(status, durationMs, true,
-                FAILURE_BSSID_BLOCKED_BY_FRAMEWORK_REASON_STRING);
+        addToBlocklist(status, durationMs, FAILURE_BSSID_BLOCKED_BY_FRAMEWORK_REASON_STRING);
     }
 
     private String getFailureReasonString(@FailureReason int reasonCode) {
@@ -289,7 +285,7 @@
             }
             addToBlocklist(entry,
                     getBlocklistDurationWithExponentialBackoff(currentStreak, baseBlockDurationMs),
-                    false, getFailureReasonString(reasonCode));
+                    getFailureReasonString(reasonCode));
             mWifiScoreCard.incrementBssidBlocklistStreak(ssid, bssid, reasonCode);
             return true;
         }
@@ -403,8 +399,7 @@
      */
     public void clearBssidBlocklistForSsid(@NonNull String ssid) {
         int prevSize = mBssidStatusMap.size();
-        mBssidStatusMap.entrySet().removeIf(e -> !e.getValue().doNotClearUntilTimeout
-                && e.getValue().ssid.equals(ssid));
+        mBssidStatusMap.entrySet().removeIf(e -> e.getValue().ssid.equals(ssid));
         int diff = prevSize - mBssidStatusMap.size();
         if (diff > 0) {
             localLog(TAG + " clearBssidBlocklistForSsid: SSID=" + ssid
@@ -418,7 +413,7 @@
     public void clearBssidBlocklist() {
         if (mBssidStatusMap.size() > 0) {
             int prevSize = mBssidStatusMap.size();
-            mBssidStatusMap.entrySet().removeIf(e -> !e.getValue().doNotClearUntilTimeout);
+            mBssidStatusMap.clear();
             localLog(TAG + " clearBssidBlocklist: num BSSIDs cleared="
                     + (prevSize - mBssidStatusMap.size()));
         }
@@ -506,9 +501,6 @@
         // The following are used to flag how long this BSSID stays in the blocklist.
         public boolean isInBlocklist;
         public long blocklistEndTimeMs;
-        // Indicate that this BSSID should not be removed from blacklist through other means
-        // such as |clearBssidBlocklist| and |clearBssidBlocklistForSsid|
-        public boolean doNotClearUntilTimeout;
 
         BssidStatus(String bssid, String ssid) {
             this.bssid = bssid;
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 6941fbf..588564e 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -59,6 +59,7 @@
 import android.net.SocketKeepalive;
 import android.net.StaticIpConfiguration;
 import android.net.TcpKeepalivePacketData;
+import android.net.Uri;
 import android.net.ip.IIpClient;
 import android.net.ip.IpClientCallbacks;
 import android.net.ip.IpClientManager;
@@ -146,6 +147,7 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -3580,6 +3582,7 @@
 
         // Let's remove any ephemeral or passpoint networks.
         mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks();
+        mWifiConfigManager.clearUserTemporarilyDisabledList();
     }
 
     void registerConnected() {
@@ -4472,7 +4475,7 @@
         }
 
         @Override
-        public void onValidationStatus(int status, @Nullable String redirectUrl) {
+        public void onValidationStatus(int status, @Nullable Uri redirectUri) {
             if (this != mNetworkAgent) return;
             if (status == mLastNetworkStatus) return;
             mLastNetworkStatus = status;
@@ -4499,10 +4502,10 @@
         }
 
         @Override
-        public void onStartSocketKeepalive(int slot, int intervalSeconds,
+        public void onStartSocketKeepalive(int slot, @NonNull Duration interval,
                 @NonNull KeepalivePacketData packet) {
             ClientModeImpl.this.sendMessage(
-                    CMD_START_IP_PACKET_OFFLOAD, slot, intervalSeconds, packet);
+                    CMD_START_IP_PACKET_OFFLOAD, slot, (int) interval.getSeconds(), packet);
         }
 
         @Override
@@ -4633,6 +4636,10 @@
                     .build();
             final NetworkCapabilities nc = getCapabilities(getCurrentWifiConfiguration());
             synchronized (mNetworkAgentLock) {
+                // This should never happen.
+                if (mNetworkAgent != null) {
+                    Log.wtf(TAG, "mNetworkAgent is not null: " + mNetworkAgent);
+                }
                 mNetworkAgent = new WifiNetworkAgent(mContext, getHandler().getLooper(),
                         "WifiNetworkAgent", nc, mLinkProperties, 60, naConfig,
                         mNetworkFactory.getProvider());
@@ -5054,7 +5061,7 @@
     }
 
     private void sendConnectedState() {
-        mNetworkAgent.setConnected();
+        mNetworkAgent.markConnected();
         sendNetworkChangeBroadcast(DetailedState.CONNECTED);
     }
 
diff --git a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java
index f4bd95f..4f7b4e8 100644
--- a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java
+++ b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java
@@ -18,7 +18,6 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.wifi.ScanResult;
@@ -51,12 +50,12 @@
     public static final String AVAILABLE_NETWORK_NOTIFIER_TAG =
             "com.android.server.wifi.ConnectToNetworkNotification.AVAILABLE_NETWORK_NOTIFIER_TAG";
 
-    private Context mContext;
+    private WifiContext mContext;
     private WifiInjector mWifiInjector;
     private FrameworkFacade mFrameworkFacade;
 
     public ConnectToNetworkNotificationBuilder(
-            Context context,
+            WifiContext context,
             WifiInjector wifiInjector,
             FrameworkFacade framework) {
         mContext = context;
@@ -158,7 +157,7 @@
             CharSequence title, CharSequence content, String extraData) {
         return mFrameworkFacade.makeNotificationBuilder(mContext,
                 WifiService.NOTIFICATION_NETWORK_AVAILABLE)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setTicker(title)
                 .setContentTitle(title)
diff --git a/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java
index a16dac2..6f0a487 100644
--- a/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java
+++ b/service/java/com/android/server/wifi/ConnectionFailureNotificationBuilder.java
@@ -21,7 +21,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
@@ -44,14 +43,14 @@
     public static final String RANDOMIZATION_SETTINGS_NETWORK_SSID =
             "com.android.server.wifi.RANDOMIZATION_SETTINGS_NETWORK_SSID";
 
-    private Context mContext;
+    private WifiContext mContext;
     private String mPackageName;
     private FrameworkFacade mFrameworkFacade;
     private WifiConnectivityManager mWifiConnectivityManager;
     private NotificationManager mNotificationManager;
     private Handler mHandler;
 
-    public ConnectionFailureNotificationBuilder(Context context, String packageName,
+    public ConnectionFailureNotificationBuilder(WifiContext context, String packageName,
             FrameworkFacade framework) {
         mContext = context;
         mPackageName = packageName;
@@ -81,7 +80,7 @@
 
         return mFrameworkFacade.makeNotificationBuilder(
                 mContext, WifiService.NOTIFICATION_NETWORK_ALERTS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setTicker(title)
                 .setContentTitle(title)
diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java
index 11572f1..02b1191 100644
--- a/service/java/com/android/server/wifi/DppManager.java
+++ b/service/java/com/android/server/wifi/DppManager.java
@@ -489,6 +489,7 @@
             // Convert from HAL codes to WifiManager/user codes
             switch (dppStatusCode) {
                 case DppSuccessCode.CONFIGURATION_SENT:
+                    mDppMetrics.updateDppR1CapableEnrolleeResponderDevices();
                     dppSuccessCode = EasyConnectStatusCallback
                             .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT;
                     break;
@@ -540,6 +541,7 @@
                     break;
 
                 case DppProgressCode.CONFIGURATION_SENT_WAITING_RESPONSE:
+                    mDppMetrics.updateDppR2CapableEnrolleeResponderDevices();
                     dppProgressCode = EasyConnectStatusCallback
                             .EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE;
                     break;
@@ -642,6 +644,7 @@
         if (isNetworkInScanCache & !channelMatch) {
             Log.d(TAG, "Update the error code to NOT_COMPATIBLE"
                     + " as enrollee didn't scan network's operating channel");
+            mDppMetrics.updateDppR2EnrolleeResponderIncompatibleConfiguration();
             return false;
         }
         return true;
diff --git a/service/java/com/android/server/wifi/DppMetrics.java b/service/java/com/android/server/wifi/DppMetrics.java
index 231e7f1..bd62313 100644
--- a/service/java/com/android/server/wifi/DppMetrics.java
+++ b/service/java/com/android/server/wifi/DppMetrics.java
@@ -18,13 +18,17 @@
 
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT;
 
 import android.net.wifi.EasyConnectStatusCallback;
@@ -90,6 +94,35 @@
     }
 
     /**
+     * Update number of DPP R1 capable enrollee responder devices.
+     */
+    public void updateDppR1CapableEnrolleeResponderDevices() {
+        synchronized (mLock) {
+            mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices++;
+        }
+    }
+
+    /**
+     * Update number of DPP R2 capable enrollee responder devices.
+     */
+    public void updateDppR2CapableEnrolleeResponderDevices() {
+        synchronized (mLock) {
+            mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices++;
+        }
+    }
+
+    /**
+     * Update number of times DPP R2 compatibility check detected
+     * that enrollee responder device is incompatible with the
+     * network.
+     */
+    public void updateDppR2EnrolleeResponderIncompatibleConfiguration() {
+        synchronized (mLock) {
+            mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration++;
+        }
+    }
+
+    /**
      * Update DPP Configurator success counter
      */
     public void updateDppConfiguratorSuccess(
@@ -102,6 +135,12 @@
                             mHistogramDppConfiguratorSuccessCode.get(WifiMetricsProto.WifiDppLog
                                     .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT) + 1);
                     break;
+                case EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED:
+                    mHistogramDppConfiguratorSuccessCode.put(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED,
+                            mHistogramDppConfiguratorSuccessCode.get(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED) + 1);
+                    break;
                 default:
                     break;
             }
@@ -168,6 +207,25 @@
                             mHistogramDppFailureCode.get(WifiMetricsProto.WifiDppLog
                                     .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK) + 1);
                     break;
+                case EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK:
+                    mHistogramDppFailureCode.put(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK,
+                            mHistogramDppFailureCode.get(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK) + 1);
+                    break;
+                case EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION:
+                    mHistogramDppFailureCode.put(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION,
+                            mHistogramDppFailureCode.get(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION) + 1);
+                    break;
+                case EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION:
+                    mHistogramDppFailureCode.put(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION,
+                            mHistogramDppFailureCode.get(WifiMetricsProto.WifiDppLog
+                                    .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION)
+                                    + 1);
+                    break;
                 default:
                     break;
             }
@@ -199,6 +257,12 @@
                     + mWifiDppLogProto.numDppEnrolleeInitiatorRequests);
             pw.println("mWifiDppLogProto.numDppEnrolleeSuccess="
                     + mWifiDppLogProto.numDppEnrolleeSuccess);
+            pw.println("mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices="
+                    + mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices);
+            pw.println("mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices="
+                    + mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices);
+            pw.println("mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration="
+                    + mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration);
 
             if (mHistogramDppFailureCode.size() > 0) {
                 pw.println("mHistogramDppFailureCode=");
@@ -226,6 +290,9 @@
             mWifiDppLogProto.numDppConfiguratorInitiatorRequests = 0;
             mWifiDppLogProto.numDppEnrolleeInitiatorRequests = 0;
             mWifiDppLogProto.numDppEnrolleeSuccess = 0;
+            mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices = 0;
+            mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices = 0;
+            mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration = 0;
             mHistogramDppFailureCode.clear();
             mHistogramDppOperationTime.clear();
             mHistogramDppConfiguratorSuccessCode.clear();
@@ -276,6 +343,12 @@
                     mWifiDppLogProto.numDppConfiguratorInitiatorRequests;
             log.numDppEnrolleeInitiatorRequests = mWifiDppLogProto.numDppEnrolleeInitiatorRequests;
             log.numDppEnrolleeSuccess = mWifiDppLogProto.numDppEnrolleeSuccess;
+            log.numDppR1CapableEnrolleeResponderDevices =
+                    mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices;
+            log.numDppR2CapableEnrolleeResponderDevices =
+                    mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices;
+            log.numDppR2EnrolleeResponderIncompatibleConfiguration =
+                    mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration;
             log.dppFailureCode = consolidateDppFailure(mHistogramDppFailureCode);
             log.dppConfiguratorSuccessCode =
                     consolidateDppSuccess(mHistogramDppConfiguratorSuccessCode);
diff --git a/service/java/com/android/server/wifi/EapFailureNotifier.java b/service/java/com/android/server/wifi/EapFailureNotifier.java
index 1613bbf..3cc80b9 100644
--- a/service/java/com/android/server/wifi/EapFailureNotifier.java
+++ b/service/java/com/android/server/wifi/EapFailureNotifier.java
@@ -42,7 +42,7 @@
     private static final String ERROR_MESSAGE_OVERLAY_PREFIX = "wifi_eap_error_message_code_";
 
     private static final long CANCEL_TIMEOUT_MILLISECONDS = 5 * 60 * 1000;
-    private final Context mContext;
+    private final WifiContext mContext;
     private final NotificationManager mNotificationManager;
     private final FrameworkFacade mFrameworkFacade;
     private final TelephonyUtil mTelephonyUtil;
@@ -51,7 +51,7 @@
     public static final int NOTIFICATION_ID = SystemMessage.NOTE_WIFI_EAP_FAILURE;
     private String mCurrentShownSsid;
 
-    public EapFailureNotifier(Context context, FrameworkFacade frameworkFacade,
+    public EapFailureNotifier(WifiContext context, FrameworkFacade frameworkFacade,
             TelephonyUtil telephonyUtil) {
         mContext = context;
         mFrameworkFacade = frameworkFacade;
@@ -77,7 +77,7 @@
                 mTelephonyUtil.getBestMatchSubscriptionId(config));
         if (res == null) return;
         int resourceId = res.getIdentifier(ERROR_MESSAGE_OVERLAY_PREFIX + errorCode,
-                "string", WifiContext.WIFI_OVERLAY_APK_PKG_NAME);
+                "string", mContext.getWifiOverlayApkPkgName());
 
         if (resourceId == 0) return;
         String errorMessage = res.getString(resourceId, config.SSID);
@@ -96,7 +96,7 @@
                 WifiService.NOTIFICATION_NETWORK_ALERTS)
                 .setAutoCancel(true)
                 .setTimeoutAfter(CANCEL_TIMEOUT_MILLISECONDS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setContentTitle(mContext.getString(
                         com.android.wifi.resources.R.string.wifi_available_title_failed_to_connect))
@@ -114,11 +114,11 @@
      *  Returns the resources from the given context for the MCC/MNC
      *  associated with the subscription.
      */
-    private Resources getResourcesForSubId(Context context, int subId) {
+    private Resources getResourcesForSubId(WifiContext context, int subId) {
         Context resourceContext = null;
         try {
             resourceContext = context.createPackageContext(
-                    WifiContext.WIFI_OVERLAY_APK_PKG_NAME, 0);
+                    context.getWifiOverlayApkPkgName(), 0);
         } catch (PackageManager.NameNotFoundException ex) {
             return null;
         }
diff --git a/service/java/com/android/server/wifi/SimRequiredNotifier.java b/service/java/com/android/server/wifi/SimRequiredNotifier.java
index 19e369d..8e3d22d 100644
--- a/service/java/com/android/server/wifi/SimRequiredNotifier.java
+++ b/service/java/com/android/server/wifi/SimRequiredNotifier.java
@@ -19,7 +19,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.wifi.WifiConfiguration;
@@ -33,11 +32,11 @@
  */
 public class SimRequiredNotifier {
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final FrameworkFacade mFrameworkFacade;
     private final NotificationManager mNotificationManager;
 
-    public SimRequiredNotifier(Context context, FrameworkFacade framework) {
+    public SimRequiredNotifier(WifiContext context, FrameworkFacade framework) {
         mContext = context;
         mFrameworkFacade = framework;
         mNotificationManager =
@@ -84,7 +83,7 @@
                 .setTicker(title)
                 .setContentText(message)
                 .setStyle(new Notification.BigTextStyle().bigText(message))
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         R.drawable.stat_notify_wifi_in_range))
                 .setContentIntent(launchWirelessSettings())
                 .build();
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 42a228e..44bbc73 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -22,7 +22,6 @@
 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
 
 import android.annotation.NonNull;
-import android.content.Context;
 import android.content.Intent;
 import android.net.MacAddress;
 import android.net.wifi.ScanResult;
@@ -74,7 +73,7 @@
     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
             + " Soft AP Send Message Timeout";
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final FrameworkFacade mFrameworkFacade;
     private final WifiNative mWifiNative;
 
@@ -158,7 +157,7 @@
         }
     };
 
-    public SoftApManager(@NonNull Context context,
+    public SoftApManager(@NonNull WifiContext context,
                          @NonNull Looper looper,
                          @NonNull FrameworkFacade framework,
                          @NonNull WifiNative wifiNative,
diff --git a/service/java/com/android/server/wifi/SoftApNotifier.java b/service/java/com/android/server/wifi/SoftApNotifier.java
index 741cab8..45114a5 100644
--- a/service/java/com/android/server/wifi/SoftApNotifier.java
+++ b/service/java/com/android/server/wifi/SoftApNotifier.java
@@ -19,7 +19,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 
@@ -41,11 +40,11 @@
     public static final int NOTIFICATION_ID_SOFTAP_AUTO_DISABLED =
             SystemMessage.NOTE_SOFTAP_AUTO_DISABLED;
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final FrameworkFacade mFrameworkFacade;
     private final NotificationManager mNotificationManager;
 
-    public SoftApNotifier(Context context, FrameworkFacade framework) {
+    public SoftApNotifier(WifiContext context, FrameworkFacade framework) {
         mContext = context;
         mFrameworkFacade = framework;
         mNotificationManager =
@@ -76,7 +75,7 @@
 
         return mFrameworkFacade.makeNotificationBuilder(mContext,
                 WifiService.NOTIFICATION_NETWORK_STATUS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         R.drawable.ic_wifi_settings))
                 .setContentTitle(title)
                 .setContentText(contentSummary)
diff --git a/service/java/com/android/server/wifi/WakeupNotificationFactory.java b/service/java/com/android/server/wifi/WakeupNotificationFactory.java
index 11713ba..9eb69b7 100644
--- a/service/java/com/android/server/wifi/WakeupNotificationFactory.java
+++ b/service/java/com/android/server/wifi/WakeupNotificationFactory.java
@@ -18,7 +18,6 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 
@@ -41,11 +40,11 @@
     /** Notification channel ID for onboarding messages. */
     public static final int ONBOARD_ID = SystemMessage.NOTE_WIFI_WAKE_ONBOARD;
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final WifiInjector mWifiInjector;
     private final FrameworkFacade mFrameworkFacade;
 
-    WakeupNotificationFactory(Context context, WifiInjector wifiInjector,
+    WakeupNotificationFactory(WifiContext context, WifiInjector wifiInjector,
                               FrameworkFacade frameworkFacade) {
         mContext = context;
         mWifiInjector = wifiInjector;
@@ -68,7 +67,7 @@
 
         return mFrameworkFacade.makeNotificationBuilder(mContext,
                 WifiService.NOTIFICATION_NETWORK_STATUS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         R.drawable.ic_wifi_settings))
                 .setTicker(title)
                 .setContentTitle(title)
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 972cd52..4921f8c 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -36,7 +36,6 @@
 import java.util.Random;
 
 import javax.annotation.Nullable;
-import javax.crypto.Mac;
 
 /**
  * Provides API for reading/writing soft access point configuration.
@@ -68,7 +67,6 @@
     private final WifiMetrics mWifiMetrics;
     private final BackupManagerProxy mBackupManagerProxy;
     private final MacAddressUtil mMacAddressUtil;
-    private final Mac mMac;
     private final WifiConfigManager mWifiConfigManager;
     private final ActiveModeWarden mActiveModeWarden;
     private boolean mHasNewDataToSerialize = false;
@@ -118,11 +116,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_HOTSPOT_CONFIG_USER_TAPPED_CONTENT);
         mMacAddressUtil = wifiInjector.getMacAddressUtil();
-        mMac = mMacAddressUtil.obtainMacRandHashFunctionForSap(Process.WIFI_UID);
-        if (mMac == null) {
-            Log.wtf(TAG, "Failed to obtain secret for SAP MAC randomization."
-                    + " All randomized MAC addresses are lost!");
-        }
     }
 
     /**
@@ -290,7 +283,8 @@
         SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config);
         if (config.getBssid() == null && context.getResources().getBoolean(
                 R.bool.config_wifi_ap_mac_randomization_supported)) {
-            MacAddress macAddress = mMacAddressUtil.calculatePersistentMac(config.getSsid(), mMac);
+            MacAddress macAddress = mMacAddressUtil.calculatePersistentMac(config.getSsid(),
+                    mMacAddressUtil.obtainMacRandHashFunctionForSap(Process.WIFI_UID));
             if (macAddress == null) {
                 Log.e(TAG, "Failed to calculate MAC from SSID. "
                         + "Generating new random MAC instead.");
diff --git a/service/java/com/android/server/wifi/WifiBackupDataParser.java b/service/java/com/android/server/wifi/WifiBackupDataParser.java
index 7699c55..e61cab2 100644
--- a/service/java/com/android/server/wifi/WifiBackupDataParser.java
+++ b/service/java/com/android/server/wifi/WifiBackupDataParser.java
@@ -42,4 +42,11 @@
      */
     List<WifiConfiguration> parseNetworkConfigurationsFromXml(XmlPullParser in, int outerTagDepth,
             int minorVersion) throws XmlPullParserException, IOException;
+
+    /**
+     * Get the highest supported minor version for this major version.
+     * This is used for generating the version code when serializing the data.
+     * @return Indicating the max supported minor version by this major version parser.
+     */
+    int getHighestSupportedMinorVersion();
 }
diff --git a/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java b/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java
index b787df2..89ab9a9 100644
--- a/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java
+++ b/service/java/com/android/server/wifi/WifiBackupDataV1Parser.java
@@ -141,6 +141,7 @@
                 IpConfigurationXmlUtil.XML_TAG_PROXY_PAC_FILE,
             }));
 
+    @Override
     public List<WifiConfiguration> parseNetworkConfigurationsFromXml(XmlPullParser in,
             int outerTagDepth, int minorVersion) throws XmlPullParserException, IOException {
         // clamp down the minorVersion to the highest one that this parser version supports
@@ -165,6 +166,11 @@
         return configurations;
     }
 
+    @Override
+    public int getHighestSupportedMinorVersion() {
+        return HIGHEST_SUPPORTED_MINOR_VERSION;
+    }
+
     /**
      * Parses the configuration data elements from the provided XML stream to a Configuration.
      *
@@ -350,6 +356,9 @@
                 case WifiConfigurationXmlUtil.XML_TAG_METERED_OVERRIDE:
                     configuration.meteredOverride = (int) value;
                     break;
+                case WifiConfigurationXmlUtil.XML_TAG_IS_AUTO_JOIN:
+                    configuration.allowAutojoin = (boolean) value;
+                    break;
                 default:
                     // should never happen, since other tags are filtered out earlier
                     throw new XmlPullParserException(
diff --git a/service/java/com/android/server/wifi/WifiBackupRestore.java b/service/java/com/android/server/wifi/WifiBackupRestore.java
index 078cb77..c55236e 100644
--- a/service/java/com/android/server/wifi/WifiBackupRestore.java
+++ b/service/java/com/android/server/wifi/WifiBackupRestore.java
@@ -81,7 +81,7 @@
      * Note that bumping up only the minor version will still allow restoring the backup set to
      * lower versions of SDK_INT.
      */
-    private static final float CURRENT_BACKUP_DATA_VERSION = 1.1f;
+    private static final int CURRENT_BACKUP_DATA_MAJOR_VERSION = 1;
 
     /** This list of older versions will be used to restore data from older backups. */
     /**
@@ -128,12 +128,36 @@
     private byte[] mDebugLastBackupDataRetrieved;
     private byte[] mDebugLastBackupDataRestored;
     private byte[] mDebugLastSupplicantBackupDataRestored;
+    private byte[] mDebugLastIpConfigBackupDataRestored;
 
     public WifiBackupRestore(WifiPermissionsUtil wifiPermissionsUtil) {
         mWifiPermissionsUtil = wifiPermissionsUtil;
     }
 
     /**
+     * Retrieve the version for serialization.
+     */
+    private Float getVersion() {
+        WifiBackupDataParser parser =
+                getWifiBackupDataParser(CURRENT_BACKUP_DATA_MAJOR_VERSION);
+        if (parser == null) {
+            Log.e(TAG, "Major version of backup data is unknown to this Android"
+                    + " version; not backing up");
+            return null;
+        }
+        int minorVersion = parser.getHighestSupportedMinorVersion();
+        Float version;
+        try {
+            version = Float.valueOf(
+                    CURRENT_BACKUP_DATA_MAJOR_VERSION + "." + minorVersion);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Failed to generate version", e);
+            return null;
+        }
+        return version;
+    }
+
+    /**
      * Retrieve an XML byte stream representing the data that needs to be backed up from the
      * provided configurations.
      *
@@ -154,7 +178,9 @@
             // Start writing the XML stream.
             XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
 
-            XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_BACKUP_DATA_VERSION);
+            Float version = getVersion();
+            if (version == null) return null;
+            XmlUtil.writeNextValue(out, XML_TAG_VERSION, version.floatValue());
 
             writeNetworkConfigurationsToXml(out, configurations);
 
@@ -342,6 +368,7 @@
 
         if (mVerboseLoggingEnabled) {
             mDebugLastSupplicantBackupDataRestored = supplicantData;
+            mDebugLastIpConfigBackupDataRestored = ipConfigData;
         }
 
         SupplicantBackupMigration.SupplicantNetworks supplicantNetworks =
@@ -394,6 +421,7 @@
             mDebugLastBackupDataRetrieved = null;
             mDebugLastBackupDataRestored = null;
             mDebugLastSupplicantBackupDataRestored = null;
+            mDebugLastIpConfigBackupDataRestored = null;
         }
     }
 
@@ -415,10 +443,14 @@
                     + createLogFromBackupData(mDebugLastBackupDataRestored));
         }
         if (mDebugLastSupplicantBackupDataRestored != null) {
-            pw.println("Last old backup data restored: "
+            pw.println("Last old supplicant backup data restored: "
                     + SupplicantBackupMigration.createLogFromBackupData(
                             mDebugLastSupplicantBackupDataRestored));
         }
+        if (mDebugLastIpConfigBackupDataRestored != null) {
+            pw.println("Last old ipconfig backup data restored: "
+                    + mDebugLastIpConfigBackupDataRestored);
+        }
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index b13251d..4e4f51d 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -198,16 +198,19 @@
 
     /**
      * General sorting algorithm of all networks for scanning purposes:
-     * Place the configurations in descending order of their |numAssociation| values. If networks
-     * have the same |numAssociation|, place the configurations with
+     * Place the configurations in ascending order of their AgeIndex. AgeIndex is based on most
+     * recently connected order. The lower the more recently connected.
+     * If networks have the same AgeIndex, place the configurations with
      * |lastSeenInQualifiedNetworkSelection| set first.
      */
-    public static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator =
+    private final WifiConfigurationUtil.WifiConfigurationComparator mScanListComparator =
             new WifiConfigurationUtil.WifiConfigurationComparator() {
                 @Override
                 public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
-                    if (a.numAssociation != b.numAssociation) {
-                        return Long.compare(b.numAssociation, a.numAssociation);
+                    int indexA = mLruConnectionTracker.getAgeIndexOfNetwork(a);
+                    int indexB = mLruConnectionTracker.getAgeIndexOfNetwork(b);
+                    if (indexA != indexB) {
+                        return Integer.compare(indexA, indexB);
                     } else {
                         boolean isConfigALastSeen =
                                 a.getNetworkSelectionStatus()
@@ -2634,22 +2637,6 @@
     }
 
     /**
-     * Find the most recently connected network from a list of networks, and place it at top
-     */
-    private void putMostRecentlyConnectedNetworkAtTop(List<WifiConfiguration> networks) {
-        WifiConfiguration lastConnectedNetwork =
-                networks.stream()
-                        .max(Comparator.comparing(
-                                (WifiConfiguration config) -> config.lastConnected))
-                        .get();
-        if (lastConnectedNetwork.lastConnected != 0) {
-            int lastConnectedNetworkIdx = networks.indexOf(lastConnectedNetwork);
-            networks.remove(lastConnectedNetworkIdx);
-            networks.add(0, lastConnectedNetwork);
-        }
-    }
-
-    /**
      * Retrieves a list of channels for partial single scans
      *
      * @param ageInMillis only consider scan details whose timestamps are more recent than this.
@@ -2675,10 +2662,7 @@
         }
 
         // Sort the networks with the most frequent ones at the front of the network list.
-        Collections.sort(networks, sScanListComparator);
-
-        // Find the most recently connected network and move it to the front of the network list.
-        putMostRecentlyConnectedNetworkAtTop(networks);
+        Collections.sort(networks, mScanListComparator);
 
         Set<Integer> channelSet = new HashSet<>();
         long nowInMillis = mClock.getWallClockMillis();
@@ -2793,7 +2777,7 @@
         List<WifiConfiguration> networks = getConfiguredNetworks();
         // Remove any non hidden networks.
         networks.removeIf(config -> !config.hiddenSSID);
-        networks.sort(sScanListComparator);
+        networks.sort(mScanListComparator);
         // The most frequently connected network has the highest priority now.
         for (WifiConfiguration config : networks) {
             hiddenList.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
@@ -2877,9 +2861,8 @@
     }
 
     /**
-     * Clear all deleted ephemeral networks.
+     * Clear all user temporarily disabled networks.
      */
-    @VisibleForTesting
     public void clearUserTemporarilyDisabledList() {
         mUserTemporarilyDisabledList.clear();
     }
@@ -3447,4 +3430,8 @@
         }
         return scanMaxRssi;
     }
+
+    public Comparator<WifiConfiguration> getScanListComparator() {
+        return mScanListComparator;
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 31fcfca..712dbcd 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -52,7 +52,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -1302,11 +1301,8 @@
         if (networks.isEmpty()) {
             return Collections.EMPTY_LIST;
         }
-        Collections.sort(networks, WifiConfigManager.sScanListComparator);
-        if (mContext.getResources().getBoolean(R.bool.config_wifiPnoRecencySortingEnabled)) {
-            // Find the most recently connected network and move it to the front of the list.
-            putMostRecentlyConnectedNetworkAtTop(networks);
-        }
+        Collections.sort(networks, mConfigManager.getScanListComparator());
+
         WifiScoreCard scoreCard = null;
         if (mContext.getResources().getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled)) {
             scoreCard = mWifiInjector.getWifiScoreCard();
@@ -1334,22 +1330,6 @@
         return pnoList;
     }
 
-    /**
-     * Find the most recently connected network from a list of networks, and place it at top
-     */
-    private void putMostRecentlyConnectedNetworkAtTop(List<WifiConfiguration> networks) {
-        WifiConfiguration lastConnectedNetwork =
-                networks.stream()
-                        .max(Comparator.comparing(
-                                (WifiConfiguration config) -> config.lastConnected))
-                        .get();
-        if (lastConnectedNetwork.lastConnected != 0) {
-            int lastConnectedNetworkIdx = networks.indexOf(lastConnectedNetwork);
-            networks.remove(lastConnectedNetworkIdx);
-            networks.add(0, lastConnectedNetwork);
-        }
-    }
-
     // Stop PNO scan.
     private void stopPnoScan() {
         if (!mPnoScanStarted) return;
diff --git a/service/java/com/android/server/wifi/WifiContext.java b/service/java/com/android/server/wifi/WifiContext.java
index 2040d73..b360a8e 100644
--- a/service/java/com/android/server/wifi/WifiContext.java
+++ b/service/java/com/android/server/wifi/WifiContext.java
@@ -19,18 +19,29 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.util.Log;
 
+import com.android.server.wifi.util.Environment;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * Wrapper for context to override getResources method. Resources for wifi mainline jar needs to be
  * fetched from the resources APK.
  */
 public class WifiContext extends ContextWrapper {
     private static final String TAG = "WifiContext";
-    public static final String WIFI_OVERLAY_APK_PKG_NAME = "com.android.wifi.resources";
+    /** Intent action that is used to identify ServiceWifiResources.apk */
+    private static final String ACTION_RESOURCES_APK =
+            "com.android.server.wifi.intent.action.SERVICE_WIFI_RESOURCES_APK";
+
+    private String mWifiOverlayApkPkgName;
 
     // Cached resources from the resources APK.
     private AssetManager mWifiAssetsFromApk;
@@ -41,9 +52,45 @@
         super(contextBase);
     }
 
+    /** Get the package name of ServiceWifiResources.apk */
+    public String getWifiOverlayApkPkgName() {
+        if (mWifiOverlayApkPkgName != null) {
+            return mWifiOverlayApkPkgName;
+        }
+
+        List<ResolveInfo> resolveInfos = getPackageManager().queryIntentActivities(
+                new Intent(ACTION_RESOURCES_APK),
+                PackageManager.MATCH_SYSTEM_ONLY);
+
+        // remove apps that don't live in the Wifi apex
+        resolveInfos.removeIf(info ->
+                !Environment.isAppInWifiApex(info.activityInfo.applicationInfo));
+
+        if (resolveInfos.isEmpty()) {
+            // Resource APK not loaded yet, print a stack trace to see where this is called from
+            Log.e(TAG, "Attempted to fetch resources before Wifi Resources APK is loaded!",
+                    new IllegalStateException());
+            return null;
+        }
+
+        if (resolveInfos.size() > 1) {
+            // multiple apps found, log a warning, but continue
+            Log.w(TAG, "Found > 1 APK that can resolve Wifi Resources APK intent: "
+                    + resolveInfos.stream()
+                            .map(info -> info.activityInfo.applicationInfo.packageName)
+                            .collect(Collectors.joining(", ")));
+        }
+
+        // Assume the first ResolveInfo is the one we're looking for
+        ResolveInfo info = resolveInfos.get(0);
+        mWifiOverlayApkPkgName = info.activityInfo.applicationInfo.packageName;
+        Log.i(TAG, "Found Wifi Resources APK at: " + mWifiOverlayApkPkgName);
+        return mWifiOverlayApkPkgName;
+    }
+
     private Context getResourcesApkContext() {
         try {
-            return createPackageContext(WIFI_OVERLAY_APK_PKG_NAME, 0);
+            return createPackageContext(getWifiOverlayApkPkgName(), 0);
         } catch (PackageManager.NameNotFoundException e) {
             Log.wtf(TAG, "Failed to load resources", e);
         }
diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java
index fd29934..ba1de32 100644
--- a/service/java/com/android/server/wifi/WifiDataStall.java
+++ b/service/java/com/android/server/wifi/WifiDataStall.java
@@ -48,7 +48,7 @@
     // Maximum time that a data stall start time stays valid.
     public static final long VALIDITY_PERIOD_OF_DATA_STALL_START_MS = 30 * 1000; // 0.5 minutes
     // Default Tx packet error rate when there is no Tx attempt
-    public static final int DEFAULT_TX_PACKET_ERROR_RATE = 20;
+    public static final int DEFAULT_TX_PACKET_ERROR_RATE = 5;
     // Default CCA level when CCA stats are not available
     public static final int DEFAULT_CCA_LEVEL_2G = CHANNEL_UTILIZATION_SCALE * 16 / 100;
     public static final int DEFAULT_CCA_LEVEL_ABOVE_2G = CHANNEL_UTILIZATION_SCALE * 6 / 100;
@@ -230,6 +230,7 @@
         int currFrequency = wifiInfo.getFrequency();
         mWifiChannelUtilization.refreshChannelStatsAndChannelUtilization(newStats, currFrequency);
         int ccaLevel = mWifiChannelUtilization.getUtilizationRatio(currFrequency);
+        mWifiMetrics.incrementChannelUtilizationCount(ccaLevel, currFrequency);
 
         if (oldStats == null || newStats == null) {
             // First poll after new association
@@ -242,6 +243,7 @@
             }
             mIsThroughputSufficient = true;
             mWifiMetrics.resetWifiIsUnusableLinkLayerStats();
+            mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
             return WifiIsUnusableEvent.TYPE_UNKNOWN;
         }
 
@@ -322,6 +324,7 @@
         } else {
             mRxTputKbps = INVALID_THROUGHPUT;
         }
+        mWifiMetrics.incrementThroughputKbpsCount(mTxTputKbps, mRxTputKbps, currFrequency);
 
         mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps,
                 isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs);
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index a6aa034..6daca90 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -83,7 +83,7 @@
 
     static WifiInjector sWifiInjector = null;
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final BatteryStatsManager mBatteryStats;
     private final FrameworkFacade mFrameworkFacade;
     private final DeviceConfigFacade mDeviceConfigFacade;
@@ -169,7 +169,7 @@
     private final SettingsMigrationDataHolder mSettingsMigrationDataHolder;
     private final LruConnectionTracker mLruConnectionTracker;
 
-    public WifiInjector(Context context) {
+    public WifiInjector(WifiContext context) {
         if (context == null) {
             throw new IllegalStateException(
                     "WifiInjector should not be initialized with a null Context.");
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index fb61737..c563a4b 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -442,6 +442,26 @@
     // Connection duration stats collected while link layer stats reports are on
     private final ConnectionDurationStats mConnectionDurationStats = new ConnectionDurationStats();
 
+    private static final int[] CHANNEL_UTILIZATION_BUCKETS =
+            {25, 50, 75, 100, 125, 150, 175, 200, 225};
+
+    private final IntHistogram mChannelUtilizationHistogram2G =
+            new IntHistogram(CHANNEL_UTILIZATION_BUCKETS);
+
+    private final IntHistogram mChannelUtilizationHistogramAbove2G =
+            new IntHistogram(CHANNEL_UTILIZATION_BUCKETS);
+
+    private static final int[] THROUGHPUT_MBPS_BUCKETS =
+            {1, 5, 10, 15, 25, 50, 100, 150, 200, 300, 450, 600, 800, 1200, 1600};
+    private final IntHistogram mTxThroughputMbpsHistogram2G =
+            new IntHistogram(THROUGHPUT_MBPS_BUCKETS);
+    private final IntHistogram mRxThroughputMbpsHistogram2G =
+            new IntHistogram(THROUGHPUT_MBPS_BUCKETS);
+    private final IntHistogram mTxThroughputMbpsHistogramAbove2G =
+            new IntHistogram(THROUGHPUT_MBPS_BUCKETS);
+    private final IntHistogram mRxThroughputMbpsHistogramAbove2G =
+            new IntHistogram(THROUGHPUT_MBPS_BUCKETS);
+
     // Wi-Fi off metrics
     private final WifiOffMetrics mWifiOffMetrics = new WifiOffMetrics();
 
@@ -2039,6 +2059,54 @@
     }
 
     /**
+     * Increment occurrence count of channel utilization
+     * @param channelUtilization Channel utilization of current network
+     * @param frequency Channel frequency of current network
+     */
+    @VisibleForTesting
+    public void incrementChannelUtilizationCount(int channelUtilization, int frequency) {
+        if (channelUtilization < InformationElementUtil.BssLoad.MIN_CHANNEL_UTILIZATION
+                || channelUtilization > InformationElementUtil.BssLoad.MAX_CHANNEL_UTILIZATION) {
+            return;
+        }
+        synchronized (mLock) {
+            if (frequency <= KnownBandsChannelHelper.BAND_24_GHZ_END_FREQ) {
+                mChannelUtilizationHistogram2G.increment(channelUtilization);
+            } else {
+                mChannelUtilizationHistogramAbove2G.increment(channelUtilization);
+            }
+        }
+    }
+
+    /**
+     * Increment occurrence count of Tx and Rx throughput
+     * @param txThroughputKbps Tx throughput of current network in Kbps
+     * @param rxThroughputKbps Rx throughput of current network in Kbps
+     * @param frequency Channel frequency of current network in MHz
+     */
+    @VisibleForTesting
+    public void incrementThroughputKbpsCount(int txThroughputKbps, int rxThroughputKbps,
+            int frequency) {
+        synchronized (mLock) {
+            if (frequency <= KnownBandsChannelHelper.BAND_24_GHZ_END_FREQ) {
+                if (txThroughputKbps >= 0) {
+                    mTxThroughputMbpsHistogram2G.increment(txThroughputKbps / 1000);
+                }
+                if (rxThroughputKbps >= 0) {
+                    mRxThroughputMbpsHistogram2G.increment(rxThroughputKbps / 1000);
+                }
+            } else {
+                if (txThroughputKbps >= 0) {
+                    mTxThroughputMbpsHistogramAbove2G.increment(txThroughputKbps / 1000);
+                }
+                if (rxThroughputKbps >= 0) {
+                    mRxThroughputMbpsHistogramAbove2G.increment(rxThroughputKbps / 1000);
+                }
+            }
+        }
+    }
+
+    /**
      * Increment count of Watchdog successes.
      */
     public void incrementNumLastResortWatchdogSuccesses() {
@@ -3403,6 +3471,18 @@
                         + mWifiOffMetrics.toString());
                 pw.println("mWifiLogProto.softApConfigLimitationMetrics="
                         + mSoftApConfigLimitationMetrics.toString());
+                pw.println("mChannelUtilizationHistogram2G:\n"
+                        + mChannelUtilizationHistogram2G);
+                pw.println("mChannelUtilizationHistogramAbove2G:\n"
+                        + mChannelUtilizationHistogramAbove2G);
+                pw.println("mTxThroughputMbpsHistogram2G:\n"
+                        + mTxThroughputMbpsHistogram2G);
+                pw.println("mRxThroughputMbpsHistogram2G:\n"
+                        + mRxThroughputMbpsHistogram2G);
+                pw.println("mTxThroughputMbpsHistogramAbove2G:\n"
+                        + mTxThroughputMbpsHistogramAbove2G);
+                pw.println("mRxThroughputMbpsHistogramAbove2G:\n"
+                        + mRxThroughputMbpsHistogramAbove2G);
             }
         }
     }
@@ -3974,6 +4054,22 @@
             mWifiLogProto.connectionDurationStats = mConnectionDurationStats.toProto();
             mWifiLogProto.wifiOffMetrics = mWifiOffMetrics.toProto();
             mWifiLogProto.softApConfigLimitationMetrics = mSoftApConfigLimitationMetrics.toProto();
+            mWifiLogProto.channelUtilizationHistogram =
+                    new WifiMetricsProto.ChannelUtilizationHistogram();
+            mWifiLogProto.channelUtilizationHistogram.utilization2G =
+                    mChannelUtilizationHistogram2G.toProto();
+            mWifiLogProto.channelUtilizationHistogram.utilizationAbove2G =
+                    mChannelUtilizationHistogramAbove2G.toProto();
+            mWifiLogProto.throughputMbpsHistogram =
+                    new WifiMetricsProto.ThroughputMbpsHistogram();
+            mWifiLogProto.throughputMbpsHistogram.tx2G =
+                    mTxThroughputMbpsHistogram2G.toProto();
+            mWifiLogProto.throughputMbpsHistogram.txAbove2G =
+                    mTxThroughputMbpsHistogramAbove2G.toProto();
+            mWifiLogProto.throughputMbpsHistogram.rx2G =
+                    mRxThroughputMbpsHistogram2G.toProto();
+            mWifiLogProto.throughputMbpsHistogram.rxAbove2G =
+                    mRxThroughputMbpsHistogramAbove2G.toProto();
         }
     }
 
@@ -4166,6 +4262,12 @@
             mWifiLockLowLatencyActiveSessionDurationSecHistogram.clear();
             mWifiLockStats.clear();
             mWifiToggleStats.clear();
+            mChannelUtilizationHistogram2G.clear();
+            mChannelUtilizationHistogramAbove2G.clear();
+            mTxThroughputMbpsHistogram2G.clear();
+            mRxThroughputMbpsHistogram2G.clear();
+            mTxThroughputMbpsHistogramAbove2G.clear();
+            mRxThroughputMbpsHistogramAbove2G.clear();
             mPasspointProvisionFailureCounts.clear();
             mNumProvisionSuccess = 0;
             mBssidBlocklistStats = new BssidBlocklistStats();
diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
index c82539b..19c9da1 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
@@ -126,7 +126,7 @@
      */
     private static final int NUMBER_OF_HIDDEN_NETWORK_FOR_ONE_SCAN = 100;
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final Resources mResources;
     private final Handler mHandler;
     private final AppOpsManager mAppOps;
@@ -568,7 +568,7 @@
                 }
             };
 
-    public WifiNetworkSuggestionsManager(Context context, Handler handler,
+    public WifiNetworkSuggestionsManager(WifiContext context, Handler handler,
             WifiInjector wifiInjector, WifiPermissionsUtil wifiPermissionsUtil,
             WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore,
             WifiMetrics wifiMetrics, TelephonyUtil telephonyUtil,
@@ -800,15 +800,15 @@
     public @WifiManager.NetworkSuggestionsStatusCode int add(
             List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName,
             @Nullable String featureId) {
-        if (mVerboseLoggingEnabled) {
-            Log.v(TAG, "Adding " + networkSuggestions.size() + " networks from " + packageName);
-        }
-        if (networkSuggestions.isEmpty()) {
+        if (networkSuggestions == null || networkSuggestions.isEmpty()) {
             Log.w(TAG, "Empty list of network suggestions for " + packageName + ". Ignoring");
             return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
         }
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "Adding " + networkSuggestions.size() + " networks from " + packageName);
+        }
         if (!validateNetworkSuggestions(networkSuggestions)) {
-            Log.e(TAG, "Invalid suggestion from app: " + packageName);
+            Log.e(TAG, "Invalid suggestion add from app: " + packageName);
             return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID;
         }
         if (!validateCarrierNetworkSuggestions(networkSuggestions, uid, packageName)) {
@@ -928,6 +928,9 @@
 
     private boolean validateNetworkSuggestions(List<WifiNetworkSuggestion> networkSuggestions) {
         for (WifiNetworkSuggestion wns : networkSuggestions) {
+            if (wns == null || wns.wifiConfiguration == null) {
+                return false;
+            }
             if (wns.passpointConfiguration == null) {
                 if (!WifiConfigurationUtil.validate(wns.wifiConfiguration,
                         WifiConfigurationUtil.VALIDATE_FOR_ADD)) {
@@ -1037,9 +1040,18 @@
      */
     public @WifiManager.NetworkSuggestionsStatusCode int remove(
             List<WifiNetworkSuggestion> networkSuggestions, int uid, String packageName) {
+        if (networkSuggestions == null) {
+            Log.w(TAG, "Null list of network suggestions for " + packageName + ". Ignoring");
+            return WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
+        }
         if (mVerboseLoggingEnabled) {
             Log.v(TAG, "Removing " + networkSuggestions.size() + " networks from " + packageName);
         }
+
+        if (!validateNetworkSuggestions(networkSuggestions)) {
+            Log.e(TAG, "Invalid suggestion remove from app: " + packageName);
+            return WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID;
+        }
         PerAppInfo perAppInfo = mActiveNetworkSuggestionsPerApp.get(packageName);
         if (perAppInfo == null) {
             Log.e(TAG, "Failed to remove network suggestions for " + packageName
@@ -1305,7 +1317,7 @@
         CharSequence appName = getAppName(packageName, uid);
         Notification notification = mFrameworkFacade.makeNotificationBuilder(
                 mContext, WifiService.NOTIFICATION_NETWORK_STATUS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setTicker(mResources.getString(R.string.wifi_suggestion_title))
                 .setContentTitle(mResources.getString(R.string.wifi_suggestion_title))
@@ -1347,7 +1359,7 @@
 
         Notification notification = mFrameworkFacade.makeNotificationBuilder(
                 mContext, WifiService.NOTIFICATION_NETWORK_STATUS)
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setTicker(mResources.getString(
                         R.string.wifi_suggestion_imsi_privacy_title, carrierName))
@@ -1979,11 +1991,12 @@
                 continue;
             }
             if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
-                Log.v(TAG, "Carrier privilege revoked for " + appInfo.packageName);
+                Log.i(TAG, "Carrier privilege revoked for " + appInfo.packageName);
                 removeInternal(Collections.EMPTY_LIST, appInfo.packageName, appInfo);
                 mActiveNetworkSuggestionsPerApp.remove(appInfo.packageName);
                 continue;
             }
+            Log.i(TAG, "Carrier privilege granted for " + appInfo.packageName);
             appInfo.carrierId = carrierId;
             for (ExtendedWifiNetworkSuggestion ewns : appInfo.extNetworkSuggestions) {
                 ewns.wns.wifiConfiguration.carrierId = carrierId;
@@ -2031,7 +2044,14 @@
             @NonNull List<WifiNetworkSuggestion> wifiNetworkSuggestions,
             @NonNull List<ScanResult> scanResults) {
         Map<WifiNetworkSuggestion, List<ScanResult>> filteredScanResults = new HashMap<>();
+        if (wifiNetworkSuggestions == null || wifiNetworkSuggestions.isEmpty()
+                || scanResults == null || scanResults.isEmpty()) {
+            return filteredScanResults;
+        }
         for (WifiNetworkSuggestion suggestion : wifiNetworkSuggestions) {
+            if (suggestion == null || suggestion.wifiConfiguration == null) {
+                continue;
+            }
             if (suggestion.passpointConfiguration != null) {
                 filteredScanResults.put(suggestion,
                         mWifiInjector.getPasspointManager().getMatchingScanResults(
diff --git a/service/java/com/android/server/wifi/WifiService.java b/service/java/com/android/server/wifi/WifiService.java
index 552afd4..0197246 100644
--- a/service/java/com/android/server/wifi/WifiService.java
+++ b/service/java/com/android/server/wifi/WifiService.java
@@ -39,12 +39,15 @@
     public static final String NOTIFICATION_NETWORK_ALERTS = "NETWORK_ALERTS";
     public static final String NOTIFICATION_NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
 
-    final WifiServiceImpl mImpl;
+    private final WifiServiceImpl mImpl;
+    private final WifiContext mWifiContext;
 
     public WifiService(Context contextBase) {
-        super(new WifiContext(contextBase));
-        mImpl = new WifiServiceImpl(getContext(), new WifiInjector(getContext()),
-                new WifiAsyncChannel(TAG));
+        super(contextBase);
+        mWifiContext = new WifiContext(contextBase);
+        WifiInjector injector = new WifiInjector(mWifiContext);
+        WifiAsyncChannel channel =  new WifiAsyncChannel(TAG);
+        mImpl = new WifiServiceImpl(mWifiContext, injector, channel);
     }
 
     @Override
@@ -56,7 +59,7 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            createNotificationChannels(getContext());
+            createNotificationChannels(mWifiContext);
             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 2e93728..91f7768 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -115,6 +115,7 @@
 import com.android.server.wifi.util.ApConfigUtil;
 import com.android.server.wifi.util.ExternalCallbackTracker;
 import com.android.server.wifi.util.RssiUtil;
+import com.android.server.wifi.util.ScanResultUtil;
 import com.android.server.wifi.util.WifiHandler;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.wifi.resources.R;
@@ -1035,8 +1036,10 @@
 
         public boolean setEnablingIfAllowed() {
             synchronized (mLock) {
-                if (mTetheredSoftApState == WIFI_AP_STATE_ENABLING) return false;
-                if (mTetheredSoftApState == WIFI_AP_STATE_ENABLED) return false;
+                if (mTetheredSoftApState != WIFI_AP_STATE_DISABLED
+                        && mTetheredSoftApState != WIFI_AP_STATE_FAILED) {
+                    return false;
+                }
                 mTetheredSoftApState = WIFI_AP_STATE_ENABLING;
                 return true;
             }
@@ -2192,6 +2195,10 @@
         if (mVerboseLoggingEnabled) {
             mLog.info("getMatchingPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush();
         }
+        if (!ScanResultUtil.validateScanResultList(scanResults)) {
+            Log.e(TAG, "Attempt to retrieve passpoint with invalid scanResult List");
+            return Collections.emptyMap();
+        }
         return mWifiThreadRunner.call(
             () -> mPasspointManager.getAllMatchingPasspointProfilesForScanResults(scanResults),
                 Collections.emptyMap());
@@ -2212,6 +2219,11 @@
         if (mVerboseLoggingEnabled) {
             mLog.info("getMatchingOsuProviders uid=%").c(Binder.getCallingUid()).flush();
         }
+
+        if (!ScanResultUtil.validateScanResultList(scanResults)) {
+            Log.e(TAG, "Attempt to retrieve OsuProviders with invalid scanResult List");
+            return Collections.emptyMap();
+        }
         return mWifiThreadRunner.call(
             () -> mPasspointManager.getMatchingOsuProviders(scanResults), Collections.emptyMap());
     }
@@ -2288,8 +2300,8 @@
             mLog.info("getWifiConfigsForMatchedNetworkSuggestions uid=%").c(
                     Binder.getCallingUid()).flush();
         }
-        if (scanResults == null) {
-            Log.e(TAG, "Attempt to retrieve WifiConfiguration with null scanResult List");
+        if (!ScanResultUtil.validateScanResultList(scanResults)) {
+            Log.e(TAG, "Attempt to retrieve WifiConfiguration with invalid scanResult List");
             return new ArrayList<>();
         }
         return mWifiThreadRunner.call(
@@ -2701,7 +2713,7 @@
 
             return mWifiThreadRunner.call(
                     () -> {
-                        if (scanResults == null || scanResults.isEmpty()) {
+                        if (!ScanResultUtil.validateScanResultList(scanResults)) {
                             return mWifiNetworkSuggestionsManager.getMatchingScanResults(
                                     networkSuggestions, mScanRequestProxy.getScanResults());
                         } else {
@@ -3494,6 +3506,8 @@
                         }
                         // Enable all networks restored.
                         mWifiConfigManager.enableNetwork(networkId, false, callingUid, null);
+                        // Restore auto-join param.
+                        mWifiConfigManager.allowAutojoin(networkId, configuration.allowAutojoin);
                     }
                 });
     }
diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java
index 6d7eeed..eb4210f 100644
--- a/service/java/com/android/server/wifi/WifiShellCommand.java
+++ b/service/java/com/android/server/wifi/WifiShellCommand.java
@@ -26,19 +26,23 @@
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.nl80211.WifiNl80211Manager;
 import android.os.BasicShellCommandHandler;
 import android.os.Binder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.text.TextUtils;
 
 import com.android.server.wifi.util.ApConfigUtil;
+import com.android.server.wifi.util.ArrayUtils;
 import com.android.server.wifi.util.ScanResultUtil;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CountDownLatch;
@@ -60,6 +64,26 @@
  */
 public class WifiShellCommand extends BasicShellCommandHandler {
     private static String SHELL_PACKAGE_NAME = "com.android.shell";
+    // These don't require root access.
+    // However, these do perform permission checks in the corresponding WifiService methods.
+    private static final String[] NON_PRIVILEGED_COMMANDS = {
+            "add-suggestion",
+            "connect-network",
+            "forget-network",
+            "get-country-code",
+            "help",
+            "-h",
+            "list-scan-results",
+            "list-networks",
+            "list-suggestions",
+            "remove-suggestion",
+            "remove-all-suggestions",
+            "set-verbose-logging",
+            "set-wifi-enabled",
+            "start-scan",
+            "status",
+    };
+
     private final ClientModeImpl mClientModeImpl;
     private final WifiLockManager mWifiLockManager;
     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
@@ -86,16 +110,22 @@
 
     @Override
     public int onCommand(String cmd) {
+        // Treat no command as help command.
+        if (cmd == null || cmd.equals("")) {
+            cmd = "help";
+        }
         // Explicit exclusion from root permission
-        // Do not require root permission to maintain backwards compatibility with
-        // `svc wifi [enable|disable]`.
-        if (!"set-wifi-enabled".equals(cmd)) {
-            checkRootPermission();
+        if (ArrayUtils.indexOf(NON_PRIVILEGED_COMMANDS, cmd) == -1) {
+            final int uid = Binder.getCallingUid();
+            if (uid != Process.ROOT_UID) {
+                throw new SecurityException(
+                        "Uid " + uid + " does not have access to " + cmd + " wifi command");
+            }
         }
 
         final PrintWriter pw = getOutPrintWriter();
         try {
-            switch (cmd != null ? cmd : "") {
+            switch (cmd) {
                 case "set-ipreach-disconnect": {
                     boolean enabled;
                     String nextArg = getNextArgRequired();
@@ -358,12 +388,6 @@
                     return 0;
                 }
                 case "set-wifi-enabled": {
-                    // This command is explicitly exempted from checkRootPermission() (see beginning
-                    // of this method).
-                    // Do not require root permission to maintain backwards compatibility with
-                    // `svc wifi [enable|disable]`.
-                    // However, setWifiEnabled() does perform its own check for the
-                    // android.Manifest.permission.CHANGE_WIFI_STATE permission.
                     boolean enabled;
                     String nextArg = getNextArgRequired();
                     if ("enabled".equals(nextArg)) {
@@ -433,20 +457,21 @@
                 case "connect-network": {
                     String ssid = getNextArgRequired();
                     String type = getNextArgRequired();
-                    String optionalPassphrase = getNextArg();
                     WifiConfiguration configuration = new WifiConfiguration();
                     configuration.SSID = "\"" + ssid + "\"";
                     if (TextUtils.equals(type, "wpa3")) {
                         configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+                        configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
                     } else if (TextUtils.equals(type, "wpa2")) {
                         configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+                        configuration.preSharedKey = "\"" + getNextArgRequired() + "\"";
                     } else if (TextUtils.equals(type, "owe")) {
                         configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
                     } else if (TextUtils.equals(type, "open")) {
                         configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
-                    }
-                    if (optionalPassphrase != null) {
-                        configuration.preSharedKey = "\"" + optionalPassphrase + "\"";
+                    } else {
+                        pw.println("Unknown network type " + type);
+                        return -1;
                     }
                     CountDownLatch countDownLatch = new CountDownLatch(1);
                     IActionListener.Stub actionListener = new IActionListener.Stub() {
@@ -521,6 +546,49 @@
                     }
                     mWifiService.enableVerboseLogging(enabled ? 1 : 0);
                     break;
+                case "add-suggestion":
+                    mWifiService.addNetworkSuggestions(
+                            Arrays.asList(buildSuggestion(pw)), SHELL_PACKAGE_NAME, null);
+                    break;
+                case "remove-suggestion":
+                    mWifiService.removeNetworkSuggestions(
+                            Arrays.asList(buildSuggestion(pw)), SHELL_PACKAGE_NAME);
+                    break;
+                case "remove-all-suggestions":
+                    mWifiService.removeNetworkSuggestions(
+                            Collections.emptyList(), SHELL_PACKAGE_NAME);
+                    break;
+                case "list-suggestions":
+                    List<WifiNetworkSuggestion> suggestions =
+                            mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
+                    if (suggestions == null || suggestions.isEmpty()) {
+                        pw.println("No suggestions");
+                    } else {
+                        pw.println("SSID                         Security type");
+                        for (WifiNetworkSuggestion suggestion : suggestions) {
+                            String securityType = null;
+                            if (WifiConfigurationUtil.isConfigForSaeNetwork(
+                                    suggestion.getWifiConfiguration())) {
+                                securityType = "wpa3";
+                            } else if (WifiConfigurationUtil.isConfigForPskNetwork(
+                                    suggestion.getWifiConfiguration())) {
+                                securityType = "wpa2";
+                            } else if (WifiConfigurationUtil.isConfigForEapNetwork(
+                                    suggestion.getWifiConfiguration())) {
+                                securityType = "eap";
+                            } else if (WifiConfigurationUtil.isConfigForOweNetwork(
+                                    suggestion.getWifiConfiguration())) {
+                                securityType = "owe";
+                            } else if (WifiConfigurationUtil.isConfigForOpenNetwork(
+                                    suggestion.getWifiConfiguration())) {
+                                securityType = "open";
+                            }
+                            pw.println(String.format("%-32s %-4s",
+                                    WifiInfo.sanitizeSsid(suggestion.getWifiConfiguration().SSID),
+                                    securityType));
+                        }
+                    }
+                    break;
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -531,6 +599,27 @@
         return -1;
     }
 
+    private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) {
+        String ssid = getNextArgRequired();
+        String type = getNextArgRequired();
+        WifiNetworkSuggestion.Builder suggestionBuilder =
+                new WifiNetworkSuggestion.Builder();
+        suggestionBuilder.setSsid(ssid);
+        if (TextUtils.equals(type, "wpa3")) {
+            suggestionBuilder.setWpa3Passphrase(getNextArgRequired());
+        } else if (TextUtils.equals(type, "wpa2")) {
+            suggestionBuilder.setWpa2Passphrase(getNextArgRequired());
+        } else if (TextUtils.equals(type, "owe")) {
+            suggestionBuilder.setIsEnhancedOpen(true);
+        } else if (TextUtils.equals(type, "open")) {
+            // nothing to do.
+        } else {
+            pw.println("Unknown network type " + type);
+            return null;
+        }
+        return suggestionBuilder.build();
+    }
+
     private int sendLinkProbe(PrintWriter pw) throws InterruptedException {
         // Note: should match WifiNl80211Manager#SEND_MGMT_FRAME_TIMEOUT_MS
         final int sendMgmtFrameTimeoutMs = 1000;
@@ -583,22 +672,63 @@
                 || Arrays.binarySearch(allowed6gFreq, apChannelMHz) >= 0;
     }
 
-    private void checkRootPermission() {
-        final int uid = Binder.getCallingUid();
-        if (uid == 0) {
-            // Root can do anything.
-            return;
-        }
-        throw new SecurityException("Uid " + uid + " does not have access to wifi commands");
+    private void onHelpNonPrivileged(PrintWriter pw) {
+        pw.println("  get-country-code");
+        pw.println("    Gets country code as a two-letter string");
+        pw.println("  set-wifi-enabled enabled|disabled");
+        pw.println("    Enables/disables Wifi on this device.");
+        pw.println("  list-scan-results");
+        pw.println("    Lists the latest scan results");
+        pw.println("  start-scan");
+        pw.println("    Start a new scan");
+        pw.println("  list-networks");
+        pw.println("    Lists the saved networks");
+        pw.println("  connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>]");
+        pw.println("    Connect to a network with provided params and save");
+        pw.println("    <ssid> - SSID of the network");
+        pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
+        pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
+        pw.println("           - 'open' - Open networks (Most prevalent)");
+        pw.println("           - 'owe' - Enhanced open networks");
+        pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
+        pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
+        pw.println("           - 'wpa3' - WPA-3 PSK networks");
+        pw.println("  forget-network <networkId>");
+        pw.println("    Remove the network mentioned by <networkId>");
+        pw.println("        - Use list-networks to retrieve <networkId> for the network");
+        pw.println("  status");
+        pw.println("    Current wifi status");
+        pw.println("  set-verbose-logging enabled|disabled ");
+        pw.println("    Set the verbose logging enabled or disabled");
+        pw.println("  add-suggestion <ssid> open|owe|wpa2|wpa3 [<passphrase>]");
+        pw.println("    Add a network suggestion with provided params");
+        pw.println("    Use 'network-suggestions-set-user-approved " + SHELL_PACKAGE_NAME + " yes'"
+                +  " to approve suggestions added via shell (Needs root access)");
+        pw.println("    <ssid> - SSID of the network");
+        pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
+        pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
+        pw.println("           - 'open' - Open networks (Most prevalent)");
+        pw.println("           - 'owe' - Enhanced open networks");
+        pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
+        pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
+        pw.println("           - 'wpa3' - WPA-3 PSK networks");
+        pw.println("  remove-suggestion <ssid> open|owe|wpa2|wpa3");
+        pw.println("    Remove a network suggestion with provided params");
+        pw.println("    <ssid> - SSID of the network");
+        pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
+        pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
+        pw.println("           - 'open' - Open networks (Most prevalent)");
+        pw.println("           - 'owe' - Enhanced open networks");
+        pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
+        pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
+        pw.println("           - 'wpa3' - WPA-3 PSK networks");
+        pw.println("  remove-all-suggestions");
+        pw.println("    Removes all suggestions added via shell");
+        pw.println("  list-suggestions");
+        pw.println("    Lists the suggested networks added via shell");
     }
 
-    @Override
-    public void onHelp() {
-        final PrintWriter pw = getOutPrintWriter();
-
-        pw.println("Wi-Fi (wifi) commands:");
-        pw.println("  help");
-        pw.println("    Print this help text.");
+    private void onHelpPrivileged(PrintWriter pw) {
         pw.println("  set-ipreach-disconnect enabled|disabled");
         pw.println("    Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.");
         pw.println("  get-ipreach-disconnect");
@@ -632,42 +762,27 @@
         pw.println("    or left for normal   operation.");
         pw.println("  force-country-code enabled <two-letter code> | disabled ");
         pw.println("    Sets country code to <two-letter code> or left for normal value");
-        pw.println("  get-country-code");
-        pw.println("    Gets country code as a two-letter string");
         pw.println("  set-wifi-watchdog enabled|disabled");
         pw.println("    Sets whether wifi watchdog should trigger recovery");
         pw.println("  get-wifi-watchdog");
         pw.println("    Gets setting of wifi watchdog trigger recovery.");
-        pw.println("  set-wifi-enabled enabled|disabled");
-        pw.println("    Enables/disables Wifi on this device.");
         pw.println("  get-softap-supported-features");
         pw.println("    Gets softap supported features. Will print 'wifi_softap_acs_supported'");
         pw.println("    and/or 'wifi_softap_wpa3_sae_supported', each on a separate line.");
         pw.println("  settings-reset");
         pw.println("    Initiates wifi settings reset");
-        pw.println("  list-scan-results");
-        pw.println("    Lists the latest scan results");
-        pw.println("  start-scan");
-        pw.println("    Start a new scan");
-        pw.println("  list-networks");
-        pw.println("    Lists the saved networks");
-        pw.println("  connect-network <ssid> open|owe|wpa2|wpa3 [<passphrase>]");
-        pw.println("    Connect to a network with provided params and save");
-        pw.println("    <ssid> - SSID of the network");
-        pw.println("    open|owe|wpa2|wpa3 - Security type of the network.");
-        pw.println("        - Use 'open' or 'owe' for networks with no passphrase");
-        pw.println("           - 'open' - Open networks (Most prevalent)");
-        pw.println("           - 'owe' - Enhanced open networks");
-        pw.println("        - Use 'wpa2' or 'wpa3' for networks with passphrase");
-        pw.println("           - 'wpa2' - WPA-2 PSK networks (Most prevalent)");
-        pw.println("           - 'wpa3' - WPA-3 PSK networks");
-        pw.println("  forget-network <networkId>");
-        pw.println("    Remove the network mentioned by <networkId>");
-        pw.println("        - Use list-networks to retrieve <networkId> for the network");
-        pw.println("  status");
-        pw.println("    Current wifi status");
-        pw.println("  set-verbose-logging enabled|disabled ");
-        pw.println("    Set the verbose logging enabled or disabled");
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("Wi-Fi (wifi) commands:");
+        pw.println("  help or -h");
+        pw.println("    Print this help text.");
+        onHelpNonPrivileged(pw);
+        if (Binder.getCallingUid() == Process.ROOT_UID) {
+            onHelpPrivileged(pw);
+        }
         pw.println();
     }
 }
diff --git a/service/java/com/android/server/wifi/WrongPasswordNotifier.java b/service/java/com/android/server/wifi/WrongPasswordNotifier.java
index d086ad5..9975f97 100644
--- a/service/java/com/android/server/wifi/WrongPasswordNotifier.java
+++ b/service/java/com/android/server/wifi/WrongPasswordNotifier.java
@@ -42,11 +42,11 @@
     // Flag indicating if a wrong password error is detected for the current connection.
     private boolean mWrongPasswordDetected;
 
-    private final Context mContext;
+    private final WifiContext mContext;
     private final NotificationManager mNotificationManager;
     private final FrameworkFacade mFrameworkFacade;
 
-    public WrongPasswordNotifier(Context context, FrameworkFacade frameworkFacade) {
+    public WrongPasswordNotifier(WifiContext context, FrameworkFacade frameworkFacade) {
         mContext = context;
         mFrameworkFacade = frameworkFacade;
         mNotificationManager =
@@ -86,7 +86,7 @@
                 .setAutoCancel(true)
                 .setTimeoutAfter(CANCEL_TIMEOUT_MILLISECONDS)
                 // TODO(zqiu): consider creating a new icon.
-                .setSmallIcon(Icon.createWithResource(WifiContext.WIFI_OVERLAY_APK_PKG_NAME,
+                .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
                 .setContentTitle(mContext.getString(
                         com.android.wifi.resources.R.string.wifi_available_title_failed_to_connect))
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index 682af56..18ff047 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -1554,7 +1554,7 @@
          * Tell the network agent the network is now connected.
          */
         public void setConnected(WifiAwareNetworkAgent networkAgent) {
-            networkAgent.setConnected();
+            networkAgent.markConnected();
         }
     }
 
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
index 1884536..ad8480a 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
@@ -20,6 +20,8 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Network;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiManager;
@@ -53,7 +55,7 @@
 import com.android.server.wifi.hotspot2.soap.command.BrowserUri;
 import com.android.server.wifi.hotspot2.soap.command.PpsMoData;
 import com.android.server.wifi.hotspot2.soap.command.SppCommand;
-import com.android.wifi.resources.R;
+import com.android.server.wifi.util.Environment;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -781,24 +783,43 @@
             }
 
             Intent intent = new Intent(WifiManager.ACTION_PASSPOINT_LAUNCH_OSU_VIEW);
-            intent.setPackage(mContext.getResources()
-                    .getString(R.string.config_wifiOsuLoginPackage));
             intent.putExtra(WifiManager.EXTRA_OSU_NETWORK, mNetwork);
             intent.putExtra(WifiManager.EXTRA_URL, mWebUrl);
+            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            // Verify that the intent will resolve to an activity
-            if (intent.resolveActivity(mContext.getPackageManager()) != null) {
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-                invokeProvisioningCallback(PROVISIONING_STATUS,
-                        ProvisioningCallback.OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE);
-                changeState(STATE_WAITING_FOR_REDIRECT_RESPONSE);
-            } else {
+            List<ResolveInfo> resolveInfos = mContext.getPackageManager()
+                    .queryIntentActivities(
+                            intent,
+                            PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_SYSTEM_ONLY);
+            if (resolveInfos == null || resolveInfos.isEmpty()) {
                 Log.e(TAG, "can't resolve the activity for the intent");
                 resetStateMachineForFailure(ProvisioningCallback.OSU_FAILURE_NO_OSU_ACTIVITY_FOUND);
+                return;
             }
+
+            if (resolveInfos.size() > 1) {
+                if (mVerboseLoggingEnabled) {
+                    Log.i(TAG, "Multiple OsuLogin apps found: "
+                            + resolveInfos.stream()
+                                    .map(info -> info.activityInfo.applicationInfo.packageName)
+                                    .collect(Collectors.joining(", ")));
+                }
+
+                // if multiple apps are found, filter out the default implementation supplied
+                // in the Wifi apex and let other implementations override.
+                resolveInfos.removeIf(info ->
+                        Environment.isAppInWifiApex(info.activityInfo.applicationInfo));
+            }
+            // forcefully resolve to the first one
+            String packageName = resolveInfos.get(0).activityInfo.applicationInfo.packageName;
+            intent.setPackage(packageName);
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "Opening OsuLogin app: " + packageName);
+            }
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+            invokeProvisioningCallback(PROVISIONING_STATUS,
+                    ProvisioningCallback.OSU_STATUS_WAITING_FOR_REDIRECT_RESPONSE);
+            changeState(STATE_WAITING_FOR_REDIRECT_RESPONSE);
         }
 
         /**
diff --git a/service/java/com/android/server/wifi/util/Environment.java b/service/java/com/android/server/wifi/util/Environment.java
index b4df26d..a423d55 100644
--- a/service/java/com/android/server/wifi/util/Environment.java
+++ b/service/java/com/android/server/wifi/util/Environment.java
@@ -17,6 +17,7 @@
 package com.android.server.wifi.util;
 
 import android.content.ApexEnvironment;
+import android.content.pm.ApplicationInfo;
 import android.os.UserHandle;
 
 import java.io.File;
@@ -33,6 +34,13 @@
     private static final String WIFI_APEX_NAME = "com.android.wifi";
 
     /**
+     * The path where the Wifi apex is mounted.
+     * Current value = "/apex/com.android.wifi"
+     */
+    private static final String WIFI_APEX_PATH =
+            new File("/apex", WIFI_APEX_NAME).getAbsolutePath();
+
+    /**
      * Wifi shared folder.
      */
     public static File getWifiSharedDirectory() {
@@ -46,4 +54,12 @@
         return ApexEnvironment.getApexEnvironment(WIFI_APEX_NAME)
                 .getCredentialProtectedDataDirForUser(UserHandle.of(userId));
     }
+
+    /**
+     * Returns true if the app is in the Wifi apex, false otherwise.
+     * Checks if the app's path starts with "/apex/com.android.wifi".
+     */
+    public static boolean isAppInWifiApex(ApplicationInfo appInfo) {
+        return appInfo.sourceDir.startsWith(WIFI_APEX_PATH);
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/ScanResultUtil.java b/service/java/com/android/server/wifi/util/ScanResultUtil.java
index 320490c..dc2281a 100644
--- a/service/java/com/android/server/wifi/util/ScanResultUtil.java
+++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java
@@ -243,4 +243,24 @@
             }
         }
     }
+
+    /**
+     * Check if ScarResult list is valid.
+     */
+    public static boolean validateScanResultList(List<ScanResult> scanResults) {
+        if (scanResults == null || scanResults.isEmpty()) {
+            return false;
+        }
+        for (ScanResult scanResult : scanResults) {
+            if (!validate(scanResult)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean validate(ScanResult scanResult) {
+        return scanResult != null && scanResult.SSID != null
+                && scanResult.capabilities != null && scanResult.BSSID != null;
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java
index 9b541db..b7b2d9e 100644
--- a/service/java/com/android/server/wifi/util/TelephonyUtil.java
+++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java
@@ -1143,6 +1143,7 @@
     public int getCarrierIdForPackageWithCarrierPrivileges(String packageName) {
         List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
         if (subInfoList == null || subInfoList.isEmpty()) {
+            if (mVerboseLogEnabled) Log.v(TAG, "No subs for carrier privilege check");
             return TelephonyManager.UNKNOWN_CARRIER_ID;
         }
         for (SubscriptionInfo info : subInfoList) {
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
index d20ee7e..1984e41 100644
--- a/service/java/com/android/server/wifi/util/XmlUtil.java
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -456,8 +456,6 @@
                     configuration.allowedSuiteBCiphers.toByteArray());
             XmlUtil.writeNextValue(out, XML_TAG_SHARED, configuration.shared);
             XmlUtil.writeNextValue(out, XML_TAG_IS_AUTO_JOIN, configuration.allowAutojoin);
-            XmlUtil.writeNextValue(out, XML_TAG_IS_TRUSTED, configuration.trusted);
-
         }
 
         /**
@@ -487,6 +485,7 @@
                 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
                 throws XmlPullParserException, IOException {
             writeCommonElementsToXml(out, configuration, encryptionUtil);
+            XmlUtil.writeNextValue(out, XML_TAG_IS_TRUSTED, configuration.trusted);
             XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
             XmlUtil.writeNextValue(out, XML_TAG_STATUS, configuration.status);
             XmlUtil.writeNextValue(out, XML_TAG_FQDN, configuration.FQDN);
diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto
index f2ed398..34eb283 100644
--- a/service/proto/src/metrics.proto
+++ b/service/proto/src/metrics.proto
@@ -640,6 +640,12 @@
 
   // Metrics related to limitation in soft ap config
   optional SoftApConfigLimitationMetrics soft_ap_config_limitation_metrics = 178;
+
+  // WiFi channel utilization histogram of various RF bands
+  optional ChannelUtilizationHistogram channel_utilization_histogram = 179;
+
+  // WiFi Tx and Rx throughput histogram at various RF bands
+  optional ThroughputMbpsHistogram throughput_mbps_histogram = 180;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -2602,6 +2608,15 @@
   // Easy Connect (DPP) operation time bucket
   repeated HistogramBucketInt32 dpp_operation_time = 7;
 
+  // Number of Enrollee-Responder peer devices supporting DPP protocol version 1
+  optional int32 num_dpp_r1_capable_enrollee_responder_devices = 8;
+
+  // Number of Enrollee-Responder peer devices supporting DPP protocol version 2
+  optional int32 num_dpp_r2_capable_enrollee_responder_devices = 9;
+
+  // Number of Enrollee-Responder incompatible configurations found in DPP R2 compatibility check.
+  optional int32 num_dpp_r2_enrollee_responder_incompatible_configuration = 10;
+
   // Histogram bucket for Wi-Fi DPP configurator success
   message DppConfiguratorSuccessStatusHistogramBucket {
     // status type defining the bucket
@@ -2626,6 +2641,9 @@
 
     // Easy Connect Configurator success event: Configuration sent to enrollee
     EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 1;
+
+    // Easy Connect Configurator success event: Configuration applied by enrollee
+    EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 2;
   }
 
   enum DppFailureCode {
@@ -2661,6 +2679,15 @@
     // Easy Connect Failure event: Invalid network provided to Easy Connect configurator.
     // Network must either be WPA3-Personal (SAE) or WPA2-Personal (PSK).
     EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = 9;
+
+    // Easy Connect R2 Failure event: Enrollee cannot find the network.
+    EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = 10;
+
+    // Easy Connect R2 Failure event: Enrollee failed to authenticate with the network.
+    EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = 11;
+
+    // Easy Connect R2 Failure event: Enrollee rejected the configuration.
+    EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = 12;
   }
 }
 
@@ -3055,3 +3082,25 @@
   // setting limitation
   repeated Int32Count max_client_setting_when_reach_histogram = 4;
 }
+
+// Channel utilization histogram
+// The valid utilization range is between 0 and 255 corresponding to
+// utilization ratio between 0 and 1
+message ChannelUtilizationHistogram {
+  // Histogram at 2G
+  repeated HistogramBucketInt32 utilization_2g = 1;
+  // Histogram above 2G
+  repeated HistogramBucketInt32 utilization_above_2g = 2;
+}
+
+// Throughput histogram
+message ThroughputMbpsHistogram {
+  // Tx histogram at 2G
+  repeated HistogramBucketInt32 tx_2g = 1;
+  // Tx histogram above 2G
+  repeated HistogramBucketInt32 tx_above_2g = 2;
+  // Rx histogram at 2G
+  repeated HistogramBucketInt32 rx_2g = 3;
+  // Rx histogram above 2G
+  repeated HistogramBucketInt32 rx_above_2g = 4;
+}
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index e51e640..b5037b7 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -399,7 +399,4 @@
 
     <!-- Enable WPA2 to WPA3 auto-upgrade offload to capable Driver/Firmware -->
     <bool translatable="false" name="config_wifiSaeUpgradeOffloadEnabled">false</bool>
-
-    <!-- Package name for the OSU Login APK -->
-    <string translatable="false" name="config_wifiOsuLoginPackage">com.android.hotspot2.osulogin</string>
 </resources>
diff --git a/service/res/values/overlayable.xml b/service/res/values/overlayable.xml
index a6a37a3..7244911 100644
--- a/service/res/values/overlayable.xml
+++ b/service/res/values/overlayable.xml
@@ -126,7 +126,6 @@
           <item type="integer" name="config_wifiPollRssiIntervalMilliseconds" />
           <item type="bool" name="config_wifiSaeUpgradeEnabled" />
           <item type="bool" name="config_wifiSaeUpgradeOffloadEnabled" />
-          <item type="string" name="config_wifiOsuLoginPackage" />
           <!-- Params from config.xml that can be overlayed -->
 
           <!-- Params from strings.xml that can be overlayed -->
@@ -190,7 +189,6 @@
           <item type="string" name="wifi_eap_error_message_code_32766" />
           <item type="string" name="wifi_softap_auto_shutdown_timeout_expired_title" />
           <item type="string" name="wifi_softap_auto_shutdown_timeout_expired_summary" />
-          <item type="string" name="wifi_softap_auto_shutdown_timeout_expired_detail" />
           <item type="string" name="wifi_sim_required_title" />
           <item type="string" name="wifi_sim_required_message" />
           <!-- Params from strings.xml that can be overlayed -->
diff --git a/service/resources-certs/com.android.wifi.resources.pk8 b/service/resources-certs/com.android.wifi.resources.pk8
new file mode 100644
index 0000000..50cca90
--- /dev/null
+++ b/service/resources-certs/com.android.wifi.resources.pk8
Binary files differ
diff --git a/service/resources-certs/com.android.wifi.resources.x509.pem b/service/resources-certs/com.android.wifi.resources.x509.pem
new file mode 100644
index 0000000..c321be1
--- /dev/null
+++ b/service/resources-certs/com.android.wifi.resources.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGJzCCBA+gAwIBAgIUYJCfWCLIVw8RN9EmWeX78BvlReMwDQYJKoZIhvcNAQEL
+BQAwgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR0wGwYDVQQDDBRTZXJ2aWNlV2lmaVJlc291cmNlczEiMCAGCSqGSIb3DQEJ
+ARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAgFw0yMDAzMjUwMzA0MDFaGA80NzU4MDIx
+OTAzMDQwMVowgaExCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYw
+FAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQL
+DAdBbmRyb2lkMR0wGwYDVQQDDBRTZXJ2aWNlV2lmaVJlc291cmNlczEiMCAGCSqG
+SIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAOqOCNZuwS5nIEg5wUPop40+WSAZEQ5SL/uSNU1dnGMSxkD5
+QqZQ3eWKlLSvP1FXtbQPTojXn2fa8w2P39dUAe33NXgpSHZVk18DMgeepNCAbynJ
+fciqYCMjnT3G1scIZP8HCxAXsuoynqLSms7IyombgM4iePVKKKwRpe3F3BmWgjgz
+RatW6C2CJLHc4KgCD53b4w9XHrQ8l3L1c3bgIwjaDSd0VxKIa41KtHHWp3LuvxX5
+4wz6tbBNW5ZMNDEfvF7CGmTCkdbMAYkWcJxZxA57E2tpxXd+2TnEDo6HL7Yq28jG
+r8jN6U63zRsKHLMi+Dtyei0w1nBxtTf5KsWI7B5Cxavl+V+tCsnNfq5Gu+vjJnlL
+0kpFcBixG/TPv7H+NgyO5D7joAyGSctfMkTPWdSRD/FNTs30yil+hxUIu4mFYtXO
++c6yIm11OBmGz1IoqetgbBhQiUwy+p8oqge2NtFDaIJ3dkd5cmNUF/EXI89aUhu0
+o/Vo2coUpR/cYZTgexfk2RKcH3RkuG69HXofrC9SGY0RI7GgmaG0e1inBjVrOydP
+1XXJmF3DV8yy8NmkJklbljsGD52XuBC6VQ0eIh7cLKWjgMCJFFNkNgU0syZ3+bma
+28FWvzJzd3ii5seWqePGgcrfdgjxblNzQEKWw0Me9Wn/S0OKOz/5OAM9Z7RDAgMB
+AAGjUzBRMB0GA1UdDgQWBBQjYEU0cbWxEO42h68+ttN5+wDSITAfBgNVHSMEGDAW
+gBQjYEU0cbWxEO42h68+ttN5+wDSITAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4ICAQCatnV7MZsIQTPo3AbbvmSPfyFM4/ncRDao+hxxvDKwMMWw6O2r
+uKPIbdj3xcH4vwvM2v3Kg+YCNrRgTTG23QHXNzHgXM75o1q7djGEiuOaZ6lwGh0G
+KD0B1xnYVRKqpacoP7SJ2MLWpum1i6c12P3s4JLBIiU4OtHKOozai37YMFM1Iwla
+QsGwgXyScRPs091aGe0G4eC0/10skHW0ASS4VUjbvqQE6opfMLdoMhuqAxTY8Nlz
+51B2smf/w7AAU4dpOE5sgdctbWpMNovAZe8yxt3Wk48pFIYPwTRXM6wSXoqYzX23
+s/rJmuGzX3PKV5hVZazYetj5clK/HB4Y/Clf3bVaoOKKSUgtOT3KB732yHIfFJDl
+vhqe5niRNf2Decy6aYJFw/IqMYM/3Fh4B1JcDFHzOq4Ta6yuvhqbXVdbM5dPzcgQ
+CO640v20uQVMjpknnxdBu7QxLMYuAfeuJkoWnvRtjSwQHPisnCWfxu6ryBIN3Xg7
+HtiEv+CVrvyq2UGbKhCtk/Z50eOdyRTAldoP3AcGbakZHtKAuz/m9TlTYDCnh/tH
+79soVqj2HMaM5XzslueGaERt149pr8uOhhK4NO+e1bc65Fw9ysHVrVn4pcaq8OGm
+yPSAsaHXcskBG5CywWfPcBaSij/0+QLbddie1cyeCFxz0aDypNMH+SCgsA==
+-----END CERTIFICATE-----
diff --git a/service/resources-certs/key.pem b/service/resources-certs/key.pem
new file mode 100644
index 0000000..31acc3c
--- /dev/null
+++ b/service/resources-certs/key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDqjgjWbsEuZyBI
+OcFD6KeNPlkgGREOUi/7kjVNXZxjEsZA+UKmUN3lipS0rz9RV7W0D06I159n2vMN
+j9/XVAHt9zV4KUh2VZNfAzIHnqTQgG8pyX3IqmAjI509xtbHCGT/BwsQF7LqMp6i
+0prOyMqJm4DOInj1SiisEaXtxdwZloI4M0WrVugtgiSx3OCoAg+d2+MPVx60PJdy
+9XN24CMI2g0ndFcSiGuNSrRx1qdy7r8V+eMM+rWwTVuWTDQxH7xewhpkwpHWzAGJ
+FnCcWcQOexNracV3ftk5xA6Ohy+2KtvIxq/IzelOt80bChyzIvg7cnotMNZwcbU3
++SrFiOweQsWr5flfrQrJzX6uRrvr4yZ5S9JKRXAYsRv0z7+x/jYMjuQ+46AMhknL
+XzJEz1nUkQ/xTU7N9MopfocVCLuJhWLVzvnOsiJtdTgZhs9SKKnrYGwYUIlMMvqf
+KKoHtjbRQ2iCd3ZHeXJjVBfxFyPPWlIbtKP1aNnKFKUf3GGU4HsX5NkSnB90ZLhu
+vR16H6wvUhmNESOxoJmhtHtYpwY1azsnT9V1yZhdw1fMsvDZpCZJW5Y7Bg+dl7gQ
+ulUNHiIe3Cylo4DAiRRTZDYFNLMmd/m5mtvBVr8yc3d4oubHlqnjxoHK33YI8W5T
+c0BClsNDHvVp/0tDijs/+TgDPWe0QwIDAQABAoICAQCXq2SXlGKiqNi9G2uXh/fg
+Y3KqSMs4oXiZij+hWWjov5knJocsmJoD9gqWk8Ozy62ZU1SLNDLFq1UDbgdma9Ib
+mIEgxEExE8EJ7T0KpFEa6hH3yWzmtxUXY84nxk3Xwdvc9Sb4LvroafcefmTQVhJx
+zPYLVUs2AdAvA/JxwEHE4gguYykn1DiqRL+Jq7Qxr7N9dlRq9QDLt2qPUZxqQCEn
+diiPXLF5MHGrmaT4/76jmTJC+aUSHYQjDsKse3Y8VbMu6wC6Kv63EI/ln3cOAxW9
+72PtTyX7UTESI27uRwhZGXmpaKD+jffRiRS96VoJlBFED5BlbHEdoMl3+kzoEtPq
+uIA+8gdl0pXtQpzR340d6vqSU+THnqhR4OCyhZXRSJAqyBIlG7pWmNMnMJoARUIq
+q3vJmiakgNGcFqFaddYL8dUdZiwBg37WIG40GUhEaSaPc9BYpX4mTxheWRqPUHuj
+BUQpZA8qUZx4zlkJ8OpqCznU5wCPs6CRxB9+MXh7+Rk4l0aEir2yvQpkQuoH+qx8
+HzinDpj6B+PPDuHhQkVu7EZxq/hnl3RktHghT+qOH36Z4f6yx6+UqR724NcHC4zn
+igS4JP1wq7egedPbr1v+GZWWa+/HIkUicy1gT2FxvlLAXiYBCQh9lQW80tPLREE5
+OC++g3Znn4BhAxC9Nf9MIQKCAQEA+A/Mq6etUdjEER0F22MMm2H5wca/x+VAPw03
+0PkpuWD6hEXIWEq5olowMAmsy99XDQmLhP4qnARNbVcqJ3/Ad8TDiPc9u4ofHvEI
+r0BiB+90Y+MSydjtE2JCGqZ+7f1o9gv02Ym28ZNmF786oJKoCLTF7oarzuFSyiBL
+V6dcE6zzkaMNpHj9U2boeI/vgdhUlLPZ3ES+o2FUCWvuuhfkj6NheCuyqhKiLTnW
+1M3sLjSbxQKLkY0ebPK0I3l8pasBKRE34MUWRRc5sHQquvdv5YHRLHsL1Ti4zzTM
+jPejMWsBHFdygA2EIz144PV0rLQAXURtu1fp864oRhMmQeIvqwKCAQEA8g+VDmyR
+QJrhea199judCRvURxsFR9Cy6ZQpGdYpK9OQcHA1AGig4hCW2S1+ZIdJCOWvjdBQ
+X2SbtM0tzjOkwqfsMjYubXcPMLB2tM8eOSPD/kOfDL4+G8wjGhDeua8loTOg3Jzl
+MeSCDrPhO5gIzXDr+ysnMQblLrL36YJb+PTwMHgk1NdgCQ9PBa2BHbKpPiK3mMM0
+qQXz+zRublvB0uXYaNlUpgdL35fXo5ChKgrMvk9rOiozh/jE/SFTM1iRGU4WJUlN
+KB1CxXR5Cn+lYeO8yF+aZO0a7lWq9R8i2bXsMReQIr+sOBE2cAZP+VP18chujSkP
+DnAwoLVxmHXVyQKCAQAA9h6285lqXcq0vkYNiZ68425SX/DSV4MeO3Dh8HVG8KdH
+5/VU0kVR6cOSDBeNWro9pLHTvCByJloeeF4DdS1LyYlweHTWyzw/pHOCKl+25Cix
+Umn6OksA7jqPW+HWDktf2MAEL9JzsTyODwGtKaRZFEpIGGGGFb16ZxGjr1ReByeS
+gu1Em+tvbVCtVvF4sVvyj5fikKmkfHYU60QrmHgcTmfMTW8N1bCnODgq7vlhXHbW
+FqJv1/osNeyYzpm7EqSYgiaTSnBBqEti8bBQtTDL1Or4nyl2lBezReMdEMCjKmUA
+tR4OfP7sHArh5FGlcbUmp2M9fKO9fAlP7DcTvkqtAoIBAQDp0hd/6Wud5n5rFSWZ
+1xfoFpPFY9qD9pr8enwBUxhOF31sv1bTYD4YYUH147Y10WDBUW11JYadvweRbKkU
+iFpdFexYzHGol9t6gtsH6RIey+elExjuLE6+d0BpC2a6Iu/MeZynvn6+5SakoSmu
+cTv/h1bMNnETML/tjj9ftua045WonEWnu6wu1DTXHTSdxVkqhkqnK9kQdImrXIhX
+3haqbA/RqC3WezHVeE162Fh1zhzcsMa5Vs6UR7+xbKF79c+jjARkXBxF5Y38Qngx
+pf/RQTW4sHDpkQf0tZgAU+VMPCk9eq9mgZQQTMAzEkXqaCopNh1kCgdbQRAYDWz/
+gsPRAoIBAQCVVhnmVFI3+u0vFcnJsKScw/D/hUcv2czUje0Li7fz84lrT2sXiSfH
+iFsvG9SwFsrSvTVcAy/agumE3nzsvymTQvdu7irVJczKw4lUdx22ZqdupqQRsUMR
+2kx2fzPLUlG5eHnFUpyUDIVwPSfmjSv6MwF2ScWFLWyR0coPDgYz/EdUENDSXkzW
+CCxbF7lH7jc60vOVqMH0HyFT+k5SOQDVWckzQzGLoMhKobv9FHijCnc/jdsAYkOx
++MMdUcFf1X4WjHb9U5be/YrhiOO4Ar41hEm2ezsrcreszr69Zv4Tw3BLpr/nkS37
+adx3U1hB9OzUKVm21xtowKxcrEsOpvR1
+-----END PRIVATE KEY-----
diff --git a/tests/wifitests/Android.bp b/tests/wifitests/Android.bp
index 4d808b7..7fd2a5f 100644
--- a/tests/wifitests/Android.bp
+++ b/tests/wifitests/Android.bp
@@ -86,7 +86,7 @@
         "jsr305",
         "services",
         // load the resources from the resources APK.
-        "wifi-service-resources",
+        "ServiceWifiResources",
         "unsupportedappusage",
     ],
 
diff --git a/tests/wifitests/src/com/android/server/wifi/BssidBlocklistMonitorTest.java b/tests/wifitests/src/com/android/server/wifi/BssidBlocklistMonitorTest.java
index 00afa91..2567faa 100644
--- a/tests/wifitests/src/com/android/server/wifi/BssidBlocklistMonitorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/BssidBlocklistMonitorTest.java
@@ -628,14 +628,15 @@
         when(mClock.getWallClockMillis()).thenReturn(0L);
         long testDuration = 5500L;
         mBssidBlocklistMonitor.blockBssidForDurationMs(TEST_BSSID_1, TEST_SSID_1, testDuration);
+        assertEquals(1, mBssidBlocklistMonitor.updateAndGetBssidBlocklist().size());
 
-        // Verify that the BSSID is not removed from blocklist dispite of regular "clear" calls.
-        when(mClock.getWallClockMillis()).thenReturn(testDuration);
-        mBssidBlocklistMonitor.clearBssidBlocklist();
+        // Verify that the BSSID is removed from blocklist by clearBssidBlocklistForSsid
         mBssidBlocklistMonitor.clearBssidBlocklistForSsid(TEST_SSID_1);
-        Set<String> bssidList = mBssidBlocklistMonitor.updateAndGetBssidBlocklist();
-        assertEquals(1, bssidList.size());
-        assertTrue(bssidList.contains(TEST_BSSID_1));
+        assertEquals(0, mBssidBlocklistMonitor.updateAndGetBssidBlocklist().size());
+
+        // Add the BSSID to blocklist again.
+        mBssidBlocklistMonitor.blockBssidForDurationMs(TEST_BSSID_1, TEST_SSID_1, testDuration);
+        assertEquals(1, mBssidBlocklistMonitor.updateAndGetBssidBlocklist().size());
 
         // Verify that the BSSID is removed from blocklist once the specified duration is over.
         when(mClock.getWallClockMillis()).thenReturn(testDuration + 1);
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index 6028497..d912020 100644
--- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -3324,6 +3324,7 @@
         mLooper.dispatchAll();
         verify(mIpClient).shutdown();
         verify(mWifiConfigManager).removeAllEphemeralOrPasspointConfiguredNetworks();
+        verify(mWifiConfigManager).clearUserTemporarilyDisabledList();
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java b/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
index 19d17c9..e274d68 100644
--- a/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
@@ -481,6 +481,7 @@
         dppEventCallback.onSuccess(CONFIGURATION_SENT);
         mLooper.dispatchAll();
         verify(mDppCallback).onSuccess(eq(EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT));
+        verify(mDppMetrics, times(1)).updateDppR1CapableEnrolleeResponderDevices();
         verify(mDppCallback, never()).onSuccessConfigReceived(anyInt());
         verify(mDppCallback, never()).onFailure(anyInt(), anyString(), anyString(), any());
         verify(mDppMetrics).updateDppConfiguratorInitiatorRequests();
@@ -861,6 +862,7 @@
         mLooper.dispatchAll();
         verify(mDppCallback)
                 .onProgress(eq(EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE));
+        verify(mDppMetrics, times(1)).updateDppR2CapableEnrolleeResponderDevices();
 
         // Generate an onSuccess callback
         dppEventCallback.onSuccess(CONFIGURATION_APPLIED);
@@ -912,6 +914,10 @@
         verify(mDppMetrics).updateDppConfiguratorInitiatorRequests();
         verify(mDppMetrics).updateDppFailure(eq(appFailure));
         verify(mDppMetrics).updateDppOperationTime(anyInt());
+        if ((internalFailure == CANNOT_FIND_NETWORK)
+                && (appFailure == EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE)) {
+            verify(mDppMetrics, times(1)).updateDppR2EnrolleeResponderIncompatibleConfiguration();
+        }
         verifyNoMoreInteractions(mDppMetrics);
         verifyCleanUpResources();
     }
diff --git a/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java
index fd43578..e43da8f 100644
--- a/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/DppMetricsTest.java
@@ -18,13 +18,17 @@
 
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT;
+import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED;
 import static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT;
 
 import static com.android.server.wifi.DppMetrics.DPP_OPERATION_TIME;
@@ -185,10 +189,16 @@
         for (int i = 0; i < value; i++) {
             mDppMetrics.updateDppConfiguratorSuccess(EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT);
         }
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppConfiguratorSuccess(
+                    EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED);
+        }
 
         // Confirm that the consolidated log has the expected value
         checkDppSuccesses(WifiMetricsProto.WifiDppLog.EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT,
                 value);
+        checkDppSuccesses(WifiMetricsProto.WifiDppLog
+                .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED, value);
     }
 
     /**
@@ -378,6 +388,65 @@
     }
 
     /**
+     * Test EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppFailureCannotFindNetwork() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppFailure(EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK);
+        }
+
+        // Confirm that the consolidated log has the expected value
+        checkDppFailures(WifiMetricsProto.WifiDppLog.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK,
+                value);
+    }
+
+    /**
+     * Test EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppFailureEnrolleeAuthentication() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppFailure(EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION);
+        }
+
+        // Confirm that the consolidated log has the expected value
+        checkDppFailures(WifiMetricsProto.WifiDppLog
+                .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION, value);
+    }
+
+    /**
+     * Test
+     * EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppFailureEnrolleeRejectedConfiguration() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppFailure(
+                    EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION);
+        }
+
+        // Confirm that the consolidated log has the expected value
+        checkDppFailures(WifiMetricsProto.WifiDppLog
+                .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION, value);
+    }
+
+    /**
      * Test DPP operation time histogram. Pick a single time value from each bucket by selecting
      * the max value minus 1, and call the update method random amount of times with this value.
      * Then confirm that the output histogram has the expected value in each target bucket.
@@ -410,4 +479,61 @@
             checkOperationBucketEqualsTo(i, value);
         }
     }
+
+    /**
+     * Test numDppR1CapableEnrolleeResponderDevices
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppR1CapableEnrolleeResponderDevices() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppR1CapableEnrolleeResponderDevices();
+        }
+
+        // Confirm that the consolidated log has the expected value
+        WifiMetricsProto.WifiDppLog mWifiDppLogProto = mDppMetrics.consolidateProto();
+        assertEquals(mWifiDppLogProto.numDppR1CapableEnrolleeResponderDevices, value);
+    }
+
+    /**
+     * Test numDppR2CapableEnrolleeResponderDevices
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppR2CapableEnrolleeResponderDevices() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppR2CapableEnrolleeResponderDevices();
+        }
+
+        // Confirm that the consolidated log has the expected value
+        WifiMetricsProto.WifiDppLog mWifiDppLogProto = mDppMetrics.consolidateProto();
+        assertEquals(mWifiDppLogProto.numDppR2CapableEnrolleeResponderDevices, value);
+    }
+
+    /**
+     * Test numDppR2EnrolleeResponderIncompatibleConfiguration
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testUpdateDppR2EnrolleeResponderIncompatibleConfiguration() throws Exception {
+        // Get a random value and call the update method 'value' times
+        int value = getNumOfTimes(MAX_ITERATIONS) + 1;
+
+        for (int i = 0; i < value; i++) {
+            mDppMetrics.updateDppR2EnrolleeResponderIncompatibleConfiguration();
+        }
+
+        // Confirm that the consolidated log has the expected value
+        WifiMetricsProto.WifiDppLog mWifiDppLogProto = mDppMetrics.consolidateProto();
+        assertEquals(mWifiDppLogProto.numDppR2EnrolleeResponderIncompatibleConfiguration, value);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
index ea030a4..08076dd 100644
--- a/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
@@ -16,13 +16,14 @@
 
 package com.android.server.wifi;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.net.wifi.WifiConfiguration;
@@ -32,10 +33,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static org.mockito.Mockito.lenient;
-
 import com.android.server.wifi.util.TelephonyUtil;
 
 import org.junit.After;
@@ -52,7 +49,7 @@
  */
 @SmallTest
 public class EapFailureNotifierTest extends WifiBaseTest {
-    @Mock Context mContext;
+    @Mock WifiContext mContext;
     @Mock Resources mResources;
     @Mock NotificationManager mNotificationManager;
     @Mock FrameworkFacade mFrameworkFacade;
@@ -93,6 +90,7 @@
         when(mResources.getString(eq(0), anyString())).thenReturn(null);
         when(mResources.getString(eq(1), anyString())).thenReturn("Error Message");
         when(mContext.createPackageContext(anyString(), eq(0))).thenReturn(mContext);
+        when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources");
         mEapFailureNotifier =
                 new EapFailureNotifier(mContext, mFrameworkFacade, mTelephonyUtil);
     }
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index 0e63b51..a7e21c9 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -117,7 +117,7 @@
     private SoftApInfo mTestSoftApInfo;
     private SoftApCapability mTestSoftApCapability;
 
-    @Mock Context mContext;
+    @Mock WifiContext mContext;
     @Mock Resources mResources;
     @Mock WifiNative mWifiNative;
     @Mock WifiManager.SoftApCallback mCallback;
@@ -153,6 +153,7 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getSystemService(NotificationManager.class))
                 .thenReturn(mNotificationManager);
+        when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources");
 
         when(mResources.getInteger(R.integer.config_wifiFrameworkSoftApShutDownTimeoutMilliseconds))
                 .thenReturn((int) TEST_DEFAULT_SHUTDOWN_TIMEOUT_MILLS);
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApNotifierTest.java
index 9ff8934..3d4d239 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApNotifierTest.java
@@ -45,7 +45,7 @@
 public class SoftApNotifierTest extends WifiBaseTest {
     private static final String TEST_SSID = "Test SSID";
 
-    @Mock Context mContext;
+    @Mock WifiContext mContext;
     @Mock Resources mResources;
     @Mock NotificationManager mNotificationManager;
     @Mock FrameworkFacade mFrameworkFacade;
@@ -61,8 +61,8 @@
         when(mContext.getSystemService(NotificationManager.class))
                 .thenReturn(mNotificationManager);
         when(mContext.getResources()).thenReturn(mResources);
-        mSoftApNotifier =
-                new SoftApNotifier(mContext, mFrameworkFacade);
+        when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources");
+        mSoftApNotifier = new SoftApNotifier(mContext, mFrameworkFacade);
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 3abdb06..3a5ce52 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -23,6 +23,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
@@ -35,6 +37,7 @@
 import android.net.MacAddress;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.SoftApConfiguration.Builder;
+import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Handler;
 import android.os.test.TestLooper;
@@ -492,6 +495,21 @@
     }
 
     @Test
+    public void randomizeBssid_fallbackPathWhenMacCalculationFails() throws Exception {
+        mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, true);
+        // Setup the MAC calculation to fail.
+        when(mMacAddressUtil.calculatePersistentMac(any(), any())).thenReturn(null);
+        SoftApConfiguration baseConfig = new SoftApConfiguration.Builder().build();
+
+        WifiApConfigStore store = createWifiApConfigStore();
+        SoftApConfiguration config = store.randomizeBssidIfUnset(mContext, baseConfig);
+
+        // Verify that some randomized MAC address is still generated
+        assertNotNull(config.getBssid());
+        assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, config.getBssid().toString());
+    }
+
+    @Test
     public void randomizeBssid_usesFactoryMacWhenRandomizationOff() throws Exception {
         mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, false);
         SoftApConfiguration baseConfig = new SoftApConfiguration.Builder().build();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
index 878d3f5..30c171f 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
@@ -54,135 +54,178 @@
 public class WifiBackupRestoreTest extends WifiBaseTest {
 
     private static final String WIFI_BACKUP_DATA_WITH_UNSUPPORTED_TAG =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-            + "<WifiBackupData>"
-            + "<int name=\"Version\" value=\"1\" />"
-            + "<NetworkList>"
-            + "<Network>"
-            + "<WifiConfiguration>"
-            + "<string name=\"ConfigKey\">&quot;GoogleGuest-Legacy&quot;NONE</string>"
-            + "<string name=\"SSID\">&quot;GoogleGuest-Legacy&quot;</string>"
-            + "<null name=\"BSSID\" />"
-            + "<null name=\"PreSharedKey\" />"
-            + "<null name=\"WEPKeys\" />"
-            + "<int name=\"WEPTxKeyIndex\" value=\"0\" />"
-            + "<boolean name=\"HiddenSSID\" value=\"false\" />"
-            + "<boolean name=\"RequirePMF\" value=\"false\" />"
-            + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>"
-            + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>"
-            + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>"
-            + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>"
-            + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>"
-            + "<boolean name=\"Shared\" value=\"true\" />"
-            + "<null name=\"SimSlot\" />"
-            + "</WifiConfiguration>"
-            + "<IpConfiguration>"
-            + "<string name=\"IpAssignment\">DHCP</string>"
-            + "<string name=\"ProxySettings\">NONE</string>"
-            + "</IpConfiguration>"
-            + "</Network>"
-            + "</NetworkList>"
-            + "</WifiBackupData>";
+            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+            + "<WifiBackupData>\n"
+            + "<int name=\"Version\" value=\"1\" />\n"
+            + "<NetworkList>\n"
+            + "<Network>\n"
+            + "<WifiConfiguration>\n"
+            + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                    + "&quot;NONE</string>\n"
+            + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                    + "&quot;</string>\n"
+            + "<null name=\"BSSID\" />\n"
+            + "<null name=\"PreSharedKey\" />\n"
+            + "<null name=\"WEPKeys\" />\n"
+            + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+            + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+            + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+            + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+            + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>\n"
+            + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>\n"
+            + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>\n"
+            + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>\n"
+            + "<boolean name=\"Shared\" value=\"true\" />\n"
+            + "<null name=\"SimSlot\" />\n"
+            + "</WifiConfiguration>\n"
+            + "<IpConfiguration>\n"
+            + "<string name=\"IpAssignment\">DHCP</string>\n"
+            + "<string name=\"ProxySettings\">NONE</string>\n"
+            + "</IpConfiguration>\n"
+            + "</Network>\n"
+            + "</NetworkList>\n"
+            + "</WifiBackupData>\n";
 
     // |AllowedKeyMgmt|, |AllowedProtocols|, |AllowedAuthAlgorithms|, |AllowedGroupCiphers| and
     // |AllowedPairwiseCiphers| fields have invalid values in them.
     // NOTE: The byte values are encoded in little endian
     private static final String WIFI_BACKUP_DATA_WITH_UNSUPPORTED_VALUES_IN_BITSETS =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                    + "<WifiBackupData>"
-                    + "<int name=\"Version\" value=\"1\" />"
-                    + "<NetworkList>"
-                    + "<Network>"
-                    + "<WifiConfiguration>"
-                    + "<string name=\"ConfigKey\">&quot;GoogleGuest-Legacy&quot;NONE</string>"
-                    + "<string name=\"SSID\">&quot;GoogleGuest-Legacy&quot;</string>"
-                    + "<null name=\"BSSID\" />"
-                    + "<null name=\"PreSharedKey\" />"
-                    + "<null name=\"WEPKeys\" />"
-                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />"
-                    + "<boolean name=\"HiddenSSID\" value=\"false\" />"
-                    + "<boolean name=\"RequirePMF\" value=\"false\" />"
+            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                    + "<WifiBackupData>\n"
+                    + "<int name=\"Version\" value=\"1\" />\n"
+                    + "<NetworkList>\n"
+                    + "<Network>\n"
+                    + "<WifiConfiguration>\n"
+                    + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;NONE</string>\n"
+                    + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;</string>\n"
+                    + "<null name=\"BSSID\" />\n"
+                    + "<null name=\"PreSharedKey\" />\n"
+                    + "<null name=\"WEPKeys\" />\n"
+                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+                    + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+                    + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
                     // Valid Value: 01
-                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"3\">010002</byte-array>"
+                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"3\">010002</byte-array>\n"
                     // Valid Value: 03
-                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">13</byte-array>"
+                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">13</byte-array>\n"
                     // Valid Value: 01
-                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">11</byte-array>"
+                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">11</byte-array>\n"
                     // Valid Value: 0f
-                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">8f</byte-array>"
+                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">8f</byte-array>\n"
                     // Valid Value: 06
-                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">26</byte-array>"
-                    + "<boolean name=\"Shared\" value=\"true\" />"
-                    + "<null name=\"SimSlot\" />"
-                    + "</WifiConfiguration>"
-                    + "<IpConfiguration>"
-                    + "<string name=\"IpAssignment\">DHCP</string>"
-                    + "<string name=\"ProxySettings\">NONE</string>"
-                    + "</IpConfiguration>"
-                    + "</Network>"
-                    + "</NetworkList>"
-                    + "</WifiBackupData>";
+                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">26</byte-array>\n"
+                    + "<boolean name=\"Shared\" value=\"true\" />\n"
+                    + "<null name=\"SimSlot\" />\n"
+                    + "</WifiConfiguration>\n"
+                    + "<IpConfiguration>\n"
+                    + "<string name=\"IpAssignment\">DHCP</string>\n"
+                    + "<string name=\"ProxySettings\">NONE</string>\n"
+                    + "</IpConfiguration>\n"
+                    + "</Network>\n"
+                    + "</NetworkList>\n"
+                    + "</WifiBackupData>\n";
 
     private static final String WIFI_BACKUP_DATA_V1_0 =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                    + "<WifiBackupData>"
-                    + "<float name=\"Version\" value=\"1.0\" />"
-                    + "<NetworkList>"
-                    + "<Network>"
-                    + "<WifiConfiguration>"
-                    + "<string name=\"ConfigKey\">&quot;GoogleGuest-Legacy&quot;NONE</string>"
-                    + "<string name=\"SSID\">&quot;GoogleGuest-Legacy&quot;</string>"
-                    + "<null name=\"BSSID\" />"
-                    + "<null name=\"PreSharedKey\" />"
-                    + "<null name=\"WEPKeys\" />"
-                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />"
-                    + "<boolean name=\"HiddenSSID\" value=\"false\" />"
-                    + "<boolean name=\"RequirePMF\" value=\"false\" />"
-                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>"
-                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>"
-                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>"
-                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>"
-                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>"
-                    + "<boolean name=\"Shared\" value=\"true\" />"
-                    + "</WifiConfiguration>"
-                    + "<IpConfiguration>"
-                    + "<string name=\"IpAssignment\">DHCP</string>"
-                    + "<string name=\"ProxySettings\">NONE</string>"
-                    + "</IpConfiguration>"
-                    + "</Network>"
-                    + "</NetworkList>"
-                    + "</WifiBackupData>";
+            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                    + "<WifiBackupData>\n"
+                    + "<float name=\"Version\" value=\"1.0\" />\n"
+                    + "<NetworkList>\n"
+                    + "<Network>\n"
+                    + "<WifiConfiguration>\n"
+                    + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;NONE</string>\n"
+                    + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;</string>\n"
+                    + "<null name=\"BSSID\" />\n"
+                    + "<null name=\"PreSharedKey\" />\n"
+                    + "<null name=\"WEPKeys\" />\n"
+                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+                    + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+                    + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>\n"
+                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>\n"
+                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>\n"
+                    + "<boolean name=\"Shared\" value=\"true\" />\n"
+                    + "</WifiConfiguration>\n"
+                    + "<IpConfiguration>\n"
+                    + "<string name=\"IpAssignment\">DHCP</string>\n"
+                    + "<string name=\"ProxySettings\">NONE</string>\n"
+                    + "</IpConfiguration>\n"
+                    + "</Network>\n"
+                    + "</NetworkList>\n"
+                    + "</WifiBackupData>\n";
 
     private static final String WIFI_BACKUP_DATA_V1_1 =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                    + "<WifiBackupData>"
-                    + "<float name=\"Version\" value=\"1.1\" />"
-                    + "<NetworkList>"
-                    + "<Network>"
-                    + "<WifiConfiguration>"
-                    + "<string name=\"ConfigKey\">&quot;GoogleGuest-Legacy&quot;NONE</string>"
-                    + "<string name=\"SSID\">&quot;GoogleGuest-Legacy&quot;</string>"
-                    + "<null name=\"BSSID\" />"
-                    + "<null name=\"PreSharedKey\" />"
-                    + "<null name=\"WEPKeys\" />"
-                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />"
-                    + "<boolean name=\"HiddenSSID\" value=\"false\" />"
-                    + "<boolean name=\"RequirePMF\" value=\"false\" />"
-                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>"
-                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>"
-                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>"
-                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>"
-                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>"
-                    + "<boolean name=\"Shared\" value=\"true\" />"
-                    + "<int name=\"MeteredOverride\" value=\"1\" />"
-                    + "</WifiConfiguration>"
-                    + "<IpConfiguration>"
-                    + "<string name=\"IpAssignment\">DHCP</string>"
-                    + "<string name=\"ProxySettings\">NONE</string>"
-                    + "</IpConfiguration>"
-                    + "</Network>"
-                    + "</NetworkList>"
-                    + "</WifiBackupData>";
+            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                    + "<WifiBackupData>\n"
+                    + "<float name=\"Version\" value=\"1.1\" />\n"
+                    + "<NetworkList>\n"
+                    + "<Network>\n"
+                    + "<WifiConfiguration>\n"
+                    + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;NONE</string>\n"
+                    + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;</string>\n"
+                    + "<null name=\"BSSID\" />\n"
+                    + "<null name=\"PreSharedKey\" />\n"
+                    + "<null name=\"WEPKeys\" />\n"
+                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+                    + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+                    + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>\n"
+                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>\n"
+                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>\n"
+                    + "<boolean name=\"Shared\" value=\"true\" />\n"
+                    + "<int name=\"MeteredOverride\" value=\"1\" />\n"
+                    + "</WifiConfiguration>\n"
+                    + "<IpConfiguration>\n"
+                    + "<string name=\"IpAssignment\">DHCP</string>\n"
+                    + "<string name=\"ProxySettings\">NONE</string>\n"
+                    + "</IpConfiguration>\n"
+                    + "</Network>\n"
+                    + "</NetworkList>\n"
+                    + "</WifiBackupData>\n";
+
+    private static final String WIFI_BACKUP_DATA_V1_2 =
+            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                    + "<WifiBackupData>\n"
+                    + "<float name=\"Version\" value=\"1.2\" />\n"
+                    + "<NetworkList>\n"
+                    + "<Network>\n"
+                    + "<WifiConfiguration>\n"
+                    + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;NONE</string>\n"
+                    + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
+                        + "&quot;</string>\n"
+                    + "<null name=\"PreSharedKey\" />\n"
+                    + "<null name=\"WEPKeys\" />\n"
+                    + "<int name=\"WEPTxKeyIndex\" value=\"0\" />\n"
+                    + "<boolean name=\"HiddenSSID\" value=\"false\" />\n"
+                    + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+                    + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedProtocols\" num=\"1\">03</byte-array>\n"
+                    + "<byte-array name=\"AllowedAuthAlgos\" num=\"1\">01</byte-array>\n"
+                    + "<byte-array name=\"AllowedGroupCiphers\" num=\"1\">0f</byte-array>\n"
+                    + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"1\">06</byte-array>\n"
+                    + "<byte-array name=\"AllowedGroupMgmtCiphers\" num=\"0\"></byte-array>\n"
+                    + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
+                    + "<boolean name=\"Shared\" value=\"true\" />\n"
+                    + "<boolean name=\"AutoJoinEnabled\" value=\"false\" />\n"
+                    + "<int name=\"MeteredOverride\" value=\"1\" />\n"
+                    + "</WifiConfiguration>\n"
+                    + "<IpConfiguration>\n"
+                    + "<string name=\"IpAssignment\">DHCP</string>\n"
+                    + "<string name=\"ProxySettings\">NONE</string>\n"
+                    + "</IpConfiguration>\n"
+                    + "</Network>\n"
+                    + "</NetworkList>\n"
+                    + "</WifiBackupData>\n";
 
     @Mock WifiPermissionsUtil mWifiPermissionsUtil;
     private WifiBackupRestore mWifiBackupRestore;
@@ -334,7 +377,7 @@
      */
     private static WifiConfiguration createNetworkForConfigurationWithUnsupportedTag() {
         final WifiConfiguration config = new WifiConfiguration();
-        config.SSID = "\"GoogleGuest-Legacy\"";
+        config.SSID = "\"" + WifiConfigurationTestUtil.TEST_SSID + "\"";
         config.wepTxKeyIndex = 0;
         config.hiddenSSID = false;
         config.requirePmf = false;
@@ -387,7 +430,7 @@
     private static WifiConfiguration
             createNetworkForConfigurationWithUnsupportedValuesInBitsetsInRestore() {
         final WifiConfiguration config = new WifiConfiguration();
-        config.SSID = "\"GoogleGuest-Legacy\"";
+        config.SSID = "\"" + WifiConfigurationTestUtil.TEST_SSID + "\"";
         config.wepTxKeyIndex = 0;
         config.hiddenSSID = false;
         config.requirePmf = false;
@@ -954,9 +997,43 @@
                 mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
         WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
                 configurations, retrievedConfigurations);
+    }
 
-        // No valid data to check in dump.
-        mCheckDump = false;
+    /**
+     * Verify that restoring of configuration from a 1.1 version backup data.
+     */
+    @Test
+    public void testRestoreFromV1_1BackupData() {
+        List<WifiConfiguration> configurations = new ArrayList<>();
+        configurations.add(createNetworkForConfigurationWithV1_1Data());
+
+        byte[] backupData = WIFI_BACKUP_DATA_V1_1.getBytes();
+        List<WifiConfiguration> retrievedConfigurations =
+                mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+        WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+                configurations, retrievedConfigurations);
+    }
+
+    /**
+     * Verify that restoring of configuration from a 1.2 version backup data.
+     */
+    @Test
+    public void testRestoreFromV1_2BackupData() {
+        List<WifiConfiguration> configurations = new ArrayList<>();
+        configurations.add(createNetworkForConfigurationWithV1_1Data());
+
+        byte[] backupData = WIFI_BACKUP_DATA_V1_2.getBytes();
+        List<WifiConfiguration> retrievedConfigurations =
+                mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
+        WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+                configurations, retrievedConfigurations);
+
+        // Also, assert in the reverse direction to ensure the serialization logic matches.
+        // Note: This will stop working when we bump up the version. Then we'll need to copy
+        // the below assert to the test for the latest version.
+        assertEquals(WIFI_BACKUP_DATA_V1_2,
+                new String(mWifiBackupRestore.retrieveBackupDataFromConfigurations(
+                        retrievedConfigurations)));
     }
 
     /**
@@ -965,7 +1042,7 @@
      */
     private static WifiConfiguration createNetworkForConfigurationWithV1_0Data() {
         final WifiConfiguration config = new WifiConfiguration();
-        config.SSID = "\"GoogleGuest-Legacy\"";
+        config.SSID = "\"" + WifiConfigurationTestUtil.TEST_SSID + "\"";
         config.wepTxKeyIndex = 0;
         config.hiddenSSID = false;
         config.requirePmf = false;
@@ -990,34 +1067,24 @@
     }
 
     /**
-     * Verify that restoring of configuration from a 1.1 version backup data.
-     */
-    @Test
-    public void testRestoreFromV1_1BackupData() {
-        List<WifiConfiguration> configurations = new ArrayList<>();
-        configurations.add(createNetworkForConfigurationWithV1_1Data());
-
-        byte[] backupData = WIFI_BACKUP_DATA_V1_1.getBytes();
-        List<WifiConfiguration> retrievedConfigurations =
-                mWifiBackupRestore.retrieveConfigurationsFromBackupData(backupData);
-        WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
-                configurations, retrievedConfigurations);
-
-        // No valid data to check in dump.
-        mCheckDump = false;
-    }
-
-    /**
      * Creates correct WiFiConfiguration that should be parsed out of
      * {@link #WIFI_BACKUP_DATA_V1_1} configuration which contains 1.1 version backup.
      */
     private static WifiConfiguration createNetworkForConfigurationWithV1_1Data() {
         final WifiConfiguration config = createNetworkForConfigurationWithV1_0Data();
         config.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
-
         return config;
     }
 
+    /**
+     * Creates correct WiFiConfiguration that should be parsed out of
+     * {@link #WIFI_BACKUP_DATA_V1_1} configuration which contains 1.2 version backup.
+     */
+    private static WifiConfiguration createNetworkForConfigurationWithV1_2Data() {
+        final WifiConfiguration config = createNetworkForConfigurationWithV1_1Data();
+        config.allowAutojoin = false;
+        return config;
+    }
 
     /**
      * Helper method to write a list of networks in wpa_supplicant.conf format to the output stream.
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 1147dd9..a892831 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -71,6 +71,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -141,7 +142,7 @@
     @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
     @Mock private WifiScoreCard mWifiScoreCard;
     @Mock private PerNetwork mPerNetwork;
-    @Mock private LruConnectionTracker mLruConnectionTracker;
+    private LruConnectionTracker mLruConnectionTracker;
 
     private MockResources mResources;
     private InOrder mContextConfigStoreMockOrder;
@@ -175,6 +176,7 @@
                 R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels,
                 TEST_MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCAN);
         mResources.setBoolean(R.bool.config_wifi_connected_mac_randomization_supported, true);
+        mResources.setInteger(R.integer.config_wifiMaxPnoSsidCount, 16);
         when(mContext.getResources()).thenReturn(mResources);
 
         // Setup UserManager profiles for the default user.
@@ -225,6 +227,7 @@
 
         mTelephonyUtil = new TelephonyUtil(mTelephonyManager, mSubscriptionManager,
                 mock(FrameworkFacade.class), mock(Context.class), mock(Handler.class));
+        mLruConnectionTracker = new LruConnectionTracker(100, mContext);
         createWifiConfigManager();
         mWifiConfigManager.addOnNetworkUpdateListener(mWcmListener);
         // static mocking
@@ -3992,25 +3995,20 @@
         verifyAddNetworkToWifiConfigManager(network1);
         verifyAddNetworkToWifiConfigManager(network2);
         verifyAddNetworkToWifiConfigManager(network3);
+        mWifiConfigManager.updateNetworkAfterConnect(network3.networkId);
 
-        // Now set scan results in 2 of them to set the corresponding
+        // Now set scan results of network2 to set the corresponding
         // {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} field.
-        assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
-                network1.networkId, createScanDetailForNetwork(network1).getScanResult(), 54));
-        assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
-                network3.networkId, createScanDetailForNetwork(network3).getScanResult(), 54));
-
-        // Now increment |network3|'s association count. This should ensure that this network
-        // is preferred over |network1|.
-        assertTrue(mWifiConfigManager.updateNetworkAfterConnect(network3.networkId));
+        assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(network2.networkId,
+                createScanDetailForNetwork(network2).getScanResult(), 54));
 
         // Retrieve the hidden network list & verify the order of the networks returned.
         List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
                 mWifiConfigManager.retrieveHiddenNetworkList();
         assertEquals(3, hiddenNetworks.size());
         assertEquals(network3.SSID, hiddenNetworks.get(0).ssid);
-        assertEquals(network1.SSID, hiddenNetworks.get(1).ssid);
-        assertEquals(network2.SSID, hiddenNetworks.get(2).ssid);
+        assertEquals(network2.SSID, hiddenNetworks.get(1).ssid);
+        assertEquals(network1.SSID, hiddenNetworks.get(2).ssid);
     }
 
     /**
@@ -5316,10 +5314,6 @@
         // Verify if the config store write was triggered without this new configuration.
         verifyNetworkNotInConfigStoreData(configuration);
         verify(mBssidBlocklistMonitor, atLeastOnce()).handleNetworkRemoved(configuration.SSID);
-        ArgumentCaptor<WifiConfiguration> captor = ArgumentCaptor.forClass(WifiConfiguration.class);
-        verify(mLruConnectionTracker).removeNetwork(captor.capture());
-        assertEquals(configuration.networkId, captor.getValue().networkId);
-        reset(mLruConnectionTracker);
     }
 
     /**
@@ -5515,9 +5509,7 @@
         WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(networkId);
         assertTrue("hasEverConnected expected to be true after connection.",
                 retrievedNetwork.getNetworkSelectionStatus().hasEverConnected());
-        ArgumentCaptor<WifiConfiguration> captor = ArgumentCaptor.forClass(WifiConfiguration.class);
-        verify(mLruConnectionTracker).addNetwork(captor.capture());
-        assertEquals(networkId, captor.getValue().networkId);
+        assertEquals(0, mLruConnectionTracker.getAgeIndexOfNetwork(retrievedNetwork));
     }
 
     /**
@@ -5652,8 +5644,7 @@
         verifyAddNetworkToWifiConfigManager(testNetwork1);
         WifiConfiguration testNetwork2 = WifiConfigurationTestUtil.createOpenNetwork();
         verifyAddNetworkToWifiConfigManager(testNetwork2);
-        when(mLruConnectionTracker.isMostRecentlyConnected(any()))
-                .thenReturn(false).thenReturn(true);
+        mWifiConfigManager.updateNetworkAfterConnect(testNetwork2.networkId);
         mWifiConfigManager.saveToStore(true);
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkStoreData =
                 captureWriteNetworksListStoreData();
@@ -5661,4 +5652,39 @@
         assertFalse(sharedNetwork.get(0).isMostRecentlyConnected);
         assertTrue(sharedNetwork.get(1).isMostRecentlyConnected);
     }
+
+    /**
+     * Verify scan comparator gives the most recently connected network highest priority
+     */
+    @Test
+    public void testScanComparator() {
+        WifiConfiguration network1 = WifiConfigurationTestUtil.createOpenNetwork();
+        verifyAddNetworkToWifiConfigManager(network1);
+        WifiConfiguration network2 = WifiConfigurationTestUtil.createOpenNetwork();
+        verifyAddNetworkToWifiConfigManager(network2);
+        WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenNetwork();
+        verifyAddNetworkToWifiConfigManager(network3);
+        WifiConfiguration network4 = WifiConfigurationTestUtil.createOpenNetwork();
+        verifyAddNetworkToWifiConfigManager(network4);
+
+        // Connect two network in order, network3 --> network2
+        verifyUpdateNetworkAfterConnectHasEverConnectedTrue(network3.networkId);
+        verifyUpdateNetworkAfterConnectHasEverConnectedTrue(network2.networkId);
+        // Set network4 {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} to true.
+        assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(network4.networkId,
+                createScanDetailForNetwork(network4).getScanResult(), 54));
+        List<WifiConfiguration> networkList = mWifiConfigManager.getConfiguredNetworks();
+        // Expected order should be based on connection order and last qualified selection.
+        Collections.sort(networkList, mWifiConfigManager.getScanListComparator());
+        assertEquals(network2.SSID, networkList.get(0).SSID);
+        assertEquals(network3.SSID, networkList.get(1).SSID);
+        assertEquals(network4.SSID, networkList.get(2).SSID);
+        assertEquals(network1.SSID, networkList.get(3).SSID);
+        // Remove network to check the age index changes.
+        mWifiConfigManager.removeNetwork(network3.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME);
+        assertEquals(Integer.MAX_VALUE, mLruConnectionTracker.getAgeIndexOfNetwork(network3));
+        assertEquals(0, mLruConnectionTracker.getAgeIndexOfNetwork(network2));
+        mWifiConfigManager.removeNetwork(network2.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME);
+        assertEquals(Integer.MAX_VALUE, mLruConnectionTracker.getAgeIndexOfNetwork(network2));
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 028bf02..ede5c8b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -54,6 +54,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.util.LruConnectionTracker;
 import com.android.server.wifi.util.ScanResultUtil;
 import com.android.wifi.resources.R;
 
@@ -73,6 +74,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -127,6 +129,10 @@
         mMinPacketRateActiveTraffic = mResources.getInteger(
                 R.integer.config_wifiFrameworkMinPacketPerSecondActiveTraffic);
         when(mWifiLastResortWatchdog.shouldIgnoreBssidUpdate(anyString())).thenReturn(false);
+        mLruConnectionTracker = new LruConnectionTracker(100, mContext);
+        Comparator<WifiConfiguration> comparator =
+                Comparator.comparingInt(mLruConnectionTracker::getAgeIndexOfNetwork);
+        when(mWifiConfigManager.getScanListComparator()).thenReturn(comparator);
     }
 
     private void setUpResources(MockResources resources) {
@@ -175,6 +181,7 @@
     private WifiConfigManager mWifiConfigManager;
     private WifiInfo mWifiInfo;
     private LocalLog mLocalLog;
+    private LruConnectionTracker mLruConnectionTracker;
     @Mock private WifiInjector mWifiInjector;
     @Mock private NetworkScoreManager mNetworkScoreManager;
     @Mock private Clock mClock;
@@ -2910,6 +2917,9 @@
         networkList.add(network1);
         networkList.add(network2);
         networkList.add(network3);
+        mLruConnectionTracker.addNetwork(network3);
+        mLruConnectionTracker.addNetwork(network2);
+        mLruConnectionTracker.addNetwork(network1);
         when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList);
         // Retrieve the Pno network list & verify.
         List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
@@ -2950,8 +2960,9 @@
         List<WifiConfiguration> networkList = new ArrayList<>();
         networkList.add(network1);
         networkList.add(network2);
+        mLruConnectionTracker.addNetwork(network2);
+        mLruConnectionTracker.addNetwork(network1);
         when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList);
-
         // Retrieve the Pno network list and verify.
         // Frequencies should be empty since no scan results have been received yet.
         List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
@@ -3006,37 +3017,17 @@
         WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork();
         WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
         WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+        mLruConnectionTracker.addNetwork(network1);
+        mLruConnectionTracker.addNetwork(network2);
+        mLruConnectionTracker.addNetwork(network3);
         List<WifiConfiguration> networkList = new ArrayList<>();
         networkList.add(network1);
         networkList.add(network2);
         networkList.add(network3);
         when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList);
-        // Set up network as:
-        // Network2 has the highest association number.
-        // Network3 is most recently connected.
-        // Network1 the second for both association number and most recently.
-        long firstConnectionTimeMillis = 45677;
-        long secondConnectionTimeMillis = firstConnectionTimeMillis + 45;
-        network1.numAssociation = 2;
-        network2.numAssociation = 3;
-        network3.numAssociation = 1;
-        network1.lastConnected = firstConnectionTimeMillis;
-        network3.lastConnected = secondConnectionTimeMillis;
-        //When config_wifiPnoRecencySortingEnabled is false, will prefer most connected network.
-        mResources.setBoolean(R.bool.config_wifiPnoRecencySortingEnabled, false);
         List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
                 mWifiConnectivityManager.retrievePnoNetworkList();
         assertEquals(3, pnoNetworks.size());
-        assertEquals(network2.SSID, pnoNetworks.get(0).ssid);
-        assertEquals(network1.SSID, pnoNetworks.get(1).ssid);
-        assertEquals(network3.SSID, pnoNetworks.get(2).ssid);
-
-        //When config_wifiPnoRecencySortingEnabled is true, after prefer most connected network.
-        //The most recently connected network will move the first.
-        mResources.setBoolean(R.bool.config_wifiPnoRecencySortingEnabled, true);
-        pnoNetworks =
-                mWifiConnectivityManager.retrievePnoNetworkList();
-        assertEquals(3, pnoNetworks.size());
         assertEquals(network3.SSID, pnoNetworks.get(0).ssid);
         assertEquals(network2.SSID, pnoNetworks.get(1).ssid);
         assertEquals(network1.SSID, pnoNetworks.get(2).ssid);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
index bb8c74d..cfe1ee8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
@@ -204,17 +204,21 @@
     }
 
     /**
-     * Verify throughtput when Rx link speed is unavailable
+     * Verify throughtput when Rx link speed is unavailable.
+     * Also verify the logging of channel utilization and throughput.
      */
     @Test
     public void verifyThroughputNoRxLinkSpeed() throws Exception {
         mWifiDataStall.checkDataStallAndThroughputSufficiency(null, mNewLlStats, mWifiInfo);
+        verify(mWifiMetrics).incrementChannelUtilizationCount(10, 5850);
+        verify(mWifiMetrics).incrementThroughputKbpsCount(50_000, 150_000, 5850);
         assertEquals(50_000, mWifiDataStall.getTxThroughputKbps());
         assertEquals(150_000, mWifiDataStall.getRxThroughputKbps());
         when(mWifiInfo.getRxLinkSpeedMbps()).thenReturn(-1);
         mWifiDataStall.checkDataStallAndThroughputSufficiency(mOldLlStats, mNewLlStats, mWifiInfo);
         assertEquals(960, mWifiDataStall.getTxThroughputKbps());
         assertEquals(-1, mWifiDataStall.getRxThroughputKbps());
+        verify(mWifiMetrics).incrementThroughputKbpsCount(960, -1, 5850);
     }
 
     /**
@@ -226,6 +230,7 @@
 
         assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiDataStall
                 .checkDataStallAndThroughputSufficiency(mOldLlStats, mNewLlStats, mWifiInfo));
+        verify(mWifiMetrics).incrementThroughputKbpsCount(960, 9609, 5850);
         verifyUpdateWifiIsUnusableLinkLayerStats();
         when(mClock.getElapsedSinceBootMillis()).thenReturn(
                 10L + DeviceConfigFacade.DEFAULT_DATA_STALL_DURATION_MS);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java
index 2c73d09..6af10d0 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiInjectorTest.java
@@ -16,10 +16,6 @@
 
 package com.android.server.wifi;
 
-import static org.mockito.Mockito.*;
-
-import android.content.Context;
-
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -31,7 +27,7 @@
 @SmallTest
 public class WifiInjectorTest extends WifiBaseTest {
 
-    @Mock private Context mContext;
+    @Mock private WifiContext mContext;
     private WifiInjector mInjector;
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 315a17b..841a132 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -4564,4 +4564,82 @@
                 mDecodedProto.softApConfigLimitationMetrics.maxClientSettingWhenReachHistogram);
     }
 
+    /**
+     * Test the logging of channel utilization
+     */
+    @Test
+    public void testChannelUtilization() throws Exception {
+        mWifiMetrics.incrementChannelUtilizationCount(180, 2412);
+        mWifiMetrics.incrementChannelUtilizationCount(150, 2412);
+        mWifiMetrics.incrementChannelUtilizationCount(230, 2412);
+        mWifiMetrics.incrementChannelUtilizationCount(20, 5510);
+        mWifiMetrics.incrementChannelUtilizationCount(50, 5510);
+
+        dumpProtoAndDeserialize();
+
+        HistogramBucketInt32[] expected2GHistogram = {
+                buildHistogramBucketInt32(150, 175, 1),
+                buildHistogramBucketInt32(175, 200, 1),
+                buildHistogramBucketInt32(225, Integer.MAX_VALUE, 1),
+        };
+
+        HistogramBucketInt32[] expectedAbove2GHistogram = {
+                buildHistogramBucketInt32(Integer.MIN_VALUE, 25, 1),
+                buildHistogramBucketInt32(50, 75, 1),
+        };
+
+        assertHistogramBucketsEqual(expected2GHistogram,
+                mDecodedProto.channelUtilizationHistogram.utilization2G);
+        assertHistogramBucketsEqual(expectedAbove2GHistogram,
+                mDecodedProto.channelUtilizationHistogram.utilizationAbove2G);
+    }
+
+    /**
+     * Test the logging of Tx and Rx throughput
+     */
+    @Test
+    public void testThroughput() throws Exception {
+        mWifiMetrics.incrementThroughputKbpsCount(500, 800, 2412);
+        mWifiMetrics.incrementThroughputKbpsCount(5_000, 4_000, 2412);
+        mWifiMetrics.incrementThroughputKbpsCount(54_000, 48_000, 2412);
+        mWifiMetrics.incrementThroughputKbpsCount(50_000, 49_000, 5510);
+        mWifiMetrics.incrementThroughputKbpsCount(801_000, 790_000, 5510);
+        mWifiMetrics.incrementThroughputKbpsCount(1100_000, 1200_000, 5510);
+        mWifiMetrics.incrementThroughputKbpsCount(1599_000, 1800_000, 6120);
+        dumpProtoAndDeserialize();
+
+        HistogramBucketInt32[] expectedTx2GHistogramMbps = {
+                buildHistogramBucketInt32(Integer.MIN_VALUE, 1, 1),
+                buildHistogramBucketInt32(5, 10, 1),
+                buildHistogramBucketInt32(50, 100, 1),
+        };
+
+        HistogramBucketInt32[] expectedRx2GHistogramMbps = {
+                buildHistogramBucketInt32(Integer.MIN_VALUE, 1, 1),
+                buildHistogramBucketInt32(1, 5, 1),
+                buildHistogramBucketInt32(25, 50, 1),
+        };
+
+        HistogramBucketInt32[] expectedTxAbove2GHistogramMbps = {
+                buildHistogramBucketInt32(50, 100, 1),
+                buildHistogramBucketInt32(800, 1200, 2),
+                buildHistogramBucketInt32(1200, 1600, 1),
+        };
+
+        HistogramBucketInt32[] expectedRxAbove2GHistogramMbps = {
+                buildHistogramBucketInt32(25, 50, 1),
+                buildHistogramBucketInt32(600, 800, 1),
+                buildHistogramBucketInt32(1200, 1600, 1),
+                buildHistogramBucketInt32(1600, Integer.MAX_VALUE, 1),
+        };
+
+        assertHistogramBucketsEqual(expectedTx2GHistogramMbps,
+                mDecodedProto.throughputMbpsHistogram.tx2G);
+        assertHistogramBucketsEqual(expectedTxAbove2GHistogramMbps,
+                mDecodedProto.throughputMbpsHistogram.txAbove2G);
+        assertHistogramBucketsEqual(expectedRx2GHistogramMbps,
+                mDecodedProto.throughputMbpsHistogram.rx2G);
+        assertHistogramBucketsEqual(expectedRxAbove2GHistogramMbps,
+                mDecodedProto.throughputMbpsHistogram.rxAbove2G);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
index f26ceb6..bb090ca 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
@@ -124,7 +124,7 @@
     private static final int TEST_NETWORK_ID = 110;
     private static final int TEST_CARRIER_ID = 1911;
 
-    private @Mock Context mContext;
+    private @Mock WifiContext mContext;
     private @Mock Resources mResources;
     private @Mock AppOpsManager mAppOpsManager;
     private @Mock NotificationManager mNotificationManger;
@@ -210,6 +210,7 @@
         when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
         when(mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
                 .thenReturn(mock(LayoutInflater.class));
+        when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources");
         when(mActivityManager.isLowRamDevice()).thenReturn(false);
         when(mActivityManager.getPackageImportance(any())).thenReturn(
                 IMPORTANCE_FOREGROUND_SERVICE);
@@ -649,6 +650,24 @@
     }
 
     /**
+     * Verify add or remove suggestion list with null object will result error code.
+     */
+    @Test
+    public void testAddRemoveNetworkSuggestionWithNullObject() {
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID,
+                mWifiNetworkSuggestionsManager.add(Collections.singletonList(null),
+                        TEST_UID_1, TEST_PACKAGE_1, TEST_FEATURE));
+        WifiNetworkSuggestion networkSuggestion = new WifiNetworkSuggestion(
+                WifiConfigurationTestUtil.createOpenNetwork(), null, false, false, true, true);
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+                mWifiNetworkSuggestionsManager.add(Collections.singletonList(networkSuggestion),
+                        TEST_UID_1, TEST_PACKAGE_1, TEST_FEATURE));
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
+                mWifiNetworkSuggestionsManager.remove(Collections.singletonList(null),
+                        TEST_UID_1, TEST_PACKAGE_1));
+    }
+
+    /**
      * Verify a successful lookup of a single network suggestion matching the provided scan detail.
      */
     @Test
@@ -3292,6 +3311,7 @@
         List<ScanResult> allSrList = new ArrayList<>() {{
                 add(passpointScanResult);
                 add(nonPasspointScanResult);
+                add(null);
                 }};
         when(mPasspointManager.getMatchingScanResults(eq(mockPasspoint), eq(allSrList)))
                 .thenReturn(ppSrList);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index 8286f1d..ca8c9db 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -174,6 +174,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -225,6 +226,12 @@
     private static final int TEST_AP_FREQUENCY = 2412;
     private static final int TEST_AP_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ;
     private static final int NETWORK_CALLBACK_ID = 1100;
+    private static final String TEST_CAP = "[RSN-PSK-CCMP]";
+    private static final String TEST_SSID = "Sid's Place";
+    private static final String TEST_SSID_WITH_QUOTES = "\"" + TEST_SSID + "\"";
+    private static final String TEST_BSSID = "01:02:03:04:05:06";
+    private static final String TEST_PACKAGE = "package";
+    private static final int TEST_NETWORK_ID = 567;
 
     private SoftApInfo mTestSoftApInfo;
     private AsyncChannel mAsyncChannel;
@@ -1558,12 +1565,6 @@
         verify(mScanRequestProxy).startScan(Binder.getCallingUid(), SCAN_PACKAGE_NAME);
     }
 
-    static final String TEST_SSID = "Sid's Place";
-    static final String TEST_SSID_WITH_QUOTES = "\"" + TEST_SSID + "\"";
-    static final String TEST_BSSID = "01:02:03:04:05:06";
-    static final String TEST_PACKAGE = "package";
-    static final int TEST_NETWORK_ID = 567;
-
     private void setupForGetConnectionInfo() {
         WifiInfo wifiInfo = new WifiInfo();
         wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
@@ -3138,9 +3139,9 @@
     }
 
     /**
-     * Verify that the call to getAllMatchingFqdnsForScanResults is not redirected to specific API
-     * syncGetAllMatchingFqdnsForScanResults when the caller doesn't have NETWORK_SETTINGS
-     * permissions and NETWORK_SETUP_WIZARD.
+     * Verify that the call to getAllMatchingPasspointProfilesForScanResults is not redirected to
+     * specific API getAllMatchingPasspointProfilesForScanResults when the caller doesn't have
+     * NETWORK_SETTINGS permissions and NETWORK_SETUP_WIZARD.
      */
     @Test(expected = SecurityException.class)
     public void testGetAllMatchingPasspointProfilesForScanResultsWithoutPermissions() {
@@ -3148,6 +3149,34 @@
     }
 
     /**
+     * Verify that the call to getAllMatchingPasspointProfilesForScanResults is redirected to
+     * specific API getAllMatchingPasspointProfilesForScanResults when the caller have
+     * NETWORK_SETTINGS permissions and NETWORK_SETUP_WIZARD.
+     */
+    @Test
+    public void testGetAllMatchingPasspointProfilesForScanResultsWithPermissions() {
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        mWifiServiceImpl.getAllMatchingPasspointProfilesForScanResults(createScanResultList());
+        mLooper.dispatchAll();
+        verify(mPasspointManager).getAllMatchingPasspointProfilesForScanResults(any());
+    }
+
+    /**
+     * Verify that the call to getAllMatchingPasspointProfilesForScanResults is not redirected to
+     * specific API getAllMatchingPasspointProfilesForScanResults when the caller provider invalid
+     * ScanResult.
+     */
+    @Test
+    public void testGetAllMatchingPasspointProfilesForScanResultsWithInvalidScanResult() {
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        mWifiServiceImpl.getAllMatchingPasspointProfilesForScanResults(new ArrayList<>());
+        mLooper.dispatchAll();
+        verify(mPasspointManager, never()).getAllMatchingPasspointProfilesForScanResults(any());
+    }
+
+    /**
      * Verify that the call to getWifiConfigsForPasspointProfiles is not redirected to specific API
      * syncGetWifiConfigsForPasspointProfiles when the caller doesn't have NETWORK_SETTINGS
      * permissions and NETWORK_SETUP_WIZARD.
@@ -3168,6 +3197,33 @@
     }
 
     /**
+     * Verify that the call to getMatchingOsuProviders is redirected to specific API
+     * syncGetMatchingOsuProviders when the caller have NETWORK_SETTINGS
+     * permissions and NETWORK_SETUP_WIZARD.
+     */
+    @Test
+    public void testGetMatchingOsuProvidersWithPermissions() {
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        mWifiServiceImpl.getMatchingOsuProviders(createScanResultList());
+        mLooper.dispatchAll();
+        verify(mPasspointManager).getMatchingOsuProviders(any());
+    }
+
+    /**
+     * Verify that the call to getMatchingOsuProviders is not redirected to specific API
+     * syncGetMatchingOsuProviders when the caller provider invalid ScanResult
+     */
+    @Test
+    public void testGetMatchingOsuProvidersWithInvalidScanResult() {
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        mWifiServiceImpl.getMatchingOsuProviders(new ArrayList<>());
+        mLooper.dispatchAll();
+        verify(mPasspointManager, never()).getMatchingOsuProviders(any());
+    }
+
+    /**
      * Verify that the call to getMatchingPasspointConfigsForOsuProviders is not redirected to
      * specific API syncGetMatchingPasspointConfigsForOsuProviders when the caller doesn't have
      * NETWORK_SETTINGS permissions and NETWORK_SETUP_WIZARD.
@@ -5372,7 +5428,8 @@
     public void testGetWifiConfigsForMatchedNetworkSuggestionsWithSettingPermissions() {
         when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
                 anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
-        mWifiServiceImpl.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>());
+        mWifiServiceImpl
+                .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(createScanResultList());
         mLooper.dispatchAll();
         verify(mWifiNetworkSuggestionsManager)
                 .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any());
@@ -5387,12 +5444,24 @@
     public void testGetWifiConfigsForMatchedNetworkSuggestionsWithSetupWizardPermissions() {
         when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETUP_WIZARD),
                 anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
-        mWifiServiceImpl.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>());
+        mWifiServiceImpl
+                .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(createScanResultList());
         mLooper.dispatchAll();
         verify(mWifiNetworkSuggestionsManager)
                 .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any());
     }
 
+    @Test
+    public void testGetWifiConfigsForMatchedNetworkSuggestionsWithInvalidScanResults() {
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+        mWifiServiceImpl
+                .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>());
+        mLooper.dispatchAll();
+        verify(mWifiNetworkSuggestionsManager, never())
+                .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any());
+    }
+
     /**
      * Verify that a call to setWifiConnectedNetworkScorer throws a SecurityException if
      * the caller does not have WIFI_UPDATE_USABILITY_STATS_SCORE permission.
@@ -5670,4 +5739,9 @@
         verify(mSettingsStore, never()).handleWifiScanAlwaysAvailableToggled(anyBoolean());
         verify(mActiveModeWarden, never()).scanAlwaysModeChanged();
     }
+
+    private List<ScanResult> createScanResultList() {
+        return Collections.singletonList(new ScanResult(WifiSsid.createFromAsciiEncoded(TEST_SSID),
+                TEST_SSID, TEST_BSSID, 1245, 0, TEST_CAP, -78, 2450, 1025, 22, 33, 20, 0, 0, true));
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java
index 3b4bdfd..4afcfba 100644
--- a/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WrongPasswordNotifierTest.java
@@ -42,7 +42,7 @@
 public class WrongPasswordNotifierTest extends WifiBaseTest {
     private static final String TEST_SSID = "Test SSID";
 
-    @Mock Context mContext;
+    @Mock WifiContext mContext;
     @Mock Resources mResources;
     @Mock NotificationManager mNotificationManager;
     @Mock FrameworkFacade mFrameworkFacade;
@@ -58,8 +58,8 @@
         when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
                 .thenReturn(mNotificationManager);
         when(mContext.getResources()).thenReturn(mResources);
-        mWrongPassNotifier =
-                new WrongPasswordNotifier(mContext, mFrameworkFacade);
+        when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources");
+        mWrongPassNotifier = new WrongPasswordNotifier(mContext, mFrameworkFacade);
     }
 
     /**
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 f234a1e..6423f95 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
@@ -62,7 +62,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.server.wifi.MockResources;
 import com.android.server.wifi.WifiBaseTest;
 import com.android.server.wifi.WifiMetrics;
 import com.android.server.wifi.WifiNative;
@@ -80,7 +79,6 @@
 import com.android.server.wifi.hotspot2.soap.command.BrowserUri;
 import com.android.server.wifi.hotspot2.soap.command.PpsMoData;
 import com.android.server.wifi.hotspot2.soap.command.SppCommand;
-import com.android.wifi.resources.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -258,16 +256,13 @@
         when(mOsuServerConnection.exchangeSoapMessage(
                 any(SoapSerializationEnvelope.class))).thenReturn(true);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        MockResources mockResources = new MockResources();
-        mockResources.setString(R.string.config_wifiOsuLoginPackage, OSU_APP_PACKAGE);
-        when(mContext.getResources()).thenReturn(mockResources);
         ResolveInfo resolveInfo = new ResolveInfo();
         resolveInfo.activityInfo = new ActivityInfo();
         resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
         resolveInfo.activityInfo.name = OSU_APP_NAME;
         resolveInfo.activityInfo.applicationInfo.packageName = OSU_APP_PACKAGE;
-        when(mPackageManager.resolveActivity(any(Intent.class),
-                eq(PackageManager.MATCH_DEFAULT_ONLY))).thenReturn(resolveInfo);
+        when(mPackageManager.queryIntentActivities(any(Intent.class), anyInt()))
+                .thenReturn(Arrays.asList(resolveInfo));
 
         Map<String, byte[]> trustCertInfo = new HashMap<>();
         trustCertInfo.put("https://testurl.com", "testData".getBytes());
@@ -632,7 +627,7 @@
         verifyNoMoreInteractions(mCallback);
     }
 
-   /**
+    /**
      * Verifies that the right provisioning callbacks are invoked as the provisioner connects
      * to OSU AP and OSU server and that invalid server URL generates the right error callback.
      */
@@ -782,8 +777,7 @@
     @Test
     public void verifyNoOsuActivityFoundFailure() throws RemoteException {
         // There is no activity found for the intent
-        when(mPackageManager.resolveActivity(any(Intent.class),
-                eq(PackageManager.MATCH_DEFAULT_ONLY))).thenReturn(null);
+        when(mPackageManager.queryIntentActivities(any(Intent.class), anyInt())).thenReturn(null);
         stopAfterStep(STEP_SERVER_CONNECT);
 
         // Server validation passed
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
index 0f69316..e58d083 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
@@ -31,7 +31,9 @@
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Unit tests for {@link com.android.server.wifi.util.ScanResultUtil}.
@@ -243,6 +245,26 @@
         assertTrue(ScanResultUtil.isScanResultForFilsSha384Network(input));
     }
 
+    /**
+     * Verify ScanResultList validation.
+     */
+    @Test
+    public void testValidateScanResultList() {
+        List<ScanResult> scanResults = new ArrayList<>();
+        assertFalse(ScanResultUtil.validateScanResultList(null));
+        assertFalse(ScanResultUtil.validateScanResultList(scanResults));
+        scanResults.add(null);
+        assertFalse(ScanResultUtil.validateScanResultList(scanResults));
+        ScanResult scanResult = new ScanResult();
+        scanResults.clear();
+        scanResults.add(scanResult);
+        assertFalse(ScanResultUtil.validateScanResultList(scanResults));
+        scanResult.SSID = "test";
+        scanResult.capabilities = "[RSN-PSK-CCMP]";
+        scanResult.BSSID = "ab:cd:01:ef:45:89";
+        assertTrue(ScanResultUtil.validateScanResultList(scanResults));
+    }
+
     private static InformationElement createIE(int id, byte[] bytes) {
         InformationElement ie = new InformationElement();
         ie.id = id;