Snap for 7716385 from 6f35a068d028a304267594576c53805e93c71d0c to mainline-resolv-release

Change-Id: I0875cedcb5f73e99eb92cab6f663854f0632a8a2
diff --git a/framework/java/android/net/wifi/ScanResult.java b/framework/java/android/net/wifi/ScanResult.java
index efdf09b..fb831d9 100644
--- a/framework/java/android/net/wifi/ScanResult.java
+++ b/framework/java/android/net/wifi/ScanResult.java
@@ -908,6 +908,8 @@
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public static final int EID_TIM = 5;
         /** @hide */
+        public static final int EID_COUNTRY = 7;
+        /** @hide */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public static final int EID_BSS_LOAD = 11;
         /** @hide */
diff --git a/service/ServiceWifiResources/res/values-te/strings.xml b/service/ServiceWifiResources/res/values-te/strings.xml
index 966f002..98a2bad 100644
--- a/service/ServiceWifiResources/res/values-te/strings.xml
+++ b/service/ServiceWifiResources/res/values-te/strings.xml
@@ -65,7 +65,7 @@
     <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>కు కనెక్ట్ చేయడం సాధ్యపడదు"</string>
     <string name="wifi_cannot_connect_with_randomized_mac_message" msgid="4834133226521813352">"మీ గోప్యతా సెట్టింగ్‌లను మార్చడానికి నొక్కి, మళ్లీ ప్రయత్నించండి"</string>
     <string name="wifi_disable_mac_randomization_dialog_title" msgid="2054540994993681606">"గోప్యతా సెట్టింగ్‌ను మార్చాలా?"</string>
-    <string name="wifi_disable_mac_randomization_dialog_message" msgid="8874064864332248988">"కనెక్ట్ చేయడానికి, <xliff:g id="SSID">%1$s</xliff:g> అనేది ప్రత్యేకమైన ఐడెంటిఫయర్ అయిన మీ పరికరం యొక్క MAC చిరునామాను ఉపయోగించాల్సి ఉంటుంది. ప్రస్తుతం, ఈ నెట్‌వర్క్ కోసం మీ గోప్యతా సెట్టింగ్ యాదృచ్ఛిక ఐడెంటిఫయర్‌ను ఉపయోగిస్తుంది. \n\nఈ మార్పు వలన మీ పరికర లొకేషన్‌ను ట్రాక్ చేయడానికి సమీప పరికరాలకు అనుమతి లభించవచ్చు."</string>
+    <string name="wifi_disable_mac_randomization_dialog_message" msgid="8874064864332248988">"కనెక్ట్ చేయడానికి, <xliff:g id="SSID">%1$s</xliff:g> అనేది ప్రత్యేకమైన ఐడెంటిఫయర్ అయిన మీ పరికరం యొక్క MAC అడ్రస్‌ను ఉపయోగించాల్సి ఉంటుంది. ప్రస్తుతం, ఈ నెట్‌వర్క్ కోసం మీ గోప్యతా సెట్టింగ్ యాదృచ్ఛిక ఐడెంటిఫయర్‌ను ఉపయోగిస్తుంది. \n\nఈ మార్పు వలన మీ పరికర లొకేషన్‌ను ట్రాక్ చేయడానికి సమీప పరికరాలకు అనుమతి లభించవచ్చు."</string>
     <string name="wifi_disable_mac_randomization_dialog_confirm_text" msgid="6954419863076751626">"సెట్టింగ్‌ను మార్చండి"</string>
     <string name="wifi_disable_mac_randomization_dialog_success" msgid="5849155828154391387">"సెట్టింగ్ అప్‌డేట్ చేయబడింది. మళ్లీ కనెక్ట్ చేయడానికి ట్రై చేయండి."</string>
     <string name="wifi_disable_mac_randomization_dialog_failure" msgid="2894643619143813096">"గోప్యతా సెట్టింగ్‌లను మార్చడం సాధ్యం కాదు"</string>
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java
index 1686d70..b2e1ef5 100644
--- a/service/java/com/android/server/wifi/ActiveModeWarden.java
+++ b/service/java/com/android/server/wifi/ActiveModeWarden.java
@@ -1935,6 +1935,13 @@
                         requestInfo.listener.onAnswer(cmmForSameBssid);
                         return;
                     }
+                    // The CMM having BSSID conflict is exactly the one being requested.
+                    // Simply return the CMM in this case. The requestor will be responsible to
+                    // make sure it does not trigger the connection again when already connected.
+                    if (cmmForSameBssid.getRole() == requestInfo.clientRole) {
+                        requestInfo.listener.onAnswer(cmmForSameBssid);
+                        return;
+                    }
                     // Existing secondary CMM connected to the same ssid/bssid.
                     if (!canRequestMoreClientModeManagersInRole(
                             requestInfo.requestorWs, requestInfo.clientRole)) {
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index e85db36..d0e36bb 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -3271,6 +3271,8 @@
             // Block to make sure IpClient has really shut down, lest cleanup
             // race with, say, bringup code over in tethering.
             mIpClientCallbacks.awaitShutdown();
+            mIpClientCallbacks = null;
+            mIpClient = null;
         }
         deregisterForWifiMonitorEvents(); // uses mInterfaceName, must call before nulling out
         // TODO: b/79504296 This broadcast has been deprecated and should be removed
@@ -3600,6 +3602,7 @@
                         boolean isIpClientStarted = startIpClient(config, true);
                         if (isIpClientStarted) {
                             mIpClientWithPreConnection = true;
+                            transitionTo(mL2ConnectingState);
                             break;
                         }
                     }
@@ -6329,6 +6332,7 @@
                     .withPreconnection()
                     .withApfCapabilities(
                     mWifiNative.getApfCapabilities(mInterfaceName))
+                    .withDisplayName(config.SSID)
                     .withLayer2Information(layer2Info);
             if (isUsingMacRandomization) {
                 // Use EUI64 address generation for link-local IPv6 addresses.
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index aa9c7a9..e648923 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -858,19 +858,33 @@
                         if (isBridgedMode()) {
                             boolean isFallbackToSingleAp = false;
                             int newSingleApBand = 0;
-                            for (ClientModeManager cmm
-                                    : mActiveModeWarden.getClientModeManagers()) {
-                                WifiInfo wifiConnectedInfo = cmm.syncRequestConnectionInfo();
-                                int wifiFrequency = wifiConnectedInfo.getFrequency();
-                                if (wifiFrequency > 0
-                                        && !mSafeChannelFrequencyList.contains(
-                                        wifiFrequency)) {
-                                    Log.d(getTag(), "Wifi connected to unavailable freq: "
-                                            + wifiFrequency);
+                            final List<ClientModeManager> cmms =
+                                    mActiveModeWarden.getClientModeManagers();
+                            if (cmms.size() != 0) {
+                                if (ApConfigUtil.isStaWithBridgedModeSupported(mContext)) {
+                                    for (ClientModeManager cmm
+                                            : mActiveModeWarden.getClientModeManagers()) {
+                                        WifiInfo wifiConnectedInfo =
+                                                cmm.syncRequestConnectionInfo();
+                                        int wifiFrequency = wifiConnectedInfo.getFrequency();
+                                        if (wifiFrequency > 0
+                                                && !mSafeChannelFrequencyList.contains(
+                                                wifiFrequency)) {
+                                            Log.d(getTag(), "Wifi connected to unavailable freq: "
+                                                    + wifiFrequency);
+                                            isFallbackToSingleAp = true;
+                                            break;
+                                        }
+                                    }
+                                } else {
+                                    // The client mode exist but DUT doesn't support
+                                    // STA + bridged AP, we should fallback to single AP mode.
+                                    Log.d(getTag(), " STA iface exist but device doesn't support"
+                                            + " STA + Bridged AP");
                                     isFallbackToSingleAp = true;
-                                    break;
                                 }
                             }
+
                             for (int configuredBand : mCurrentSoftApConfiguration.getBands()) {
                                 int availableBand = ApConfigUtil.removeUnavailableBands(
                                         mCurrentSoftApCapability,
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java
index 4949972..55d83ce 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java
@@ -318,8 +318,13 @@
                         .getCandidateSecurityParams();
                 if (params != null
                         && params.getSecurityType() == WifiConfiguration.SECURITY_TYPE_SAE) {
-                    mStaIfaceHal.logCallback("SAE incorrect password");
-                    isWrongPwd = true;
+                    // If this is ever connected, the password should be correct.
+                    isWrongPwd = !curConfiguration.getNetworkSelectionStatus().hasEverConnected();
+                    if (isWrongPwd) {
+                        mStaIfaceHal.logCallback("SAE incorrect password");
+                    } else {
+                        mStaIfaceHal.logCallback("SAE association rejection");
+                    }
                 }
             } else if (assocRejectInfo.statusCode == StatusCode.CHALLENGE_FAIL
                     && WifiConfigurationUtil.isConfigForWepNetwork(curConfiguration)) {
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index e8507ce..7712693 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -457,7 +457,8 @@
                 mNetworkSelector.selectNetwork(secondaryCmmCandidates);
         // No oem paid/private selected, fallback to legacy flow (should never happen!).
         if (secondaryCmmCandidate == null
-                || secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate() == null) {
+                || secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate() == null
+                || (!secondaryCmmCandidate.oemPaid && !secondaryCmmCandidate.oemPrivate)) {
             localLog(listenerName + ": No secondary candidate");
             handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
                     listenerName,
@@ -468,20 +469,15 @@
         }
         String secondaryCmmCandidateBssid =
                 secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate().BSSID;
-        WorkSource secondaryRequestorWs = null;
+
+        // At this point secondaryCmmCandidate must be either oemPaid, oemPrivate, or both.
         // OEM_PAID takes precedence over OEM_PRIVATE, so attribute to OEM_PAID requesting app.
-        if (secondaryCmmCandidate.oemPaid
-                && mActiveModeWarden.canRequestMoreClientModeManagersInRole(
-                mOemPaidConnectionRequestorWs, ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
-            secondaryRequestorWs = mOemPaidConnectionRequestorWs;
-        } else if (secondaryCmmCandidate.oemPrivate
-                && mActiveModeWarden.canRequestMoreClientModeManagersInRole(
-                mOemPrivateConnectionRequestorWs, ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
-            secondaryRequestorWs = mOemPrivateConnectionRequestorWs;
-        }
-        // Secondary STA not available, fallback to legacy flow.
+        WorkSource secondaryRequestorWs = secondaryCmmCandidate.oemPaid
+                ? mOemPaidConnectionRequestorWs : mOemPrivateConnectionRequestorWs;
+
         if (secondaryRequestorWs == null) {
-            localLog(listenerName + ": No secondary STA available");
+            localLog(listenerName + ": Requestor worksource is null in long live STA use-case,"
+                    + "  falling back to single client mode manager flow.");
             handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
                     listenerName,
                     Stream.concat(primaryCmmCandidates.stream(), secondaryCmmCandidates.stream())
@@ -489,6 +485,7 @@
                     handleScanResultsListener);
             return;
         }
+
         WifiConfiguration primaryCmmCandidate =
                 mNetworkSelector.selectNetwork(primaryCmmCandidates);
         // Request for a new client mode manager to spin up concurrent connection
@@ -2278,12 +2275,12 @@
      * @param bssid the failed network.
      * @param ssid identifies the failed network.
      */
-    public void handleConnectionAttemptEnded(@NonNull ActiveModeManager activeModeManager,
+    public void handleConnectionAttemptEnded(@NonNull ClientModeManager clientModeManager,
             int failureCode, @NonNull String bssid, @NonNull String ssid) {
         List<ClientModeManager> internetConnectivityCmms =
                 mActiveModeWarden.getInternetConnectivityClientModeManagers();
-        if (!(internetConnectivityCmms.contains(activeModeManager))) {
-            Log.w(TAG, "Ignoring call from non primary Mode Manager " + activeModeManager,
+        if (!internetConnectivityCmms.contains(clientModeManager)) {
+            Log.w(TAG, "Ignoring call from non primary Mode Manager " + clientModeManager,
                     new Throwable());
             return;
         }
@@ -2295,11 +2292,16 @@
             mOpenNetworkNotifier.handleWifiConnected(ssidUnquoted);
         } else {
             mOpenNetworkNotifier.handleConnectionFailure();
-            retryConnectionOnLatestCandidates(bssid, ssid);
+            // Only attempt to reconnect when connection on the primary CMM fails, since MBB
+            // CMM will be destroyed after the connection failure.
+            if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
+                retryConnectionOnLatestCandidates(clientModeManager, bssid, ssid);
+            }
         }
     }
 
-    private void retryConnectionOnLatestCandidates(String bssid, String ssid) {
+    private void retryConnectionOnLatestCandidates(@NonNull ClientModeManager clientModeManager,
+            String bssid, String ssid) {
         try {
             if (mLatestCandidates == null || mLatestCandidates.size() == 0
                     || mClock.getElapsedSinceBootMillis() - mLatestCandidatesTimestampMs
@@ -2333,7 +2335,12 @@
                 mWifiBlocklistMonitor.blockBssidForDurationMs(bssid, ssid,
                         TEMP_BSSID_BLOCK_DURATION,
                         WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, 0);
-                connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate);
+                triggerConnectToNetworkUsingCmm(clientModeManager, candidate,
+                        ClientModeImpl.SUPPLICANT_BSSID_ANY);
+                // since using primary manager to connect, stop any existing managers in the
+                // secondary transient role since they are no longer needed.
+                mActiveModeWarden.stopAllClientModeManagersInRole(
+                        ROLE_CLIENT_SECONDARY_TRANSIENT);
             }
         } catch (IllegalArgumentException e) {
             localLog("retryConnectionOnLatestCandidates: failed to create MacAddress from bssid="
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index 0398200..6edf9f1 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.net.wifi.WifiInfo;
 import android.os.SystemProperties;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -50,6 +51,9 @@
 public class WifiCountryCode {
     private static final String TAG = "WifiCountryCode";
     private static final String BOOT_DEFAULT_WIFI_COUNTRY_CODE = "ro.boot.wificountrycode";
+    private static final int PKT_COUNT_HIGH_PKT_PER_SEC = 16;
+    private static final int DISCONNECT_WIFI_COUNT_MAX = 1;
+    private static final String COUNTRY_CODE_WORLD = "00";
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
     private final ActiveModeWarden mActiveModeWarden;
@@ -79,6 +83,7 @@
     private String mTelephonyCountryTimestamp = null;
     private String mDriverCountryTimestamp = null;
     private String mReadyTimestamp = null;
+    private int mDisconnectWifiToForceUpdateCount = 0;
 
     private class ModeChangeCallbackInternal implements ActiveModeWarden.ModeChangeCallback {
         @Override
@@ -315,7 +320,7 @@
         // Empty country code.
         if (TextUtils.isEmpty(countryCode)) {
             if (mContext.getResources()
-                        .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss)) {
+                    .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss)) {
                 Log.d(TAG, "Received empty country code, reset to default country code");
                 mTelephonyCountryCode = null;
             }
@@ -343,11 +348,54 @@
             updateCountryCode();
         } else {
             Log.d(TAG, "skip update supplicant not ready yet");
+            disconnectWifiToForceUpdateIfNeeded();
         }
 
         return true;
     }
 
+    private void disconnectWifiToForceUpdateIfNeeded() {
+        if (shouldDisconnectWifiToForceUpdate()) {
+            Log.d(TAG, "Disconnect wifi to force update");
+            for (ClientModeManager cmm :
+                    mActiveModeWarden.getInternetConnectivityClientModeManagers()) {
+                if (!cmm.isConnected()) {
+                    continue;
+                }
+                cmm.disconnect();
+            }
+            mDisconnectWifiToForceUpdateCount++;
+        }
+    }
+
+    private boolean shouldDisconnectWifiToForceUpdate() {
+        if (mTelephonyCountryCode == null || mTelephonyCountryCode.equals(mDriverCountryCode)) {
+            return false;
+        }
+
+        if (mDisconnectWifiToForceUpdateCount >= DISCONNECT_WIFI_COUNT_MAX) {
+            return false;
+        }
+
+        if (mDriverCountryCode != null
+                && !mDriverCountryCode.equalsIgnoreCase(COUNTRY_CODE_WORLD)) {
+            return false;
+        }
+
+        for (ClientModeManager cmm :
+                mActiveModeWarden.getInternetConnectivityClientModeManagers()) {
+            if (!cmm.isConnected()) {
+                continue;
+            }
+            WifiInfo wifiInfo = cmm.syncRequestConnectionInfo();
+            if (wifiInfo.getSuccessfulTxPacketsPerSecond() < PKT_COUNT_HIGH_PKT_PER_SEC
+                    && wifiInfo.getSuccessfulRxPacketsPerSecond() < PKT_COUNT_HIGH_PKT_PER_SEC) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Method to get the Country Code that was sent to wpa_supplicant.
      *
@@ -399,12 +447,12 @@
     }
 
     /**
-     * Method to dump the current state of this WifiCounrtyCode object.
+     * Method to dump the current state of this WifiCountryCode object.
      */
     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("mRevertCountryCodeOnCellularLoss: "
                 + mContext.getResources().getBoolean(
-                        R.bool.config_wifi_revert_country_code_on_cellular_loss));
+                R.bool.config_wifi_revert_country_code_on_cellular_loss));
         pw.println("DefaultCountryCode(system property): " + getOemDefaultCountryCode());
         pw.println("DefaultCountryCode(config store): "
                 + mSettingsConfigStore.get(WIFI_DEFAULT_COUNTRY_CODE));
@@ -416,6 +464,7 @@
         pw.println("mReadyTimestamp: " + mReadyTimestamp);
         pw.println("isReady: " + isReady());
         pw.println("mAmmToReadyForChangeMap: " + mAmmToReadyForChangeMap);
+        pw.println("mDisconnectWifiToForceUpdateCount: " + mDisconnectWifiToForceUpdateCount);
     }
 
     private void updateCountryCode() {
@@ -426,7 +475,7 @@
         // There are two reasons:
         // 1. Wpa supplicant may silently modify the country code.
         // 2. If Wifi restarted therefore wpa_supplicant also restarted,
-        // the country code counld be reset to '00' by wpa_supplicant.
+        // the country code could be reset to '00' by wpa_supplicant.
         if (country != null) {
             setCountryCodeNative(country);
         }
@@ -497,4 +546,3 @@
         }
     }
 }
-
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 3445b76..3affdef 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -210,6 +210,9 @@
     public static final int MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS = 1000 * 3600; // 1 hour
     public static final int PASSPOINT_DEAUTH_IMMINENT_SCOPE_ESS = 0;
     public static final int PASSPOINT_DEAUTH_IMMINENT_SCOPE_BSS = 1;
+    public static final int COUNTRY_CODE_CONFLICT_WIFI_SCAN = -1;
+    public static final int COUNTRY_CODE_CONFLICT_WIFI_SCAN_TELEPHONY = -2;
+    public static final int MAX_COUNTRY_CODE_COUNT = 4;
     // Histogram for WifiConfigStore IO duration times. Indicates the following 5 buckets (in ms):
     //   < 50
     //   [50, 100)
@@ -237,6 +240,7 @@
     private RttMetrics mRttMetrics;
     private final PnoScanMetrics mPnoScanMetrics = new PnoScanMetrics();
     private final WifiLinkLayerUsageStats mWifiLinkLayerUsageStats = new WifiLinkLayerUsageStats();
+    private final TelephonyManager mTelephonyManager;
     /** Mapping of radio id values to RadioStats objects. */
     private final SparseArray<RadioStats> mRadioStats = new SparseArray<>();
     private final ExperimentValues mExperimentValues = new ExperimentValues();
@@ -394,6 +398,7 @@
     private final SparseIntArray mObservedHotspotR3ApsPerEssInScanHistogram = new SparseIntArray();
 
     private final SparseIntArray mObserved80211mcApInScanHistogram = new SparseIntArray();
+    private final IntCounter mCountryCodeScanHistogram = new IntCounter();
 
     // link probing stats
     private final IntCounter mLinkProbeSuccessRssiCounts = new IntCounter(-85, -65);
@@ -1525,6 +1530,7 @@
         setScreenState(context.getSystemService(PowerManager.class).isInteractive());
 
         mScanMetrics = new ScanMetrics(context, clock);
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
     }
 
     /** Sets internal ScoringParams member */
@@ -3409,6 +3415,8 @@
                 mWifiLogProto.partialAllSingleScanListenerResults++;
                 return;
             }
+            updateCountryCodeScanStats(scanDetails);
+
             Set<ScanResultMatchInfo> ssids = new HashSet<ScanResultMatchInfo>();
             int bssids = 0;
             Set<ScanResultMatchInfo> openSsids = new HashSet<ScanResultMatchInfo>();
@@ -3547,6 +3555,35 @@
         }
     }
 
+    private void updateCountryCodeScanStats(List<ScanDetail> scanDetails) {
+        String countryCode = null;
+        int countryCodeCount = 0;
+        for (ScanDetail scanDetail : scanDetails) {
+            NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+            String countryCodeCurr = networkDetail.getCountryCode();
+            if (countryCodeCurr == null) {
+                continue;
+            }
+            if (countryCode == null) {
+                countryCode = countryCodeCurr;
+                countryCodeCount = 1;
+                continue;
+            }
+            if (!countryCodeCurr.equalsIgnoreCase(countryCode)) {
+                mCountryCodeScanHistogram.increment(COUNTRY_CODE_CONFLICT_WIFI_SCAN);
+                return;
+            }
+            countryCodeCount++;
+        }
+        String countryCodeTelephony = mTelephonyManager.getNetworkCountryIso();
+        if (countryCodeCount > 0 && !TextUtils.isEmpty(countryCodeTelephony)
+                && !countryCodeTelephony.equalsIgnoreCase(countryCode)) {
+            mCountryCodeScanHistogram.increment(COUNTRY_CODE_CONFLICT_WIFI_SCAN_TELEPHONY);
+            return;
+        }
+        mCountryCodeScanHistogram.increment(Math.min(countryCodeCount, MAX_COUNTRY_CODE_COUNT));
+    }
+
     /** Increments the occurence of a "Connect to Network" notification. */
     public void incrementConnectToNetworkNotification(String notifierTag, int notificationType) {
         synchronized (mLock) {
@@ -4094,6 +4131,8 @@
 
                 pw.println("mWifiLogProto.observed80211mcSupportingApsInScanHistogram"
                         + mObserved80211mcApInScanHistogram);
+                pw.println("mWifiLogProto.CountryCodeScanHistogram="
+                        + mCountryCodeScanHistogram.toString());
                 pw.println("mWifiLogProto.bssidBlocklistStats:");
                 pw.println(mBssidBlocklistStats.toString());
 
@@ -5057,6 +5096,7 @@
             mWifiLogProto.passpointDeauthImminentScope = mPasspointDeauthImminentScope.toProto();
             mWifiLogProto.recentFailureAssociationStatus =
                     mRecentFailureAssociationStatus.toProto();
+            mWifiLogProto.countryCodeScanHistogram = mCountryCodeScanHistogram.toProto();
         }
     }
 
@@ -5216,6 +5256,7 @@
             mObservedHotspotR1ApsPerEssInScanHistogram.clear();
             mObservedHotspotR2ApsPerEssInScanHistogram.clear();
             mObservedHotspotR3ApsPerEssInScanHistogram.clear();
+            mCountryCodeScanHistogram.clear();
             mSoftApEventListTethered.clear();
             mSoftApEventListLocalOnly.clear();
             mWifiWakeMetrics.clear();
diff --git a/service/java/com/android/server/wifi/WifiScoreCard.java b/service/java/com/android/server/wifi/WifiScoreCard.java
index b5d8b75..493f339 100644
--- a/service/java/com/android/server/wifi/WifiScoreCard.java
+++ b/service/java/com/android/server/wifi/WifiScoreCard.java
@@ -1340,13 +1340,19 @@
 
             long txBytesDelta = txBytes - mLastTxBytes;
             long rxBytesDelta = rxBytes - mLastRxBytes;
+            int txLinkSpeedMbps = wifiInfo.getTxLinkSpeedMbps();
+            int txBandwidthCapMbps =  (txLinkSpeedMbps == LINK_SPEED_UNKNOWN)
+                    ? wifiInfo.getMaxSupportedTxLinkSpeedMbps() : txLinkSpeedMbps;
             if (txBytesDelta * RX_OVER_TX_BYTE_RATIO_MAX >= rxBytesDelta) {
-                updateBandwidthSample(txBytesDelta, LINK_TX, onTimeMs,
-                        wifiInfo.getMaxSupportedTxLinkSpeedMbps());
+                updateBandwidthSample(txBytesDelta, LINK_TX, onTimeMs, txBandwidthCapMbps);
             }
 
-            updateBandwidthSample(rxBytesDelta, LINK_RX, onTimeMs,
-                    wifiInfo.getMaxSupportedRxLinkSpeedMbps());
+            int rxLinkSpeedMbps = wifiInfo.getRxLinkSpeedMbps();
+            // Rx link speed is not available in many devices. In these cases, fall back to Tx link
+            // speed which is available in most devices.
+            int rxBandwidthCapMbps = (rxLinkSpeedMbps == LINK_SPEED_UNKNOWN)
+                    ? txBandwidthCapMbps : rxLinkSpeedMbps;
+            updateBandwidthSample(rxBytesDelta, LINK_RX, onTimeMs, rxBandwidthCapMbps);
 
             if (!mBandwidthSampleValid[LINK_RX] && !mBandwidthSampleValid[LINK_TX]) {
                 return;
@@ -1380,13 +1386,12 @@
         }
 
         private void updateBandwidthSample(long bytesDelta, int link, int onTimeMs,
-                int maxSupportedLinkSpeedMbps) {
-            checkAndPossiblyResetBandwidthStats(link, maxSupportedLinkSpeedMbps);
+                int bandwidthCapMbps) {
             if (bytesDelta < mByteDeltaAccThr[link]) {
                 return;
             }
             long speedKbps = bytesDelta / onTimeMs * 8;
-            if (speedKbps > (maxSupportedLinkSpeedMbps * 1000)) {
+            if (speedKbps > (bandwidthCapMbps * 1000)) {
                 return;
             }
             int linkBandwidthKbps = (int) speedKbps;
@@ -1405,26 +1410,6 @@
             }
         }
 
-        private void checkAndPossiblyResetBandwidthStats(int link, int maxSupportedLinkSpeedMbps) {
-            if (getAvgUsedLinkBandwidthKbps(link) > (maxSupportedLinkSpeedMbps * 1000)) {
-                resetBandwidthStats(link);
-            }
-        }
-
-        private void resetBandwidthStats(int link) {
-            changed = true;
-            // Reset SSID level stats
-            mBandwidthStatsValue[mBandIdx][link][mSignalLevel] = 0;
-            mBandwidthStatsCount[mBandIdx][link][mSignalLevel] = 0;
-            // Reset BSSID level stats
-            PerBssid perBssid = lookupBssid(ssid, mBssid);
-            if (perBssid != mPlaceholderPerBssid) {
-                perBssid.changed = true;
-                perBssid.bandwidthStatsValue[mBandIdx][link][mSignalLevel] = 0;
-                perBssid.bandwidthStatsCount[mBandIdx][link][mSignalLevel] = 0;
-            }
-        }
-
         private int getByteDeltaAccThr(int link) {
             int maxTimeDeltaMs = mContext.getResources().getInteger(
                     R.integer.config_wifiPollRssiIntervalMilliseconds);
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index d433787..4fb8cbf 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -1180,7 +1180,8 @@
 
         mLog.info("startTetheredHotspot uid=%").c(Binder.getCallingUid()).flush();
 
-        if (!mTetheredSoftApTracker.setEnablingIfAllowed()) {
+        if (!mWifiThreadRunner.call(
+                () -> mTetheredSoftApTracker.setEnablingIfAllowed(), false)) {
             mLog.err("Tethering is already active.").flush();
             return false;
         }
@@ -4380,8 +4381,7 @@
                 // It doesn't relate the vendor HAL, set if overlay enables it.
                 supportedFeatureSet |= WifiManager.WIFI_FEATURE_BRIDGED_AP;
             }
-            if (mContext.getResources().getBoolean(
-                    R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported)) {
+            if (ApConfigUtil.isStaWithBridgedModeSupported(mContext)) {
                 // The bridged mode requires the kernel network modules support.
                 // It doesn't relate the vendor HAL, set if overlay enables it.
                 supportedFeatureSet |= WifiManager.WIFI_FEATURE_STA_BRIDGED_AP;
diff --git a/service/java/com/android/server/wifi/coex/CoexManager.java b/service/java/com/android/server/wifi/coex/CoexManager.java
index 326bb85..2bfcf13 100644
--- a/service/java/com/android/server/wifi/coex/CoexManager.java
+++ b/service/java/com/android/server/wifi/coex/CoexManager.java
@@ -309,6 +309,11 @@
             Log.e(TAG, "setCoexUnsafeChannels called with undefined restriction flags");
             return;
         }
+        if (new HashSet(mCurrentCoexUnsafeChannels).equals(new HashSet(coexUnsafeChannels))
+                && mCoexRestrictions == coexRestrictions) {
+            // Do not update if the unsafe channels haven't changed since the last time
+            return;
+        }
         mCurrentCoexUnsafeChannels.clear();
         mCurrentCoexUnsafeChannels.addAll(coexUnsafeChannels);
         mCoexRestrictions = coexRestrictions;
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 22e8554..b43a00b 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -115,6 +115,7 @@
     private final int mAnqpOICount;
     private final long[] mRoamingConsortiums;
     private int mDtimInterval = -1;
+    private String mCountryCode;
 
     private final InformationElementUtil.ExtendedCapabilities mExtendedCapabilities;
 
@@ -167,6 +168,9 @@
         InformationElementUtil.ExtendedCapabilities extendedCapabilities =
                 new InformationElementUtil.ExtendedCapabilities();
 
+        InformationElementUtil.Country country =
+                new InformationElementUtil.Country();
+
         InformationElementUtil.TrafficIndicationMap trafficIndicationMap =
                 new InformationElementUtil.TrafficIndicationMap();
 
@@ -212,6 +216,9 @@
                     case ScanResult.InformationElement.EID_EXTENDED_CAPS:
                         extendedCapabilities.from(ie);
                         break;
+                    case ScanResult.InformationElement.EID_COUNTRY:
+                        country.from(ie);
+                        break;
                     case ScanResult.InformationElement.EID_TIM:
                         trafficIndicationMap.from(ie);
                         break;
@@ -348,6 +355,10 @@
         mCenterfreq0 = centerFreq0;
         mCenterfreq1 = centerFreq1;
 
+        if (country.isValid()) {
+            mCountryCode = country.getCountryCode();
+        }
+
         // If trafficIndicationMap is not valid, mDtimPeriod will be negative
         if (trafficIndicationMap.isValid()) {
             mDtimInterval = trafficIndicationMap.mDtimPeriod;
@@ -426,6 +437,7 @@
         mCenterfreq0 = base.mCenterfreq0;
         mCenterfreq1 = base.mCenterfreq1;
         mDtimInterval = base.mDtimInterval;
+        mCountryCode = base.mCountryCode;
         mWifiMode = base.mWifiMode;
         mMaxRate = base.mMaxRate;
         mMaxNumberSpatialStreams = base.mMaxNumberSpatialStreams;
@@ -552,6 +564,10 @@
         return mDtimInterval;
     }
 
+    public String getCountryCode() {
+        return mCountryCode;
+    }
+
     public boolean is80211McResponderSupport() {
         return mExtendedCapabilities.is80211McRTTResponder();
     }
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index c0bdd66..62f8feb 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -966,6 +966,18 @@
             }
         }
 
+        // Clear internal data when P2P is shut down due to wifi off or no client.
+        // For idle shutdown case, there are clients and data should be restored when
+        // P2P goes back P2pEnabledState.
+        // For a real shutdown case which caused by wifi off or no client, those internal
+        // data should be cleared because the caller might not clear them, ex. WFD app
+        // enables WFD, but does not disable it after leaving the app.
+        private void clearP2pInternalDataIfNecessary() {
+            if (mIsWifiEnabled && !mDeathDataByBinder.isEmpty()) return;
+
+            mThisDevice.wfdInfo = null;
+        }
+
         boolean isP2pDisabled() {
             return getCurrentState() == mP2pDisabledState;
         }
@@ -1178,12 +1190,16 @@
                                 WifiP2pManager.BUSY);
                         break;
                     case WifiP2pManager.SET_WFD_INFO:
+                        WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
                         if (!getWfdPermission(message.sendingUid)) {
                             replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
                                     WifiP2pManager.ERROR);
+                        } else if (d != null) {
+                            mThisDevice.wfdInfo = d;
+                            replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
                         } else {
                             replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
-                                    WifiP2pManager.BUSY);
+                                    WifiP2pManager.ERROR);
                         }
                         break;
                     case WifiP2pManager.REQUEST_PEERS:
@@ -1517,6 +1533,7 @@
             public void enter() {
                 if (mVerboseLoggingEnabled) logd(getName());
                 mInterfaceName = null; // reset iface name on disable.
+                clearP2pInternalDataIfNecessary();
             }
 
             private void setupInterfaceFeatures(String interfaceName) {
@@ -1581,7 +1598,8 @@
                         // which require P2P to be active.
                         if (message.what < Protocol.BASE_WIFI_P2P_MANAGER
                                 || Protocol.BASE_WIFI_P2P_SERVICE <= message.what
-                                || message.what == WifiP2pManager.UPDATE_CHANNEL_INFO) {
+                                || message.what == WifiP2pManager.UPDATE_CHANNEL_INFO
+                                || message.what == WifiP2pManager.SET_WFD_INFO) {
                             return NOT_HANDLED;
                         }
                         // If P2P is not ready, it might be disabled due
@@ -3738,11 +3756,7 @@
             }
             config.groupOwnerIntent = selectGroupOwnerIntentIfNecessary(config);
             boolean action;
-            if (triggerType == P2P_CONNECT_TRIGGER_GROUP_NEG_REQ) {
-                // If this is called from the GO negotiation path, the sender initiated
-                // a group negotiation.
-                action = FORM_GROUP;
-            } else if (triggerType == P2P_CONNECT_TRIGGER_INVITATION_REQ) {
+            if (triggerType == P2P_CONNECT_TRIGGER_INVITATION_REQ) {
                 // The group owner won't report it is a Group Owner always.
                 // If this is called from the invitation path, the sender should be in
                 // a group, and the target should be a group owner.
@@ -4070,6 +4084,10 @@
             mServiceTransactionId = 0;
             mServiceDiscReqId = null;
 
+            if (null != mThisDevice.wfdInfo) {
+                setWfdInfo(mThisDevice.wfdInfo);
+            }
+
             updatePersistentNetworks(RELOAD);
             enableVerboseLogging(mSettingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED));
         }
diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java
index d09e798..4f881e1 100644
--- a/service/java/com/android/server/wifi/util/ApConfigUtil.java
+++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java
@@ -713,6 +713,17 @@
     }
 
     /**
+     * Helper function to get HAL support STA + bridged AP or not.
+     *
+     * @param context the caller context used to get value from resource file.
+     * @return true if supported, false otherwise.
+     */
+    public static boolean isStaWithBridgedModeSupported(@NonNull Context context) {
+        return SdkLevel.isAtLeastS() && context.getResources().getBoolean(
+                    R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported);
+    }
+
+    /**
      * Helper function to get HAL support client force disconnect or not.
      *
      * @param context the caller context used to get value from resource file.
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index 92e5d35..ebcf379 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -36,6 +36,7 @@
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.List;
+import java.util.Locale;
 
 public class InformationElementUtil {
     private static final String TAG = "InformationElementUtil";
@@ -1704,4 +1705,58 @@
             return sbuf.toString();
         }
     }
+
+    /**
+     * This util class determines country related information in beacon/probe response
+     */
+    public static class Country {
+        private boolean mValid = false;
+        public String mCountryCode = "00";
+
+        /**
+         * Parse the Information Element Country Information field. Note that element ID and length
+         * fields are already removed.
+         *
+         * Country IE format (size unit: byte)
+         *
+         * ElementID | Length | country string | triplet | padding
+         *      1          1          3            Q*x       0 or 1
+         * First two bytes of country string are country code
+         * See 802.11 spec dot11CountryString definition.
+         */
+        public void from(InformationElement ie) {
+            mValid = false;
+            if (ie == null || ie.bytes == null || ie.bytes.length < 3) return;
+            ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
+            try {
+                char letter1 = (char) (data.get() & Constants.BYTE_MASK);
+                char letter2 = (char) (data.get() & Constants.BYTE_MASK);
+                char letter3 = (char) (data.get() & Constants.BYTE_MASK);
+                // See 802.11 spec dot11CountryString definition.
+                // ' ', 'O', 'I' are for all operation, outdoor, indoor environments, respectively.
+                mValid = (letter3 == ' ' || letter3 == 'O' || letter3 == 'I')
+                        && Character.isLetterOrDigit((int) letter1)
+                        && Character.isLetterOrDigit((int) letter2);
+                if (mValid) {
+                    mCountryCode = (String.valueOf(letter1) + letter2).toUpperCase(Locale.US);
+                }
+            } catch (BufferUnderflowException e) {
+                return;
+            }
+        }
+
+        /**
+         * Is this a valid country information element.
+         */
+        public boolean isValid() {
+            return mValid;
+        }
+
+        /**
+         * @return country code indicated in beacon/probe response frames
+         */
+        public String getCountryCode() {
+            return mCountryCode;
+        }
+    }
 }
diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto
index 35db206..cc8b154 100644
--- a/service/proto/src/metrics.proto
+++ b/service/proto/src/metrics.proto
@@ -767,6 +767,13 @@
   // Histogram corresponding to the number of times recent connection failure status are reported
   // per WifiConfiguration.RecentFailureReason
   repeated Int32Count recent_failure_association_status = 218;
+
+  // Histogram of country codes observed from scan results.
+  // Bucket WifiMetrics.COUNTRY_CODE_CONFLICT_WIFI_SCAN for the code conflict within scan results.
+  // Bucket WifiMetrics.COUNTRY_CODE_CONFLICT_WIFI_SCAN_TELEPHONY for the code conflict between wifi
+  // and telephony.
+  // Bucket value is capped to WifiMetrics.MAX_COUNTRY_CODE_COUNT.
+  repeated Int32Count country_code_scan_histogram = 219;
 }
 
 // Information that gets logged for every WiFi connection.
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
index 1b32b8d..d694d8a 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
@@ -2799,6 +2799,47 @@
         assertEquals(additionalClientModeManager, requestedClientModeManager.getValue());
     }
 
+    public void requestAdditionalClientModeManagerWhenAlreadyPresentSameBssid(
+            ClientConnectivityRole role) throws Exception {
+        ConcreteClientModeManager additionalClientModeManager =
+                mock(ConcreteClientModeManager.class);
+        ExternalClientModeManagerRequestListener externalRequestListener = mock(
+                ExternalClientModeManagerRequestListener.class);
+        requestAdditionalClientModeManager(role, additionalClientModeManager,
+                externalRequestListener, TEST_SSID_2, TEST_BSSID_2);
+
+        ArgumentCaptor<ClientModeManager> requestedClientModeManager =
+                ArgumentCaptor.forClass(ClientModeManager.class);
+        verify(externalRequestListener).onAnswer(requestedClientModeManager.capture());
+        assertEquals(additionalClientModeManager, requestedClientModeManager.getValue());
+
+        // set additional CMM connected to ssid2/bssid2
+        WifiConfiguration config2 = new WifiConfiguration();
+        config2.SSID = TEST_SSID_2;
+        when(additionalClientModeManager.getConnectedWifiConfiguration()).thenReturn(config2);
+        when(additionalClientModeManager.getConnectedBssid()).thenReturn(TEST_BSSID_2);
+
+        // request for the same SSID/BSSID and expect the existing CMM to get returned twice.
+        if (role == ROLE_CLIENT_LOCAL_ONLY) {
+            mActiveModeWarden.requestLocalOnlyClientModeManager(
+                    externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2);
+        } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
+            mActiveModeWarden.requestSecondaryLongLivedClientModeManager(
+                    externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2);
+        } else if (role == ROLE_CLIENT_SECONDARY_TRANSIENT) {
+            mActiveModeWarden.requestSecondaryTransientClientModeManager(
+                    externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2);
+        }
+        mLooper.dispatchAll();
+
+        // Don't make another client mode manager.
+        verify(mWifiInjector, times(1))
+                .makeClientModeManager(any(), any(), eq(role), anyBoolean());
+        // Returns the existing client mode manager.
+        verify(externalRequestListener, times(2)).onAnswer(requestedClientModeManager.capture());
+        assertEquals(additionalClientModeManager, requestedClientModeManager.getValue());
+    }
+
     private void requestAdditionalClientModeManagerWhenConnectingToPrimaryBssid(
             ClientConnectivityRole role) throws Exception {
         enterClientModeActiveState();
@@ -2900,6 +2941,18 @@
     }
 
     @Test
+    public void requestLocalOnlyClientModeManagerWhenAlreadyPresentSameBssid() throws Exception {
+        // Ensure that we can create more client ifaces.
+        when(mWifiNative.isItPossibleToCreateStaIface(any())).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled))
+                .thenReturn(true);
+        assertTrue(mActiveModeWarden.canRequestMoreClientModeManagersInRole(
+                TEST_WORKSOURCE, ROLE_CLIENT_LOCAL_ONLY));
+
+        requestAdditionalClientModeManagerWhenAlreadyPresentSameBssid(ROLE_CLIENT_LOCAL_ONLY);
+    }
+
+    @Test
     public void requestLocalOnlyClientModeManagerWhenConnectingToPrimaryBssid() throws Exception {
         // Ensure that we can create more client ifaces.
         when(mWifiNative.isItPossibleToCreateStaIface(any())).thenReturn(true);
@@ -3002,6 +3055,20 @@
     }
 
     @Test
+    public void requestSecondaryLongLivedClientModeManagerWhenAlreadyPresentSameBssid()
+            throws Exception {
+        // Ensure that we can create more client ifaces.
+        when(mWifiNative.isItPossibleToCreateStaIface(any())).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled))
+                .thenReturn(true);
+        assertTrue(mActiveModeWarden.canRequestMoreClientModeManagersInRole(
+                TEST_WORKSOURCE, ROLE_CLIENT_SECONDARY_LONG_LIVED));
+
+        requestAdditionalClientModeManagerWhenAlreadyPresentSameBssid(
+                ROLE_CLIENT_SECONDARY_LONG_LIVED);
+    }
+
+    @Test
     public void requestSecondaryLongLivedClientModeManagerWhenConnectingToPrimaryBssid()
             throws Exception {
         // Ensure that we can create more client ifaces.
@@ -3078,6 +3145,21 @@
     }
 
     @Test
+    public void requestSecondaryTransientClientModeManagerWhenAlreadyPresentSameBssid()
+            throws Exception {
+        // Ensure that we can create more client ifaces.
+        when(mWifiNative.isItPossibleToCreateStaIface(any())).thenReturn(true);
+        when(mResources.getBoolean(
+                R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled))
+                .thenReturn(true);
+        assertTrue(mActiveModeWarden.canRequestMoreClientModeManagersInRole(
+                TEST_WORKSOURCE, ROLE_CLIENT_SECONDARY_TRANSIENT));
+
+        requestAdditionalClientModeManagerWhenAlreadyPresentSameBssid(
+                ROLE_CLIENT_SECONDARY_TRANSIENT);
+    }
+
+    @Test
     public void requestSecondaryTransientClientModeManagerWhenConnectingToPrimaryBssid()
             throws Exception {
         // Ensure that we can create more client ifaces.
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index 3d6fa8f..6d913a1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -5171,6 +5171,7 @@
         mCmi.sendMessage(ClientModeImpl.CMD_START_FILS_CONNECTION, 0, 0,
                 Collections.singletonList(l2Packet));
         mLooper.dispatchAll();
+        assertEquals("L2ConnectingState", mCmi.getCurrentState().getName());
     }
 
     /**
@@ -5278,6 +5279,7 @@
 
         mCmi.sendMessage(ClientModeImpl.CMD_START_CONNECT, 0, 0, TEST_BSSID_STR);
         mLooper.dispatchAll();
+        assertEquals("L2ConnectingState", mCmi.getCurrentState().getName());
 
         prepareFilsHlpPktAndSendStartConnect();
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index 5006fc9..a0c4bf1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -269,6 +269,9 @@
         when(mWifiNative.getApFactoryMacAddress(any())).thenReturn(TEST_INTERFACE_MAC_ADDRESS);
         when(mWifiApConfigStore.randomizeBssidIfUnset(any(), any())).thenAnswer(
                 (invocation) -> invocation.getArgument(1));
+        // Default init STA enabled
+        when(mResources.getBoolean(R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported))
+                .thenReturn(true);
         when(mActiveModeWarden.getClientModeManagers())
                 .thenReturn(mTestClientModeManagers);
         mTestClientModeManagers.add(mPrimaryConcreteClientModeManager);
@@ -2861,7 +2864,6 @@
     @Test
     public void testBridgedModeFallbackToSingleModeDueToCoexIsHardUnsafe()
             throws Exception {
-        //leslee
         assumeTrue(SdkLevel.isAtLeastS());
         int[] dual_bands = {SoftApConfiguration.BAND_2GHZ,
                 SoftApConfiguration.BAND_5GHZ};
@@ -3147,4 +3149,24 @@
         mLooper.dispatchAll();
         verify(mWifiNative, never()).setApCountryCode(any(), any());
     }
+
+    @Test
+    public void testFallbackToSingleModeDueToStaExistButStaWithBridgedApNotSupported()
+            throws Exception {
+        assumeTrue(SdkLevel.isAtLeastS());
+        when(mResources.getBoolean(R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported))
+                .thenReturn(false);
+        int[] dual_bands = {SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ};
+        Builder configBuilder = new SoftApConfiguration.Builder();
+        configBuilder.setSsid(TEST_SSID);
+        configBuilder.setBands(dual_bands);
+
+        SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
+                mTestSoftApCapability);
+        // Reset band to 2.4G | 5G to generate expected configuration since it should fallback to
+        // single AP mode
+        configBuilder.setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
+        startSoftApAndVerifyEnabled(apConfig, TEST_COUNTRY_CODE, configBuilder.build());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
index 7d429b3..6ef8b54 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
@@ -1266,6 +1266,42 @@
     }
 
     /**
+     * Tests the handling of association rejection for WPA3-Personal networks
+     */
+    @Test
+    public void testWpa3AuthRejectionEverConnected() throws Exception {
+        executeAndValidateInitializationSequence();
+        assertNotNull(mISupplicantStaIfaceCallback);
+
+        WifiConfiguration config = executeAndValidateConnectSequenceWithKeyMgmt(
+                SUPPLICANT_NETWORK_ID, false,
+                WifiConfiguration.SECURITY_TYPE_SAE, null, true);
+        mISupplicantStaIfaceCallback.onStateChanged(
+                ISupplicantStaIfaceCallback.State.ASSOCIATING,
+                NativeUtil.macAddressToByteArray(BSSID),
+                SUPPLICANT_NETWORK_ID,
+                NativeUtil.decodeSsid(SUPPLICANT_SSID));
+        int statusCode = ISupplicantStaIfaceCallback.StatusCode.UNSPECIFIED_FAILURE;
+        mISupplicantStaIfaceCallback.onAssociationRejected(
+                NativeUtil.macAddressToByteArray(BSSID), statusCode, false);
+        verify(mWifiMonitor, never()).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME),
+                anyInt(), anyInt());
+        ArgumentCaptor<AssocRejectEventInfo> assocRejectEventInfoCaptor =
+                ArgumentCaptor.forClass(AssocRejectEventInfo.class);
+        verify(mWifiMonitor).broadcastAssociationRejectionEvent(
+                eq(WLAN0_IFACE_NAME), assocRejectEventInfoCaptor.capture());
+        AssocRejectEventInfo assocRejectEventInfo =
+                (AssocRejectEventInfo) assocRejectEventInfoCaptor.getValue();
+        assertNotNull(assocRejectEventInfo);
+        assertEquals(SUPPLICANT_SSID, assocRejectEventInfo.ssid);
+        assertEquals(BSSID, assocRejectEventInfo.bssid);
+        assertEquals(statusCode, assocRejectEventInfo.statusCode);
+        assertFalse(assocRejectEventInfo.timedOut);
+        assertNull(assocRejectEventInfo.oceRssiBasedAssocRejectInfo);
+        assertNull(assocRejectEventInfo.mboAssocDisallowedInfo);
+    }
+
+    /**
      * Tests the handling of incorrect network passwords for WEP networks.
      */
     @Test
@@ -2926,11 +2962,12 @@
      * @param haveExistingNetwork Removes the existing network.
      * @param securityType The security type.
      * @param wepKey if configurations are for a WEP network else null.
+     * @param hasEverConnected indicate that this configuration is ever connected or not.
      * @return the WifiConfiguration object of the new network to connect.
      */
     private WifiConfiguration executeAndValidateConnectSequenceWithKeyMgmt(
             final int newFrameworkNetworkId, final boolean haveExistingNetwork,
-            int securityType, String wepKey) throws Exception {
+            int securityType, String wepKey, boolean hasEverConnected) throws Exception {
         setupMocksForConnectSequence(haveExistingNetwork);
         WifiConfiguration config = new WifiConfiguration();
         config.setSecurityParams(securityType);
@@ -2940,6 +2977,7 @@
         WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
                 new WifiConfiguration.NetworkSelectionStatus();
         networkSelectionStatus.setCandidateSecurityParams(config.getSecurityParams(securityType));
+        networkSelectionStatus.setHasEverConnected(hasEverConnected);
         config.setNetworkSelectionStatus(networkSelectionStatus);
         assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config));
         validateConnectSequence(haveExistingNetwork, 1);
@@ -2947,6 +2985,23 @@
     }
 
     /**
+     * Helper function to execute all the actions to perform connection to the network.
+     *
+     * @param newFrameworkNetworkId Framework Network Id of the new network to connect.
+     * @param haveExistingNetwork Removes the existing network.
+     * @param securityType The security type.
+     * @param wepKey if configurations are for a WEP network else null.
+     * @return the WifiConfiguration object of the new network to connect.
+     */
+    private WifiConfiguration executeAndValidateConnectSequenceWithKeyMgmt(
+            final int newFrameworkNetworkId, final boolean haveExistingNetwork,
+            int securityType, String wepKey) throws Exception {
+        return executeAndValidateConnectSequenceWithKeyMgmt(
+                newFrameworkNetworkId, haveExistingNetwork,
+                securityType, wepKey, false);
+    }
+
+    /**
      * Setup mocks for roam sequence.
      */
     private void setupMocksForRoamSequence(String roamBssid) throws Exception {
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index d2acbae..0606093 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -41,6 +41,7 @@
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.eq;
@@ -867,31 +868,6 @@
     }
 
     @Test
-    public void secondaryLongLived_noSecondaryStaAvailable() {
-        setupMocksForSecondaryLongLivedTests();
-
-        // Set screen to on
-        setScreenState(true);
-
-        // OEM paid connection allowed.
-        mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource());
-
-        // STA + STA is supported, but not available.
-        when(mActiveModeWarden.canRequestMoreClientModeManagersInRole(
-                any(), eq(ROLE_CLIENT_SECONDARY_LONG_LIVED))).thenReturn(false);
-
-        // Set WiFi to disconnected state
-        mWifiConnectivityManager.handleConnectionStateChanged(
-                mPrimaryClientModeManager,
-                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
-
-        verify(mPrimaryClientModeManager).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any");
-        verify(mActiveModeWarden, never()).requestSecondaryLongLivedClientModeManager(
-                any(), any(), any(), any());
-    }
-
-    @Test
     public void secondaryLongLived_secondaryStaRequestReturnsNull() {
         setupMocksForSecondaryLongLivedTests();
 
@@ -932,7 +908,7 @@
         // OEM paid connection allowed.
         mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource());
 
-        // STA + STA is supported, but secondary STA request returns null
+        // STA + STA is supported, but secondary STA request returns the primary
         doAnswer(new AnswerWithArguments() {
             public void answer(ExternalClientModeManagerRequestListener listener,
                     WorkSource requestorWs, String ssid, String bssid) {
@@ -981,6 +957,19 @@
                 CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any");
         verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager(
                 any(), any(), any(), any());
+
+        // Simulate connection failing on the secondary
+        clearInvocations(mSecondaryClientModeManager, mPrimaryClientModeManager, mWifiNS);
+        mWifiConnectivityManager.handleConnectionAttemptEnded(
+                mSecondaryClientModeManager,
+                WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, CANDIDATE_BSSID,
+                CANDIDATE_SSID);
+        // verify connection is never restarted when a connection on the secondary STA fails.
+        verify(mWifiNS, never()).selectNetwork(any());
+        verify(mSecondaryClientModeManager, never()).startConnectToNetwork(
+                anyInt(), anyInt(), any());
+        verify(mPrimaryClientModeManager, never()).startConnectToNetwork(
+                anyInt(), anyInt(), any());
     }
 
     @Test
@@ -1055,6 +1044,55 @@
     }
 
     /**
+     * Verify that when the secondary is already connecting to the selected secondary network,
+     * we only connect the primary STA.
+     */
+    @Test
+    public void secondaryLongLived_secondaryStaRequestSucceedsWhenSecondaryAlreadyConnecting() {
+        setupMocksForSecondaryLongLivedTests();
+
+        // 2 candidates - 1 oem paid, other regular.
+        // Mark the first candidate oem private only
+        when(mCandidate1.isOemPaid()).thenReturn(false);
+        when(mCandidate1.isOemPrivate()).thenReturn(true);
+        mCandidateWifiConfig1.oemPaid = false;
+        mCandidateWifiConfig1.oemPrivate = true;
+
+        // mock secondary STA to already connecting to the target OEM private network
+        when(mSecondaryClientModeManager.getConnectingWifiConfiguration()).thenReturn(
+                mCandidateWifiConfig1);
+
+        // Add the second regular candidate.
+        mCandidateList.add(mCandidate2);
+
+        // Set screen to on
+        setScreenState(true);
+
+        // OEM paid connection allowed.
+        mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource());
+
+        // Network selection setup for primary.
+        when(mWifiNS.selectNetwork(argThat(
+                candidates -> (candidates != null && candidates.size() == 1
+                        // not oem paid or oem private.
+                        && !(candidates.get(0).isOemPaid() || candidates.get(0).isOemPrivate()))
+        ))).thenReturn(mCandidateWifiConfig2);
+
+        // Set WiFi to disconnected state
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+        // connection triggered on only on primary to CANDIDATE_NETWORK_ID_2.
+        verify(mPrimaryClientModeManager).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID_2, Process.WIFI_UID, "any");
+        verify(mSecondaryClientModeManager, never()).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any");
+        verify(mActiveModeWarden).requestSecondaryLongLivedClientModeManager(
+                any(), any(), eq(CANDIDATE_SSID), any());
+    }
+
+    /**
      *  Wifi enters disconnected state while screen is on.
      *
      * Expected behavior: WifiConnectivityManager calls
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
index fda21db..c833bf4 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -26,6 +26,7 @@
 
 import android.app.test.MockAnswerUtil.AnswerWithArguments;
 import android.content.Context;
+import android.net.wifi.WifiInfo;
 import android.telephony.TelephonyManager;
 
 import androidx.test.filters.SmallTest;
@@ -41,6 +42,8 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -62,7 +65,9 @@
     @Mock ClientModeImplMonitor mClientModeImplMonitor;
     @Mock WifiNative mWifiNative;
     @Mock WifiSettingsConfigStore mSettingsConfigStore;
+    @Mock WifiInfo mWifiInfo;
     private WifiCountryCode mWifiCountryCode;
+    private List<ClientModeManager> mClientManagerList;
 
     @Captor
     private ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackCaptor;
@@ -80,8 +85,16 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mClientManagerList = new ArrayList<>();
+        mClientManagerList.add(mClientModeManager);
+        when(mActiveModeWarden.getInternetConnectivityClientModeManagers())
+                .thenReturn(mClientManagerList);
         when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY);
         when(mClientModeManager.setCountryCode(anyString())).thenReturn(true);
+        when(mClientModeManager.isConnected()).thenReturn(true);
+        when(mClientModeManager.syncRequestConnectionInfo()).thenReturn(mWifiInfo);
+        when(mWifiInfo.getSuccessfulTxPacketsPerSecond()).thenReturn(10.0);
+        when(mWifiInfo.getSuccessfulRxPacketsPerSecond()).thenReturn(5.0);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .thenReturn(mTelephonyManager);
 
@@ -233,17 +246,34 @@
      */
     @Test
     public void telephonyCountryCodeChangeAfterL2Connected() throws Exception {
+        mWifiCountryCode.setDefaultCountryCode("00");
         // Supplicant starts.
         mModeChangeCallbackCaptor.getValue().onActiveModeManagerAdded(mClientModeManager);
         // Wifi get L2 connected.
         mClientModeImplListenerCaptor.getValue().onConnectionStart(mClientModeManager);
+
+        // Wifi traffic is high
+        when(mWifiInfo.getSuccessfulTxPacketsPerSecond()).thenReturn(20.0);
         // Telephony country code arrives.
         mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode);
-        // Telephony coutry code won't be applied at this time.
-        assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
+        // Telephony country code won't be applied at this time.
+        assertEquals("00", mWifiCountryCode.getCountryCodeSentToDriver());
+        // Wifi is not forced to disconnect
+        verify(mClientModeManager, times(0)).disconnect();
+
+        // Wifi traffic is low
+        when(mWifiInfo.getSuccessfulTxPacketsPerSecond()).thenReturn(10.0);
+        // Telephony country code arrives for multiple times
+        for (int i = 0; i < 3; i++) {
+            mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode);
+        }
+        // Telephony country code still won't be applied.
+        assertEquals("00", mWifiCountryCode.getCountryCodeSentToDriver());
+        // Wifi is forced to disconnect
+        verify(mClientModeManager, times(1)).disconnect();
 
         mClientModeImplListenerCaptor.getValue().onConnectionEnd(mClientModeManager);
-        // Telephony coutry is applied after supplicant is ready.
+        // Telephony country is applied after supplicant is ready.
         verify(mClientModeManager, times(2)).setCountryCode(anyString());
         assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
     }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index abb08fa..6084a6c 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -196,6 +196,7 @@
     @Mock PowerManager mPowerManager;
     @Mock WifiMonitor mWifiMonitor;
     @Mock ActiveModeWarden mActiveModeWarden;
+    @Mock TelephonyManager mTelephonyManager;
 
     @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
     @Captor ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackArgumentCaptor;
@@ -211,6 +212,8 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
         when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mTelephonyManager.getNetworkCountryIso()).thenReturn("US");
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
         mWifiMetrics = new WifiMetrics(mContext, mFacade, mClock, mTestLooper.getLooper(),
                 new WifiAwareMetrics(mClock), new RttMetrics(mClock), mWifiPowerMetrics,
                 mWifiP2pMetrics, mDppMetrics, mWifiMonitor);
@@ -3218,6 +3221,46 @@
     }
 
     /**
+     * Test that country code stats are collected correctly
+     */
+    @Test
+    public void testCountryCodeStats() throws Exception {
+        ScanDetail mockScanDetailUs = mock(ScanDetail.class);
+        ScanDetail mockScanDetailNonUs = mock(ScanDetail.class);
+        NetworkDetail mockNetworkDetailUs = mock(NetworkDetail.class);
+        NetworkDetail mockNetworkDetailNonUs = mock(NetworkDetail.class);
+        when(mockNetworkDetailUs.getCountryCode()).thenReturn("US");
+        when(mockNetworkDetailNonUs.getCountryCode()).thenReturn("CA");
+        ScanResult mockScanResult = mock(ScanResult.class);
+        mockScanResult.capabilities = "";
+        when(mockScanDetailUs.getScanResult()).thenReturn(mockScanResult);
+        when(mockScanDetailNonUs.getScanResult()).thenReturn(mockScanResult);
+        when(mockScanDetailUs.getNetworkDetail()).thenReturn(mockNetworkDetailUs);
+        when(mockScanDetailNonUs.getNetworkDetail()).thenReturn(mockNetworkDetailNonUs);
+        List<ScanDetail> scan = new ArrayList<ScanDetail>();
+
+        for (int i = 0; i < (WifiMetrics.MAX_COUNTRY_CODE_COUNT + 1); i++) {
+            scan.add(mockScanDetailUs);
+        }
+        mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+        when(mTelephonyManager.getNetworkCountryIso()).thenReturn("CA");
+        mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+        scan.add(mockScanDetailNonUs);
+        mWifiMetrics.incrementAvailableNetworksHistograms(scan, true);
+
+        dumpProtoAndDeserialize();
+
+        Int32Count[] expectedCountryCodeScanHistogram = {
+                buildInt32Count(WifiMetrics.COUNTRY_CODE_CONFLICT_WIFI_SCAN_TELEPHONY, 1),
+                buildInt32Count(WifiMetrics.COUNTRY_CODE_CONFLICT_WIFI_SCAN, 1),
+                buildInt32Count(WifiMetrics.MAX_COUNTRY_CODE_COUNT, 1),
+        };
+        assertKeyCountsEqual(expectedCountryCodeScanHistogram,
+                mDecodedProto.countryCodeScanHistogram);
+    }
+
+
+    /**
      * Test Open Network Notification blocklist size and feature state are not cleared when proto
      * is dumped.
      */
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
index d48d72a..0df19c0 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreCardTest.java
@@ -144,6 +144,8 @@
         mWifiInfo.setSSID(TEST_SSID_1);
         mWifiInfo.setBSSID(TEST_BSSID_1.toString());
         mWifiInfo.setNetworkId(TEST_NETWORK_CONFIG_ID);
+        mWifiInfo.setTxLinkSpeedMbps(866);
+        mWifiInfo.setRxLinkSpeedMbps(866);
         mWifiInfo.setMaxSupportedTxLinkSpeedMbps(866);
         mWifiInfo.setMaxSupportedRxLinkSpeedMbps(866);
         millisecondsPass(0);
@@ -1785,7 +1787,7 @@
     @Test
     public void testLinkBandwidthLargeByteCountReturnNonNegativeValue() {
         mWifiInfo.setRssi(-70);
-        mWifiInfo.setMaxSupportedRxLinkSpeedMbps(200_000);
+        mWifiInfo.setRxLinkSpeedMbps(200_000);
         mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
         PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
         mWifiInfo.setFrequency(5210);
@@ -1816,7 +1818,7 @@
         }
 
         // Report cold start BW for Tx because the calculated value is higher than
-        // maxSupportedTxLinkSpeedMbps.
+        // txLinkSpeedMbps.
         assertEquals(10_000, perNetwork.getTxLinkBandwidthKbps());
         assertEquals(128_000_000, perNetwork.getRxLinkBandwidthKbps());
 
@@ -1841,9 +1843,8 @@
     }
 
     @Test
-    public void testLinkBandwidthResetInvalidStats() {
+    public void testLinkBandwidthInvalidBytes() {
         mWifiInfo.setRssi(-70);
-        mWifiInfo.setMaxSupportedRxLinkSpeedMbps(200_000);
         mWifiScoreCard.noteConnectionAttempt(mWifiInfo, -53, mWifiInfo.getSSID());
         PerNetwork perNetwork = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
         mWifiScoreCard.noteIpConfiguration(mWifiInfo);
@@ -1858,16 +1859,8 @@
             millisecondsPass(3_000);
             perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
         }
-        assertEquals(128_000_000, perNetwork.getRxLinkBandwidthKbps());
-        // Reduce max supported Rx link speed so that stats in the memory become invalid
-        // and fall back to cold start values
-        mWifiInfo.setMaxSupportedRxLinkSpeedMbps(100);
-        for (int i = 0; i < BANDWIDTH_STATS_COUNT_THR + 2; i++) {
-            addTotalBytes(txBytes, rxBytes);
-            millisecondsPass(3_000);
-            perNetwork.updateLinkBandwidth(mOldLlStats, mNewLlStats, mWifiInfo);
-        }
-        assertEquals(10_070, perNetwork.getRxLinkBandwidthKbps());
+        assertEquals(10_000, perNetwork.getTxLinkBandwidthKbps());
+        assertEquals(10_000, perNetwork.getRxLinkBandwidthKbps());
     }
 
     @Test
diff --git a/service/tests/wifitests/src/com/android/server/wifi/coex/CoexManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/coex/CoexManagerTest.java
index fbddee8..18ee415 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/coex/CoexManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/coex/CoexManagerTest.java
@@ -867,4 +867,34 @@
         // The remote callback has an extra call since it was notified on registration.
         verify(remoteCallback, times(4)).onCoexUnsafeChannelsChanged(any(), anyInt());
     }
+
+    /**
+     * Verify that multiple calls to setCoexUnsafeChannels with the same unsafe channels will only
+     * update the driver the first time.
+     */
+    @Test
+    public void testSetCoexUnsafeChannels_multipleSameChannels_updatesDriverOnce() {
+        CoexManager coexManager = createCoexManager();
+
+        coexManager.setCoexUnsafeChannels(new ArrayList<>(), 0);
+        coexManager.setCoexUnsafeChannels(new ArrayList<>(), 0);
+        coexManager.setCoexUnsafeChannels(new ArrayList<>(), 0);
+        // Default state after initialization is no channels and no restrictions, so no update to
+        // the driver is necessary
+        verify(mMockWifiNative, times(0)).setCoexUnsafeChannels(any(), anyInt());
+
+        coexManager.setCoexUnsafeChannels(
+                Arrays.asList(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6)), COEX_RESTRICTION_SOFTAP);
+        coexManager.setCoexUnsafeChannels(
+                Arrays.asList(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6)), COEX_RESTRICTION_SOFTAP);
+        coexManager.setCoexUnsafeChannels(
+                Arrays.asList(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6)), COEX_RESTRICTION_SOFTAP);
+        // Driver should be updated only once for the same unsafe channels
+        verify(mMockWifiNative, times(1)).setCoexUnsafeChannels(any(), anyInt());
+
+        // Change in restrictions with same unsafe channels should trigger an update
+        coexManager.setCoexUnsafeChannels(
+                Arrays.asList(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6)), 0);
+        verify(mMockWifiNative, times(2)).setCoexUnsafeChannels(any(), anyInt());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
index 69413ab..20eeea8 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java
@@ -3285,25 +3285,6 @@
     }
 
     /**
-     * Verify WifiP2pManager.SET_WFD_INFO_FAILED is returned when p2p is disabled.
-     */
-    @Test
-    public void testSetWfdInfoFailureWhenP2pDisabled() throws Exception {
-        mTestThisDevice.wfdInfo = new WifiP2pWfdInfo();
-        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
-        when(mWifiPermissionsWrapper.getUidPermission(anyString(), anyInt()))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-        sendSetWfdInfoMsg(mClientMessenger, mTestThisDevice.wfdInfo);
-        verify(mWifiInjector).getWifiPermissionsWrapper();
-        verify(mWifiPermissionsWrapper).getUidPermission(
-                eq(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY), anyInt());
-        verify(mClientHandler).sendMessage(mMessageCaptor.capture());
-        Message message = mMessageCaptor.getValue();
-        assertEquals(WifiP2pManager.SET_WFD_INFO_FAILED, message.what);
-        assertEquals(WifiP2pManager.BUSY, message.arg1);
-    }
-
-    /**
      * Verify WifiP2pManager.SET_WFD_INFO_FAILED is returned when wfd permission denied
      * and p2p is disabled.
      */
@@ -4529,4 +4510,109 @@
         verify(mWifiNative).teardownInterface();
         verify(mWifiMonitor).stopMonitoring(anyString());
     }
+
+    /**
+     * Verify the WFD info is set again on going back to P2pEnabledState
+     * for the IdleShutdown case.
+     */
+    @Test
+    public void testWfdInfoIsSetAtP2pEnabledStateForIdleShutdown() throws Exception {
+        // Move to enabled state
+        forceP2pEnabled(mClient1);
+        mTestThisDevice.status = mTestThisDevice.AVAILABLE;
+
+        mTestThisDevice.wfdInfo = new WifiP2pWfdInfo();
+        mTestThisDevice.wfdInfo.setEnabled(true);
+        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+        when(mWifiPermissionsWrapper.getUidPermission(anyString(), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mWifiNative.setWfdEnable(anyBoolean())).thenReturn(true);
+        when(mWifiNative.setWfdDeviceInfo(anyString())).thenReturn(true);
+        sendSetWfdInfoMsg(mClientMessenger, mTestThisDevice.wfdInfo);
+
+        // P2P is off due to IDLE and data should be kept for the resume.
+        mWifiP2pServiceImpl.mP2pIdleShutdownMessage.onAlarm();
+        mLooper.dispatchAll();
+        verify(mWifiNative).teardownInterface();
+        verify(mWifiMonitor).stopMonitoring(anyString());
+        sendSimpleMsg(null, WifiP2pMonitor.SUP_DISCONNECTION_EVENT);
+
+        reset(mWifiNative);
+        when(mWifiNative.setupInterface(any(), any(), any())).thenReturn(IFACE_NAME_P2P);
+        when(mWifiNative.setWfdEnable(anyBoolean())).thenReturn(true);
+        when(mWifiNative.setWfdDeviceInfo(anyString())).thenReturn(true);
+
+        // send a command to resume P2P
+        sendSimpleMsg(mClientMessenger, WifiP2pManager.REQUEST_P2P_STATE);
+
+        // Restore data for resuming from idle shutdown.
+        verify(mWifiNative).setWfdEnable(eq(true));
+        verify(mWifiNative).setWfdDeviceInfo(eq(mTestThisDevice.wfdInfo.getDeviceInfoHex()));
+    }
+
+    /**
+     * Verify the WFD info is set again on going back to P2pEnabledState
+     * for the normal shutdown case.
+     */
+    @Test
+    public void testWfdInfoIsSetAtP2pEnabledStateForNormalShutdown() throws Exception {
+        // Move to enabled state
+        forceP2pEnabled(mClient1);
+        mTestThisDevice.status = mTestThisDevice.AVAILABLE;
+
+        mTestThisDevice.wfdInfo = new WifiP2pWfdInfo();
+        mTestThisDevice.wfdInfo.setEnabled(true);
+        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+        when(mWifiPermissionsWrapper.getUidPermission(anyString(), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mWifiNative.setWfdEnable(anyBoolean())).thenReturn(true);
+        when(mWifiNative.setWfdDeviceInfo(anyString())).thenReturn(true);
+        sendSetWfdInfoMsg(mClientMessenger, mTestThisDevice.wfdInfo);
+
+        // P2P is really disabled when wifi is off.
+        simulateWifiStateChange(false);
+        mLooper.dispatchAll();
+        verify(mWifiNative).teardownInterface();
+        verify(mWifiMonitor).stopMonitoring(anyString());
+
+        reset(mWifiNative);
+        when(mWifiNative.setupInterface(any(), any(), any())).thenReturn(IFACE_NAME_P2P);
+        when(mWifiNative.setWfdEnable(anyBoolean())).thenReturn(true);
+        when(mWifiNative.setWfdDeviceInfo(anyString())).thenReturn(true);
+
+        // send a command to resume P2P
+        sendSimpleMsg(mClientMessenger, WifiP2pManager.REQUEST_P2P_STATE);
+        mLooper.dispatchAll();
+
+        // In normal case, wfd info is cleared.
+        verify(mWifiNative, never()).setWfdEnable(anyBoolean());
+        verify(mWifiNative, never()).setWfdDeviceInfo(anyString());
+    }
+
+    /**
+     * Verify the WFD info is set if WFD info is set at P2pDisabledState.
+     */
+    @Test
+    public void testWfdInfoIsSetAtP2pEnabledWithPreSetWfdInfo() throws Exception {
+        mTestThisDevice.wfdInfo = new WifiP2pWfdInfo();
+        mTestThisDevice.wfdInfo.setEnabled(true);
+        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+        when(mWifiPermissionsWrapper.getUidPermission(anyString(), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mWifiNative.setWfdEnable(anyBoolean())).thenReturn(true);
+        when(mWifiNative.setWfdDeviceInfo(anyString())).thenReturn(true);
+        sendSetWfdInfoMsg(mClientMessenger, mTestThisDevice.wfdInfo);
+
+        // At disabled state, WFD info is stored in the service, but not set actually.
+        verify(mWifiNative, never()).setWfdEnable(anyBoolean());
+        verify(mWifiNative, never()).setWfdDeviceInfo(any());
+
+        // Move to enabled state
+        forceP2pEnabled(mClient1);
+        mTestThisDevice.status = mTestThisDevice.AVAILABLE;
+
+        // Restore data for resuming from idle shutdown.
+        verify(mWifiNative).setWfdEnable(eq(true));
+        verify(mWifiNative).setWfdDeviceInfo(eq(mTestThisDevice.wfdInfo.getDeviceInfoHex()));
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
index 942adbc..43169f1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
@@ -1836,5 +1836,22 @@
                       2412, 72000000, true, true, true, false));
     }
 
+    /**
+     * Verify that the country code is parsed correctly from country IE
+     */
+    @Test
+    public void getCountryCodeWithCountryIE() throws Exception {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        /** Country IE format (size unit: byte)
+         *
+         * |ElementID | Length | country string | triplet | padding
+         *      1          1          3            Q*x       0 or 1
+         */
+        ie.bytes = new byte[]{(byte) 0x75, (byte) 0x73, (byte) 0x49};
+        InformationElementUtil.Country country = new InformationElementUtil.Country();
+        country.from(ie);
+        assertEquals("US", country.getCountryCode());
+    }
     // TODO: SAE, OWN, SUITE_B
 }