Fix obsolete comment for checkAndStartWifi() am: b0e755e4e6

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Wifi/+/2971286

Change-Id: I49fa69b4026f8e3caadf13e5b36620f0d7b67d68
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OsuLogin/res/values-sq/strings.xml b/OsuLogin/res/values-sq/strings.xml
index a73a660..1bc96a5 100644
--- a/OsuLogin/res/values-sq/strings.xml
+++ b/OsuLogin/res/values-sq/strings.xml
@@ -2,6 +2,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="8761601515208336125">"OsuLogin"</string>
-    <string name="action_bar_label" msgid="5057300678161244769">"Regjistrimi në linjë"</string>
+    <string name="action_bar_label" msgid="5057300678161244769">"Regjistrimi online"</string>
     <string name="sign_up_failed" msgid="7134357507175557124">"Regjistrimi dështoi"</string>
 </resources>
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 15c8261..28459f4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,7 +1,7 @@
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-metrics_pdd_hook = ${REPO_ROOT}/packages/modules/Wifi/metrics_pdd_hook.py "service/proto/src/metrics.proto" ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
-resource_overlayable_hook = ${REPO_ROOT}/packages/modules/Wifi/overlayable_hook.py ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
+wifi_upload_hook = ${REPO_ROOT}/packages/modules/Wifi/wifi_upload_hook.py ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
+wifi_checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} --config_xml ${REPO_ROOT}/packages/modules/Wifi/wifi-preupload-checks.xml
 
 hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c983f5c..0e93c9d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -23,6 +23,9 @@
       "options": [
         {
           "exclude-annotation": "android.net.wifi.cts.VirtualDeviceNotSupported"
+        },
+        {
+          "exclude-annotation": "com.android.compatibility.common.util.NonMainlineTest"
         }
       ]
     }
diff --git a/WIFI_OWNERS b/WIFI_OWNERS
index 874d898..5ead12e 100644
--- a/WIFI_OWNERS
+++ b/WIFI_OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 33618
+
 etancohen@google.com
 arabawy@google.com
 satk@google.com
diff --git a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
index 34fc8e3..7bbfed3 100644
--- a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
+++ b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java
@@ -35,7 +35,6 @@
 import android.os.CountDownTimer;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.Vibrator;
 import android.text.Editable;
@@ -144,26 +143,24 @@
         return mWifiManager;
     }
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction() != Intent.ACTION_CLOSE_SYSTEM_DIALOGS) {
-                return;
-            }
-            PowerManager powerManager = context.getSystemService(PowerManager.class);
-            if (powerManager == null) {
-                return;
-            }
-            if (!powerManager.isInteractive()) {
-                // Ignore screen off case.
-                return;
-            }
-            // Cancel all dialogs for ACTION_CLOSE_SYSTEM_DIALOGS (e.g. Home button pressed).
-            for (int i = 0; i < mActiveDialogsPerId.size(); i++) {
-                mActiveDialogsPerId.get(i).cancel();
-            }
-        }
-    };
+    private final BroadcastReceiver mBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (!Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                        return;
+                    }
+                    if (intent.getBooleanExtra(
+                            WifiManager.EXTRA_CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI, false)) {
+                        return;
+                    }
+                    // Cancel all dialogs for ACTION_CLOSE_SYSTEM_DIALOGS (e.g. Home button
+                    // pressed).
+                    for (int i = 0; i < mActiveDialogsPerId.size(); i++) {
+                        mActiveDialogsPerId.valueAt(i).cancel();
+                    }
+                }
+            };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -254,7 +251,7 @@
                 // Before U, we don't have INTERNAL_SYSTEM_WINDOW permission to always show at the
                 // top, so close all dialogs when we're not visible anymore.
                 for (int i = 0; i < mActiveDialogsPerId.size(); i++) {
-                    mActiveDialogsPerId.get(i).cancel();
+                    mActiveDialogsPerId.valueAt(i).cancel();
                 }
             }
             return;
@@ -283,7 +280,7 @@
         // We don't expect to be destroyed while dialogs are still up, but make sure to cancel them
         // just in case.
         for (int i = 0; i < mActiveDialogsPerId.size(); i++) {
-            mActiveDialogsPerId.get(i).cancel();
+            mActiveDialogsPerId.valueAt(i).cancel();
         }
     }
 
@@ -406,9 +403,12 @@
                             // Round up to the nearest whole second.
                             secondsRemaining++;
                         }
-                        dialog.setMessage(MessageFormat.format(
+                        TextView timeRemaining = dialog.getWindow().findViewById(
+                                getWifiViewId("time_remaining"));
+                        timeRemaining.setText(MessageFormat.format(
                                 getWifiString("wifi_p2p_invitation_seconds_remaining"),
                                 secondsRemaining));
+                        timeRemaining.setVisibility(View.VISIBLE);
                     }
                 }
 
@@ -586,15 +586,15 @@
             @Nullable final String deviceName,
             final boolean isPinRequested,
             @Nullable final String displayPin) {
-        final View textEntryView = getWifiLayoutInflater()
-                .inflate(getWifiLayoutId("wifi_p2p_dialog"), null);
-        ViewGroup group = textEntryView.findViewById(getWifiViewId("info"));
         if (TextUtils.isEmpty(deviceName)) {
             Log.w(TAG, "P2P Invitation Received dialog device name is null or empty."
                     + " id=" + dialogId
                     + " deviceName=" + deviceName
                     + " displayPin=" + displayPin);
         }
+        final View textEntryView = getWifiLayoutInflater()
+                .inflate(getWifiLayoutId("wifi_p2p_dialog"), null);
+        ViewGroup group = textEntryView.findViewById(getWifiViewId("info"));
         addRowToP2pDialog(group, getWifiString("wifi_p2p_from_message"), deviceName);
 
         final EditText pinEditText;
@@ -612,8 +612,6 @@
 
         AlertDialog dialog = getWifiAlertDialogBuilder("wifi_p2p_invitation_received_dialog")
                 .setTitle(getWifiString("wifi_p2p_invitation_to_connect_title"))
-                // Set the message to "" to allow us to modify it after building (b/36913966).
-                .setMessage("")
                 .setView(textEntryView)
                 .setPositiveButton(getWifiString("accept"), (dialogPositive, which) -> {
                     String pin = null;
@@ -663,7 +661,8 @@
                     pinEditText.requestFocus();
                     pinEditText.setSelection(pinEditText.getText().length());
                 }
-                dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
+                dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(
+                        pinEditText.length() == 4 || pinEditText.length() == 8);
             });
             pinEditText.addTextChangedListener(new TextWatcher() {
                 @Override
@@ -684,11 +683,8 @@
                         // configuration change.
                         intent.putExtra(EXTRA_DIALOG_P2P_PIN_INPUT, s);
                     }
-                    if (s.length() == 4 || s.length() == 8) {
-                        dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(true);
-                    } else {
-                        dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false);
-                    }
+                    dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(
+                            s.length() == 4 || s.length() == 8);
                 }
             });
         } else {
diff --git a/flags/Android.bp b/flags/Android.bp
new file mode 100644
index 0000000..ba7141d
--- /dev/null
+++ b/flags/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+    name: "wifi_aconfig_flags",
+    package: "com.android.wifi.flags",
+    srcs: ["wifi_flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "wifi_aconfig_flags_lib",
+    aconfig_declarations: "wifi_aconfig_flags",
+    min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.wifi",
+    ],
+    visibility: [
+        "//frameworks/opt/net/wifi/libs/WifiTrackerLib:__subpackages__",
+        "//packages/modules/Wifi:__subpackages__",
+        "//cts/tests/tests/wifi:__subpackages__",
+    ],
+}
\ No newline at end of file
diff --git a/flags/wifi_flags.aconfig b/flags/wifi_flags.aconfig
new file mode 100644
index 0000000..a35702c
--- /dev/null
+++ b/flags/wifi_flags.aconfig
@@ -0,0 +1,114 @@
+package: "com.android.wifi.flags"
+
+flag {
+    name: "test_flag_function"
+    namespace: "wifi"
+    description: "This flag aim to test the flag release for wifi stack"
+    bug: "301145404"
+}
+
+flag {
+    name: "runtime_disable_pno_scan"
+    namespace: "wifi"
+    description: "Control the API to disable the PNO runtime"
+    bug: "282821585"
+}
+
+
+flag {
+    name: "delay_save_to_store"
+    namespace: "wifi"
+    description: "Control the feature delay the save to store in batch to reduce the blocking time"
+    bug: "302172794"
+}
+
+flag {
+    name: "single_wifi_thread"
+    namespace: "wifi"
+    description: "Control the feature that move all Wifi service to a single thread"
+    bug: "302593617"
+}
+
+flag {
+    name: "vendor_parcelable_parameters"
+    namespace: "wifi"
+    description: "Control the APIs that allow additional vendor-specific parcelables"
+    bug: "296069900"
+}
+
+flag {
+    name: "verbose_logging_for_aware_only"
+    namespace: "wifi"
+    description: "Control the API that allow only enable verbose logging for Wifi Aware"
+    bug: "289273616"
+}
+
+flag {
+    name: "add_channel_stats_per_link"
+    namespace: "wifi"
+    description: "Control the API that get the channel stats per link from WifiUsabilityStatsEntry"
+    bug: "276385220"
+}
+
+flag {
+    name: "mlo_link_capabilities_info"
+    namespace: "wifi"
+    description: "Control the API that get the MLO link capabilities"
+    bug: "262643386"
+}
+
+flag {
+    name: "disable_reason_unwanted_low_rssi"
+    namespace: "wifi"
+    description: "Control the API that as disable because of unwanted network under sufficient rssi"
+    bug: "262643386"
+}
+
+flag {
+    name: "low_latency_lock_listener"
+    namespace: "wifi"
+    description: "Control the API that add/remove the low latency lock listener"
+    bug: "277556184"
+}
+
+flag {
+    name: "network_provider_battery_charging_status"
+    namespace: "wifi"
+    description: "Control the API that allows setting / reading the NetworkProviderInfo's battery charging status"
+    bug: "305067231"
+}
+
+flag {
+    name: "shared_connectivity_broadcast_receiver_test_api"
+    namespace: "wifi"
+    description: "Control the test API for SharedConnectivityManager's getBroadcastReceiver() method"
+    bug: "305067231"
+}
+
+flag {
+    name: "anqp_request_wait_for_response"
+    namespace: "wifi"
+    description: "Make the ANQP request in a queue and wait for the previous response before trigger the next request"
+    bug: "310037751"
+}
+
+flag {
+    name: "wep_usage"
+    namespace: "wifi"
+    description: "Control the API for wep usage"
+    bug: "275367422"
+}
+
+flag {
+    name: "wpa_personal_usage"
+    namespace: "wifi"
+    description: "Control the API for wpa personal usage"
+    bug: "275367422"
+}
+
+flag {
+    name: "add_subscription_id"
+    namespace: "wifi"
+    description: "Add new API to set the subscription id in the Wifi Info"
+    bug: "236669534"
+}
diff --git a/framework/Android.bp b/framework/Android.bp
index 096cfae..68e3323 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -134,6 +134,7 @@
     libs: [
         "framework-annotations-lib",
         "framework-connectivity.stubs.module_lib",
+        "framework-location.stubs.module_lib",
     ],
     installable: false,
     visibility: [
@@ -153,17 +154,27 @@
     ],
 
     impl_only_libs: [
+        "framework-location.stubs.module_lib",
         "framework-connectivity.stubs.module_lib",
     ],
 
     public: {
-        libs: ["framework-connectivity.stubs"],
+        libs: [
+            "framework-location.stubs",
+            "framework-connectivity.stubs",
+        ],
     },
     system: {
-        libs: ["framework-connectivity.stubs.system"],
+        libs: [
+            "framework-location.stubs.system",
+            "framework-connectivity.stubs.system",
+        ],
     },
     module_lib: {
-        libs: ["framework-connectivity.stubs.module_lib"],
+        libs: [
+            "framework-location.stubs.module_lib",
+            "framework-connectivity.stubs.module_lib",
+        ],
     },
     api_srcs: [
         ":wifi_javadoc_only_files",
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 31d563a..40d2765 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -427,7 +427,7 @@
     method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
     method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
     method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
-    method @FlaggedApi("com.android.net.wifi.add_subscription_id") @NonNull public android.net.wifi.WifiInfo.Builder setSubscriptionId(int);
+    method @FlaggedApi("com.android.wifi.flags.add_subscription_id") @NonNull public android.net.wifi.WifiInfo.Builder setSubscriptionId(int);
   }
 
   public class WifiManager {
@@ -499,6 +499,7 @@
     method public boolean isTlsV13Supported();
     method public boolean isTrustOnFirstUseSupported();
     method public boolean isWapiSupported();
+    method @FlaggedApi("com.android.wifi.flags.wep_usage") public boolean isWepSupported();
     method public boolean isWifiDisplayR2Supported();
     method public boolean isWifiEnabled();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiPasspointEnabled();
@@ -507,6 +508,7 @@
     method public boolean isWpa3SaePublicKeySupported();
     method public boolean isWpa3SaeSupported();
     method public boolean isWpa3SuiteBSupported();
+    method @FlaggedApi("com.android.wifi.flags.wpa_personal_usage") public boolean isWpaPersonalSupported();
     method @Deprecated public boolean pingSupplicant();
     method public void queryAutojoinGlobal(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated public boolean reassociate();
diff --git a/framework/api/lint-baseline.txt b/framework/api/lint-baseline.txt
index 7ab3f6c..f448242 100644
--- a/framework/api/lint-baseline.txt
+++ b/framework/api/lint-baseline.txt
@@ -1,4 +1,10 @@
 // Baseline format: 1.0
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_BSSID:
+    Field WifiManager.EXTRA_BSSID: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_WIFI_INFO:
+    Field WifiManager.EXTRA_WIFI_INFO: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
 GenericException: android.net.wifi.WifiManager.LocalOnlyHotspotReservation#finalize():
     Methods must not throw generic exceptions (`java.lang.Throwable`)
 GenericException: android.net.wifi.WifiManager.MulticastLock#finalize():
@@ -7,5 +13,73 @@
     Methods must not throw generic exceptions (`java.lang.Throwable`)
 
 
+RequiresPermission: android.net.wifi.WifiConfiguration#setMacRandomizationSetting(int):
+    Method 'setMacRandomizationSetting' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiInfo#getMacAddress():
+    Method 'getMacAddress' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addLocalOnlyConnectionFailureListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyConnectionFailureListener):
+    Method 'addLocalOnlyConnectionFailureListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addSuggestionConnectionStatusListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.SuggestionConnectionStatusListener):
+    Method 'addSuggestionConnectionStatusListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#allowAutojoinGlobal(boolean):
+    Method 'allowAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getChannelData(java.util.concurrent.Executor, java.util.function.Consumer<java.util.List<android.os.Bundle>>):
+    Method 'getChannelData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPasspointConfigurations():
+    Method 'getPasspointConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getScanResults():
+    Method 'getScanResults' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#queryAutojoinGlobal(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+    Method 'queryAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerScanResultsCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.ScanResultsCallback):
+    Method 'registerScanResultsCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#removePasspointConfiguration(String):
+    Method 'removePasspointConfiguration' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#setWifiEnabled(boolean):
+    Method 'setWifiEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler):
+    Method 'startLocalOnlyHotspot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startScan():
+    Method 'startScan' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiNetworkSuggestion.Builder#setSubscriptionId(int):
+    Method 'setSubscriptionId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.aware.IdentityChangedListener#onIdentityChanged(byte[]):
+    Method 'onIdentityChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#attach(android.net.wifi.aware.AttachCallback, android.net.wifi.aware.IdentityChangedListener, android.os.Handler):
+    Method 'attach' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'publish' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'subscribe' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'addLocalService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'connect' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSocialChannels(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSocialChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSpecificFrequency(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSpecificFrequency' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverServices' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#getListenState(android.net.wifi.p2p.WifiP2pManager.Channel, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+    Method 'getListenState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestDeviceInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener):
+    Method 'requestDeviceInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener):
+    Method 'requestGroupInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener):
+    Method 'requestPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#startListening(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'startListening' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+    Method 'startRanging' documentation mentions permissions already declared by @RequiresPermission
+
+
 VisiblySynchronized: android.net.wifi.WifiManager.WifiLock#finalize():
     Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.net.wifi.WifiManager.WifiLock.finalize()
diff --git a/framework/api/module-lib-lint-baseline.txt b/framework/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..24552e0
--- /dev/null
+++ b/framework/api/module-lib-lint-baseline.txt
@@ -0,0 +1,145 @@
+// Baseline format: 1.0
+DeprecationMismatch: android.net.wifi.RttManager:
+    Class android.net.wifi.RttManager: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ParcelableRttParams:
+    Class android.net.wifi.RttManager.ParcelableRttParams: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ParcelableRttResults:
+    Class android.net.wifi.RttManager.ParcelableRttResults: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ResponderCallback:
+    Class android.net.wifi.RttManager.ResponderCallback: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ResponderConfig:
+    Class android.net.wifi.RttManager.ResponderConfig: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttCapabilities:
+    Class android.net.wifi.RttManager.RttCapabilities: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttListener:
+    Class android.net.wifi.RttManager.RttListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttParams:
+    Class android.net.wifi.RttManager.RttParams: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttResult:
+    Class android.net.wifi.RttManager.RttResult: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.WifiInformationElement:
+    Class android.net.wifi.RttManager.WifiInformationElement: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_BSSID:
+    Field WifiManager.EXTRA_BSSID: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_WIFI_INFO:
+    Field WifiManager.EXTRA_WIFI_INFO: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings):
+    Method android.net.wifi.WifiScanner.configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]):
+    Method android.net.wifi.WifiScanner.configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener):
+    Method android.net.wifi.WifiScanner.startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener):
+    Method android.net.wifi.WifiScanner.startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener):
+    Method android.net.wifi.WifiScanner.stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener):
+    Method android.net.wifi.WifiScanner.stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.BssidInfo:
+    Class android.net.wifi.WifiScanner.BssidInfo: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.BssidListener:
+    Class android.net.wifi.WifiScanner.BssidListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.HotlistSettings:
+    Class android.net.wifi.WifiScanner.HotlistSettings: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.WifiChangeListener:
+    Class android.net.wifi.WifiScanner.WifiChangeListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.WifiChangeSettings:
+    Class android.net.wifi.WifiScanner.WifiChangeSettings: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
+RequiresPermission: android.net.wifi.WifiConfiguration#setMacRandomizationSetting(int):
+    Method 'setMacRandomizationSetting' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiConfiguration#setRepeaterEnabled(boolean):
+    Method 'setRepeaterEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiInfo#getMacAddress():
+    Method 'getMacAddress' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addLocalOnlyConnectionFailureListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyConnectionFailureListener):
+    Method 'addLocalOnlyConnectionFailureListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addSuggestionConnectionStatusListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.SuggestionConnectionStatusListener):
+    Method 'addSuggestionConnectionStatusListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addWifiLowLatencyLockListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.WifiLowLatencyLockListener):
+    Method 'addWifiLowLatencyLockListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addWifiVerboseLoggingStatusChangedListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener):
+    Method 'addWifiVerboseLoggingStatusChangedListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#allowAutojoinGlobal(boolean):
+    Method 'allowAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getChannelData(java.util.concurrent.Executor, java.util.function.Consumer<java.util.List<android.os.Bundle>>):
+    Method 'getChannelData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPasspointConfigurations():
+    Method 'getPasspointConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPrivilegedConfiguredNetworks():
+    Method 'getPrivilegedConfiguredNetworks' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPrivilegedConnectedNetwork():
+    Method 'getPrivilegedConnectedNetwork' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getScanResults():
+    Method 'getScanResults' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#queryAutojoinGlobal(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+    Method 'queryAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerNetworkRequestMatchCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.NetworkRequestMatchCallback):
+    Method 'registerNetworkRequestMatchCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerScanResultsCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.ScanResultsCallback):
+    Method 'registerScanResultsCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerSoftApCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.SoftApCallback):
+    Method 'registerSoftApCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerTrafficStateCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.TrafficStateCallback):
+    Method 'registerTrafficStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#removePasspointConfiguration(String):
+    Method 'removePasspointConfiguration' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#removeWifiVerboseLoggingStatusChangedListener(android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener):
+    Method 'removeWifiVerboseLoggingStatusChangedListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#setWifiEnabled(boolean):
+    Method 'setWifiEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.SoftApConfiguration, java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyHotspotCallback):
+    Method 'startLocalOnlyHotspot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler):
+    Method 'startLocalOnlyHotspot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startScan():
+    Method 'startScan' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback):
+    Method 'unregisterNetworkRequestMatchCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiNetworkSuggestion.Builder#setSubscriptionId(int):
+    Method 'setSubscriptionId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiScanner#registerScanListener(java.util.concurrent.Executor, android.net.wifi.WifiScanner.ScanListener):
+    Method 'registerScanListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.IdentityChangedListener#onIdentityChanged(byte[]):
+    Method 'onIdentityChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#attach(android.net.wifi.aware.AttachCallback, android.net.wifi.aware.IdentityChangedListener, android.os.Handler):
+    Method 'attach' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#enableInstantCommunicationMode(boolean):
+    Method 'enableInstantCommunicationMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'publish' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'subscribe' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'addLocalService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'connect' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSocialChannels(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSocialChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSpecificFrequency(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSpecificFrequency' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverServices' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#getListenState(android.net.wifi.p2p.WifiP2pManager.Channel, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+    Method 'getListenState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestDeviceInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener):
+    Method 'requestDeviceInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener):
+    Method 'requestGroupInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener):
+    Method 'requestPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestPersistentGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener):
+    Method 'requestPersistentGroupInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#startListening(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'startListening' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+    Method 'startRanging' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.rtt.WifiRttManager#startRanging(android.os.WorkSource, android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+    Method 'startRanging' documentation mentions permissions already declared by @RequiresPermission
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 3f45a2a..190a4ad 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -29,6 +29,19 @@
     field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
   }
 
+  @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public final class OuiKeyedData implements android.os.Parcelable {
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public int describeContents();
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") @NonNull public android.os.PersistableBundle getData();
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public int getOui();
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.OuiKeyedData> CREATOR;
+  }
+
+  @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public static final class OuiKeyedData.Builder {
+    ctor @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") public OuiKeyedData.Builder(int, @NonNull android.os.PersistableBundle);
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") @NonNull public android.net.wifi.OuiKeyedData build();
+  }
+
   public final class QosPolicyParams implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public java.net.InetAddress getDestinationAddress();
@@ -349,6 +362,7 @@
     method public int getMaxNumberOfClients();
     method @NonNull public android.net.MacAddress getPersistentRandomizedMacAddress();
     method public long getShutdownTimeoutMillis();
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") @NonNull public java.util.List<android.net.wifi.OuiKeyedData> getVendorData();
     method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getVendorElements();
     method public boolean isAutoShutdownEnabled();
     method public boolean isBridgedModeOpportunisticShutdownEnabled();
@@ -393,6 +407,7 @@
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0xffffffff) long);
     method @Deprecated @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
+    method @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters") @NonNull public android.net.wifi.SoftApConfiguration.Builder setVendorData(@NonNull java.util.List<android.net.wifi.OuiKeyedData>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setVendorElements(@NonNull java.util.List<android.net.wifi.ScanResult.InformationElement>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWifiSsid(@Nullable android.net.wifi.WifiSsid);
   }
@@ -515,6 +530,7 @@
     field @Deprecated public static final int DISABLED_NO_INTERNET_PERMANENT = 6; // 0x6
     field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4
     field @Deprecated public static final int DISABLED_TRANSITION_DISABLE_INDICATION = 13; // 0xd
+    field @Deprecated @FlaggedApi("com.android.wifi.flags.disable_reason_unwanted_low_rssi") public static final int DISABLED_UNWANTED_LOW_RSSI = 14; // 0xe
     field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0
     field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2
     field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1
@@ -587,6 +603,7 @@
   public class WifiManager {
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void addQosPolicies(@NonNull java.util.List<android.net.wifi.QosPolicyParams>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<java.lang.Integer>>);
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void addWifiLowLatencyLockListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiLowLatencyLockListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void addWifiNetworkStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiNetworkStateChangedListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void addWifiVerboseLoggingStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
@@ -610,6 +627,8 @@
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
+    method @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info") @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void getMaxMloAssociationLinkCount(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info") @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void getMaxMloStrLinkCount(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public static int getMaxNumberOfPoliciesPerQosRequest();
     method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void getMloMode(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void getNetworkSelectionConfig(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.net.wifi.WifiNetworkSelectionConfig>);
@@ -618,6 +637,7 @@
     method @Nullable @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}, conditional=true) public android.net.wifi.WifiConfiguration getPrivilegedConnectedNetwork();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}, conditional=true) public java.util.Set<android.net.wifi.WifiSsid> getSsidsAllowlist();
+    method @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info") @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void getSupportedSimultaneousBandCombinations(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<int[]>>);
     method public int getVerboseLoggingLevel();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
@@ -626,6 +646,7 @@
     method public boolean isApMacRandomizationSupported();
     method public boolean isConnectedMacRandomizationSupported();
     method @Deprecated public boolean isDeviceToDeviceRttSupported();
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public boolean isLowLatencyModeSupported();
     method public boolean isPortableHotspotSupported();
     method public boolean isStaConcurrencyForRestrictedConnectionsSupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean isThirdPartyAppEnablingWifiConfirmationDialogEnabled();
@@ -635,6 +656,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void notifyMinimumRequiredWifiSecurityLevelChanged(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void notifyWifiSsidPolicyChanged(@NonNull android.app.admin.WifiSsidPolicy);
     method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void queryLastConfiguredTetheredApPassphraseSinceBoot(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.String>);
+    method @FlaggedApi("com.android.wifi.flags.wep_usage") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void queryWepAllowed(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void registerActiveCountryCodeChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.ActiveCountryCodeChangedCallback);
     method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void registerCoexCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.CoexCallback);
     method @RequiresPermission(android.Manifest.permission.NEARBY_WIFI_DEVICES) public void registerLocalOnlyHotspotSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
@@ -645,6 +667,7 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void removeAppState(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void removeQosPolicies(@NonNull int[]);
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public void removeWifiLowLatencyLockListener(@NonNull android.net.wifi.WifiManager.WifiLowLatencyLockListener);
     method public void removeWifiNetworkStateChangedListener(@NonNull android.net.wifi.WifiManager.WifiNetworkStateChangedListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeWifiVerboseLoggingStatusChangedListener(@NonNull android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener);
     method @RequiresPermission(android.Manifest.permission.RESTART_WIFI_SUBSYSTEM) public void restartWifiSubsystem();
@@ -667,6 +690,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void setOneShotScreenOnConnectivityScanDelayMillis(@IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE) public void setOverrideCountryCode(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int);
+    method @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setPnoScanState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void setScreenOnScanSchedule(@Nullable java.util.List<android.net.wifi.WifiManager.ScreenOnScanSchedule>);
@@ -674,8 +698,9 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}, conditional=true) public void setSsidsAllowlist(@NonNull java.util.Set<android.net.wifi.WifiSsid>);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean setStaConcurrencyForMultiInternetMode(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setThirdPartyAppEnablingWifiConfirmationDialogEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingLevel(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.DUMP}) public void setVerboseLoggingEnabled(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.DUMP}) public void setVerboseLoggingLevel(int);
+    method @FlaggedApi("com.android.wifi.flags.wep_usage") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setWepAllowed(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setWifiPasspointEnabled(boolean);
@@ -729,6 +754,7 @@
     field public static final int API_SCANNING_ENABLED = 1; // 0x1
     field public static final int API_SET_NETWORK_SELECTION_CONFIG = 8; // 0x8
     field public static final int API_SET_ONE_SHOT_SCREEN_ON_CONNECTIVITY_SCAN_DELAY = 7; // 0x7
+    field @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan") public static final int API_SET_PNO_SCAN_ENABLED = 36; // 0x24
     field public static final int API_SET_SCAN_SCHEDULE = 6; // 0x6
     field public static final int API_SET_TDLS_ENABLED = 34; // 0x22
     field public static final int API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS = 35; // 0x23
@@ -782,6 +808,9 @@
     field public static final int MLO_MODE_LOW_POWER = 3; // 0x3
     field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
     field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
+    field @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan") public static final int PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT = 0; // 0x0
+    field @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan") public static final int PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE = 1; // 0x1
+    field @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan") public static final int PNO_SCAN_STATE_ENABLED = 2; // 0x2
     field public static final int QOS_REQUEST_STATUS_ALREADY_ACTIVE = 1; // 0x1
     field public static final int QOS_REQUEST_STATUS_FAILURE_UNKNOWN = 4; // 0x4
     field public static final int QOS_REQUEST_STATUS_INSUFFICIENT_RESOURCES = 2; // 0x2
@@ -796,6 +825,7 @@
     field public static final int VERBOSE_LOGGING_LEVEL_DISABLED = 0; // 0x0
     field public static final int VERBOSE_LOGGING_LEVEL_ENABLED = 1; // 0x1
     field public static final int VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY = 2; // 0x2
+    field @FlaggedApi("com.android.wifi.flags.verbose_logging_for_aware_only") public static final int VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY = 3; // 0x3
     field @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
     field public static final int WIFI_AP_STATE_DISABLED = 11; // 0xb
     field public static final int WIFI_AP_STATE_DISABLING = 10; // 0xa
@@ -903,6 +933,12 @@
     method public void onStop(int);
   }
 
+  @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public static interface WifiManager.WifiLowLatencyLockListener {
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public void onActivatedStateChanged(boolean);
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public default void onActiveUsersChanged(@NonNull int[]);
+    method @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener") public default void onOwnershipChanged(@NonNull int[]);
+  }
+
   public static interface WifiManager.WifiNetworkStateChangedListener {
     method public void onWifiNetworkStateChanged(int, int);
     field public static final int WIFI_NETWORK_STATUS_AUTHENTICATING = 4; // 0x4
@@ -1172,10 +1208,12 @@
     method public long getTotalBeaconRx();
     method public long getTotalBeaconRx(int);
     method public long getTotalCcaBusyFreqTimeMillis();
+    method @FlaggedApi("com.android.wifi.flags.add_channel_stats_per_link") public long getTotalCcaBusyFreqTimeMillis(int);
     method public long getTotalHotspot2ScanTimeMillis();
     method public long getTotalNanScanTimeMillis();
     method public long getTotalPnoScanTimeMillis();
     method public long getTotalRadioOnFreqTimeMillis();
+    method @FlaggedApi("com.android.wifi.flags.add_channel_stats_per_link") public long getTotalRadioOnFreqTimeMillis(int);
     method public long getTotalRadioOnTimeMillis();
     method public long getTotalRadioRxTimeMillis();
     method public long getTotalRadioTxTimeMillis();
diff --git a/framework/api/system-lint-baseline.txt b/framework/api/system-lint-baseline.txt
index 4fb6ff0..8bfe131 100644
--- a/framework/api/system-lint-baseline.txt
+++ b/framework/api/system-lint-baseline.txt
@@ -1,4 +1,52 @@
 // Baseline format: 1.0
+DeprecationMismatch: android.net.wifi.RttManager:
+    Class android.net.wifi.RttManager: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ParcelableRttParams:
+    Class android.net.wifi.RttManager.ParcelableRttParams: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ParcelableRttResults:
+    Class android.net.wifi.RttManager.ParcelableRttResults: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ResponderCallback:
+    Class android.net.wifi.RttManager.ResponderCallback: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.ResponderConfig:
+    Class android.net.wifi.RttManager.ResponderConfig: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttCapabilities:
+    Class android.net.wifi.RttManager.RttCapabilities: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttListener:
+    Class android.net.wifi.RttManager.RttListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttParams:
+    Class android.net.wifi.RttManager.RttParams: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.RttResult:
+    Class android.net.wifi.RttManager.RttResult: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.RttManager.WifiInformationElement:
+    Class android.net.wifi.RttManager.WifiInformationElement: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_BSSID:
+    Field WifiManager.EXTRA_BSSID: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_WIFI_INFO:
+    Field WifiManager.EXTRA_WIFI_INFO: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings):
+    Method android.net.wifi.WifiScanner.configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]):
+    Method android.net.wifi.WifiScanner.configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener):
+    Method android.net.wifi.WifiScanner.startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener):
+    Method android.net.wifi.WifiScanner.startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener):
+    Method android.net.wifi.WifiScanner.stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner#stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener):
+    Method android.net.wifi.WifiScanner.stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.BssidInfo:
+    Class android.net.wifi.WifiScanner.BssidInfo: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.BssidListener:
+    Class android.net.wifi.WifiScanner.BssidListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.HotlistSettings:
+    Class android.net.wifi.WifiScanner.HotlistSettings: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.WifiChangeListener:
+    Class android.net.wifi.WifiScanner.WifiChangeListener: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.net.wifi.WifiScanner.WifiChangeSettings:
+    Class android.net.wifi.WifiScanner.WifiChangeSettings: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
 MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
     
 
@@ -13,3 +61,101 @@
     Bare field lastConnectUid must be marked final, or moved behind accessors if mutable
 MutableBareField: android.net.wifi.WifiConfiguration#subscriptionId:
     Bare field subscriptionId must be marked final, or moved behind accessors if mutable
+
+
+RequiresPermission: android.net.wifi.WifiConfiguration#setMacRandomizationSetting(int):
+    Method 'setMacRandomizationSetting' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiConfiguration#setRepeaterEnabled(boolean):
+    Method 'setRepeaterEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiInfo#getMacAddress():
+    Method 'getMacAddress' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addLocalOnlyConnectionFailureListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyConnectionFailureListener):
+    Method 'addLocalOnlyConnectionFailureListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addSuggestionConnectionStatusListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.SuggestionConnectionStatusListener):
+    Method 'addSuggestionConnectionStatusListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addWifiLowLatencyLockListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.WifiLowLatencyLockListener):
+    Method 'addWifiLowLatencyLockListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#addWifiVerboseLoggingStatusChangedListener(java.util.concurrent.Executor, android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener):
+    Method 'addWifiVerboseLoggingStatusChangedListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#allowAutojoinGlobal(boolean):
+    Method 'allowAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getChannelData(java.util.concurrent.Executor, java.util.function.Consumer<java.util.List<android.os.Bundle>>):
+    Method 'getChannelData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPasspointConfigurations():
+    Method 'getPasspointConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPrivilegedConfiguredNetworks():
+    Method 'getPrivilegedConfiguredNetworks' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getPrivilegedConnectedNetwork():
+    Method 'getPrivilegedConnectedNetwork' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#getScanResults():
+    Method 'getScanResults' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#queryAutojoinGlobal(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+    Method 'queryAutojoinGlobal' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerNetworkRequestMatchCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.NetworkRequestMatchCallback):
+    Method 'registerNetworkRequestMatchCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerScanResultsCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.ScanResultsCallback):
+    Method 'registerScanResultsCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerSoftApCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.SoftApCallback):
+    Method 'registerSoftApCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#registerTrafficStateCallback(java.util.concurrent.Executor, android.net.wifi.WifiManager.TrafficStateCallback):
+    Method 'registerTrafficStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#removePasspointConfiguration(String):
+    Method 'removePasspointConfiguration' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#removeWifiVerboseLoggingStatusChangedListener(android.net.wifi.WifiManager.WifiVerboseLoggingStatusChangedListener):
+    Method 'removeWifiVerboseLoggingStatusChangedListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#setWifiEnabled(boolean):
+    Method 'setWifiEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.SoftApConfiguration, java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyHotspotCallback):
+    Method 'startLocalOnlyHotspot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler):
+    Method 'startLocalOnlyHotspot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#startScan():
+    Method 'startScan' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiManager#unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback):
+    Method 'unregisterNetworkRequestMatchCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.WifiNetworkSuggestion.Builder#setSubscriptionId(int):
+    Method 'setSubscriptionId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.WifiScanner#registerScanListener(java.util.concurrent.Executor, android.net.wifi.WifiScanner.ScanListener):
+    Method 'registerScanListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.IdentityChangedListener#onIdentityChanged(byte[]):
+    Method 'onIdentityChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#attach(android.net.wifi.aware.AttachCallback, android.net.wifi.aware.IdentityChangedListener, android.os.Handler):
+    Method 'attach' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#enableInstantCommunicationMode(boolean):
+    Method 'enableInstantCommunicationMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'publish' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+    Method 'subscribe' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#addLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'addLocalService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'connect' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'createGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSocialChannels(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSocialChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverPeersOnSpecificFrequency(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverPeersOnSpecificFrequency' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'discoverServices' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#getListenState(android.net.wifi.p2p.WifiP2pManager.Channel, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+    Method 'getListenState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestDeviceInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener):
+    Method 'requestDeviceInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener):
+    Method 'requestGroupInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener):
+    Method 'requestPeers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#requestPersistentGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener):
+    Method 'requestPersistentGroupInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.p2p.WifiP2pManager#startListening(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
+    Method 'startListening' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+    Method 'startRanging' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.wifi.rtt.WifiRttManager#startRanging(android.os.WorkSource, android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+    Method 'startRanging' documentation mentions permissions already declared by @RequiresPermission
diff --git a/framework/java/android/net/wifi/BaseWifiService.java b/framework/java/android/net/wifi/BaseWifiService.java
index fa3c7b9..7dc7cac 100644
--- a/framework/java/android/net/wifi/BaseWifiService.java
+++ b/framework/java/android/net/wifi/BaseWifiService.java
@@ -383,17 +383,39 @@
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    /**
+     * Following method is deprecated with
+     * {@link BaseWifiService#acquireWifiLock(IBinder, int, String, WorkSource, String, Bundle)}
+     * @deprecated This is no longer supported.
+     */
+    @Deprecated
     public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws,
+            String packageName, Bundle extras) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Following method is deprecated with
+     * {@link BaseWifiService#updateWifiLockWorkSource(IBinder, WorkSource, String, Bundle)}
+     * @deprecated This is no longer supported.
+     */
+    @Deprecated
     public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws, String packageName,
+            Bundle extras) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean releaseWifiLock(IBinder lock) {
         throw new UnsupportedOperationException();
     }
@@ -812,6 +834,12 @@
     }
 
     @Override
+    public void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle,
+            String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
             List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
@@ -1065,4 +1093,14 @@
             Bundle extras) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void setWepAllowed(boolean isAllowed) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void queryWepAllowed(@NonNull IBooleanListener listener) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/framework/java/android/net/wifi/IWifiManager.aidl b/framework/java/android/net/wifi/IWifiManager.aidl
index 73baebb..6e62c22 100644
--- a/framework/java/android/net/wifi/IWifiManager.aidl
+++ b/framework/java/android/net/wifi/IWifiManager.aidl
@@ -198,9 +198,9 @@
 
     boolean isScanAlwaysAvailable();
 
-    boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
+    boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws, in String packageName, in Bundle extras);
 
-    void updateWifiLockWorkSource(IBinder lock, in WorkSource ws);
+    void updateWifiLockWorkSource(IBinder lock, in WorkSource ws, in String packageName, in Bundle extras);
 
     boolean releaseWifiLock(IBinder lock);
 
@@ -370,6 +370,8 @@
 
     void setExternalPnoScanRequest(in IBinder binder, in IPnoScanResultsCallback callback, in List<WifiSsid> ssids, in int[] frequencies, String packageName, String featureId);
 
+    void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle, String packageName);
+
     void clearExternalPnoScanRequest();
 
     void getLastCallerInfoForApi(int api, in ILastCallerListener listener);
@@ -466,4 +468,8 @@
     void getMaxMloStrLinkCount(in IIntegerListener listener, in Bundle extras);
 
     void getSupportedSimultaneousBandCombinations(in IWifiBandsListener listener, in Bundle extras);
+
+    void setWepAllowed(boolean isAllowed);
+
+    void queryWepAllowed(in IBooleanListener listener);
 }
diff --git a/framework/java/android/net/wifi/MloLink.java b/framework/java/android/net/wifi/MloLink.java
index 0173d80..a4ca763 100644
--- a/framework/java/android/net/wifi/MloLink.java
+++ b/framework/java/android/net/wifi/MloLink.java
@@ -141,6 +141,14 @@
         mRssi = source.mRssi;
         mRxLinkSpeed = source.mRxLinkSpeed;
         mTxLinkSpeed = source.mTxLinkSpeed;
+        mLostTxPacketsPerSecond = source.mLostTxPacketsPerSecond;
+        mTxRetriedTxPacketsPerSecond = source.mTxRetriedTxPacketsPerSecond;
+        mSuccessfulRxPacketsPerSecond = source.mSuccessfulRxPacketsPerSecond;
+        mSuccessfulTxPacketsPerSecond = source.mSuccessfulTxPacketsPerSecond;
+        txBad = source.txBad;
+        txRetries = source.txRetries;
+        txSuccess = source.txSuccess;
+        rxSuccess = source.rxSuccess;
 
         mStaMacAddress = ((redactions & NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS) != 0)
                 || source.mStaMacAddress == null
@@ -151,13 +159,9 @@
                 ? null : MacAddress.fromString(source.mApMacAddress.toString());
     }
 
-    /** Returns the Wi-Fi band of this link as one of:
-     *      {@link WifiScanner#WIFI_BAND_UNSPECIFIED},
-     *      {@link WifiScanner#WIFI_BAND_24_GHZ},
-     *      {@link WifiScanner#WIFI_BAND_5_GHZ},
-     *      {@link WifiScanner#WIFI_BAND_6_GHZ}
-     */
-    public @WifiAnnotations.WifiBandBasic int getBand() {
+    /** Returns the Wi-Fi band of this link. */
+    @ScanResult.WifiBand
+    public int getBand() {
         return mBand;
     }
 
@@ -328,6 +332,101 @@
         return mRssi;
     }
 
+    /**
+     * Running total count of lost (not ACKed) transmitted unicast data packets.
+     *
+     * @hide
+     */
+    public long txBad;
+    /**
+     * Running total count of transmitted unicast data retry packets.
+     *
+     * @hide
+     */
+    public long txRetries;
+    /**
+     * Running total count of successfully transmitted (ACKed) unicast data packets.
+     *
+     * @hide
+     */
+    public long txSuccess;
+    /**
+     * Running total count of received unicast data packets.
+     *
+     * @hide
+     */
+    public long rxSuccess;
+    /**
+     * Time stamp when packet counts are last updated.
+     *
+     * @hide
+     */
+    public long lastPacketCountUpdateTimeStamp = Long.MIN_VALUE;
+
+    private double mLostTxPacketsPerSecond;
+
+    /**
+     * Average rate of lost transmitted packets, in units of packets per second.
+     *
+     * @hide
+     */
+    public double getLostTxPacketsPerSecond() {
+        return mLostTxPacketsPerSecond;
+    }
+
+    /** @hide */
+    public void setLostTxPacketsPerSecond(double lostTxPacketsPerSecond) {
+        mLostTxPacketsPerSecond = lostTxPacketsPerSecond;
+    }
+
+    private double mTxRetriedTxPacketsPerSecond;
+
+    /**
+     * Average rate of transmitted retry packets, in units of packets per second.
+     *
+     * @hide
+     */
+    public double getRetriedTxPacketsPerSecond() {
+        return mTxRetriedTxPacketsPerSecond;
+    }
+
+    /** @hide */
+    public void setRetriedTxPacketsRate(double txRetriedTxPacketsPerSecond) {
+        mTxRetriedTxPacketsPerSecond = txRetriedTxPacketsPerSecond;
+    }
+
+    private double mSuccessfulTxPacketsPerSecond;
+
+    /**
+     * Average rate of successfully transmitted unicast packets, in units of packets per second.
+     *
+     * @hide
+     */
+    public double getSuccessfulTxPacketsPerSecond() {
+        return mSuccessfulTxPacketsPerSecond;
+    }
+
+    /** @hide */
+    public void setSuccessfulTxPacketsPerSecond(double successfulTxPacketsPerSecond) {
+        mSuccessfulTxPacketsPerSecond = successfulTxPacketsPerSecond;
+    }
+
+    private double mSuccessfulRxPacketsPerSecond;
+
+    /**
+     * Average rate of received unicast data packets, in units of packets per second.
+     *
+     * @hide
+     */
+    public double getSuccessfulRxPacketsPerSecond() {
+        return mSuccessfulRxPacketsPerSecond;
+    }
+
+    /** @hide */
+    public void setSuccessfulRxPacketsPerSecond(double successfulRxPacketsPerSecond) {
+        mSuccessfulRxPacketsPerSecond = successfulRxPacketsPerSecond;
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) return true;
@@ -341,15 +440,41 @@
                 && mState == that.mState
                 && mRssi == that.mRssi
                 && mRxLinkSpeed == that.mRxLinkSpeed
-                && mTxLinkSpeed == that.mTxLinkSpeed;
+                && mTxLinkSpeed == that.mTxLinkSpeed
+                && mTxRetriedTxPacketsPerSecond == that.mTxRetriedTxPacketsPerSecond
+                && mSuccessfulTxPacketsPerSecond == that.mSuccessfulTxPacketsPerSecond
+                && mLostTxPacketsPerSecond == that.mLostTxPacketsPerSecond
+                && mSuccessfulRxPacketsPerSecond == that.mSuccessfulRxPacketsPerSecond
+                && txBad == that.txBad
+                && txRetries == that.txRetries
+                && txSuccess == that.txSuccess
+                && rxSuccess == that.rxSuccess;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mBand, mChannel, mLinkId, mApMacAddress, mStaMacAddress, mState);
+        return Objects.hash(
+                mBand,
+                mChannel,
+                mLinkId,
+                mApMacAddress,
+                mStaMacAddress,
+                mState,
+                mRssi,
+                mRxLinkSpeed,
+                mTxLinkSpeed,
+                mTxRetriedTxPacketsPerSecond,
+                mSuccessfulTxPacketsPerSecond,
+                mLostTxPacketsPerSecond,
+                mSuccessfulRxPacketsPerSecond,
+                txBad,
+                txRetries,
+                txSuccess,
+                rxSuccess);
     }
 
-    private String getStateString(@MloLinkState int state) {
+    /** @hide */
+    public static String getStateString(@MloLinkState int state) {
         switch(state) {
             case MLO_LINK_STATE_INVALID:
                 return "MLO_LINK_STATE_INVALID";
@@ -426,6 +551,14 @@
         dest.writeInt(mTxLinkSpeed);
         dest.writeParcelable(mApMacAddress, flags);
         dest.writeParcelable(mStaMacAddress, flags);
+        dest.writeDouble(mLostTxPacketsPerSecond);
+        dest.writeDouble(mSuccessfulTxPacketsPerSecond);
+        dest.writeDouble(mSuccessfulRxPacketsPerSecond);
+        dest.writeDouble(mTxRetriedTxPacketsPerSecond);
+        dest.writeLong(txBad);
+        dest.writeLong(txRetries);
+        dest.writeLong(txSuccess);
+        dest.writeLong(rxSuccess);
     }
 
     /** Implement the Parcelable interface */
@@ -442,6 +575,14 @@
                     link.mTxLinkSpeed = in.readInt();
                     link.mApMacAddress = in.readParcelable(MacAddress.class.getClassLoader());
                     link.mStaMacAddress = in.readParcelable(MacAddress.class.getClassLoader());
+                    link.mLostTxPacketsPerSecond = in.readDouble();
+                    link.mSuccessfulTxPacketsPerSecond = in.readDouble();
+                    link.mSuccessfulRxPacketsPerSecond = in.readDouble();
+                    link.mTxRetriedTxPacketsPerSecond = in.readDouble();
+                    link.txBad = in.readLong();
+                    link.txRetries = in.readLong();
+                    link.txSuccess = in.readLong();
+                    link.rxSuccess = in.readLong();
                     return link;
                 }
 
diff --git a/framework/java/android/net/wifi/OuiKeyedData.java b/framework/java/android/net/wifi/OuiKeyedData.java
new file mode 100644
index 0000000..8f2a153
--- /dev/null
+++ b/framework/java/android/net/wifi/OuiKeyedData.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.wifi.util.PersistableBundleUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.Objects;
+
+/**
+ * Vendor-provided data for HAL configuration.
+ *
+ * @hide
+ */
+@FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+@SystemApi
+public final class OuiKeyedData implements Parcelable {
+    private static final String TAG = "OuiKeyedData";
+
+    /** 24-bit OUI identifier to identify the vendor/OEM. */
+    private final int mOui;
+    /** PersistableBundle containing the vendor-defined data. */
+    private final PersistableBundle mData;
+
+    private OuiKeyedData(int oui, @NonNull PersistableBundle data) {
+        mOui = oui;
+        mData = (data != null) ? data.deepCopy() : null;
+    }
+
+    /**
+     * Get the OUI for this object.
+     *
+     * <p>See {@link Builder#Builder(int, PersistableBundle)}}
+     */
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    public int getOui() {
+        return mOui;
+    }
+
+    /**
+     * Get the data for this object.
+     *
+     * <p>See {@link Builder#Builder(int, PersistableBundle)}}
+     */
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    public @NonNull PersistableBundle getData() {
+        return mData;
+    }
+
+    private static boolean validateOui(int oui) {
+        // OUI must be a non-zero 24-bit value.
+        return oui != 0 && (oui & 0xFF000000) == 0;
+    }
+
+    /**
+     * Validate the parameters in this instance.
+     *
+     * @return true if all parameters are valid, false otherwise
+     * @hide
+     */
+    public boolean validate() {
+        return validateOui(mOui) && (getData() != null);
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        OuiKeyedData that = (OuiKeyedData) o;
+        return mOui == that.mOui && PersistableBundleUtils.isEqual(mData, that.mData);
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @Override
+    public int hashCode() {
+        return Objects.hash(mOui, PersistableBundleUtils.getHashCode(mData));
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @Override
+    public String toString() {
+        return "{oui=" + Integer.toHexString(mOui) + ", data=" + getData() + "}";
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mOui);
+        dest.writePersistableBundle(mData);
+    }
+
+    /** @hide */
+    OuiKeyedData(@NonNull Parcel in) {
+        this.mOui = in.readInt();
+        this.mData = in.readPersistableBundle();
+    }
+
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @NonNull
+    public static final Parcelable.Creator<OuiKeyedData> CREATOR =
+            new Parcelable.Creator<OuiKeyedData>() {
+                @Override
+                public OuiKeyedData createFromParcel(Parcel in) {
+                    return new OuiKeyedData(in);
+                }
+
+                @Override
+                public OuiKeyedData[] newArray(int size) {
+                    return new OuiKeyedData[size];
+                }
+            };
+
+    /** Builder for {@link OuiKeyedData}. */
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    public static final class Builder {
+        private final int mOui;
+        private final @NonNull PersistableBundle mData;
+
+        /**
+         * Constructor for {@link Builder}.
+         *
+         * @param oui 24-bit OUI identifier to identify the vendor/OEM. See
+         *     https://standards-oui.ieee.org/ for more information.
+         * @param data PersistableBundle containing additional configuration data. The definition
+         *     should be provided by the vendor, and should be known to both the caller and to the
+         *     vendor's implementation of the Wi-Fi HALs.
+         */
+        @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+        public Builder(int oui, @NonNull PersistableBundle data) {
+            mOui = oui;
+            mData = data;
+        }
+
+        /** Construct an OuiKeyedData object with the specified parameters. */
+        @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+        @NonNull
+        public OuiKeyedData build() {
+            OuiKeyedData ouiKeyedData = new OuiKeyedData(mOui, mData);
+            if (!ouiKeyedData.validate()) {
+                throw new IllegalArgumentException("Provided parameters are invalid");
+            }
+            return ouiKeyedData;
+        }
+    }
+}
diff --git a/framework/java/android/net/wifi/ScanResult.java b/framework/java/android/net/wifi/ScanResult.java
index 5d287e4..1eb35e4 100644
--- a/framework/java/android/net/wifi/ScanResult.java
+++ b/framework/java/android/net/wifi/ScanResult.java
@@ -29,6 +29,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import com.android.modules.utils.build.SdkLevel;
 
@@ -48,6 +49,8 @@
  * but does not currently report them to external clients.
  */
 public final class ScanResult implements Parcelable {
+
+    private static final String TAG = "ScanResult";
     /**
      * The network name.
      *
@@ -536,6 +539,20 @@
     public static final int WIFI_BAND_60_GHZ = WifiScanner.WIFI_BAND_60_GHZ;
 
     /**
+     * Constant used for dual 5GHz multi-internet use-case only. Not to be used for regular scan
+     * result reporting.
+     * @hide
+     */
+    public static final int WIFI_BAND_5_GHZ_LOW = WifiScanner.WIFI_BAND_5_GHZ_LOW;
+
+    /**
+     * Constant used for dual 5GHz multi-internet use-case only. Not to be used for regular scan
+     * result reporting.
+     * @hide
+     */
+    public static final int WIFI_BAND_5_GHZ_HIGH = WifiScanner.WIFI_BAND_5_GHZ_HIGH;
+
+    /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -870,6 +887,16 @@
      * @hide
      */
     public static final int BAND_60_GHZ_END_FREQ_MHZ = 70200;
+    /**
+     * The highest frequency in 5GHz low
+     * @hide
+     */
+    public static final int BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ = 5320;
+    /**
+     * The lowest frequency in 5GHz high
+     * @hide
+     */
+    public static final int BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ = 5500;
 
     /**
      * Utility function to check if a frequency within 2.4 GHz band
@@ -933,6 +960,35 @@
     }
 
     /**
+     * Utility function to check whether 2 frequencies are valid for multi-internet connection
+     * when dual-5GHz is supported.
+     *
+     * The allowed combinations are:
+     * - 2.4GHz + Any 5GHz
+     * - 2.4GHz + 6Ghz
+     * - 5GHz low + 5GHz high
+     * - 5GHz low + 6GHz
+     * @hide
+     */
+    public static boolean isValidCombinedBandForDual5GHz(int freqMhz1, int freqMhz2) {
+        int band1 = toBand(freqMhz1);
+        int band2 = toBand(freqMhz2);
+        if (band1 == WIFI_BAND_24_GHZ || band2 == WIFI_BAND_24_GHZ) {
+            return band1 != band2;
+        }
+
+        // 5GHz Low : b1 36-48 b2 52-64(5320)
+        // 5GHz High : b3 100(5500)-144 b4 149-165
+        if ((freqMhz1 <= BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ
+                && freqMhz2 >= BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ)
+                    || (freqMhz2 <= BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ
+                        && freqMhz1 >= BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Utility function to convert Wi-Fi channel number to frequency in MHz.
      *
      * Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016
@@ -1185,6 +1241,14 @@
          */
         public static final int EID_EXT_MULTI_LINK = 107;
         /**
+         * Multi-Link IE Fragment sub element ID: see IEEE 802.11be Specification section 9.4.2.312
+         * Multi-Link element.
+         *
+         * @hide
+         */
+        public static final int EID_FRAGMENT_SUB_ELEMENT_MULTI_LINK = 254;
+
+        /**
          * EHT Capabilities IE extension id: see IEEE 802.11be Specification section 9.4.2.1
          *
          * @hide
@@ -1535,6 +1599,7 @@
 
     /** Implement the Parcelable interface {@hide} */
     public void writeToParcel(Parcel dest, int flags) {
+        long start = dest.dataSize();
         if (wifiSsid != null) {
             dest.writeInt(1);
             wifiSsid.writeToParcel(dest, flags);
@@ -1572,6 +1637,7 @@
         else {
             dest.writeInt(0);
         }
+        int anqpElementsPayloadSize = 0;
         if (anqpElements != null) {
             dest.writeInt(anqpElements.length);
             for (AnqpInformationElement element : anqpElements) {
@@ -1579,6 +1645,7 @@
                 dest.writeInt(element.getElementId());
                 dest.writeInt(element.getPayload().length);
                 dest.writeByteArray(element.getPayload());
+                anqpElementsPayloadSize += element.getPayload().length;
             }
         } else {
             dest.writeInt(0);
@@ -1600,6 +1667,18 @@
         dest.writeParcelable(mApMldMacAddress, flags);
         dest.writeInt(mApMloLinkId);
         dest.writeTypedList(mAffiliatedMloLinks);
+        if (dest.dataSize() - start > 10000) {
+            Log.e(
+                    TAG,
+                    " Abnormal ScanResult: "
+                            + this
+                            + ". The size is "
+                            + (dest.dataSize() - start)
+                            + ". The informationElements size is "
+                            + informationElements.length
+                            + ". The anqpPayload size is "
+                            + anqpElementsPayloadSize);
+        }
     }
 
     /** Implement the Parcelable interface */
diff --git a/framework/java/android/net/wifi/SoftApCapability.java b/framework/java/android/net/wifi/SoftApCapability.java
index 9a5afaa..0607db4 100644
--- a/framework/java/android/net/wifi/SoftApCapability.java
+++ b/framework/java/android/net/wifi/SoftApCapability.java
@@ -234,6 +234,20 @@
     }
 
     /**
+     * Set SoftAp Capabilities
+     * @param value Boolean to set value 0 or 1
+     * @param features @HotspotFeatures represents which feature to access
+     * @hide
+     */
+    public void setSupportedFeatures(boolean value, @HotspotFeatures long features) {
+        if (value) {
+            mSupportedFeatures |= features;
+        } else {
+            mSupportedFeatures &= ~features;
+        }
+    }
+
+    /**
      * Set supported channel list in target band type.
      *
      * @param band One of the following band types:
diff --git a/framework/java/android/net/wifi/SoftApConfiguration.java b/framework/java/android/net/wifi/SoftApConfiguration.java
index 3de0735..dde9891 100644
--- a/framework/java/android/net/wifi/SoftApConfiguration.java
+++ b/framework/java/android/net/wifi/SoftApConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -53,19 +54,12 @@
 /**
  * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
  *
- * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
- * framework how it should configure a hotspot.
+ * <p>This is input for the framework provided by a client app, i.e. it exposes knobs to instruct
+ * the framework how it should configure a hotspot.
  *
- * System apps can use this to configure a tethered hotspot using
- * {@code WifiManager#startTetheredHotspot(SoftApConfiguration)} and
- * {@code WifiManager#setSoftApConfiguration(SoftApConfiguration)}
- * or local-only hotspot using
- * {@code WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
- * WifiManager.LocalOnlyHotspotCallback)}.
+ * <p>System apps can use this to configure a tethered hotspot or local-only hotspot.
  *
- * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to
- * create a new instance.
- *
+ * <p>Instances of this class are immutable.
  */
 public final class SoftApConfiguration implements Parcelable {
 
@@ -407,6 +401,9 @@
      */
     private final long mBridgedModeOpportunisticShutdownTimeoutMillis;
 
+    /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */
+    private @NonNull List<OuiKeyedData> mVendorData;
+
     /**
      * THe definition of security type OPEN.
      */
@@ -450,19 +447,32 @@
     public @interface SecurityType {}
 
     /** Private constructor for Builder and Parcelable implementation. */
-    private SoftApConfiguration(@Nullable WifiSsid ssid, @Nullable MacAddress bssid,
-            @Nullable String passphrase, boolean hiddenSsid, @NonNull SparseIntArray channels,
-            @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
-            long shutdownTimeoutMillis, boolean clientControlByUser,
-            @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList,
-            int macRandomizationSetting, boolean bridgedModeOpportunisticShutdownEnabled,
-            boolean ieee80211axEnabled, boolean ieee80211beEnabled, boolean isUserConfiguration,
+    private SoftApConfiguration(
+            @Nullable WifiSsid ssid,
+            @Nullable MacAddress bssid,
+            @Nullable String passphrase,
+            boolean hiddenSsid,
+            @NonNull SparseIntArray channels,
+            @SecurityType int securityType,
+            int maxNumberOfClients,
+            boolean shutdownTimeoutEnabled,
+            long shutdownTimeoutMillis,
+            boolean clientControlByUser,
+            @NonNull List<MacAddress> blockedList,
+            @NonNull List<MacAddress> allowedList,
+            int macRandomizationSetting,
+            boolean bridgedModeOpportunisticShutdownEnabled,
+            boolean ieee80211axEnabled,
+            boolean ieee80211beEnabled,
+            boolean isUserConfiguration,
             long bridgedModeOpportunisticShutdownTimeoutMillis,
             @NonNull List<ScanResult.InformationElement> vendorElements,
             @Nullable MacAddress persistentRandomizedMacAddress,
-            @NonNull Set<Integer> allowedAcsChannels24g, @NonNull Set<Integer> allowedAcsChannels5g,
+            @NonNull Set<Integer> allowedAcsChannels24g,
+            @NonNull Set<Integer> allowedAcsChannels5g,
             @NonNull Set<Integer> allowedAcsChannels6g,
-            @WifiAnnotations.Bandwidth int maxChannelBandwidth) {
+            @WifiAnnotations.Bandwidth int maxChannelBandwidth,
+            @Nullable List<OuiKeyedData> vendorData) {
         mWifiSsid = ssid;
         mBssid = bssid;
         mPassphrase = passphrase;
@@ -493,6 +503,7 @@
         mAllowedAcsChannels5g = new HashSet<>(allowedAcsChannels5g);
         mAllowedAcsChannels6g = new HashSet<>(allowedAcsChannels6g);
         mMaxChannelBandwidth = maxChannelBandwidth;
+        mVendorData = new ArrayList<>(vendorData);
     }
 
     @Override
@@ -525,24 +536,43 @@
                 && mBridgedModeOpportunisticShutdownTimeoutMillis
                         == other.mBridgedModeOpportunisticShutdownTimeoutMillis
                 && Objects.equals(mVendorElements, other.mVendorElements)
-                && Objects.equals(mPersistentRandomizedMacAddress,
-                        other.mPersistentRandomizedMacAddress)
+                && Objects.equals(
+                        mPersistentRandomizedMacAddress, other.mPersistentRandomizedMacAddress)
                 && Objects.equals(mAllowedAcsChannels2g, other.mAllowedAcsChannels2g)
                 && Objects.equals(mAllowedAcsChannels5g, other.mAllowedAcsChannels5g)
                 && Objects.equals(mAllowedAcsChannels6g, other.mAllowedAcsChannels6g)
-                && mMaxChannelBandwidth == other.mMaxChannelBandwidth;
+                && mMaxChannelBandwidth == other.mMaxChannelBandwidth
+                && Objects.equals(mVendorData, other.mVendorData);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mWifiSsid, mBssid, mPassphrase, mHiddenSsid,
-                mChannels.toString(), mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
-                mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
-                mAllowedClientList, mMacRandomizationSetting,
-                mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled, mIeee80211beEnabled,
-                mIsUserConfiguration, mBridgedModeOpportunisticShutdownTimeoutMillis,
-                mVendorElements, mPersistentRandomizedMacAddress, mAllowedAcsChannels2g,
-                mAllowedAcsChannels5g, mAllowedAcsChannels6g, mMaxChannelBandwidth);
+        return Objects.hash(
+                mWifiSsid,
+                mBssid,
+                mPassphrase,
+                mHiddenSsid,
+                mChannels.toString(),
+                mSecurityType,
+                mMaxNumberOfClients,
+                mAutoShutdownEnabled,
+                mShutdownTimeoutMillis,
+                mClientControlByUser,
+                mBlockedClientList,
+                mAllowedClientList,
+                mMacRandomizationSetting,
+                mBridgedModeOpportunisticShutdownEnabled,
+                mIeee80211axEnabled,
+                mIeee80211beEnabled,
+                mIsUserConfiguration,
+                mBridgedModeOpportunisticShutdownTimeoutMillis,
+                mVendorElements,
+                mPersistentRandomizedMacAddress,
+                mAllowedAcsChannels2g,
+                mAllowedAcsChannels5g,
+                mAllowedAcsChannels6g,
+                mMaxChannelBandwidth,
+                mVendorData);
     }
 
     @Override
@@ -576,6 +606,7 @@
         sbuf.append(" \n mAllowedAcsChannels5g = ").append(mAllowedAcsChannels5g);
         sbuf.append(" \n mAllowedAcsChannels6g = ").append(mAllowedAcsChannels6g);
         sbuf.append(" \n mMaxChannelBandwidth = ").append(mMaxChannelBandwidth);
+        sbuf.append(" \n mVendorData = ").append(mVendorData);
         return sbuf.toString();
     }
 
@@ -605,6 +636,7 @@
         writeHashSetInt(dest, mAllowedAcsChannels5g);
         writeHashSetInt(dest, mAllowedAcsChannels6g);
         dest.writeInt(mMaxChannelBandwidth);
+        dest.writeList(mVendorData);
     }
 
     /* Reference from frameworks/base/core/java/android/os/Parcel.java */
@@ -669,41 +701,65 @@
         return set;
     }
 
+    /* Read List<OuiKeyedData> from Parcel */
+    @NonNull
+    private static List<OuiKeyedData> readOuiKeyedDataList(@NonNull Parcel in) {
+        List<OuiKeyedData> dataList = new ArrayList<>();
+        if (SdkLevel.isAtLeastT()) {
+            in.readList(dataList, OuiKeyedData.class.getClassLoader(), OuiKeyedData.class);
+        } else {
+            in.readList(dataList, OuiKeyedData.class.getClassLoader());
+        }
+        return dataList;
+    }
+
     @Override
     public int describeContents() {
         return 0;
     }
 
     @NonNull
-    public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() {
-        @Override
-        public SoftApConfiguration createFromParcel(Parcel in) {
-            return new SoftApConfiguration(
-                    in.readParcelable(WifiSsid.class.getClassLoader()),
-                    in.readParcelable(MacAddress.class.getClassLoader()),
-                    in.readString(), in.readBoolean(), readSparseIntArray(in), in.readInt(),
-                    in.readInt(), in.readBoolean(), in.readLong(), in.readBoolean(),
-                    in.createTypedArrayList(MacAddress.CREATOR),
-                    in.createTypedArrayList(MacAddress.CREATOR), in.readInt(), in.readBoolean(),
-                    in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readLong(),
-                    in.createTypedArrayList(ScanResult.InformationElement.CREATOR),
-                    in.readParcelable(MacAddress.class.getClassLoader()),
-                    readHashSetInt(in),
-                    readHashSetInt(in),
-                    readHashSetInt(in),
-                    in.readInt());
-        }
+    public static final Creator<SoftApConfiguration> CREATOR =
+            new Creator<SoftApConfiguration>() {
+                @Override
+                public SoftApConfiguration createFromParcel(Parcel in) {
+                    return new SoftApConfiguration(
+                            in.readParcelable(WifiSsid.class.getClassLoader()),
+                            in.readParcelable(MacAddress.class.getClassLoader()),
+                            in.readString(),
+                            in.readBoolean(),
+                            readSparseIntArray(in),
+                            in.readInt(),
+                            in.readInt(),
+                            in.readBoolean(),
+                            in.readLong(),
+                            in.readBoolean(),
+                            in.createTypedArrayList(MacAddress.CREATOR),
+                            in.createTypedArrayList(MacAddress.CREATOR),
+                            in.readInt(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readBoolean(),
+                            in.readLong(),
+                            in.createTypedArrayList(ScanResult.InformationElement.CREATOR),
+                            in.readParcelable(MacAddress.class.getClassLoader()),
+                            readHashSetInt(in),
+                            readHashSetInt(in),
+                            readHashSetInt(in),
+                            in.readInt(),
+                            readOuiKeyedDataList(in));
+                }
 
-        @Override
-        public SoftApConfiguration[] newArray(int size) {
-            return new SoftApConfiguration[size];
-        }
-    };
+                @Override
+                public SoftApConfiguration[] newArray(int size) {
+                    return new SoftApConfiguration[size];
+                }
+            };
 
     /**
      * Return the UTF-8 String set to be the SSID for the AP. If the SSID cannot be decoded as
-     * UTF-8, then this will return {@link WifiManager#UNKNOWN_SSID}
-     * See also {@link Builder#setSsid(String)}.
+     * UTF-8, then this will return {@link WifiManager#UNKNOWN_SSID}.
      *
      * @deprecated Use {@link #getWifiSsid()} instead.
      */
@@ -719,7 +775,6 @@
 
     /**
      * Return WifiSsid set to be the SSID for the AP.
-     * See also {@link Builder#setWifiSsid(WifiSsid)}.
      */
     @Nullable
     public WifiSsid getWifiSsid() {
@@ -750,7 +805,6 @@
 
     /**
      * Returns MAC address set to be BSSID for the AP.
-     * See also {@link Builder#setBssid(MacAddress)}.
      */
     @Nullable
     public MacAddress getBssid() {
@@ -759,7 +813,6 @@
 
     /**
      * Returns String set to be passphrase for current AP.
-     * See also {@link Builder#setPassphrase(String, int)}.
      */
     @Nullable
     public String getPassphrase() {
@@ -769,7 +822,6 @@
     /**
      * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
      * not (false: broadcasts its SSID) for the AP.
-     * See also {@link Builder#setHiddenSsid(boolean)}.
      */
     public boolean isHiddenSsid() {
         return mHiddenSsid;
@@ -1018,14 +1070,6 @@
     }
 
     /**
-     * @see #isIeee80211beEnabled()
-     * @hide
-     */
-    public boolean isIeee80211beEnabledInternal() {
-        return mIeee80211beEnabled;
-    }
-
-    /**
      * Returns whether or not the Soft AP is configured to enable 802.11be.
      * This is an indication that if the device support 802.11be AP then to enable or disable
      * that feature. If the device does not support 802.11be AP then this flag is ignored.
@@ -1038,7 +1082,7 @@
         if (!SdkLevel.isAtLeastT()) {
             throw new UnsupportedOperationException();
         }
-        return isIeee80211beEnabledInternal();
+        return mIeee80211beEnabled;
     }
 
     /**
@@ -1155,6 +1199,24 @@
     }
 
     /**
+     * Return the vendor-provided configuration data, if it exists. See also {@link
+     * Builder#setVendorData(List)}
+     *
+     * @return Vendor configuration data, or empty list if it does not exist.
+     * @hide
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+    @NonNull
+    @SystemApi
+    public List<OuiKeyedData> getVendorData() {
+        if (!SdkLevel.isAtLeastV()) {
+            throw new UnsupportedOperationException();
+        }
+        return mVendorData;
+    }
+
+    /**
      * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}.
      * Note that SoftApConfiguration may contain configuration which is cannot be represented
      * by the legacy WifiConfiguration, in such cases a null will be returned.
@@ -1248,6 +1310,7 @@
         private Set<Integer> mAllowedAcsChannels5g;
         private Set<Integer> mAllowedAcsChannels6g;
         private @WifiAnnotations.Bandwidth int mMaxChannelBandwidth;
+        private @Nullable List<OuiKeyedData> mVendorData;
 
         /**
          * Constructs a Builder with default values (see {@link Builder}).
@@ -1282,6 +1345,7 @@
             mAllowedAcsChannels5g = new HashSet<>();
             mAllowedAcsChannels6g = new HashSet<>();
             mMaxChannelBandwidth = SoftApInfo.CHANNEL_WIDTH_AUTO;
+            mVendorData = new ArrayList<>();
         }
 
         /**
@@ -1325,6 +1389,7 @@
                 // SoftApConfiguration.
                 mMacRandomizationSetting = RANDOMIZATION_NONE;
             }
+            mVendorData = new ArrayList<>(other.mVendorData);
         }
 
         /**
@@ -1352,15 +1417,32 @@
                     REMOVE_ZERO_FOR_TIMEOUT_SETTING) && mShutdownTimeoutMillis == DEFAULT_TIMEOUT) {
                 mShutdownTimeoutMillis = 0; // Use 0 for legacy app.
             }
-            return new SoftApConfiguration(mWifiSsid, mBssid, mPassphrase,
-                    mHiddenSsid, mChannels, mSecurityType, mMaxNumberOfClients,
-                    mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
-                    mBlockedClientList, mAllowedClientList, mMacRandomizationSetting,
-                    mBridgedModeOpportunisticShutdownEnabled, mIeee80211axEnabled,
-                    mIeee80211beEnabled, mIsUserConfiguration,
-                    mBridgedModeOpportunisticShutdownTimeoutMillis, mVendorElements,
-                    mPersistentRandomizedMacAddress, mAllowedAcsChannels2g, mAllowedAcsChannels5g,
-                    mAllowedAcsChannels6g, mMaxChannelBandwidth);
+            return new SoftApConfiguration(
+                    mWifiSsid,
+                    mBssid,
+                    mPassphrase,
+                    mHiddenSsid,
+                    mChannels,
+                    mSecurityType,
+                    mMaxNumberOfClients,
+                    mAutoShutdownEnabled,
+                    mShutdownTimeoutMillis,
+                    mClientControlByUser,
+                    mBlockedClientList,
+                    mAllowedClientList,
+                    mMacRandomizationSetting,
+                    mBridgedModeOpportunisticShutdownEnabled,
+                    mIeee80211axEnabled,
+                    mIeee80211beEnabled,
+                    mIsUserConfiguration,
+                    mBridgedModeOpportunisticShutdownTimeoutMillis,
+                    mVendorElements,
+                    mPersistentRandomizedMacAddress,
+                    mAllowedAcsChannels2g,
+                    mAllowedAcsChannels5g,
+                    mAllowedAcsChannels6g,
+                    mMaxChannelBandwidth,
+                    mVendorData);
         }
 
         /**
@@ -2221,5 +2303,26 @@
             mPersistentRandomizedMacAddress = mac;
             return this;
         }
+
+        /**
+         * Set additional vendor-provided configuration data.
+         *
+         * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided
+         *     configuration data. Note that multiple elements with the same OUI are allowed.
+         * @return Builder for chaining.
+         */
+        @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+        @FlaggedApi("com.android.wifi.flags.vendor_parcelable_parameters")
+        @NonNull
+        public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) {
+            if (!SdkLevel.isAtLeastV()) {
+                throw new UnsupportedOperationException();
+            }
+            if (vendorData == null) {
+                throw new IllegalArgumentException("setVendorData received a null value");
+            }
+            mVendorData = vendorData;
+            return this;
+        }
     }
 }
diff --git a/framework/java/android/net/wifi/WifiConfiguration.java b/framework/java/android/net/wifi/WifiConfiguration.java
index 0bea151..0b63780 100644
--- a/framework/java/android/net/wifi/WifiConfiguration.java
+++ b/framework/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -50,6 +51,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -59,7 +63,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * A class representing a configured Wi-Fi network, including the
@@ -84,9 +87,8 @@
     /** {@hide} */
     public static final String pskVarName = "psk";
     /** {@hide} */
-    @Deprecated
-    @UnsupportedAppUsage
-    public static final String[] wepKeyVarNames = { "wep_key0", "wep_key1", "wep_key2", "wep_key3" };
+    @Deprecated @UnsupportedAppUsage
+    public static final String[] wepKeyVarNames = {"wep_key0", "wep_key1", "wep_key2", "wep_key3"};
     /** {@hide} */
     @Deprecated
     public static final String wepTxKeyIdxVarName = "wep_tx_keyidx";
@@ -634,8 +636,8 @@
         if (securityParamsList == null || securityParamsList.isEmpty()) {
             throw new IllegalArgumentException("An empty security params list is invalid.");
         }
-        mSecurityParamsList = securityParamsList.stream()
-                .map(p -> new SecurityParams(p)).collect(Collectors.toList());
+        mSecurityParamsList = new ArrayList<>(securityParamsList.size());
+        securityParamsList.forEach(p -> mSecurityParamsList.add(new SecurityParams(p)));
         updateLegacySecurityParams();
     }
 
@@ -660,16 +662,21 @@
      */
     public void addSecurityParams(@SecurityType int securityType) {
         // This ensures that there won't be duplicate security types.
-        if (mSecurityParamsList.stream().anyMatch(params -> params.isSecurityType(securityType))) {
-            throw new IllegalArgumentException("duplicate security type " + securityType);
+        for (SecurityParams params : mSecurityParamsList) {
+            if (params.isSecurityType(securityType)) {
+                throw new IllegalArgumentException("duplicate security type " + securityType);
+            }
         }
         addSecurityParams(SecurityParams.createSecurityParamsBySecurityType(securityType));
     }
 
     /** @hide */
     public void addSecurityParams(@NonNull SecurityParams newParams) {
-        if (mSecurityParamsList.stream().anyMatch(params -> params.isSameSecurityType(newParams))) {
-            throw new IllegalArgumentException("duplicate security params " + newParams);
+        // This ensures that there won't be duplicate security types.
+        for (SecurityParams params : mSecurityParamsList) {
+            if (params.isSameSecurityType(newParams)) {
+                throw new IllegalArgumentException("duplicate security params " + newParams);
+            }
         }
         if (!mSecurityParamsList.isEmpty()) {
             if (newParams.isEnterpriseSecurityType() && !isEnterprise()) {
@@ -793,10 +800,12 @@
      * @hide
      */
     public void setSecurityParamsEnabled(@SecurityType int securityType, boolean enable) {
-        mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(securityType))
-                .findAny()
-                .ifPresent(params -> params.setEnabled(enable));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(securityType)) {
+                p.setEnabled(enable);
+                return;
+            }
+        }
     }
 
     /**
@@ -818,10 +827,12 @@
      */
     public void setSecurityParamsIsAddedByAutoUpgrade(
             @SecurityType int securityType, boolean isAddedByAutoUpgrade) {
-        mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(securityType))
-                .findAny()
-                .ifPresent(params -> params.setIsAddedByAutoUpgrade(isAddedByAutoUpgrade));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(securityType)) {
+                p.setIsAddedByAutoUpgrade(isAddedByAutoUpgrade);
+                return;
+            }
+        }
     }
 
     /**
@@ -844,11 +855,12 @@
      * @hide
      */
     public @Nullable SecurityParams getSecurityParams(@SecurityType int securityType) {
-        SecurityParams p = mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(securityType))
-                .findAny()
-                .orElse(null);
-        return (p != null) ? new SecurityParams(p) : null;
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(securityType)) {
+                return new SecurityParams(p);
+            }
+        }
+        return null;
     }
 
     /**
@@ -871,8 +883,12 @@
      * @hide
      */
     public boolean isSecurityType(@SecurityType int securityType) {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.isSecurityType(securityType));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(securityType)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -906,8 +922,8 @@
      * @hide
      */
     public void enableFils(boolean enableFilsSha256, boolean enableFilsSha384) {
-        mSecurityParamsList.stream()
-                .forEach(params -> params.enableFils(enableFilsSha256, enableFilsSha384));
+        mSecurityParamsList.forEach(params ->
+                params.enableFils(enableFilsSha256, enableFilsSha384));
         updateLegacySecurityParams();
     }
 
@@ -918,8 +934,12 @@
      * @hide
      */
     public boolean isFilsSha256Enabled() {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.getAllowedKeyManagement().get(KeyMgmt.FILS_SHA256));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.getAllowedKeyManagement().get(KeyMgmt.FILS_SHA256)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -929,8 +949,12 @@
      * @hide
      */
     public boolean isFilsSha384Enabled() {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.getAllowedKeyManagement().get(KeyMgmt.FILS_SHA384));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.getAllowedKeyManagement().get(KeyMgmt.FILS_SHA384)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -941,11 +965,13 @@
      * @hide
      */
     public void enableSuiteBCiphers(boolean enableEcdheEcdsa, boolean enableEcdheRsa) {
-        mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT))
-                .findAny()
-                .ifPresent(params -> params.enableSuiteBCiphers(enableEcdheEcdsa, enableEcdheRsa));
-        updateLegacySecurityParams();
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT)) {
+                p.enableSuiteBCiphers(enableEcdheEcdsa, enableEcdheRsa);
+                updateLegacySecurityParams();
+                return;
+            }
+        }
     }
 
     /**
@@ -955,8 +981,12 @@
      * @hide
      */
     public boolean isSuiteBCipherEcdheEcdsaEnabled() {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.getAllowedSuiteBCiphers().get(SuiteBCipher.ECDHE_ECDSA));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.getAllowedSuiteBCiphers().get(SuiteBCipher.ECDHE_ECDSA)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -966,8 +996,12 @@
      * @hide
      */
     public boolean isSuiteBCipherEcdheRsaEnabled() {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.getAllowedSuiteBCiphers().get(SuiteBCipher.ECDHE_RSA));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.getAllowedSuiteBCiphers().get(SuiteBCipher.ECDHE_RSA)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -979,10 +1013,12 @@
      * @hide
      */
     public void enableSaeH2eOnlyMode(boolean enable) {
-        mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(SECURITY_TYPE_SAE))
-                .findAny()
-                .ifPresent(params -> params.enableSaeH2eOnlyMode(enable));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(SECURITY_TYPE_SAE)) {
+                p.enableSaeH2eOnlyMode(enable);
+                return;
+            }
+        }
     }
 
     /**
@@ -994,10 +1030,12 @@
      * @hide
      */
     public void enableSaePkOnlyMode(boolean enable) {
-        mSecurityParamsList.stream()
-                .filter(params -> params.isSecurityType(SECURITY_TYPE_SAE))
-                .findAny()
-                .ifPresent(params -> params.enableSaePkOnlyMode(enable));
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isSecurityType(SECURITY_TYPE_SAE)) {
+                p.enableSaePkOnlyMode(enable);
+                return;
+            }
+        }
     }
 
     /** @hide */
@@ -1435,6 +1473,20 @@
     @SystemApi
     public boolean allowAutojoin = true;
 
+    /**
+     * @hide
+     */
+    public void setIpProvisioningTimedOut(boolean value) {
+        mIpProvisioningTimedOut = value;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isIpProvisioningTimedOut() {
+        return mIpProvisioningTimedOut;
+    }
+
     /** @hide **/
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static int INVALID_RSSI = -127;
@@ -1623,6 +1675,11 @@
     private boolean mIsRepeaterEnabled;
 
     /**
+     * @hide
+     */
+    private boolean mIpProvisioningTimedOut;
+
+    /**
      * Sets if this configuration is intended to be repeater enabled for expanded coverage.
      *
      * @param isRepeaterEnabled true if this network is intended to be repeater enabled,
@@ -1747,9 +1804,15 @@
      * @hide
      */
     public boolean isOpenNetwork() {
-        boolean hasNonOpenSecurityType = mSecurityParamsList.stream()
-                .anyMatch(params -> !params.isOpenSecurityType());
-        return !hasNonOpenSecurityType && !hasWepKeys();
+        if (hasWepKeys()) {
+            return false;
+        }
+        for (SecurityParams p : mSecurityParamsList) {
+            if (!p.isOpenSecurityType()) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -2140,8 +2203,8 @@
         public static final int DISABLED_TRANSITION_DISABLE_INDICATION = 13;
         /**
          * This network is temporarily disabled because of unwanted network under sufficient rssi.
-         * @hide
          */
+        @FlaggedApi("com.android.wifi.flags.disable_reason_unwanted_low_rssi")
         public static final int DISABLED_UNWANTED_LOW_RSSI = 14;
         /**
          * All other disable reasons should be strictly less than this value.
@@ -2453,6 +2516,13 @@
          */
         private boolean mHasNeverDetectedCaptivePortal = true;
 
+
+        /**
+         * Boolean tracking whether internet validation have ever completed successfully on this
+         * WifiConfiguration.
+         */
+        private boolean mHasEverValidatedInternetAccess;
+
         /**
          * set whether this network is visible in latest Qualified Network Selection
          * @param seen value set to candidate
@@ -2608,6 +2678,22 @@
             return mHasNeverDetectedCaptivePortal;
         }
 
+
+        /**
+         * Get whether internet validation was ever successful on this WifiConfiguration.
+         * @hide
+         */
+        public boolean hasEverValidatedInternetAccess() {
+            return mHasEverValidatedInternetAccess;
+        }
+
+        /**
+         * @hide
+         */
+        public void setHasEverValidatedInternetAccess(boolean everValidated) {
+            mHasEverValidatedInternetAccess = everValidated;
+        }
+
         /** @hide */
         public NetworkSelectionStatus() {
             // previously stored configs will not have this parameter, so we default to false.
@@ -2887,11 +2973,9 @@
         public void copy(NetworkSelectionStatus source) {
             mStatus = source.mStatus;
             mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
-            for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX;
-                    index++) {
-                mNetworkSeclectionDisableCounter[index] =
-                        source.mNetworkSeclectionDisableCounter[index];
-            }
+            mNetworkSeclectionDisableCounter = Arrays.copyOf(
+                    source.mNetworkSeclectionDisableCounter,
+                    source.mNetworkSeclectionDisableCounter.length);
             mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
             mTemporarilyDisabledEndTime = source.mTemporarilyDisabledEndTime;
             mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
@@ -2904,6 +2988,7 @@
             setConnectChoiceRssi(source.getConnectChoiceRssi());
             setHasEverConnected(source.hasEverConnected());
             setHasNeverDetectedCaptivePortal(source.hasNeverDetectedCaptivePortal());
+            setHasEverValidatedInternetAccess(source.hasEverValidatedInternetAccess());
         }
 
         /** @hide */
@@ -2926,6 +3011,7 @@
             }
             dest.writeInt(hasEverConnected() ? 1 : 0);
             dest.writeInt(hasNeverDetectedCaptivePortal() ? 1 : 0);
+            dest.writeBoolean(hasEverValidatedInternetAccess());
             dest.writeParcelable(getCandidateSecurityParams(), flags);
             dest.writeParcelable(getLastUsedSecurityParams(), flags);
         }
@@ -2949,6 +3035,7 @@
             }
             setHasEverConnected(in.readInt() != 0);
             setHasNeverDetectedCaptivePortal(in.readInt() != 0);
+            setHasEverValidatedInternetAccess(in.readBoolean());
             setCandidateSecurityParams((SecurityParams) in.readParcelable(null));
             setLastUsedSecurityParams((SecurityParams) in.readParcelable(null));
         }
@@ -3244,6 +3331,7 @@
         mHasPreSharedKeyChanged = false;
         mEncryptedPreSharedKey = new byte[0];
         mEncryptedPreSharedKeyIv = new byte[0];
+        mIpProvisioningTimedOut = false;
     }
 
     /**
@@ -3279,18 +3367,24 @@
      */
     @UnsupportedAppUsage
     public boolean isEnterprise() {
-        boolean hasEnterpriseSecurityType = mSecurityParamsList.stream()
-                .anyMatch(params -> params.isEnterpriseSecurityType());
-        return (hasEnterpriseSecurityType
-                && enterpriseConfig != null
-                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE);
+        if (enterpriseConfig == null
+                || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE) {
+            return false;
+        }
+        for (SecurityParams p : mSecurityParamsList) {
+            if (p.isEnterpriseSecurityType()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private static String logTimeOfDay(long millis) {
         Calendar c = Calendar.getInstance();
         if (millis >= 0) {
-            c.setTimeInMillis(millis);
-            return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+            LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis),
+                    ZoneId.systemDefault());
+            return localDateTime.toString();
         } else {
             return Long.toString(millis);
         }
@@ -3347,6 +3441,8 @@
                 .append(mNetworkSelectionStatus.hasEverConnected()).append("\n");
         sbuf.append(" hasNeverDetectedCaptivePortal: ")
                 .append(mNetworkSelectionStatus.hasNeverDetectedCaptivePortal()).append("\n");
+        sbuf.append(" hasEverValidatedInternetAccess: ")
+                .append(mNetworkSelectionStatus.hasEverValidatedInternetAccess()).append("\n");
         sbuf.append(" mCandidateSecurityParams: ")
                 .append(mNetworkSelectionStatus.getCandidateSecurityParams());
         sbuf.append(" mLastUsedSecurityParams: ")
@@ -3483,8 +3579,7 @@
         }
 
         sbuf.append("\nSecurityParams List:\n");
-        mSecurityParamsList.stream()
-                .forEach(params -> sbuf.append(params.toString()));
+        mSecurityParamsList.forEach(params -> sbuf.append(params.toString()));
 
         sbuf.append("\nEnterprise config:\n");
         sbuf.append(enterpriseConfig);
@@ -3493,8 +3588,8 @@
         sbuf.append(mIpConfiguration.toString());
 
         if (mNetworkSelectionStatus.getNetworkSelectionBSSID() != null) {
-            sbuf.append(" networkSelectionBSSID="
-                    + mNetworkSelectionStatus.getNetworkSelectionBSSID());
+            sbuf.append(" networkSelectionBSSID=").append(
+                    mNetworkSelectionStatus.getNetworkSelectionBSSID());
         }
         long now_ms = SystemClock.elapsedRealtime();
         if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
@@ -3518,15 +3613,15 @@
                         .append("sec ");
             }
         }
-        if (creatorUid != 0) sbuf.append(" cuid=" + creatorUid);
-        if (creatorName != null) sbuf.append(" cname=" + creatorName);
-        if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
-        if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
-        if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
-        sbuf.append(" lcuid=" + lastConnectUid);
-        sbuf.append(" allowAutojoin=" + allowAutojoin);
-        sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
-        sbuf.append(" mostRecentlyConnected=" + isMostRecentlyConnected);
+        if (creatorUid != 0) sbuf.append(" cuid=").append(creatorUid);
+        if (creatorName != null) sbuf.append(" cname=").append(creatorName);
+        if (lastUpdateUid != 0) sbuf.append(" luid=").append(lastUpdateUid);
+        if (lastUpdateName != null) sbuf.append(" lname=").append(lastUpdateName);
+        if (updateIdentifier != null) sbuf.append(" updateIdentifier=").append(updateIdentifier);
+        sbuf.append(" lcuid=").append(lastConnectUid);
+        sbuf.append(" allowAutojoin=").append(allowAutojoin);
+        sbuf.append(" noInternetAccessExpected=").append(noInternetAccessExpected);
+        sbuf.append(" mostRecentlyConnected=").append(isMostRecentlyConnected);
 
         sbuf.append(" ");
 
@@ -3555,7 +3650,7 @@
         if (mBssidAllowlist != null) {
             sbuf.append("bssidAllowList: [");
             for (MacAddress bssid : mBssidAllowlist) {
-                sbuf.append(bssid + ", ");
+                sbuf.append(bssid).append(", ");
             }
             sbuf.append("]");
         } else {
@@ -3933,8 +4028,8 @@
             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
             allowedGroupManagementCiphers = (BitSet) source.allowedGroupManagementCiphers.clone();
             allowedSuiteBCiphers    = (BitSet) source.allowedSuiteBCiphers.clone();
-            mSecurityParamsList = source.mSecurityParamsList.stream()
-                    .map(p -> new SecurityParams(p)).collect(Collectors.toList());
+            mSecurityParamsList = new ArrayList<>(source.mSecurityParamsList.size());
+            source.mSecurityParamsList.forEach(p -> mSecurityParamsList.add(new SecurityParams(p)));
             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
 
             defaultGwMacAddress = source.defaultGwMacAddress;
@@ -4009,6 +4104,7 @@
                     ? source.mEncryptedPreSharedKey.clone() : new byte[0];
             mEncryptedPreSharedKeyIv = source.mEncryptedPreSharedKeyIv != null
                     ? source.mEncryptedPreSharedKeyIv.clone() : new byte[0];
+            mIpProvisioningTimedOut = source.mIpProvisioningTimedOut;
         }
     }
 
@@ -4049,8 +4145,7 @@
         writeBitSet(dest, allowedSuiteBCiphers);
 
         dest.writeInt(mSecurityParamsList.size());
-        mSecurityParamsList.stream()
-                .forEach(params -> dest.writeParcelable(params, flags));
+        mSecurityParamsList.forEach(params -> dest.writeParcelable(params, flags));
 
         dest.writeParcelable(enterpriseConfig, flags);
 
@@ -4106,6 +4201,7 @@
         dest.writeBoolean(mHasPreSharedKeyChanged);
         dest.writeByteArray(mEncryptedPreSharedKey);
         dest.writeByteArray(mEncryptedPreSharedKeyIv);
+        dest.writeBoolean(mIpProvisioningTimedOut);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -4223,6 +4319,7 @@
                 if (config.mEncryptedPreSharedKeyIv == null) {
                     config.mEncryptedPreSharedKeyIv = new byte[0];
                 }
+                config.mIpProvisioningTimedOut = in.readBoolean();
                 return config;
             }
 
@@ -4296,10 +4393,14 @@
      * @hide
      */
     public boolean needsPreSharedKey() {
-        return mSecurityParamsList.stream()
-                .anyMatch(params -> params.isSecurityType(SECURITY_TYPE_PSK)
-                        || params.isSecurityType(SECURITY_TYPE_SAE)
-                        || params.isSecurityType(SECURITY_TYPE_WAPI_PSK));
+        for (SecurityParams params : mSecurityParamsList) {
+            if (params.isSecurityType(SECURITY_TYPE_PSK)
+                    || params.isSecurityType(SECURITY_TYPE_SAE)
+                    || params.isSecurityType(SECURITY_TYPE_WAPI_PSK)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
diff --git a/framework/java/android/net/wifi/WifiContext.java b/framework/java/android/net/wifi/WifiContext.java
index f923980..f94f6d4 100644
--- a/framework/java/android/net/wifi/WifiContext.java
+++ b/framework/java/android/net/wifi/WifiContext.java
@@ -28,6 +28,7 @@
 import android.net.wifi.util.Environment;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.SparseArray;
 
 import androidx.annotation.Nullable;
 
@@ -59,6 +60,9 @@
     private AssetManager mWifiAssetsFromApk;
     private Resources mWifiResourcesFromApk;
     private Resources.Theme mWifiThemeFromApk;
+    private Context mResourcesApkContext;
+    private SparseArray<WifiStringResourceWrapper> mWifiStringResourceWrapperSparseArray =
+            new SparseArray<>();
 
     public WifiContext(@NonNull Context contextBase) {
         super(contextBase);
@@ -134,16 +138,20 @@
         return info.activityInfo.applicationInfo.packageName;
     }
 
-    private Context getResourcesApkContext() {
+    /** Get the Resource APK context */
+    public Context getResourcesApkContext() {
+        if (mResourcesApkContext != null) {
+            return mResourcesApkContext;
+        }
         try {
             String packageName = getWifiOverlayApkPkgName();
             if (packageName != null) {
-                return createPackageContext(packageName, 0);
+                mResourcesApkContext = createPackageContext(packageName, 0);
             }
         } catch (PackageManager.NameNotFoundException e) {
             Log.wtf(TAG, "Failed to load resources", e);
         }
-        return null;
+        return mResourcesApkContext;
     }
 
     /**
@@ -201,12 +209,19 @@
         mWifiAssetsFromApk = null;
         mWifiResourcesFromApk = null;
         mWifiThemeFromApk = null;
+        mResourcesApkContext = null;
+        mWifiStringResourceWrapperSparseArray.clear();
     }
 
     /**
      * Returns an instance of WifiStringResourceWrapper with the given subId and carrierId.
      */
     public WifiStringResourceWrapper getStringResourceWrapper(int subId, int carrierId) {
-        return new WifiStringResourceWrapper(this, subId, carrierId);
+        if (mWifiStringResourceWrapperSparseArray.contains(subId)) {
+            return mWifiStringResourceWrapperSparseArray.get(subId);
+        }
+        WifiStringResourceWrapper wrapper = new WifiStringResourceWrapper(this, subId, carrierId);
+        mWifiStringResourceWrapperSparseArray.append(subId, wrapper);
+        return wrapper;
     }
 }
diff --git a/framework/java/android/net/wifi/WifiEnterpriseConfig.java b/framework/java/android/net/wifi/WifiEnterpriseConfig.java
index 1dc5f34..98e5e4f 100644
--- a/framework/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/framework/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -331,6 +331,68 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TlsVersion {}
 
+    /**
+     * TOFU is not enabled for this configuration.
+     * @hide
+     */
+    public static final int TOFU_STATE_NOT_ENABLED = 0;
+
+    /**
+     * TOFU is enabled pre-connection.
+     * @hide
+     */
+    public static final int TOFU_STATE_ENABLED_PRE_CONNECTION = 1;
+
+    /**
+     * Root CA was configured post-TOFU connection.
+     * @hide
+     */
+
+    public static final int TOFU_STATE_CONFIGURE_ROOT_CA = 2;
+
+    /**
+     * Certificate pinning was used post-TOFU connection.
+     * @hide
+     */
+    public static final int TOFU_STATE_CERT_PINNING = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"TOFU_STATE_"}, value = {
+            TOFU_STATE_NOT_ENABLED,
+            TOFU_STATE_ENABLED_PRE_CONNECTION,
+            TOFU_STATE_CONFIGURE_ROOT_CA,
+            TOFU_STATE_CERT_PINNING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TofuConnectionState {}
+
+    /**
+     * TOFU dialog has not been displayed to the user, or state is unknown.
+     * @hide
+     */
+    public static final int TOFU_DIALOG_STATE_UNSPECIFIED = 0;
+
+    /**
+     * TOFU dialog was rejected by the user.
+     * @hide
+     */
+    public static final int TOFU_DIALOG_STATE_REJECTED = 1;
+
+    /**
+     * TOFU dialog was accepted by the user.
+     * @hide
+     */
+    public static final int TOFU_DIALOG_STATE_ACCEPTED = 2;
+
+    /** @hide */
+    @IntDef(prefix = {"TOFU_DIALOG_STATE_"}, value = {
+            TOFU_DIALOG_STATE_UNSPECIFIED,
+            TOFU_DIALOG_STATE_REJECTED,
+            TOFU_DIALOG_STATE_ACCEPTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TofuDialogState {}
+
     @UnsupportedAppUsage
     private HashMap<String, String> mFields = new HashMap<String, String>();
     private X509Certificate[] mCaCerts;
@@ -345,6 +407,8 @@
     private boolean mUserApproveNoCaCert = false;
     // Default is 1.0, i.e. accept any TLS version.
     private int mMinimumTlsVersion = TLS_V1_0;
+    private @TofuDialogState int mTofuDialogState = TOFU_DIALOG_STATE_UNSPECIFIED;
+    private @TofuConnectionState int mTofuConnectionState = TOFU_STATE_NOT_ENABLED;
 
     // Not included in parceling, hashing, or equality because it is an internal, temporary value
     // which is valid only during an actual connection to a Passpoint network with an RCOI-based
@@ -466,6 +530,8 @@
         mSelectedRcoi = source.mSelectedRcoi;
         mMinimumTlsVersion = source.mMinimumTlsVersion;
         mIsStrictConservativePeerMode = source.mIsStrictConservativePeerMode;
+        mTofuDialogState = source.mTofuDialogState;
+        mTofuConnectionState = source.mTofuConnectionState;
     }
 
     /**
@@ -511,6 +577,8 @@
         dest.writeBoolean(mIsTrustOnFirstUseEnabled);
         dest.writeBoolean(mUserApproveNoCaCert);
         dest.writeInt(mMinimumTlsVersion);
+        dest.writeInt(mTofuDialogState);
+        dest.writeInt(mTofuConnectionState);
     }
 
     public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR =
@@ -562,6 +630,8 @@
                     enterpriseConfig.mIsTrustOnFirstUseEnabled = in.readBoolean();
                     enterpriseConfig.mUserApproveNoCaCert = in.readBoolean();
                     enterpriseConfig.mMinimumTlsVersion = in.readInt();
+                    enterpriseConfig.mTofuDialogState = in.readInt();
+                    enterpriseConfig.mTofuConnectionState = in.readInt();
                     return enterpriseConfig;
                 }
 
@@ -1642,6 +1712,8 @@
         sb.append(" minimum_tls_version: ").append(mMinimumTlsVersion).append("\n");
         sb.append(" enable_conservative_peer_mode: ")
                 .append(mIsStrictConservativePeerMode).append("\n");
+        sb.append(" tofu_dialog_state: ").append(mTofuDialogState).append("\n");
+        sb.append(" tofu_connection_state: ").append(mTofuConnectionState).append("\n");
         return sb.toString();
     }
 
@@ -1935,6 +2007,12 @@
      */
     public void enableTrustOnFirstUse(boolean enable) {
         mIsTrustOnFirstUseEnabled = enable;
+        if (mTofuConnectionState != TOFU_STATE_CONFIGURE_ROOT_CA &&
+                mTofuConnectionState != TOFU_STATE_CERT_PINNING) {
+            // Override the current pre-connection state.
+            mTofuConnectionState = enable ?
+                    TOFU_STATE_ENABLED_PRE_CONNECTION : TOFU_STATE_NOT_ENABLED;
+        }
     }
 
     /**
@@ -1947,6 +2025,54 @@
     }
 
     /**
+     * Set the TOFU connection state.
+     * @hide
+     */
+    public void setTofuConnectionState(@TofuConnectionState int state) {
+        if (state < TOFU_STATE_NOT_ENABLED || state > TOFU_STATE_CERT_PINNING) {
+            Log.e(TAG, "Invalid TOFU connection state received. state=" + state);
+            return;
+        }
+        mTofuConnectionState = state;
+    }
+
+    /**
+     * Get the TOFU connection state.
+     * @hide
+     */
+    public @TofuConnectionState int getTofuConnectionState() {
+        return mTofuConnectionState;
+    }
+
+    /**
+     * Indicate whether the user accepted the TOFU dialog.
+     * @hide
+     */
+    public void setTofuDialogApproved(boolean approved) {
+        mTofuDialogState = approved ? TOFU_DIALOG_STATE_ACCEPTED : TOFU_DIALOG_STATE_REJECTED;
+    }
+
+    /**
+     * Set the TOFU dialog state.
+     * @hide
+     */
+    public void setTofuDialogState(@TofuDialogState int state) {
+        if (state < TOFU_DIALOG_STATE_UNSPECIFIED || state > TOFU_DIALOG_STATE_ACCEPTED) {
+            Log.e(TAG, "Invalid TOFU dialog state received. state=" + state);
+            return;
+        }
+        mTofuDialogState = state;
+    }
+
+    /**
+     * Get the TOFU dialog state.
+     * @hide
+     */
+    public @TofuDialogState int getTofuDialogState() {
+        return mTofuDialogState;
+    }
+
+    /**
      * For devices with no TOFU support, indicate that the user approved that a
      * legacy TLS-based EAP configuration from a previous release can be used
      * without a Root CA certificate.
diff --git a/framework/java/android/net/wifi/WifiInfo.java b/framework/java/android/net/wifi/WifiInfo.java
index 41279ce..82ab9ac 100644
--- a/framework/java/android/net/wifi/WifiInfo.java
+++ b/framework/java/android/net/wifi/WifiInfo.java
@@ -39,8 +39,10 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
+import android.util.SparseArray;
 
 import androidx.annotation.RequiresApi;
 
@@ -79,17 +81,6 @@
     private static final String TAG = "WifiInfo";
 
     /**
-     * Stopgap for trunk stable flags, as they are not yet supported by Mainline for lack
-     * of support in udc-mainline-prod.
-     * TODO : remove this class when udc-mainline-prod is abandoned and android.net.flags.Flags is
-     * available here, and replace it with a generated Flags class created with flags.aconfig.
-     * @hide
-     */
-    private static class Flags {
-        public static final String ADD_SUBSCRIPTION_ID = "com.android.net.wifi.add_subscription_id";
-    }
-
-    /**
      * This is the map described in the Javadoc comment above. The positions
      * of the elements of the array must correspond to the ordinal values
      * of <code>DetailedState</code>.
@@ -144,6 +135,9 @@
      */
     private int mApMloLinkId;
 
+    /** Maps link id to Affiliated MLO links. */
+    private SparseArray<MloLink> mAffiliatedMloLinksMap = new SparseArray<>();
+
     /**
      * The Multi-Link Operation (MLO) affiliated Links.
      * Only applicable for Wi-Fi 7 access points.
@@ -226,6 +220,7 @@
      * Received Signal Strength Indicator
      */
     private int mRssi;
+    private long mLastRssiUpdateMillis;
 
     /**
      * Wi-Fi standard for the connection
@@ -734,7 +729,7 @@
          * Set the subscription ID.
          * @see WifiInfo#getSubscriptionId()
          */
-        @FlaggedApi(Flags.ADD_SUBSCRIPTION_ID)
+        @FlaggedApi("com.android.wifi.flags.add_subscription_id")
         @NonNull
         public Builder setSubscriptionId(int subId) {
             mWifiInfo.setSubscriptionId(subId);
@@ -829,6 +824,13 @@
         mApMloLinkId = linkId;
     }
 
+    private void mapAffiliatedMloLinks() {
+        mAffiliatedMloLinksMap.clear();
+        for (MloLink link : mAffiliatedMloLinks) {
+            mAffiliatedMloLinksMap.put(link.getLinkId(), link);
+        }
+    }
+
     /**
      * Set the Multi-Link Operation (MLO) affiliated Links.
      * Only applicable for Wi-Fi 7 access points.
@@ -837,6 +839,7 @@
      */
     public void setAffiliatedMloLinks(@NonNull List<MloLink> links) {
         mAffiliatedMloLinks = new ArrayList<MloLink>(links);
+        mapAffiliatedMloLinks();
     }
 
     /**
@@ -947,6 +950,11 @@
         return new ArrayList<MloLink>(mAffiliatedMloLinks);
     }
 
+    /** @hide */
+    public MloLink getAffiliatedMloLink(int linkId) {
+        return mAffiliatedMloLinksMap.get(linkId);
+    }
+
     /**
      * Return the associated Multi-Link Operation (MLO) Links for Wi-Fi 7 access points.
      * i.e. when {@link #getWifiStandard()} returns {@link ScanResult#WIFI_STANDARD_11BE}.
@@ -993,6 +1001,14 @@
         if (rssi > MAX_RSSI)
             rssi = MAX_RSSI;
         mRssi = rssi;
+        mLastRssiUpdateMillis = SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * @hide
+     */
+    public long getLastRssiUpdateMillis() {
+        return mLastRssiUpdateMillis;
     }
 
     /**
@@ -1154,7 +1170,7 @@
 
     /**
      * Returns the MAC address used for this connection. In case of Multi Link Operation (MLO),
-     * returned value is the mac address of the link used for association.
+     * returned value is the Station MLD MAC address.
      *
      * @return MAC address of the connection or {@code "02:00:00:00:00:00"} if the caller has
      * insufficient permission.
diff --git a/framework/java/android/net/wifi/WifiManager.java b/framework/java/android/net/wifi/WifiManager.java
index 3ed89ec..5a9a75c 100644
--- a/framework/java/android/net/wifi/WifiManager.java
+++ b/framework/java/android/net/wifi/WifiManager.java
@@ -21,11 +21,14 @@
 import static android.Manifest.permission.CHANGE_WIFI_STATE;
 import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION;
 import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_SETUP_WIZARD;
 import static android.Manifest.permission.READ_WIFI_CREDENTIAL;
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -71,7 +74,6 @@
 import android.os.WorkSource;
 import android.os.connectivity.WifiActivityEnergyInfo;
 import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.CloseGuard;
@@ -368,7 +370,8 @@
 
     /**
      * Status code if the calling app was approved by virtue of being a carrier privileged app.
-     * @see TelephonyManager#hasCarrierPrivileges().
+     *
+     * @see android.telephony.TelephonyManager#hasCarrierPrivileges()
      */
     public static final int STATUS_SUGGESTION_APPROVAL_APPROVED_BY_CARRIER_PRIVILEGE = 4;
 
@@ -384,6 +387,39 @@
     public @interface SuggestionUserApprovalStatus {}
 
     /**
+     * Disable PNO scan until device reboot.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan")
+    @SystemApi
+    public static final int PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT = 0;
+
+    /**
+     * Disable PNO scan until device reboot or Wi-Fi is toggled.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan")
+    @SystemApi
+    public static final int PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE = 1;
+
+    /**
+     * Enable PNO scan.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan")
+    @SystemApi
+    public static final int PNO_SCAN_STATE_ENABLED = 2;
+
+    /** @hide */
+    @IntDef(prefix = {"PNO_SCAN_STATE_"},
+            value = {PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT,
+                    PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE,
+                    PNO_SCAN_STATE_ENABLED
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PnoScanState {}
+
+    /**
      * If one of the removed suggestions is currently connected, that network will be disconnected
      * after a short delay as opposed to immediately (which will be done by
      * {@link #ACTION_REMOVE_SUGGESTION_DISCONNECT}). The {@link ConnectivityManager} may call the
@@ -847,10 +883,20 @@
     public static final int API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS = 35;
 
     /**
+     * A constant used in {@link WifiManager#getLastCallerInfoForApi(int, Executor, BiConsumer)}
+     * Tracks usage of {@link WifiManager#setPnoScanState(int)}
+     *
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan")
+    @SystemApi
+    public static final int API_SET_PNO_SCAN_ENABLED = 36;
+
+    /**
      * Used internally to keep track of boundary.
      * @hide
      */
-    public static final int API_MAX = 36;
+    public static final int API_MAX = 37;
 
     /**
      * Broadcast intent action indicating that a Passpoint provider icon has been received.
@@ -3902,12 +3948,25 @@
      * @hide
      */
     public static final long WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS = 1L << 57;
+
     /**
      * Support for TID-To-Link Mapping negotiation.
      * @hide
      */
     public static final long WIFI_FEATURE_T2LM_NEGOTIATION = 1L << 58;
 
+    /**
+     * Support for WEP Wi-Fi Network
+     * @hide
+     */
+    public static final long WIFI_FEATURE_WEP = 1L << 59;
+
+    /**
+     * Support for WPA PERSONAL Wi-Fi Network
+     * @hide
+     */
+    public static final long WIFI_FEATURE_WPA_PERSONAL = 1L << 60;
+
     private long getSupportedFeatures() {
         try {
             return mService.getSupportedFeatures();
@@ -4093,6 +4152,16 @@
     }
 
     /**
+     * @return true if this device supports Low latency mode.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
+    @SystemApi
+    public boolean isLowLatencyModeSupported() {
+        return isFeatureSupported(WIFI_FEATURE_LOW_LATENCY);
+    }
+
+    /**
      * Check if the chipset supports 2.4GHz band.
      * @return {@code true} if supported, {@code false} otherwise.
      */
@@ -4160,10 +4229,6 @@
      * Query whether or not the device supports concurrency of Station (STA) + multiple access
      * points (AP) (where the APs bridged together).
      *
-     * See {@link SoftApConfiguration.Builder#setBands(int[])}
-     * or {@link SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)} to configure
-     * bridged AP when the bridged AP supported.
-     *
      * @return true if this device supports concurrency of STA + multiple APs which are bridged
      *         together, false otherwise.
      */
@@ -4175,10 +4240,6 @@
      * Query whether or not the device supports multiple Access point (AP) which are bridged
      * together.
      *
-     * See {@link SoftApConfiguration.Builder#setBands(int[])}
-     * or {@link SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)} to configure
-     * bridged AP when the bridged AP supported.
-     *
      * @return true if this device supports concurrency of multiple AP which bridged together,
      *         false otherwise.
      */
@@ -7648,7 +7709,13 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
+                        Bundle extras = new Bundle();
+                        if (SdkLevel.isAtLeastS()) {
+                            extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
+                                    mContext.getAttributionSource());
+                        }
+                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource,
+                                mContext.getOpPackageName(), extras);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
                                 mService.releaseWifiLock(mBinder);
@@ -7744,7 +7811,13 @@
                 }
                 if (changed && mHeld) {
                     try {
-                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
+                        Bundle extras = new Bundle();
+                        if (SdkLevel.isAtLeastS()) {
+                            extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
+                                    mContext.getAttributionSource());
+                        }
+                        mService.updateWifiLockWorkSource(mBinder, mWorkSource,
+                                mContext.getOpPackageName(), extras);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -7803,46 +7876,50 @@
     }
 
     /**
-     * Interface for low latency lock listener. Should be extended by application and
-     * set when calling {@link WifiManager#addWifiLowLatencyLockListener(Executor,
+     * Interface for low latency lock listener. Should be extended by application and set when
+     * calling {@link WifiManager#addWifiLowLatencyLockListener(Executor,
      * WifiLowLatencyLockListener)}.
      *
      * @hide
      */
+    @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
+    @SystemApi
     public interface WifiLowLatencyLockListener {
         /**
          * Provides low latency mode is activated or not. Triggered when Wi-Fi chip enters into low
          * latency mode.
          *
-         * Note: Always called with current state when a new listener gets registered.
+         * <p>Note: Always called with current state when a new listener gets registered.
          */
+        @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
         void onActivatedStateChanged(boolean activated);
 
         /**
          * Provides UIDs (lock owners) of the applications which currently acquired low latency
          * lock. Triggered when an application acquires or releases a lock.
          *
-         * Note: Always called with UIDs of the current acquired locks when a new listener gets
+         * <p>Note: Always called with UIDs of the current acquired locks when a new listener gets
          * registered.
          *
          * @param ownerUids An array of UIDs.
          */
+        @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
         default void onOwnershipChanged(@NonNull int[] ownerUids) {}
 
         /**
          * Provides UIDs of the applications which acquired the low latency lock and is currently
-         * active. See {@link WifiManager#WIFI_MODE_FULL_LOW_LATENCY} for the conditions to be
-         * met for low latency lock to be active. Triggered when application acquiring the lock
+         * active. See {@link WifiManager#WIFI_MODE_FULL_LOW_LATENCY} for the conditions to be met
+         * for low latency lock to be active. Triggered when application acquiring the lock
          * satisfies or does not satisfy low latency conditions when the low latency mode is
-         * activated. Also gets triggered when the lock becomes active, immediately after the
-         * {@link WifiLowLatencyLockListener#onActivatedStateChanged(boolean)} callback is
-         * triggered.
+         * activated. Also gets triggered when the lock becomes active, immediately after the {@link
+         * WifiLowLatencyLockListener#onActivatedStateChanged(boolean)} callback is triggered.
          *
-         * Note: Always called with UIDs of the current active locks when a new listener gets
+         * <p>Note: Always called with UIDs of the current active locks when a new listener gets
          * registered if the Wi-Fi chip is in low latency mode.
          *
          * @param activeUids An array of UIDs.
          */
+        @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
         default void onActiveUsersChanged(@NonNull int[] activeUids) {}
     }
 
@@ -7889,9 +7966,9 @@
      * registered listener using {@link WifiManager#removeWifiLowLatencyLockListener(
      * WifiLowLatencyLockListener)}.
      *
-     * Applications should have the {@link android.Manifest.permission#NETWORK_SETTINGS} and
-     * {@link android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. Callers
-     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>Applications should have the {@link android.Manifest.permission#NETWORK_SETTINGS} and
+     * {@link android.Manifest.permission#MANAGE_WIFI_NETWORK_SELECTION} permission. Callers without
+     * the permission will trigger a {@link java.lang.SecurityException}.
      *
      * @param executor The Executor on which to execute the callbacks.
      * @param listener The listener for the latency mode change.
@@ -7899,10 +7976,13 @@
      * @throws SecurityException if the caller is not allowed to call this API
      * @hide
      */
+    @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
+    @SystemApi
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-    @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
-            MANAGE_WIFI_NETWORK_SELECTION})
-    public void addWifiLowLatencyLockListener(@NonNull @CallbackExecutor Executor executor,
+    @RequiresPermission(
+            anyOf = {android.Manifest.permission.NETWORK_SETTINGS, MANAGE_WIFI_NETWORK_SELECTION})
+    public void addWifiLowLatencyLockListener(
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull WifiLowLatencyLockListener listener) {
         if (executor == null) throw new IllegalArgumentException("executor cannot be null");
         if (listener == null) throw new IllegalArgumentException("listener cannot be null");
@@ -7927,13 +8007,16 @@
 
     /**
      * Removes a listener added using {@link WifiManager#addWifiLowLatencyLockListener(Executor,
-     * WifiLowLatencyLockListener)}. After calling this method, applications will no longer
-     * receive low latency mode notifications.
+     * WifiLowLatencyLockListener)}. After calling this method, applications will no longer receive
+     * low latency mode notifications.
      *
      * @param listener the listener to be removed.
      * @throws IllegalArgumentException if incorrect input arguments are provided.
      * @hide
      */
+    @FlaggedApi("com.android.wifi.flags.low_latency_lock_listener")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     public void removeWifiLowLatencyLockListener(@NonNull WifiLowLatencyLockListener listener) {
         if (listener == null) throw new IllegalArgumentException("listener cannot be null");
         if (mVerboseLoggingEnabled) {
@@ -8190,7 +8273,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.DUMP
+    })
     public void setVerboseLoggingEnabled(boolean enable) {
         enableVerboseLogging(enable ? VERBOSE_LOGGING_LEVEL_ENABLED
                 : VERBOSE_LOGGING_LEVEL_DISABLED);
@@ -8206,7 +8292,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.DUMP
+    })
     public void setVerboseLoggingLevel(@VerboseLoggingLevel int verbose) {
         enableVerboseLogging(verbose);
     }
@@ -8216,11 +8305,16 @@
             maxTargetSdk = Build.VERSION_CODES.Q,
             publicAlternatives = "Use {@code #setVerboseLoggingEnabled(boolean)} instead."
     )
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.DUMP
+    })
     public void enableVerboseLogging(@VerboseLoggingLevel int verbose) {
         try {
             mService.enableVerboseLogging(verbose);
-            mVerboseLoggingEnabled = verbose == VERBOSE_LOGGING_LEVEL_ENABLED;
+            mVerboseLoggingEnabled =
+                    verbose == VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY
+                            || verbose == VERBOSE_LOGGING_LEVEL_ENABLED;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -8238,7 +8332,9 @@
      */
     @SystemApi
     public boolean isVerboseLoggingEnabled() {
-        return getVerboseLoggingLevel() > 0;
+        int verboseLoggingLevel = getVerboseLoggingLevel();
+        return verboseLoggingLevel == VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY
+                || verboseLoggingLevel == VERBOSE_LOGGING_LEVEL_ENABLED;
     }
 
     /**
@@ -8734,6 +8830,26 @@
         return isFeatureSupported(WIFI_FEATURE_T2LM_NEGOTIATION);
     }
 
+
+    /**
+    * @return true if this device supports connections to Wi-Fi WEP networks.
+    */
+    @FlaggedApi("com.android.wifi.flags.wep_usage")
+    public boolean isWepSupported() {
+        return isFeatureSupported(WIFI_FEATURE_WEP);
+    }
+
+    /**
+    * @return true if this device supports connections to Wi-Fi WPA-Personal networks.
+    *
+    * Note that this is the older and less secure WPA-Personal protocol, not WPA2-Personal
+    * or later protocols.
+    */
+    @FlaggedApi("com.android.wifi.flags.wpa_personal_usage")
+    public boolean isWpaPersonalSupported() {
+        return isFeatureSupported(WIFI_FEATURE_WPA_PERSONAL);
+    }
+
     /**
      * Gets the factory Wi-Fi MAC addresses.
      * @return Array of String representing Wi-Fi MAC addresses sorted lexically or an empty Array
@@ -8929,23 +9045,31 @@
     public static final int VERBOSE_LOGGING_LEVEL_ENABLED = 1;
 
     /**
-     * Verbose logging mode: ENABLED_SHOW_KEY.
-     * This mode causes the Wi-Fi password and encryption keys to be output to the logcat.
-     * This is security sensitive information useful for debugging.
-     * This configuration is enabled for 30 seconds and then falls back to
-     * the regular verbose mode (i.e. to {@link VERBOSE_LOGGING_LEVEL_ENABLED}).
-     * Show key mode is not persistent, i.e. rebooting the device would fallback to
-     * the regular verbose mode.
+     * Verbose logging mode: ENABLED_SHOW_KEY. This mode causes the Wi-Fi password and encryption
+     * keys to be output to the logcat. This is security sensitive information useful for debugging.
+     * This configuration is enabled for 30 seconds and then falls back to the regular verbose mode
+     * (i.e. to {@link VERBOSE_LOGGING_LEVEL_ENABLED}). Show key mode is not persistent, i.e.
+     * rebooting the device would fallback to the regular verbose mode.
+     *
      * @hide
      */
+    @SystemApi public static final int VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY = 2;
+
+    /**
+     * Verbose logging mode: only enable for Wi-Fi Aware feature.
+     *
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.verbose_logging_for_aware_only")
     @SystemApi
-    public static final int VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY = 2;
+    public static final int VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY = 3;
 
     /** @hide */
     @IntDef(prefix = {"VERBOSE_LOGGING_LEVEL_"}, value = {
             VERBOSE_LOGGING_LEVEL_DISABLED,
             VERBOSE_LOGGING_LEVEL_ENABLED,
             VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY,
+            VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface VerboseLoggingLevel {
@@ -10263,6 +10387,86 @@
     }
 
     /**
+     * Wi-Fi Preferred Network Offload (PNO) scanning offloads scanning to the chip to save power
+     * when Wi-Fi is disconnected and the screen is off. See
+     * {@link https://source.android.com/docs/core/connect/wifi-scan} for more details.
+     * <p>
+     * This API can be used to enable or disable PNO scanning. After boot, PNO scanning is enabled
+     * by default. When PNO scanning is disabled, the Wi-Fi framework will not trigger scans at all
+     * when the screen is off. This can be used to save power on devices with small batteries.
+     *
+     * @param enabled True - enable PNO scanning
+     *                False - disable PNO scanning
+     * @param enablePnoScanAfterWifiToggle True - Wifi being enabled by
+     *                                     {@link #setWifiEnabled(boolean)} will re-enable PNO
+     *                                     scanning.
+     *                                     False - Wifi being enabled by
+     *                                     {@link #setWifiEnabled(boolean)} will not re-enable PNO
+     *                                     scanning.
+     *
+     * @throws SecurityException if the caller does not have permission.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.runtime_disable_pno_scan")
+    @SystemApi
+    @RequiresPermission(
+            anyOf = {MANAGE_WIFI_NETWORK_SELECTION, NETWORK_SETTINGS, NETWORK_SETUP_WIZARD})
+    public void setPnoScanState(@PnoScanState int pnoScanState) {
+        try {
+            boolean enabled = false;
+            boolean enablePnoScanAfterWifiToggle = false;
+            switch (pnoScanState) {
+                case PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT:
+                    break;
+                case PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE:
+                    enablePnoScanAfterWifiToggle = true;
+                    break;
+                case PNO_SCAN_STATE_ENABLED:
+                    enabled = true;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid PnoScanState");
+            }
+            mService.setPnoScanEnabled(enabled, enablePnoScanAfterWifiToggle,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Wi-Fi Preferred Network Offload (PNO) scanning offloads scanning to the chip to save power
+     * when Wi-Fi is disconnected and the screen is off. See
+     * {@link https://source.android.com/docs/core/connect/wifi-scan} for more details.
+     * <p>
+     * This API can be used to enable or disable PNO scanning. After boot, PNO scanning is enabled
+     * by default. When PNO scanning is disabled, the Wi-Fi framework will not trigger scans at all
+     * when the screen is off. This can be used to save power on devices with small batteries.
+     *
+     * @param enabled True - enable PNO scanning
+     *                False - disable PNO scanning
+     * @param enablePnoScanAfterWifiToggle True - Wifi being enabled by
+     *                                     {@link #setWifiEnabled(boolean)} will re-enable PNO
+     *                                     scanning.
+     *                                     False - Wifi being enabled by
+     *                                     {@link #setWifiEnabled(boolean)} will not re-enable PNO
+     *                                     scanning.
+     *
+     * @throws SecurityException if the caller does not have permission.
+     * @hide
+     */
+    @RequiresPermission(
+            anyOf = {MANAGE_WIFI_NETWORK_SELECTION, NETWORK_SETTINGS, NETWORK_SETUP_WIZARD})
+    public void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle) {
+        try {
+            mService.setPnoScanEnabled(enabled, enablePnoScanAfterWifiToggle,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Clear the current PNO scan request that's been set by the calling UID. Note, the call will
      * be no-op if the current PNO scan request is set by a different UID.
      *
@@ -11682,6 +11886,8 @@
      * @throws UnsupportedOperationException if the get operation is not supported on this SDK.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info")
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION)
     public void getMaxMloAssociationLinkCount(@NonNull @CallbackExecutor Executor executor,
@@ -11727,6 +11933,8 @@
      * @throws UnsupportedOperationException if the get operation is not supported on this SDK
      * @hide
      */
+    @SystemApi
+    @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info")
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION)
     public void getMaxMloStrLinkCount(@NonNull @CallbackExecutor Executor executor,
@@ -11768,6 +11976,8 @@
      * @throws UnsupportedOperationException if the get operation is not supported on this SDK.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi("com.android.wifi.flags.mlo_link_capabilities_info")
     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION)
     public void getSupportedSimultaneousBandCombinations(
@@ -11798,4 +12008,72 @@
             throw e.rethrowFromSystemServer();
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * This API allows a privileged application to set whether or not this device allows
+     * connections to Wi-Fi WEP networks.
+     *
+     * Note: The WEP connections may not work even if caller invokes this method with {@code true}
+     * because device may NOT support connections to Wi-Fi WEP networks.
+     * See: {@link #isWepSupported()}.
+     *
+     * @param isAllowed whether or not the user allow connections to Wi-Fi WEP networks.
+     * @throws SecurityException if the caller does not have permission.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.wep_usage")
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
+    public void setWepAllowed(boolean isAllowed) {
+        try {
+            mService.setWepAllowed(isAllowed);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Query whether or not this device is configured to allow connections to Wi-Fi WEP networks.
+     * @see #setWepAllowed(boolean)
+     *
+     * Note: The WEP connections may not work even if this method returns {@code true} in the
+     * result callback because device may NOT support connections to Wi-Fi WEP networks.
+     * See: {@link #isWepSupported()}.
+     *
+     * @param executor The executor on which callback will be invoked.
+     * @param resultsCallback An asynchronous callback that will return {@code Boolean} indicating
+     *                        whether wep network support is enabled/disabled.
+     *
+     * @throws SecurityException if the caller does not have permission.
+     * @throws NullPointerException if the caller provided invalid inputs.
+     * @hide
+     */
+    @FlaggedApi("com.android.wifi.flags.wep_usage")
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD
+    })
+    public void queryWepAllowed(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> resultsCallback) {
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
+        try {
+            mService.queryWepAllowed(
+                    new IBooleanListener.Stub() {
+                        @Override
+                        public void onResult(boolean value) {
+                            Binder.clearCallingIdentity();
+                            executor.execute(() -> {
+                                resultsCallback.accept(value);
+                            });
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/framework/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/framework/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index a39a83b..260a0f9 100644
--- a/framework/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/framework/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -133,7 +133,15 @@
 
         if (!mMatchLocalOnlySpecifiers) {
             // Specifiers that match non-local-only networks only match against the band.
-            return ns.getBand() == mBand;
+            if (mBand == ScanResult.WIFI_BAND_5_GHZ_LOW) {
+                return ns.getBand() == ScanResult.WIFI_BAND_5_GHZ_LOW
+                        || ns.getBand() == ScanResult.WIFI_BAND_5_GHZ;
+            } else if (mBand == ScanResult.WIFI_BAND_5_GHZ_HIGH) {
+                return ns.getBand() == ScanResult.WIFI_BAND_5_GHZ_HIGH
+                        || ns.getBand() == ScanResult.WIFI_BAND_5_GHZ;
+            } else {
+                return ns.getBand() == mBand;
+            }
         }
         if (ns.getBand() != ScanResult.UNSPECIFIED && ns.getBand() != mBand) {
             return false;
diff --git a/framework/java/android/net/wifi/WifiNetworkSpecifier.java b/framework/java/android/net/wifi/WifiNetworkSpecifier.java
index 3b1042d..a137b22 100644
--- a/framework/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/framework/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -110,6 +110,8 @@
             case UNSPECIFIED:
             case ScanResult.WIFI_BAND_24_GHZ:
             case ScanResult.WIFI_BAND_5_GHZ:
+            case ScanResult.WIFI_BAND_5_GHZ_LOW:
+            case ScanResult.WIFI_BAND_5_GHZ_HIGH:
             case ScanResult.WIFI_BAND_6_GHZ:
             case ScanResult.WIFI_BAND_60_GHZ:
                 return true;
@@ -329,7 +331,10 @@
 
         /**
          * Set the associated enterprise configuration for this network. Needed for authenticating
-         * to WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
+         * to WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description. Local-only
+         * connection will not support Trust On First Use (TOFU). If TOFU is enabled on this
+         * Enterprise Config, framework will reject the connection. See {@link
+         * WifiEnterpriseConfig#enableTrustOnFirstUse}
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
@@ -367,8 +372,11 @@
         /**
          * Set the associated enterprise configuration for this network. Needed for authenticating
          * to standard WPA3-Enterprise networks. See {@link WifiEnterpriseConfig} for description.
-         * For WPA3-Enterprise in 192-bit security mode networks,
-         * see {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} for description.
+         * For WPA3-Enterprise in 192-bit security mode networks, see {@link
+         * #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} for description. Local-only
+         * connection will not support Trust On First Use (TOFU). If TOFU is enabled on this
+         * Enterprise Config, framework will reject the connection. See {@link
+         * WifiEnterpriseConfig#enableTrustOnFirstUse}
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
@@ -384,15 +392,17 @@
         /**
          * Set the associated enterprise configuration for this network. Needed for authenticating
          * to WPA3-Enterprise in 192-bit security mode networks. See {@link WifiEnterpriseConfig}
-         * for description. Both the client and CA certificates must be provided,
-         * and must be of type of either sha384WithRSAEncryption with key length of 3072bit or
-         * more (OID 1.2.840.113549.1.1.12), or ecdsa-with-SHA384 with key length of 384bit or
-         * more (OID 1.2.840.10045.4.3.3).
+         * for description. Both the client and CA certificates must be provided, and must be of
+         * type of either sha384WithRSAEncryption with key length of 3072bit or more (OID
+         * 1.2.840.113549.1.1.12), or ecdsa-with-SHA384 with key length of 384bit or more (OID
+         * 1.2.840.10045.4.3.3). Local-only connection will not support Trust On First Use (TOFU).
+         * If TOFU is enabled on this Enterprise Config, framework will reject the connection. See
+         * {@link WifiEnterpriseConfig#enableTrustOnFirstUse}
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
-         * @throws IllegalArgumentException if the EAP type or certificates do not
-         *                                  meet 192-bit mode requirements.
+         * @throws IllegalArgumentException if the EAP type or certificates do not meet 192-bit mode
+         *     requirements.
          */
         public @NonNull Builder setWpa3Enterprise192BitModeConfig(
                 @NonNull WifiEnterpriseConfig enterpriseConfig) {
diff --git a/framework/java/android/net/wifi/WifiScanner.java b/framework/java/android/net/wifi/WifiScanner.java
index cb53afe..526ca7c 100644
--- a/framework/java/android/net/wifi/WifiScanner.java
+++ b/framework/java/android/net/wifi/WifiScanner.java
@@ -76,6 +76,18 @@
     /** @hide */
     public static final int WIFI_BAND_COUNT = 5;
 
+    /**
+     * Reserved bit for Multi-internet connection only, not for scanning.
+     * @hide
+     */
+    public static final int WIFI_BAND_INDEX_5_GHZ_LOW = 29;
+
+    /**
+     * Reserved bit for Multi-internet connection only, not for scanning.
+     * @hide
+     */
+    public static final int WIFI_BAND_INDEX_5_GHZ_HIGH = 30;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = {
@@ -100,6 +112,17 @@
     public static final int WIFI_BAND_60_GHZ = 1 << WIFI_BAND_INDEX_60_GHZ;
 
     /**
+     * Reserved for Multi-internet connection only, not for scanning.
+     * @hide
+     */
+    public static final int WIFI_BAND_5_GHZ_LOW = 1 << WIFI_BAND_INDEX_5_GHZ_LOW;
+    /**
+     * Reserved for Multi-internet connection only, not for scanning.
+     * @hide
+     */
+    public static final int WIFI_BAND_5_GHZ_HIGH = 1 << WIFI_BAND_INDEX_5_GHZ_HIGH;
+
+    /**
      * Combination of bands
      * Note that those are only the common band combinations,
      * other combinations can be created by combining any of the basic bands above
diff --git a/framework/java/android/net/wifi/WifiStringResourceWrapper.java b/framework/java/android/net/wifi/WifiStringResourceWrapper.java
index 8a4d2ac..5998483 100644
--- a/framework/java/android/net/wifi/WifiStringResourceWrapper.java
+++ b/framework/java/android/net/wifi/WifiStringResourceWrapper.java
@@ -17,7 +17,6 @@
 package android.net.wifi;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -52,7 +51,7 @@
     private final int mSubId;
     private final int mCarrierId;
 
-    private final Resources mResources;
+    private Resources mResources;
     private final String mCarrierIdPrefix;
 
     @VisibleForTesting
@@ -69,8 +68,6 @@
         mContext = context;
         mSubId = subId;
         mCarrierId = carrierId;
-
-        mResources = getResourcesForSubId();
         mCarrierIdPrefix =
                 CARRIER_ID_RESOURCE_SEPARATOR + mCarrierId + CARRIER_ID_RESOURCE_SEPARATOR;
     }
@@ -79,7 +76,7 @@
      * Returns the string corresponding to the resource ID - or null if no resources exist.
      */
     public String getString(String name, Object... args) {
-        if (mResources == null) return null;
+        if (getResourcesForSubId() == null) return null;
         int resourceId = mResources.getIdentifier(name, "string",
                 mContext.getWifiOverlayApkPkgName());
         if (resourceId == 0) return null;
@@ -105,7 +102,7 @@
      * exist.
      */
     public int getInt(String name, int defaultValue) {
-        if (mResources == null) return defaultValue;
+        if (getResourcesForSubId() == null) return defaultValue;
         int resourceId = mResources.getIdentifier(name, "integer",
                 mContext.getWifiOverlayApkPkgName());
         if (resourceId == 0) return defaultValue;
@@ -129,7 +126,7 @@
      * exist.
      */
     public boolean getBoolean(String name, boolean defaultValue) {
-        if (mResources == null) return defaultValue;
+        if (getResourcesForSubId() == null) return defaultValue;
         int resourceId = mResources.getIdentifier(name, "bool",
                 mContext.getWifiOverlayApkPkgName());
         if (resourceId == 0) return defaultValue;
@@ -177,12 +174,14 @@
      * associated with the subscription.
      */
     private Resources getResourcesForSubId() {
-        try {
-            Context resourceContext = mContext.createPackageContext(
-                    mContext.getWifiOverlayApkPkgName(), 0);
-            return SubscriptionManager.getResourcesForSubId(resourceContext, mSubId);
-        } catch (PackageManager.NameNotFoundException ex) {
+        if (mResources != null) {
+            return mResources;
+        }
+        Context context = mContext.getResourcesApkContext();
+        if (context == null) {
             return null;
         }
+        mResources = SubscriptionManager.getResourcesForSubId(context, mSubId);
+        return mResources;
     }
 }
diff --git a/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java b/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java
index 2af14bf..e147b50 100644
--- a/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java
+++ b/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -53,6 +54,18 @@
             LINK_STATE_IN_USE})
     public @interface LinkState {}
 
+    /** @hide */
+    public static String getLinkStateString(@LinkState int state) {
+        switch (state) {
+            case LINK_STATE_NOT_IN_USE:
+                return "LINK_STATE_NOT_IN_USE";
+            case LINK_STATE_IN_USE:
+                return "LINK_STATE_IN_USE";
+            default:
+                return "LINK_STATE_UNKNOWN";
+        }
+    }
+
     /** Chip does not support reporting the state of the link. */
     public static final int LINK_STATE_UNKNOWN = 0;
     /**
@@ -1160,14 +1173,14 @@
     }
 
     /**
-     * The total time CCA is on busy status on the link frequency in ms counted from the last
-     * radio chip reset.
+     * The total time CCA is on busy status on the link frequency in ms counted from the last radio
+     * chip reset.
      *
      * @param linkId Identifier of the link.
      * @return total time CCA is on busy status for the link in ms.
      * @throws NoSuchElementException if linkId is invalid.
-     * @hide
      */
+    @FlaggedApi("com.android.wifi.flags.add_channel_stats_per_link")
     public long getTotalCcaBusyFreqTimeMillis(int linkId) {
         if (mLinkStats.contains(linkId)) return mLinkStats.get(linkId).mTotalCcaBusyFreqTimeMillis;
         throw new NoSuchElementException("linkId is invalid - " + linkId);
@@ -1188,8 +1201,8 @@
      * @param linkId Identifier of the link.
      * @return The total radio on time for the link in ms.
      * @throws NoSuchElementException if linkId is invalid.
-     * @hide
      */
+    @FlaggedApi("com.android.wifi.flags.add_channel_stats_per_link")
     public long getTotalRadioOnFreqTimeMillis(int linkId) {
         if (mLinkStats.contains(linkId)) return mLinkStats.get(linkId).mTotalRadioOnFreqTimeMillis;
         throw new NoSuchElementException("linkId is invalid - " + linkId);
diff --git a/framework/java/android/net/wifi/aware/PublishConfig.java b/framework/java/android/net/wifi/aware/PublishConfig.java
index 9f3a153..3f82aed 100644
--- a/framework/java/android/net/wifi/aware/PublishConfig.java
+++ b/framework/java/android/net/wifi/aware/PublishConfig.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.net.wifi.ScanResult;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.util.HexEncoding;
 import android.os.Build;
@@ -306,12 +307,12 @@
 
     /**
      * Get the Wi-Fi band for instant communication mode for this publish session
+     *
      * @see Builder#setInstantCommunicationModeEnabled(boolean, int)
-     * @return The Wi-Fi band, one of the {@link WifiScanner#WIFI_BAND_24_GHZ}
-     * or {@link WifiScanner#WIFI_BAND_5_GHZ}. If instant communication mode is not enabled will
-     * return {@link WifiScanner#WIFI_BAND_24_GHZ} as default.
+     * @return The Wi-Fi band. If instant communication mode is not enabled will return {@link
+     *     ScanResult#WIFI_BAND_24_GHZ} as default.
      */
-    @WifiScanner.WifiBand
+    @WifiAwareManager.InstantModeBand
     public int getInstantCommunicationBand() {
         return mBand;
     }
@@ -524,25 +525,23 @@
          * this session. Use {@link Characteristics#isInstantCommunicationModeSupported()} to check
          * if the device supports this feature.
          *
-         * Note: due to increased power requirements of this mode - it will only remain enabled for
-         * 30 seconds from the time the discovery session is started.
+         * <p>Note: due to increased power requirements of this mode - it will only remain enabled
+         * for 30 seconds from the time the discovery session is started.
          *
          * @param enabled true for enable instant communication mode, default is false.
-         * @param band Either {@link WifiScanner#WIFI_BAND_24_GHZ}
-         *             or {@link WifiScanner#WIFI_BAND_5_GHZ}. When setting to
-         *             {@link WifiScanner#WIFI_BAND_5_GHZ}, device will try to enable instant
-         *             communication mode on 5Ghz, but may fall back to 2.4Ghz due to regulatory
-         *             requirements.
+         * @param band When setting to {@link ScanResult#WIFI_BAND_5_GHZ}, device will try to enable
+         *     instant communication mode on 5Ghz, but may fall back to 2.4Ghz due to regulatory
+         *     requirements.
          * @return the current {@link Builder} builder, enabling chaining of builder methods.
          */
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
         @NonNull
-        public Builder setInstantCommunicationModeEnabled(boolean enabled,
-                @WifiScanner.WifiBand int band) {
+        public Builder setInstantCommunicationModeEnabled(
+                boolean enabled, @WifiAwareManager.InstantModeBand int band) {
             if (!SdkLevel.isAtLeastT()) {
                 throw new UnsupportedOperationException();
             }
-            if (band != WifiScanner.WIFI_BAND_24_GHZ && band != WifiScanner.WIFI_BAND_5_GHZ) {
+            if (band != ScanResult.WIFI_BAND_24_GHZ && band != ScanResult.WIFI_BAND_5_GHZ) {
                 throw new IllegalArgumentException();
             }
             mBand = band;
diff --git a/framework/java/android/net/wifi/aware/SubscribeConfig.java b/framework/java/android/net/wifi/aware/SubscribeConfig.java
index 06b0f4e..1fc2952 100644
--- a/framework/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/framework/java/android/net/wifi/aware/SubscribeConfig.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.net.wifi.ScanResult;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.util.HexEncoding;
 import android.os.Build;
@@ -341,12 +342,12 @@
 
     /**
      * Check if enable instant mode on 5G for this subscribe session
+     *
      * @see Builder#setInstantCommunicationModeEnabled(boolean, int)
-     * @return The Wi-Fi band, one of the {@link WifiScanner#WIFI_BAND_24_GHZ}
-     * or {@link WifiScanner#WIFI_BAND_5_GHZ}. If instant communication mode is not enabled will
-     * return {@link WifiScanner#WIFI_BAND_24_GHZ} as default.
+     * @return If instant communication mode is not enabled will return {@link
+     *     ScanResult#WIFI_BAND_24_GHZ} as default.
      */
-    @WifiScanner.WifiBand
+    @WifiAwareManager.InstantModeBand
     public int getInstantCommunicationBand() {
         return mBand;
     }
@@ -594,25 +595,23 @@
          * this session. Use {@link Characteristics#isInstantCommunicationModeSupported()} to check
          * if the device supports this feature.
          *
-         * Note: due to increased power requirements of this mode - it will only remain enabled for
-         * 30 seconds from the time the discovery session is started.
+         * <p>Note: due to increased power requirements of this mode - it will only remain enabled
+         * for 30 seconds from the time the discovery session is started.
          *
          * @param enabled true for enable instant communication mode, default is false.
-         * @param band Either {@link WifiScanner#WIFI_BAND_24_GHZ}
-         *             or {@link WifiScanner#WIFI_BAND_5_GHZ}. When setting to
-         *             {@link WifiScanner#WIFI_BAND_5_GHZ}, device will try to enable instant
-         *             communication mode on 5Ghz, but may fall back to 2.4Ghz due to regulatory
-         *             requirements.
+         * @param band When setting to {@link ScanResult#WIFI_BAND_5_GHZ}, device will try to enable
+         *     instant communication mode on 5Ghz, but may fall back to 2.4Ghz due to regulatory
+         *     requirements.
          * @return the current {@link Builder} builder, enabling chaining of builder methods.
          */
         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
         @NonNull
-        public Builder setInstantCommunicationModeEnabled(boolean enabled,
-                @WifiScanner.WifiBand int band) {
+        public Builder setInstantCommunicationModeEnabled(
+                boolean enabled, @WifiAwareManager.InstantModeBand int band) {
             if (!SdkLevel.isAtLeastT()) {
                 throw new UnsupportedOperationException();
             }
-            if (band != WifiScanner.WIFI_BAND_24_GHZ && band != WifiScanner.WIFI_BAND_5_GHZ) {
+            if (band != ScanResult.WIFI_BAND_24_GHZ && band != ScanResult.WIFI_BAND_5_GHZ) {
                 throw new IllegalArgumentException();
             }
             mBand = band;
diff --git a/framework/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/framework/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 9ae3bd0..2236cfe 100644
--- a/framework/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/framework/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -164,6 +164,7 @@
         Parcel parcel = Parcel.obtain();
         ns.writeToParcel(parcel, 0);
         byte[] bytes = parcel.marshall();
+        parcel.recycle();
 
         mDigester.reset();
         mDigester.update(bytes);
diff --git a/framework/java/android/net/wifi/aware/WifiAwareManager.java b/framework/java/android/net/wifi/aware/WifiAwareManager.java
index 19dc61e..74551f0 100644
--- a/framework/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/framework/java/android/net/wifi/aware/WifiAwareManager.java
@@ -21,6 +21,8 @@
 import static android.Manifest.permission.CHANGE_WIFI_STATE;
 import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
 import static android.Manifest.permission.OVERRIDE_WIFI_CONFIG;
+import static android.net.wifi.ScanResult.WIFI_BAND_24_GHZ;
+import static android.net.wifi.ScanResult.WIFI_BAND_5_GHZ;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -287,6 +289,13 @@
     @SystemApi
     public static final int WIFI_AWARE_RESUME_INTERNAL_ERROR = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = {"WIFI_BAND_"},
+            value = {WIFI_BAND_24_GHZ, WIFI_BAND_5_GHZ})
+    public @interface InstantModeBand {};
+
     private final Context mContext;
     private final IWifiAwareManager mService;
 
diff --git a/framework/java/android/net/wifi/rtt/RangingRequest.java b/framework/java/android/net/wifi/rtt/RangingRequest.java
index c146bc2..aebe0d3 100644
--- a/framework/java/android/net/wifi/rtt/RangingRequest.java
+++ b/framework/java/android/net/wifi/rtt/RangingRequest.java
@@ -154,6 +154,7 @@
         for (ResponderConfig rc : mRttPeers) {
             sj.add(rc.toString());
         }
+        sj.add("mRttBurstSize=" + mRttBurstSize);
         return sj.toString();
     }
 
diff --git a/framework/java/android/net/wifi/rtt/RangingResult.java b/framework/java/android/net/wifi/rtt/RangingResult.java
index 0104431..32a83cf 100644
--- a/framework/java/android/net/wifi/rtt/RangingResult.java
+++ b/framework/java/android/net/wifi/rtt/RangingResult.java
@@ -170,10 +170,20 @@
     }
 
     /** @hide */
-    public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
-            int numSuccessfulMeasurements, byte[] lci, byte[] lcr,
-            ResponderLocation responderLocation, long timestamp) {
+    public RangingResult(
+            @RangeResultStatus int status,
+            PeerHandle peerHandle,
+            int distanceMm,
+            int distanceStdDevMm,
+            int rssi,
+            int numAttemptedMeasurements,
+            int numSuccessfulMeasurements,
+            byte[] lci,
+            byte[] lcr,
+            ResponderLocation responderLocation,
+            long timestamp,
+            int frequencyMHz,
+            int packetBw) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
@@ -187,8 +197,8 @@
         mResponderLocation = responderLocation;
         mTimestamp = timestamp;
         mIs80211mcMeasurement = true;
-        mFrequencyMHz = UNSPECIFIED;
-        mPacketBw = UNSPECIFIED;
+        mFrequencyMHz = frequencyMHz;
+        mPacketBw = packetBw;
     }
 
     /**
@@ -480,49 +490,73 @@
         dest.writeInt(mPacketBw);
     }
 
-    public static final @android.annotation.NonNull Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
-        @Override
-        public RangingResult[] newArray(int size) {
-            return new RangingResult[size];
-        }
+    public static final @android.annotation.NonNull Creator<RangingResult> CREATOR =
+            new Creator<RangingResult>() {
+                @Override
+                public RangingResult[] newArray(int size) {
+                    return new RangingResult[size];
+                }
 
-        @Override
-        public RangingResult createFromParcel(Parcel in) {
-            int status = in.readInt();
-            boolean macAddressPresent = in.readBoolean();
-            MacAddress mac = null;
-            if (macAddressPresent) {
-                mac = MacAddress.CREATOR.createFromParcel(in);
-            }
-            boolean peerHandlePresent = in.readBoolean();
-            PeerHandle peerHandle = null;
-            if (peerHandlePresent) {
-                peerHandle = new PeerHandle(in.readInt());
-            }
-            int distanceMm = in.readInt();
-            int distanceStdDevMm = in.readInt();
-            int rssi = in.readInt();
-            int numAttemptedMeasurements = in.readInt();
-            int numSuccessfulMeasurements = in.readInt();
-            byte[] lci = in.createByteArray();
-            byte[] lcr = in.createByteArray();
-            ResponderLocation responderLocation =
-                    in.readParcelable(this.getClass().getClassLoader());
-            long timestamp = in.readLong();
-            boolean isllmcMeasurement = in.readBoolean();
-            int frequencyMHz = in.readInt();
-            int packetBw = in.readInt();
-            if (peerHandlePresent) {
-                return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr,
-                        responderLocation, timestamp);
-            } else {
-                return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr,
-                        responderLocation, timestamp, isllmcMeasurement, frequencyMHz, packetBw);
-            }
-        }
-    };
+                @Override
+                public RangingResult createFromParcel(Parcel in) {
+                    int status = in.readInt();
+                    boolean macAddressPresent = in.readBoolean();
+                    MacAddress mac = null;
+                    if (macAddressPresent) {
+                        mac = MacAddress.CREATOR.createFromParcel(in);
+                    }
+                    boolean peerHandlePresent = in.readBoolean();
+                    PeerHandle peerHandle = null;
+                    if (peerHandlePresent) {
+                        peerHandle = new PeerHandle(in.readInt());
+                    }
+                    int distanceMm = in.readInt();
+                    int distanceStdDevMm = in.readInt();
+                    int rssi = in.readInt();
+                    int numAttemptedMeasurements = in.readInt();
+                    int numSuccessfulMeasurements = in.readInt();
+                    byte[] lci = in.createByteArray();
+                    byte[] lcr = in.createByteArray();
+                    ResponderLocation responderLocation =
+                            in.readParcelable(this.getClass().getClassLoader());
+                    long timestamp = in.readLong();
+                    boolean isllmcMeasurement = in.readBoolean();
+                    int frequencyMHz = in.readInt();
+                    int packetBw = in.readInt();
+                    if (peerHandlePresent) {
+                        return new RangingResult(
+                                status,
+                                peerHandle,
+                                distanceMm,
+                                distanceStdDevMm,
+                                rssi,
+                                numAttemptedMeasurements,
+                                numSuccessfulMeasurements,
+                                lci,
+                                lcr,
+                                responderLocation,
+                                timestamp,
+                                frequencyMHz,
+                                packetBw);
+                    } else {
+                        return new RangingResult(
+                                status,
+                                mac,
+                                distanceMm,
+                                distanceStdDevMm,
+                                rssi,
+                                numAttemptedMeasurements,
+                                numSuccessfulMeasurements,
+                                lci,
+                                lcr,
+                                responderLocation,
+                                timestamp,
+                                isllmcMeasurement,
+                                frequencyMHz,
+                                packetBw);
+                    }
+                }
+            };
 
     /** @hide */
     @Override
diff --git a/framework/java/android/net/wifi/util/PersistableBundleUtils.java b/framework/java/android/net/wifi/util/PersistableBundleUtils.java
new file mode 100644
index 0000000..671f217
--- /dev/null
+++ b/framework/java/android/net/wifi/util/PersistableBundleUtils.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.util;
+
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Utilities for {@link PersistableBundle}, which does not provide its own implementations of
+ * equals() and hashCode().
+ *
+ * @hide
+ */
+public class PersistableBundleUtils {
+    /** Generate a hashcode for the provided PersistableBundle. */
+    public static int getHashCode(@Nullable PersistableBundle bundle) {
+        if (bundle == null) {
+            return -1;
+        }
+
+        int iterativeHashcode = 0;
+        for (String key : bundle.keySet()) {
+            Object val = bundle.get(key);
+            if (val instanceof PersistableBundle) {
+                iterativeHashcode =
+                        Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
+            } else if (val.getClass().isArray()) {
+                iterativeHashcode = Objects.hash(iterativeHashcode, key, getArrayHashCode(val));
+            } else {
+                iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
+            }
+        }
+
+        return iterativeHashcode;
+    }
+
+    private static int getArrayHashCode(Object arr) {
+        if (arr instanceof boolean[]) {
+            return Arrays.hashCode((boolean[]) arr);
+        } else if (arr instanceof double[]) {
+            return Arrays.hashCode((double[]) arr);
+        } else if (arr instanceof int[]) {
+            return Arrays.hashCode((int[]) arr);
+        } else if (arr instanceof long[]) {
+            return Arrays.hashCode((long[]) arr);
+        } else if (arr instanceof String[]) {
+            return Arrays.hashCode((String[]) arr);
+        }
+        return -1;
+    }
+
+    private static boolean arraysEqual(Object left, Object right) {
+        if (left instanceof boolean[]) {
+            return Arrays.equals((boolean[]) left, (boolean[]) right);
+        } else if (left instanceof double[]) {
+            return Arrays.equals((double[]) left, (double[]) right);
+        } else if (left instanceof int[]) {
+            return Arrays.equals((int[]) left, (int[]) right);
+        } else if (left instanceof long[]) {
+            return Arrays.equals((long[]) left, (long[]) right);
+        } else if (left instanceof String[]) {
+            return Arrays.equals((String[]) left, (String[]) right);
+        }
+        return false;
+    }
+
+    /** Check whether the provided PersistableBundles are equal. */
+    public static boolean isEqual(
+            @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
+        // Check for pointer equality and null equality.
+        if (Objects.equals(left, right)) {
+            return true;
+        }
+        if (Objects.isNull(left) != Objects.isNull(right)) {
+            return false;
+        }
+        if (!left.keySet().equals(right.keySet())) {
+            return false;
+        }
+
+        for (String key : left.keySet()) {
+            Object leftVal = left.get(key);
+            Object rightVal = right.get(key);
+
+            if (Objects.equals(leftVal, rightVal)) {
+                continue;
+            } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
+                // If classes are different, not equal by definition.
+                return false;
+            }
+
+            if (leftVal instanceof PersistableBundle) {
+                if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
+                    return false;
+                }
+            } else if (leftVal.getClass().isArray()) {
+                if (!arraysEqual(leftVal, rightVal)) {
+                    return false;
+                }
+            } else {
+                if (!Objects.equals(leftVal, rightVal)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+}
diff --git a/framework/java/android/net/wifi/util/ScanResultUtil.java b/framework/java/android/net/wifi/util/ScanResultUtil.java
index 07b29b2..9851957 100644
--- a/framework/java/android/net/wifi/util/ScanResultUtil.java
+++ b/framework/java/android/net/wifi/util/ScanResultUtil.java
@@ -66,22 +66,6 @@
         return scanResult.capabilities.contains("WAPI-CERT");
     }
 
-    /**
-     * Helper method to check if the provided |scanResult| corresponds to a EAP network or not.
-     * This checks these conditions:
-     * - Enable EAP/SHA1, EAP/SHA256 AKM, FT/EAP, or EAP-FILS.
-     * - Not a WPA3 Enterprise only network.
-     * - Not a WPA3 Enterprise transition network.
-     */
-    public static boolean isScanResultForEapNetwork(@NonNull ScanResult scanResult) {
-        return (scanResult.capabilities.contains("EAP/SHA1")
-                        || scanResult.capabilities.contains("EAP/SHA256")
-                        || scanResult.capabilities.contains("FT/EAP")
-                        || scanResult.capabilities.contains("EAP-FILS"))
-                && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)
-                && !isScanResultForWpa3EnterpriseTransitionNetwork(scanResult);
-    }
-
     private static boolean isScanResultForPmfMandatoryNetwork(@NonNull ScanResult scanResult) {
         return scanResult.capabilities.contains("[MFPR]");
     }
@@ -91,35 +75,25 @@
     }
 
     /**
-     * Helper method to check if the provided |scanResult| corresponds to a Passpoint R1/R2
-     * network or not.
-     * Passpoint R1/R2 requirements:
-     * - WPA2 Enterprise network.
-     * - interworking bit is set.
+     * Helper method to check if the provided |scanResult| corresponds to a Passpoint R1/R2 network
+     * or not. Passpoint R1/R2 requirements:
+     * - Enterprise network not suite B.
+     * - Interworking bit is set.
      * - HotSpot Release presents.
      */
-    public static boolean isScanResultForPasspointR1R2Network(@NonNull ScanResult scanResult) {
-        if (!isScanResultForEapNetwork(scanResult)) return false;
-
+    public static boolean isEapScanResultForPasspointR1R2Network(@NonNull ScanResult scanResult) {
         return scanResult.isPasspointNetwork();
     }
 
     /**
-     * Helper method to check if the provided |scanResult| corresponds to a Passpoint R3
-     * network or not.
-     * Passpoint R3 requirements:
-     * - Must be WPA2 Enterprise network, WPA3 Enterprise network,
-     *   or WPA3 Enterprise 192-bit mode network.
-     * - interworking bit is set.
+     * Helper method to check if the provided |scanResult| corresponds to a Passpoint R3 network or
+     * not. Passpoint R3 requirements:
+     * - Enterprise network not suite B.
+     * - Interworking bit is set.
      * - HotSpot Release presents.
      * - PMF is mandatory.
      */
-    public static boolean isScanResultForPasspointR3Network(@NonNull ScanResult scanResult) {
-        if (!isScanResultForEapNetwork(scanResult)
-                && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)
-                && !isScanResultForEapSuiteBNetwork(scanResult)) {
-            return false;
-        }
+    public static boolean isEapScanResultForPasspointR3Network(@NonNull ScanResult scanResult) {
         if (!isScanResultForPmfMandatoryNetwork(scanResult)) return false;
 
         return scanResult.isPasspointNetwork();
@@ -297,13 +271,16 @@
     }
 
     /**
-     *  Helper method to check if the provided |scanResult| corresponds to a pure
-     *  WPA2 Enterprise network.
+     * Helper method to check if the provided |scanResult| corresponds to a pure WPA2 Enterprise
+     * network.
      */
-    public static boolean isScanResultForWpa2EnterpriseOnlyNetwork(@NonNull ScanResult r) {
-        return ScanResultUtil.isScanResultForEapNetwork(r)
-                && !ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(r)
-                && !ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r);
+    public static boolean isScanResultForWpa2EnterpriseOnlyNetwork(@NonNull ScanResult scanResult) {
+        return (scanResult.capabilities.contains("EAP/SHA1")
+                        || scanResult.capabilities.contains("EAP/SHA256")
+                        || scanResult.capabilities.contains("FT/EAP")
+                        || scanResult.capabilities.contains("EAP-FILS"))
+                && !isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)
+                && !isScanResultForWpa3EnterpriseTransitionNetwork(scanResult);
     }
 
     /**
@@ -312,8 +289,10 @@
      * EAP, or unknown encryption types or not.
      */
     public static boolean isScanResultForOpenNetwork(@NonNull ScanResult scanResult) {
-        return (!(isScanResultForWepNetwork(scanResult) || isScanResultForPskNetwork(scanResult)
-                || isScanResultForEapNetwork(scanResult) || isScanResultForSaeNetwork(scanResult)
+        return (!(isScanResultForWepNetwork(scanResult)
+                || isScanResultForPskNetwork(scanResult)
+                || isScanResultForWpa2EnterpriseOnlyNetwork(scanResult)
+                || isScanResultForSaeNetwork(scanResult)
                 || isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)
                 || isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)
                 || isScanResultForWapiPskNetwork(scanResult)
@@ -416,6 +395,7 @@
             return list;
         }
 
+        boolean isEapNetworkAndNotSuiteB = false;
         // WPA3 Enterprise 192-bit mode, WPA2/WPA3 enterprise network & its upgradable types
         if (ScanResultUtil.isScanResultForEapSuiteBNetwork(scanResult)) {
             list.add(SecurityParams.createSecurityParamsBySecurityType(
@@ -425,20 +405,26 @@
                     WifiConfiguration.SECURITY_TYPE_EAP));
             list.add(SecurityParams.createSecurityParamsBySecurityType(
                     WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE));
+            isEapNetworkAndNotSuiteB = true;
         } else if (ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)) {
             list.add(SecurityParams.createSecurityParamsBySecurityType(
                     WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE));
-        } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
+            isEapNetworkAndNotSuiteB = true;
+        } else if (ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(scanResult)) {
             list.add(SecurityParams.createSecurityParamsBySecurityType(
                     WifiConfiguration.SECURITY_TYPE_EAP));
+            isEapNetworkAndNotSuiteB = true;
+        }
+        if (!isEapNetworkAndNotSuiteB) {
+            return list;
         }
         // An Enterprise network might be a Passpoint network as well.
         // R3 network might be also a valid R1/R2 network.
-        if (isScanResultForPasspointR1R2Network(scanResult)) {
+        if (isEapScanResultForPasspointR1R2Network(scanResult)) {
             list.add(SecurityParams.createSecurityParamsBySecurityType(
                     WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2));
         }
-        if (isScanResultForPasspointR3Network(scanResult)) {
+        if (isEapScanResultForPasspointR3Network(scanResult)) {
             list.add(SecurityParams.createSecurityParamsBySecurityType(
                     WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3));
         }
diff --git a/framework/tests/src/android/net/wifi/OuiKeyedDataTest.java b/framework/tests/src/android/net/wifi/OuiKeyedDataTest.java
new file mode 100644
index 0000000..0edd366
--- /dev/null
+++ b/framework/tests/src/android/net/wifi/OuiKeyedDataTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public class OuiKeyedDataTest {
+    private static final String INT_FIELD_KEY = "intField";
+    private static final String STRING_FIELD_KEY = "stringField";
+    private static final String ARRAY_FIELD_KEY = "arrayField";
+
+    private final int mTestOui = 0x00112233;
+    private PersistableBundle mTestData;
+    private final int mIntField = 123;
+    private final String mStringField = "someString";
+    private final int[] mArrayField = new int[] {1, 2, 3};
+
+    @Before
+    public void setUp() {
+        assumeTrue(SdkLevel.isAtLeastV());
+        mTestData = createTestBundle();
+    }
+
+    private PersistableBundle createTestBundle() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(INT_FIELD_KEY, mIntField);
+        bundle.putString(STRING_FIELD_KEY, mStringField);
+        bundle.putIntArray(ARRAY_FIELD_KEY, mArrayField.clone());
+        return bundle;
+    }
+
+    private boolean validateTestBundle(PersistableBundle bundle) {
+        return (bundle != null)
+                && Objects.equals(bundle.getInt(INT_FIELD_KEY), mIntField)
+                && Objects.equals(bundle.getString(STRING_FIELD_KEY), mStringField)
+                && Arrays.equals(bundle.getIntArray(ARRAY_FIELD_KEY), mArrayField);
+    }
+
+    /** Tests that the builder throws an exception if given an invalid OUI. */
+    @Test
+    public void testBuilderInvalidOui() {
+        int invalidOui = 0;
+        final OuiKeyedData.Builder zeroOuiBuilder = new OuiKeyedData.Builder(invalidOui, mTestData);
+        assertThrows(IllegalArgumentException.class, () -> zeroOuiBuilder.build());
+
+        invalidOui = 0x11000000; // larger than 24 bits
+        final OuiKeyedData.Builder invalidOuiBuilder =
+                new OuiKeyedData.Builder(invalidOui, mTestData);
+        assertThrows(IllegalArgumentException.class, () -> invalidOuiBuilder.build());
+    }
+
+    /** Tests that the builder throws an exception if given a null data value. */
+    @Test
+    public void testBuilderNullData() {
+        final OuiKeyedData.Builder builder = new OuiKeyedData.Builder(mTestOui, null);
+        assertThrows(IllegalArgumentException.class, () -> builder.build());
+    }
+
+    /** Tests that this class can be properly parceled and unparceled. */
+    @Test
+    public void testParcelReadWrite() {
+        OuiKeyedData data = new OuiKeyedData.Builder(mTestOui, mTestData).build();
+        Parcel parcel = Parcel.obtain();
+        data.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+        OuiKeyedData unparceledData = OuiKeyedData.CREATOR.createFromParcel(parcel);
+
+        assertEquals(mTestOui, unparceledData.getOui());
+        assertTrue(validateTestBundle(unparceledData.getData()));
+    }
+
+    /** Tests the equality and hashcode operations on equivalent instances. */
+    @Test
+    public void testSameObjectComparison() {
+        OuiKeyedData data1 = new OuiKeyedData.Builder(mTestOui, mTestData).build();
+        OuiKeyedData data2 = new OuiKeyedData.Builder(mTestOui, mTestData).build();
+        assertTrue(data1.equals(data2));
+        assertEquals(data1.hashCode(), data2.hashCode());
+    }
+
+    /** Tests the equality and hashcode operations on different instances. */
+    @Test
+    public void testDifferentObjectComparison() {
+        PersistableBundle otherBundle = new PersistableBundle();
+        OuiKeyedData data1 = new OuiKeyedData.Builder(mTestOui, mTestData).build();
+        OuiKeyedData data2 = new OuiKeyedData.Builder(mTestOui, otherBundle).build();
+        assertFalse(data1.equals(data2));
+        assertNotEquals(data1.hashCode(), data2.hashCode());
+    }
+}
diff --git a/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java b/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java
index cf97204..b817177 100644
--- a/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -22,15 +22,18 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.mock;
 
 import android.net.MacAddress;
 import android.os.Parcel;
+import android.os.PersistableBundle;
 import android.util.SparseIntArray;
 
 import androidx.test.filters.SmallTest;
@@ -904,4 +907,49 @@
                 () -> new SoftApConfiguration.Builder()
                         .setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_80MHZ_PLUS_MHZ));
     }
+
+    @Test
+    public void testVendorDataValid() throws Exception {
+        assumeTrue(SdkLevel.isAtLeastV());
+
+        // Default value should be an empty list.
+        SoftApConfiguration config = new SoftApConfiguration.Builder().build();
+        List<OuiKeyedData> retrievedVendorData = config.getVendorData();
+        assertNotNull(retrievedVendorData);
+        assertEquals(0, retrievedVendorData.size());
+
+        List<OuiKeyedData> mockVendorData = Arrays.asList(mock(OuiKeyedData.class));
+        config = new SoftApConfiguration.Builder().setVendorData(mockVendorData).build();
+        assertTrue(mockVendorData.equals(config.getVendorData()));
+    }
+
+    @Test
+    public void testVendorDataInvalid() {
+        assumeTrue(SdkLevel.isAtLeastV());
+        SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
+        // Vendor data must be non-null, if provided.
+        assertThrows(IllegalArgumentException.class, () -> builder.setVendorData(null));
+    }
+
+    @Test
+    public void testVendorDataParcelUnparcel() {
+        assumeTrue(SdkLevel.isAtLeastV());
+
+        int oui = 0x00112233;
+        String fieldKey = "fieldKey";
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(fieldKey, 12345);
+
+        OuiKeyedData ouiKeyedData = new OuiKeyedData.Builder(oui, bundle).build();
+        List<OuiKeyedData> vendorData = Arrays.asList(ouiKeyedData);
+        SoftApConfiguration config =
+                new SoftApConfiguration.Builder().setVendorData(vendorData).build();
+
+        SoftApConfiguration unparceled = parcelUnparcel(config);
+        assertThat(unparceled).isNotSameInstanceAs(config);
+        assertThat(unparceled).isEqualTo(config);
+        assertThat(unparceled.hashCode()).isEqualTo(config.hashCode());
+        OuiKeyedData unparceledOuiKeyedData = unparceled.getVendorData().get(0);
+        assertEquals(bundle.getInt(fieldKey), unparceledOuiKeyedData.getData().getInt(fieldKey));
+    }
 }
diff --git a/framework/tests/src/android/net/wifi/WifiInfoTest.java b/framework/tests/src/android/net/wifi/WifiInfoTest.java
index 4807bec..d860b7b 100644
--- a/framework/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/framework/tests/src/android/net/wifi/WifiInfoTest.java
@@ -833,6 +833,10 @@
         link1.setRxLinkSpeedMbps(TEST_LINK_SPEED);
         link1.setTxLinkSpeedMbps(TEST_LINK_SPEED);
         link1.setState(MloLink.MLO_LINK_STATE_UNASSOCIATED);
+        link1.setLostTxPacketsPerSecond(10);
+        link1.setRetriedTxPacketsRate(20);
+        link1.setSuccessfulTxPacketsPerSecond(30);
+        link1.setSuccessfulRxPacketsPerSecond(40);
 
         // Make sure all parameters are set.
         assertNotNull(link1.getApMacAddress());
@@ -846,6 +850,15 @@
         assertEquals(TEST_MLO_LINK_ID, link1.getLinkId());
         assertEquals(WifiScanner.WIFI_BAND_5_GHZ, link1.getBand());
         assertEquals(MloLink.MLO_LINK_STATE_UNASSOCIATED, link1.getState());
+        assertEquals(10, link1.getLostTxPacketsPerSecond(), 0);
+        assertEquals(20, link1.getRetriedTxPacketsPerSecond(), 0);
+        assertEquals(30, link1.getSuccessfulTxPacketsPerSecond(), 0);
+        assertEquals(40, link1.getSuccessfulRxPacketsPerSecond(), 0);
+        // Check default values
+        assertEquals(0, link1.txBad);
+        assertEquals(0, link1.txRetries);
+        assertEquals(0, link1.txSuccess);
+        assertEquals(0, link1.rxSuccess);
 
         // Test RSSI range.
         link1.setRssi(WifiInfo.INVALID_RSSI - 1);
diff --git a/framework/tests/src/android/net/wifi/WifiManagerTest.java b/framework/tests/src/android/net/wifi/WifiManagerTest.java
index c009d97..9f2c44a 100644
--- a/framework/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/framework/tests/src/android/net/wifi/WifiManagerTest.java
@@ -33,6 +33,10 @@
 import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
 import static android.net.wifi.WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
 import static android.net.wifi.WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
@@ -53,6 +57,8 @@
 import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_T2LM_NEGOTIATION;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WEP;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA_PERSONAL;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
 import static android.net.wifi.WifiManager.WpsCallback;
@@ -3578,6 +3584,22 @@
         verify(mWifiService).flushPasspointAnqpCache(anyString());
     }
 
+    @Test
+    public void testSetPnoScanState() throws Exception {
+        mWifiManager.setPnoScanState(WifiManager.PNO_SCAN_STATE_DISABLED_UNTIL_WIFI_TOGGLE);
+        verify(mWifiService).setPnoScanEnabled(false, true, TEST_PACKAGE_NAME);
+
+        mWifiManager.setPnoScanState(WifiManager.PNO_SCAN_STATE_DISABLED_UNTIL_REBOOT);
+        verify(mWifiService).setPnoScanEnabled(false, false, TEST_PACKAGE_NAME);
+
+        mWifiManager.setPnoScanState(WifiManager.PNO_SCAN_STATE_ENABLED);
+        verify(mWifiService).setPnoScanEnabled(eq(true), anyBoolean(), any());
+
+        assertThrows(IllegalArgumentException.class, () -> mWifiManager.setPnoScanState(999));
+    }
+
+
+
     /**
      * Test behavior of isDecoratedIdentitySupported
      */
@@ -4069,4 +4091,71 @@
         mWifiManager.getMloMode(executor, resultsGetCallback);
         verify(mWifiService).getMloMode(any(IIntegerListener.Stub.class));
     }
+
+    @Test
+    public void testVerboseLogging() throws RemoteException {
+        mWifiManager.setVerboseLoggingEnabled(true);
+        verify(mWifiService).enableVerboseLogging(VERBOSE_LOGGING_LEVEL_ENABLED);
+        mWifiManager.setVerboseLoggingEnabled(false);
+        verify(mWifiService).enableVerboseLogging(VERBOSE_LOGGING_LEVEL_DISABLED);
+        when(mWifiService.getVerboseLoggingLevel()).thenReturn(VERBOSE_LOGGING_LEVEL_ENABLED);
+        assertTrue(mWifiManager.isVerboseLoggingEnabled());
+        when(mWifiService.getVerboseLoggingLevel())
+                .thenReturn(VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY);
+        assertTrue(mWifiManager.isVerboseLoggingEnabled());
+        when(mWifiService.getVerboseLoggingLevel())
+                .thenReturn(VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY);
+        assertFalse(mWifiManager.isVerboseLoggingEnabled());
+        when(mWifiService.getVerboseLoggingLevel()).thenReturn(VERBOSE_LOGGING_LEVEL_DISABLED);
+        assertFalse(mWifiManager.isVerboseLoggingEnabled());
+    }
+
+    /**
+     * Test behavior of isWepSupported
+     */
+    @Test
+    public void testIsWepSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WIFI_FEATURE_WEP));
+        assertTrue(mWifiManager.isWepSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WIFI_FEATURE_WEP));
+        assertFalse(mWifiManager.isWepSupported());
+    }
+
+    /**
+     * Test behavior of isWpaPersonalSupported
+     */
+    @Test
+    public void testIsWpaPersonalSupported() throws Exception {
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(WIFI_FEATURE_WPA_PERSONAL));
+        assertTrue(mWifiManager.isWpaPersonalSupported());
+        when(mWifiService.getSupportedFeatures())
+                .thenReturn(new Long(~WIFI_FEATURE_WPA_PERSONAL));
+        assertFalse(mWifiManager.isWpaPersonalSupported());
+    }
+
+    @Test
+    public void testSetWepAllowed() throws Exception {
+        mWifiManager.setWepAllowed(true);
+        verify(mWifiService).setWepAllowed(true);
+        mWifiManager.setWepAllowed(false);
+        verify(mWifiService).setWepAllowed(false);
+    }
+
+    @Test
+    public void testQueryWepAllowed() throws Exception {
+        Consumer<Boolean> resultsSetCallback = mock(Consumer.class);
+        SynchronousExecutor executor = mock(SynchronousExecutor.class);
+        // Null executor/callback exception.
+        assertThrows("null executor should trigger exception", NullPointerException.class,
+                () -> mWifiManager.queryWepAllowed(null, resultsSetCallback));
+        assertThrows("null listener should trigger exception", NullPointerException.class,
+                () -> mWifiManager.queryWepAllowed(executor, null));
+        // Set and verify.
+        mWifiManager.queryWepAllowed(executor, resultsSetCallback);
+        verify(mWifiService).queryWepAllowed(
+                any(IBooleanListener.Stub.class));
+    }
 }
diff --git a/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index 53a0130..bc22b80 100644
--- a/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -340,6 +340,77 @@
     }
 
     /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier}
+     * WIFI_BAND_5_GHZ_LOW and WIFI_BAND_5_GHZ_HIGH band matching.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDual5GHZBandMatching() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID + "\"";
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+
+        // Define three types of WifiNetworkAgentSpecifier: 5G, 5GLow, and 5GHigh
+        WifiNetworkAgentSpecifier wifi5GNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent, ScanResult.WIFI_BAND_5_GHZ, false);
+        WifiNetworkAgentSpecifier wifi5GLowNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent, ScanResult.WIFI_BAND_5_GHZ_LOW, false);
+        WifiNetworkAgentSpecifier wifi5GHighNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent, ScanResult.WIFI_BAND_5_GHZ_HIGH, false);
+
+        // Define three types of WifiNetworkSpecifier: 5G, 5GLow, and 5GHigh.
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                ScanResult.WIFI_BAND_5_GHZ,
+                wificonfigurationNetworkSpecifier, new int[0]);
+        WifiNetworkSpecifier wifi5GLowNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                ScanResult.WIFI_BAND_5_GHZ_LOW,
+                wificonfigurationNetworkSpecifier, new int[0]);
+        WifiNetworkSpecifier wifi5GHighNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                ScanResult.WIFI_BAND_5_GHZ_HIGH,
+                wificonfigurationNetworkSpecifier, new int[0]);
+
+        // mBand = WIFI_BAND_5_GHZ_LOW
+        // Same band matches.
+        assertTrue(wifi5GLowNetworkSpecifier.canBeSatisfiedBy(wifi5GLowNetworkAgentSpecifier));
+        assertTrue(wifi5GLowNetworkAgentSpecifier.canBeSatisfiedBy(wifi5GLowNetworkSpecifier));
+        // WIFI_BAND_5_GHZ_LOW matches with WIFI_BAND_5_GHZ
+        assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifi5GLowNetworkAgentSpecifier));
+        assertTrue(wifi5GLowNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
+
+        // mBand = WIFI_BAND_5_GHZ_HIGH
+        // Same band matches.
+        assertTrue(wifi5GHighNetworkSpecifier.canBeSatisfiedBy(wifi5GHighNetworkAgentSpecifier));
+        assertTrue(wifi5GHighNetworkAgentSpecifier.canBeSatisfiedBy(wifi5GHighNetworkSpecifier));
+        // WIFI_BAND_5_GHZ_HIGH matches with WIFI_BAND_5_GHZ
+        assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifi5GHighNetworkAgentSpecifier));
+        assertTrue(wifi5GHighNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
+
+        // mBand = WIFI_BAND_5_GHZ
+        // WIFI_BAND_5_GHZ matches with WIFI_BAND_5_GHZ_LOW
+        assertFalse(wifi5GLowNetworkSpecifier.canBeSatisfiedBy(wifi5GNetworkAgentSpecifier));
+        assertFalse(wifi5GNetworkAgentSpecifier.canBeSatisfiedBy(wifi5GLowNetworkSpecifier));
+        // WIFI_BAND_5_GHZ matches with WIFI_BAND_5_GHZ_HIGH
+        assertFalse(wifi5GHighNetworkSpecifier.canBeSatisfiedBy(wifi5GNetworkAgentSpecifier));
+        assertFalse(wifi5GNetworkAgentSpecifier.canBeSatisfiedBy(wifi5GHighNetworkSpecifier));
+    }
+
+    /**
      * Validate {@link WifiNetworkAgentSpecifier} local-only matching.
      */
     @Test
diff --git a/framework/tests/src/android/net/wifi/WifiStringResourceWrapperTest.java b/framework/tests/src/android/net/wifi/WifiStringResourceWrapperTest.java
index ec28d0e..1428dfc 100644
--- a/framework/tests/src/android/net/wifi/WifiStringResourceWrapperTest.java
+++ b/framework/tests/src/android/net/wifi/WifiStringResourceWrapperTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.validateMockitoUsage;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.telephony.SubscriptionManager;
 
@@ -51,6 +52,7 @@
 
     @Mock WifiContext mContext;
     @Mock Resources mResources;
+    @Mock Context mApkContext;
 
     WifiStringResourceWrapper mDut;
 
@@ -117,6 +119,7 @@
                 eq(RES_NAME_DISABLE_THRESHOLD + CARRIER_ID_RESOURCE_NAME_SUFFIX),
                 eq("array"), any())).thenReturn(RES_ID_NOT_FOUND);
 
+        when(mContext.getResourcesApkContext()).thenReturn(mApkContext);
         mDut = new WifiStringResourceWrapper(mContext, SUB_ID, CARRIER_ID);
         when(mResources.getIdentifier(eq(RES_NAME_OOB_PSEUDONYM_ENABLED), eq("bool"), any()))
                 .thenReturn(RES_ID_OOB_PSEUDONYM_ENABLED);
diff --git a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index b9e482e..4f4e37e 100644
--- a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -463,8 +463,21 @@
         assertEquals(result, rereadResult);
 
         // RangingResults constructed with a PeerHandle
-        result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
-                numAttemptedMeasurements, numSuccessfulMeasurements, null, null, null, timestamp);
+        result =
+                new RangingResult(
+                        status,
+                        peerHandle,
+                        distanceCm,
+                        distanceStdDevCm,
+                        rssi,
+                        numAttemptedMeasurements,
+                        numSuccessfulMeasurements,
+                        null,
+                        null,
+                        null,
+                        timestamp,
+                        RangingResult.UNSPECIFIED,
+                        RangingResult.UNSPECIFIED);
 
         parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
diff --git a/framework/tests/src/android/net/wifi/util/PersistableBundleUtilsTest.java b/framework/tests/src/android/net/wifi/util/PersistableBundleUtilsTest.java
new file mode 100644
index 0000000..54bd05b
--- /dev/null
+++ b/framework/tests/src/android/net/wifi/util/PersistableBundleUtilsTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PersistableBundle;
+
+import org.junit.Test;
+
+public class PersistableBundleUtilsTest {
+    private static final String INT_FIELD_KEY = "intField";
+    private static final String STRING_FIELD_KEY = "stringField";
+    private static final String ARRAY_FIELD_KEY = "arrayField";
+    private static final String BUNDLE_FIELD_KEY = "bundleField";
+    private static final String EXTRA_FIELD_KEY = "extraField";
+
+    private int mIntField = 12345;
+    private String mStringField = "someString";
+    private int[] mArrayField = new int[] {1, 2, 3, 4, 5};
+
+    private PersistableBundle createTestBundle() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(INT_FIELD_KEY, mIntField);
+        bundle.putString(STRING_FIELD_KEY, mStringField);
+        bundle.putIntArray(ARRAY_FIELD_KEY, mArrayField.clone());
+        return bundle;
+    }
+
+    private PersistableBundle createNestedTestBundle(int depth) {
+        PersistableBundle bundle = createTestBundle();
+        if (depth == 0) {
+            return bundle;
+        }
+        bundle.putPersistableBundle(BUNDLE_FIELD_KEY, createNestedTestBundle(depth - 1));
+        return bundle;
+    }
+
+    /** Verify that the same hashcode is returned for two different instances of the same object. */
+    @Test
+    public void testHashCode() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        assertEquals(
+                PersistableBundleUtils.getHashCode(bundle1),
+                PersistableBundleUtils.getHashCode(bundle2));
+    }
+
+    /**
+     * Verify that the same hashcode is returned for two different instances of the same nested
+     * object.
+     */
+    @Test
+    public void testHashCode_nested() {
+        PersistableBundle bundle1 = createNestedTestBundle(10);
+        PersistableBundle bundle2 = createNestedTestBundle(10);
+        assertEquals(
+                PersistableBundleUtils.getHashCode(bundle1),
+                PersistableBundleUtils.getHashCode(bundle2));
+    }
+
+    /** Verify that a different hashcode is returned for two different objects. */
+    @Test
+    public void testHashCode_different() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle2.putString(EXTRA_FIELD_KEY, "anotherStringField");
+        assertNotEquals(
+                PersistableBundleUtils.getHashCode(bundle1),
+                PersistableBundleUtils.getHashCode(bundle2));
+    }
+
+    /** Verify that two different instances of the same object are considered equal. */
+    @Test
+    public void testEquality() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        assertTrue(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /** Verify that the same instance of an object is considered equal to itself. */
+    @Test
+    public void testEquality_sameInstance() {
+        PersistableBundle bundle = createTestBundle();
+        assertTrue(PersistableBundleUtils.isEqual(bundle, bundle));
+    }
+
+    /** Verify the equality when one or both inputs are null. */
+    @Test
+    public void testEquality_nullInstance() {
+        PersistableBundle bundle = createTestBundle();
+        assertTrue(PersistableBundleUtils.isEqual(null, null));
+        assertFalse(PersistableBundleUtils.isEqual(bundle, null));
+        assertFalse(PersistableBundleUtils.isEqual(null, bundle));
+    }
+
+    /** Test that fields with a null value are compared correctly. */
+    @Test
+    public void testEquality_nullField() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle1.putString(EXTRA_FIELD_KEY, null);
+        bundle2.putString(EXTRA_FIELD_KEY, null);
+        assertTrue(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /** Verify that two objects with different keysets are not considered equal. */
+    @Test
+    public void testEquality_differentKeys() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle1.putString(EXTRA_FIELD_KEY, null);
+        assertFalse(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /**
+     * Verify that a field with the same key, but a different value type, is not considered equal.
+     */
+    @Test
+    public void testEquality_differentFieldType() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle2.remove(STRING_FIELD_KEY);
+        bundle2.putInt(STRING_FIELD_KEY, 1337);
+        assertFalse(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /**
+     * Verify that a field with the same key and value type, but a different value, is not
+     * considered equal.
+     */
+    @Test
+    public void testEquality_differentFieldVal() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle2.putString(STRING_FIELD_KEY, "differentString");
+        assertFalse(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /** Verify that an array field with a different value is not considered equal. */
+    @Test
+    public void testEquality_differentArray() {
+        PersistableBundle bundle1 = createTestBundle();
+        PersistableBundle bundle2 = createTestBundle();
+        bundle2.putIntArray(ARRAY_FIELD_KEY, new int[] {7, 8, 9});
+        assertFalse(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /** Verify that two instances of the same nested object are considered equal. */
+    @Test
+    public void testEquality_nested_same() {
+        PersistableBundle bundle1 = createNestedTestBundle(10);
+        PersistableBundle bundle2 = createNestedTestBundle(10);
+        assertTrue(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+
+    /** Verify that two different nested objects are not considered equal. */
+    @Test
+    public void testEquality_nested_different() {
+        PersistableBundle bundle1 = createNestedTestBundle(9);
+        PersistableBundle bundle2 = createNestedTestBundle(10);
+        assertFalse(PersistableBundleUtils.isEqual(bundle1, bundle2));
+    }
+}
diff --git a/framework/tests/src/android/net/wifi/util/ScanResultUtilTest.java b/framework/tests/src/android/net/wifi/util/ScanResultUtilTest.java
index d2349dc..b111633 100644
--- a/framework/tests/src/android/net/wifi/util/ScanResultUtilTest.java
+++ b/framework/tests/src/android/net/wifi/util/ScanResultUtilTest.java
@@ -24,6 +24,7 @@
 import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.ScanResult.InformationElement;
+import android.net.wifi.SecurityParams;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiSsid;
 
@@ -133,6 +134,178 @@
                 config.getDefaultSecurityParams().getSecurityType());
     }
 
+    @Test
+    public void testGenerateSecurityParamsListFromScanResult() {
+        final String ssid = "Another SSid";
+        ScanResult scanResult =
+                new ScanResult(
+                        ssid,
+                        "ab:cd:01:ef:45:89",
+                        1245,
+                        0,
+                        "",
+                        -78,
+                        2450,
+                        1025,
+                        22,
+                        33,
+                        20,
+                        0,
+                        0,
+                        true);
+        scanResult.informationElements =
+                new InformationElement[] {
+                    createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+                };
+        List<SecurityParams> securityParamsList;
+
+        scanResult.capabilities = "";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_OPEN, securityParamsList.get(0).getSecurityType());
+
+        scanResult.capabilities = "OWE_TRANSITION";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(2, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_OPEN, securityParamsList.get(0).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_OWE, securityParamsList.get(1).getSecurityType());
+
+        scanResult.capabilities = "OWE";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_OWE, securityParamsList.get(0).getSecurityType());
+
+        scanResult.capabilities = "WEP";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_WEP, securityParamsList.get(0).getSecurityType());
+
+        scanResult.capabilities = "PSK";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_PSK, securityParamsList.get(0).getSecurityType());
+
+        // WPA2 Enterprise network with none MFP capability.
+        scanResult.capabilities = "[EAP/SHA1]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+
+        // WPA2 Enterprise network with MFPC.
+        scanResult.capabilities = "[EAP/SHA1][MFPC]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+
+        // WPA2 Enterprise network with MFPR.
+        scanResult.capabilities = "[EAP/SHA1][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+
+        // WPA3 Enterprise transition network
+        scanResult.capabilities = "[RSN-EAP/SHA1+EAP/SHA256][MFPC]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(2, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
+                securityParamsList.get(1).getSecurityType());
+
+        // WPA3 Enterprise only network
+        scanResult.capabilities = "[RSN-EAP/SHA256][MFPC][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
+                securityParamsList.get(0).getSecurityType());
+
+        // Neither a valid WPA3 Enterprise transition network nor WPA3 Enterprise only network
+        // Fallback to WPA2 Enterprise
+        scanResult.capabilities = "[RSN-EAP/SHA1+EAP/SHA256][MFPC][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+
+        // WPA3 Enterprise only network
+        scanResult.capabilities = "[RSN-SUITE_B_192][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT,
+                securityParamsList.get(0).getSecurityType());
+
+        scanResult.capabilities = "WAPI-PSK";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_WAPI_PSK,
+                securityParamsList.get(0).getSecurityType());
+
+        scanResult.capabilities = "WAPI-CERT";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_WAPI_CERT,
+                securityParamsList.get(0).getSecurityType());
+
+        // Passpoint
+        scanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
+
+        scanResult.capabilities = "[EAP/SHA1]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(2, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2,
+                securityParamsList.get(1).getSecurityType());
+
+        scanResult.capabilities = "[RSN-EAP/SHA1+EAP/SHA256][MFPC]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(3, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP, securityParamsList.get(0).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
+                securityParamsList.get(1).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2,
+                securityParamsList.get(2).getSecurityType());
+
+        scanResult.capabilities = "[RSN-EAP/SHA256][MFPC][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(3, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
+                securityParamsList.get(0).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2,
+                securityParamsList.get(1).getSecurityType());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3,
+                securityParamsList.get(2).getSecurityType());
+
+        // Suite B should not be passpoint
+        scanResult.capabilities = "[RSN-SUITE_B_192][MFPR]";
+        securityParamsList = ScanResultUtil.generateSecurityParamsListFromScanResult(scanResult);
+        assertEquals(1, securityParamsList.size());
+        assertEquals(
+                WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT,
+                securityParamsList.get(0).getSecurityType());
+    }
+
     /**
      * Test that a PSK-SHA256+SAE network is detected as transition mode
      */
@@ -244,7 +417,7 @@
                 createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
         };
 
-        assertTrue(ScanResultUtil.isScanResultForEapNetwork(input));
+        assertTrue(ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(input));
     }
 
     /**
@@ -264,7 +437,7 @@
                 createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
         };
 
-        assertTrue(ScanResultUtil.isScanResultForEapNetwork(input));
+        assertTrue(ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(input));
         assertTrue(ScanResultUtil.isScanResultForFilsSha256Network(input));
     }
 
@@ -285,7 +458,7 @@
                 createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
         };
 
-        assertTrue(ScanResultUtil.isScanResultForEapNetwork(input));
+        assertTrue(ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(input));
         assertTrue(ScanResultUtil.isScanResultForFilsSha384Network(input));
     }
 
@@ -305,9 +478,9 @@
                 createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
         };
 
-        assertTrue(ScanResultUtil.isScanResultForEapNetwork(input));
-        assertFalse(ScanResultUtil.isScanResultForPasspointR1R2Network(input));
-        assertFalse(ScanResultUtil.isScanResultForPasspointR3Network(input));
+        assertTrue(ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(input));
+        assertFalse(ScanResultUtil.isEapScanResultForPasspointR1R2Network(input));
+        assertFalse(ScanResultUtil.isEapScanResultForPasspointR3Network(input));
     }
 
     private void verifyPasspointNetwork(
@@ -322,9 +495,9 @@
             input.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
         }
 
-        assertTrue(ScanResultUtil.isScanResultForEapNetwork(input));
-        assertEquals(isR1R2Network, ScanResultUtil.isScanResultForPasspointR1R2Network(input));
-        assertEquals(isR3Network, ScanResultUtil.isScanResultForPasspointR3Network(input));
+        assertTrue(ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(input));
+        assertEquals(isR1R2Network, ScanResultUtil.isEapScanResultForPasspointR1R2Network(input));
+        assertEquals(isR3Network, ScanResultUtil.isEapScanResultForPasspointR3Network(input));
     }
 
     /**
diff --git a/metrics_pdd_hook.py b/metrics_pdd_hook.py
deleted file mode 100755
index 65d467c..0000000
--- a/metrics_pdd_hook.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/python3
-
-#
-# Copyright 2020, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from __future__ import print_function
-
-from argparse import ArgumentParser
-import subprocess
-import sys
-
-
-def is_in_aosp():
-    branches = subprocess.check_output(['git', 'branch', '-vv']).splitlines()
-
-    for branch in branches:
-        # current branch starts with a '*'
-        if branch.startswith(b'*'):
-            return b'[aosp/' in branch
-
-    # otherwise assume in AOSP
-    return True
-
-
-def is_commit_msg_valid(commit_msg):
-    for line in commit_msg.splitlines():
-        line = line.strip().lower()
-        if line.startswith('updated-pdd'):
-            return True
-
-    return False
-
-
-def main():
-    parser = ArgumentParser(description='Check if the Privacy Design Doc (PDD) has been updated')
-    parser.add_argument('metrics_file', type=str, help='path to the metrics Protobuf file')
-    parser.add_argument('commit_msg', type=str, help='commit message')
-    parser.add_argument('commit_files', type=str, nargs='*', help='files changed in the commit')
-    args = parser.parse_args()
-
-    metrics_file = args.metrics_file
-    commit_msg = args.commit_msg
-    commit_files = args.commit_files
-
-    if is_in_aosp():
-        return 0
-
-    if metrics_file not in commit_files:
-        return 0
-
-    if is_commit_msg_valid(commit_msg):
-        return 0
-
-    print('This commit has changed {metrics_file}.'.format(metrics_file=metrics_file))
-    print('If this change added/changed/removed metrics collected from the device,')
-    print('please update the Wifi Metrics Privacy Design Doc (PDD) at go/wifi-metrics-pdd')
-    print('and acknowledge you have done so by adding this line to your commit message:')
-    print()
-    print('Updated-PDD: TRUE')
-    print()
-    print('Otherwise, please explain why the PDD does not need to be updated:')
-    print()
-    print('Updated-PDD: Not applicable - reformatted file')
-    print()
-    print('Please reach out to the OWNERS for more information about the Wifi Metrics PDD.')
-    return 1
-
-
-if __name__ == '__main__':
-    exit_code = main()
-    sys.exit(exit_code)
diff --git a/service/Android.bp b/service/Android.bp
index e8412e9..10bcdba 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -63,6 +63,7 @@
         "framework-configinfrastructure",
         "framework-connectivity.stubs.module_lib",
         "framework-connectivity-t.stubs.module_lib",
+        "framework-location.stubs.module_lib",
         "framework-statsd.stubs.module_lib",
         "framework-tethering.stubs.module_lib",
         "unsupportedappusage",
@@ -75,8 +76,10 @@
     ],
 
     static_libs: [
+        // Types-only package shared across the HALs
+        "android.hardware.wifi.common-V1-java",
         // AIDL vendor hal implementation
-        "android.hardware.wifi-V1-java",
+        "android.hardware.wifi-V2-java",
         // HIDL vendor hal implementation
         "android.hardware.wifi-V1.0-java",
         "android.hardware.wifi-V1.1-java",
@@ -86,14 +89,14 @@
         "android.hardware.wifi-V1.5-java",
         "android.hardware.wifi-V1.6-java",
         // AIDL hostapd implementation
-        "android.hardware.wifi.hostapd-V1-java",
+        "android.hardware.wifi.hostapd-V2-java",
         // HIDL hostapd implementation
         "android.hardware.wifi.hostapd-V1.0-java",
         "android.hardware.wifi.hostapd-V1.1-java",
         "android.hardware.wifi.hostapd-V1.2-java",
         "android.hardware.wifi.hostapd-V1.3-java",
         // AIDL supplicant implementation
-        "android.hardware.wifi.supplicant-V2-java",
+        "android.hardware.wifi.supplicant-V3-java",
         // HIDL supplicant implementation
         "android.hardware.wifi.supplicant-V1.0-java",
         "android.hardware.wifi.supplicant-V1.1-java",
@@ -118,6 +121,7 @@
         "service-entitlement",
         "wifi-lite-protos",
         "wifi-nano-protos",
+        "wifi_aconfig_flags_lib",
     ],
     apex_available: ["com.android.wifi"],
 }
diff --git a/service/ServiceWifiResources/res/layout/wifi_p2p_dialog.xml b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog.xml
index 8579493..947f976 100644
--- a/service/ServiceWifiResources/res/layout/wifi_p2p_dialog.xml
+++ b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog.xml
@@ -23,6 +23,10 @@
         android:layout_height="wrap_content"
         android:orientation="vertical">
 
+        <TextView android:id="@+id/time_remaining"
+                  style="@style/wifi_p2p_dialog_timeout"
+                  android:visibility="gone"/>
+
         <LinearLayout android:id="@+id/info"
             style="@style/wifi_section" />
 
diff --git a/service/ServiceWifiResources/res/values-af/strings.xml b/service/ServiceWifiResources/res/values-af/strings.xml
index 18b4316..5e429c9 100644
--- a/service/ServiceWifiResources/res/values-af/strings.xml
+++ b/service/ServiceWifiResources/res/values-af/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Laat toe"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Moenie toelaat nie"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑fi is aan in vliegtuigmodus"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"As jy wi-fi aangeskakel hou, sal jou foon onthou om dit aan te hou wanneer jy weer in vliegtuigmodus is"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"As jy wi-fi aangeskakel hou, sal jou toestel onthou om dit aan te hou wanneer jy weer in vliegtuigmodus is"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑fi bly aan"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Jou foon onthou om wi‑fi aan te hou in vliegtuigmodus. Skakel wi-fi af as jy nie wil hê dit moet aan bly nie."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Jou toestel onthou om wi‑fi aan te hou in vliegtuigmodus. Skakel wi-fi af as jy nie wil hê dit moet aan bly nie."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Netwerk is onbeskikbaar"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is deur jou administrateur gedeaktiveer."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Maak toe"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Skakel tussen wi-fi-netwerke oor?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Dit lyk nie of <xliff:g id="SSID_0">%1$s</xliff:g> aan die internet gekoppel is nie. Skakel oor na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Dit lyk nie of <xliff:g id="SSID_0">%1$s</xliff:g> aan die internet gekoppel is nie. Skakel oor na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is lae gehalte. Skakel oor na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Skakel oor"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Moenie oorskakel nie"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-am/strings.xml b/service/ServiceWifiResources/res/values-am/strings.xml
index 1724960..24af7e8 100644
--- a/service/ServiceWifiResources/res/values-am/strings.xml
+++ b/service/ServiceWifiResources/res/values-am/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ፍቀድ"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"አትፍቀድ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"በአውሮፕላን ሁነታ ላይ Wi-Fi በርቷል"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi‑Fiን አብርተው የሚያቆዩ ከሆነ ቀጣይ በአውሮፕላን ሁነታ ላይ ሲሆኑ ስልክዎ አብርቶ ማቆየትን ያስታውሳል።"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi‑Fiን አብርተው የሚያቆዩ ከሆነ ቀጣይ በአውሮፕላን ሁነታ ላይ ሲሆኑ መሣሪያዎ እሱን አብርቶ ማቆየትን ያስታውሳል።"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi በርቶ ይቆያል"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ስልክዎ በአውሮፕላን ሁነታ ላይ Wi-Fiን አብርቶ ማቆየትን ያስታውሳል። በርቶ እንዲቆይ ካልፈለጉ Wi-Fiን ያጥፉ።"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"መሣሪያዎ በአውሮፕላን ሁነታ ውስጥ Wi-Fiን አብርቶ ማቆየትን ያስታውሳል። በርቶ እንዲቆይ ካልፈለጉ Wi-Fiን ያጥፉ።"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"የማይገኝ አውታረ መረብ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> በእርስዎ አስተዳዳሪ ተሰናክሏል።"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ዝጋ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"የWi-Fi አውታረ መረቦች ይቀየሩ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ከበይነመረቡ ጋር የተገናኘ አይመስልም። ወደ <xliff:g id="SSID_1">%2$s</xliff:g> ይቀየር?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ከበይነመረቡ ጋር የተገናኘ አይመስልም። ወደ <xliff:g id="SSID_1">%2$s</xliff:g> ይቀየር?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ዝቅተኛ ጥራት ነው። ወደ <xliff:g id="SSID_1">%2$s</xliff:g> ይቀየር?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ቀይር"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"አትቀይር"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ar/strings.xml b/service/ServiceWifiResources/res/values-ar/strings.xml
index 8e40702..9b015a2 100644
--- a/service/ServiceWifiResources/res/values-ar/strings.xml
+++ b/service/ServiceWifiResources/res/values-ar/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"السماح"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"عدم السماح"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"‏شبكة Wi‑Fi مفعَّلة في \"وضع الطيران\""</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"‏إذا واصلت تفعيل شبكة Wi‑Fi، سيتذكر هاتفك إبقاءها مفعَّلة في المرة القادمة التي تفعِّل فيها \"وضع الطيران\"."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"‏إذا واصلت تفعيل شبكة Wi‑Fi، سيتذكر جهازك إبقاءها مفعَّلة في المرة القادمة التي تفعِّل فيها \"وضع الطيران\"."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"‏تظل شبكة Wi-Fi مفعّلة"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"‏يتذكر هاتفك إبقاء شبكة Wi‑Fi مفعَّلة في \"وضع الطيران\". يمكنك إيقاف شبكة Wi‑Fi إذا لم تكن تريد مواصلة تفعيلها."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"‏يتذكر جهازك إبقاء شبكة Wi‑Fi مفعَّلة في \"وضع الطيران\". يمكنك إيقاف شبكة Wi‑Fi إذا لم تكن تريد مواصلة تفعيلها."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"شبكة غير متوفّرة"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"تم إيقاف معرّف <xliff:g id="SSID">%1$s</xliff:g> من قِبل المشرف."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"إغلاق"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"‏هل تريد تبديل شبكات Wi-Fi؟"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"يبدو أنّ شبكة \"<xliff:g id="SSID_0">%1$s</xliff:g>\" غير متصلة بالإنترنت. هل تريد التبديل إلى \"<xliff:g id="SSID_1">%2$s</xliff:g>\"؟"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"يبدو أنّ شبكة \"<xliff:g id="SSID_0">%1$s</xliff:g>\" غير متصلة بالإنترنت. هل تريد التبديل إلى \"<xliff:g id="SSID_1">%2$s</xliff:g>\"؟"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"جودة شبكة \"<xliff:g id="SSID_0">%1$s</xliff:g>\" منخفضة. هل تريد التبديل إلى \"<xliff:g id="SSID_1">%2$s</xliff:g>\"؟"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"تبديل"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"عدم التبديل"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-as/strings.xml b/service/ServiceWifiResources/res/values-as/strings.xml
index 19e8708..7f47460 100644
--- a/service/ServiceWifiResources/res/values-as/strings.xml
+++ b/service/ServiceWifiResources/res/values-as/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"অনুমতি দিয়ক"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"অনুমতি নিদিব"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"এয়াৰপ্লেন ম’ডত ৱাই‐ফাই অন হৈ থাকিব"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"আপুনি যদি ৱাই-ফাই অন কৰি ৰাখে, পৰৱৰ্তী সময়ত আপুনি এয়াৰপ্লেন ম’ড ব্যৱহাৰ কৰিলে আপোনাৰ ফ’নটোৱে এয়া অন কৰি ৰাখিবলৈ মনত ৰাখিব"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"আপুনি যদি ৱাই-ফাই অন কৰি ৰাখে, পৰৱৰ্তী সময়ত আপুনি এয়াৰপ্লেন ম’ড ব্যৱহাৰ কৰিলে আপোনাৰ ডিভাইচটোৱে এয়া অন কৰি ৰাখিবলৈ মনত ৰাখিব"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi অন হৈ থাকিব"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"আপোনাৰ ফ’নটোৱে এয়াৰপ্লেন ম’ডত ৱাই-ফাই অন ৰাখিবলৈ মনত ৰাখে। আপুনি যদি ৱাই-ফাই অন হৈ থকাটো নিবিচাৰে, তেন্তে ইয়াক অফ কৰক।"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"আপোনাৰ ডিভাইচটোৱে এয়াৰপ্লেন ম’ডত ৱাই-ফাই অন ৰাখিবলৈ মনত ৰাখে। আপুনি যদি ৱাই-ফাই অন হৈ থকাটো নিবিচাৰে, তেন্তে ইয়াক অফ কৰক।"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"উপলব্ধ নোহোৱা নেটৱৰ্ক"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"আপোনাৰ প্ৰশাসকে <xliff:g id="SSID">%1$s</xliff:g> অক্ষম কৰিছে।"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"বন্ধ কৰক"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ৱাই-ফাই নেটৱৰ্ক সলনি কৰিবনে?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ থকা যেন লগা নাই। <xliff:g id="SSID_1">%2$s</xliff:g>লৈ সলনি কৰিবনে?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ইণ্টাৰনেটৰ সৈতে সংযোগ হৈ থকা যেন লগা নাই। <xliff:g id="SSID_1">%2$s</xliff:g>লৈ সলনি কৰিবনে?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> হৈছে নিম্ন গুণগত মানৰ। <xliff:g id="SSID_1">%2$s</xliff:g>লৈ সলনি কৰিবনে?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"সলনি কৰক"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"সলনি নকৰিব"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-az/strings.xml b/service/ServiceWifiResources/res/values-az/strings.xml
index f4f30a2..b9f8444 100644
--- a/service/ServiceWifiResources/res/values-az/strings.xml
+++ b/service/ServiceWifiResources/res/values-az/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"İcazə verin"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"İcazə verməyin"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Təyyarə rejimində Wi‑Fi aktiv qalsın"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi‑Fi\'ı aktiv saxlasanız, növbəti dəfə təyyarə rejimində olduqda telefonunuz onu aktiv saxlayacaq"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi‑Fi aktiv qalarsa, cihaz növbəti dəfə təyyarə rejimində onu aktiv saxlayacaq"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi aktiv qalacaq"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonunuz təyyarə rejimində Wi‑Fi\'ı aktiv saxlayacaq. Aktiv qalmasını istəmirsinizsə, Wi‑Fi\'ı deaktiv edin."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Cihaz təyyarə rejimində Wi‑Fi-ı aktiv saxlayır. İstəmirsinizsə, Wi‑Fi-ı deaktiv edin."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Əlçatan Şəbəkə"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> administratorunuz tərəfindən deaktiv edilib."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Bağlayın"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi şəbəkələri dəyişilsin?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> internetə qoşulmayıb. <xliff:g id="SSID_1">%2$s</xliff:g> şəbəkəsinə dəyişilsin?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> internetə qoşulmayıb. <xliff:g id="SSID_1">%2$s</xliff:g> seçiminə dəyişilsin?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> aşağı keyfiyyətlidir. <xliff:g id="SSID_1">%2$s</xliff:g> seçiminə dəyişilsin?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Dəyişin"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Dəyişməyin"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml b/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml
index 1f9aabb..97bd197 100644
--- a/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml
+++ b/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Dozvoli"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ne dozvoli"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"WiFi je uključen u režimu rada u avionu"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ako odlučite da ne isključujete WiFi, telefon će zapamtiti da ga ne isključuje sledeći put kada budete u režimu rada u avionu"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ako odlučite da ne isključujete WiFi, uređaj će zapamtiti da ga ne isključuje sledeći put kada budete u režimu rada u avionu"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WiFi ostaje uključen"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon pamti da ne treba da isključuje WiFi u režimu rada u avionu. Isključite WiFi ako ne želite da ostane uključen."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Uređaj pamti da ne treba da isključuje WiFi u režimu rada u avionu. Isključite WiFi ako ne želite da ostane uključen."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nedostupna mreža"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administrator je onemogućio <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zatvori"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Želite da promenite WiFi mrežu?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Izgleda da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana na internet. Želite da prebacite na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Izgleda da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana na internet. Želite da se prebacite na: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> je lošeg kvaliteta. Želite da se prebacite na: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Prebaci"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ne prebacuj"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-be/strings.xml b/service/ServiceWifiResources/res/values-be/strings.xml
index 2e3304e..9212251 100644
--- a/service/ServiceWifiResources/res/values-be/strings.xml
+++ b/service/ServiceWifiResources/res/values-be/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Дазволіць"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Не дазваляць"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"У рэжыме палёту сетка Wi‑Fi застанецца ўключанай"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Калі вы не выключыце сетку Wi‑Fi, падчас наступнага пераходу ў рэжым палёту тэлефон будзе захоўваць яе ўключанай"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Калі вы не выключыце Wi‑Fi, падчас наступнага пераходу ў рэжым палёту прылада будзе захоўваць яго ўключаным"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Сетка Wi‑Fi застаецца ўключанай"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"На тэлефоне ў рэжыме палёту сетка Wi‑Fi будзе заставацца ўключанай, але вы можаце выключыць яе."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"На прыладзе ў рэжыме палёту Wi‑Fi будзе заставацца ўключаным, але вы можаце выключыць яго."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Недаступная сетка"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Сетка \"<xliff:g id="SSID">%1$s</xliff:g>\" адключана вашым адміністратарам."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Закрыць"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Пераключыцца на іншую сетку Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Хутчэй за ўсё, сетка \"<xliff:g id="SSID_0">%1$s</xliff:g>\" не падключана да інтэрнэту. Пераключыцца на сетку \"<xliff:g id="SSID_1">%2$s</xliff:g>\"?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Хутчэй за ўсё, сетка \"<xliff:g id="SSID_0">%1$s</xliff:g>\" не падключана да інтэрнэту. Пераключыцца на сетку \"<xliff:g id="SSID_1">%2$s</xliff:g>\"?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Нізкая якасць у <xliff:g id="SSID_0">%1$s</xliff:g>. Пераключыцца на сетку \"<xliff:g id="SSID_1">%2$s</xliff:g>\"?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Пераключыцца"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Не пераключацца"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-bg/strings.xml b/service/ServiceWifiResources/res/values-bg/strings.xml
index a848006..16cf742 100644
--- a/service/ServiceWifiResources/res/values-bg/strings.xml
+++ b/service/ServiceWifiResources/res/values-bg/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Разрешаване"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Забраняване"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Включване на Wi‑Fi в самолетния режим"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ако не изключите функцията за Wi‑Fi, телефонът ви ще я остави активна следващия път, когато използвате самолетния режим"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ако не изключите функцията за Wi‑Fi, устройството ви ще я остави активна следващия път, когато използвате самолетния режим"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi няма да се изключи"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Функцията за Wi‑Fi ще бъде включена, докато телефонът ви е в самолетен режим. Ако не искате това, изключете я."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Функцията за Wi‑Fi ще бъде включена, докато устройството ви е в самолетен режим. Ако не искате това, изключете я."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Няма достъп до мрежата"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Функцията <xliff:g id="SSID">%1$s</xliff:g> е деактивирана от администратора ви."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Затваряне"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Да се превключат ли Wi‑Fi мрежите?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Изглежда, че <xliff:g id="SSID_0">%1$s</xliff:g> няма връзка с интернет. Да се превключи ли към <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Изглежда, че <xliff:g id="SSID_0">%1$s</xliff:g> няма връзка с интернет. Да се превключи ли към <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> e с ниско качество. Да се превключи ли към <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Превключване"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Без превключване"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-bn/strings.xml b/service/ServiceWifiResources/res/values-bn/strings.xml
index 13687d4..f438725 100644
--- a/service/ServiceWifiResources/res/values-bn/strings.xml
+++ b/service/ServiceWifiResources/res/values-bn/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"অনুমতি দিন"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"অনুমতি দেবেন না"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"\'বিমান মোড\'-এ ওয়াই-ফাই চালু থাকে"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"আপনি ওয়াই-ফাই চালু রাখলে, আপনি \'বিমান\' মোডে থাকলে পরবর্তী সময় আপনার ফোন এটি চালু রাখার জন্য মনে রাখবে"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"আপনি ওয়াই-ফাই চালু রাখলে, আপনি এরপর \'বিমান মোড\'-এ থাকলেও আপনার ডিভাইস এটি চালু রাখবে"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"ওয়াই-ফাই চালু থাকে"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"\'বিমান মোড\'-এ থাকাকালীন আপনার ফোন ওয়াই-ফাই চালু রাখে। আপনি এটি চালু না রাখতে চাইলে ওয়াই-ফাই বন্ধ করুন।"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"\'বিমান মোড\'-এ থাকাকালীন আপনার ডিভাইস ওয়াই-ফাই চালু রাখে। আপনি ওয়াই-ফাই চালু না রাখতে চাইলে এটি বন্ধ করুন।"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"নেটওয়ার্ক উপলভ্য নয়"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"আপনার অ্যাডমিনিস্ট্রেটর <xliff:g id="SSID">%1$s</xliff:g> বন্ধ করে দিয়েছেন।"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"বন্ধ করুন"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ওয়াই-ফাই নেটওয়ার্ক পাল্টাবেন?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ইন্টারনেটের সাথে কানেক্ট করা নেই। <xliff:g id="SSID_1">%2$s</xliff:g>-এ পাল্টাবেন?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ইন্টারনেটের সাথে কানেক্ট করা নেই। <xliff:g id="SSID_1">%2$s</xliff:g>-এ বদল করবেন?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g>-এর কোয়ালিটি খারাপ। <xliff:g id="SSID_1">%2$s</xliff:g>-এ বদল করবেন?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"পাল্টান"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"পাল্টাতে চাই না"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-bs/strings.xml b/service/ServiceWifiResources/res/values-bs/strings.xml
index 0ee6b29..73414bd 100644
--- a/service/ServiceWifiResources/res/values-bs/strings.xml
+++ b/service/ServiceWifiResources/res/values-bs/strings.xml
@@ -147,7 +147,7 @@
     <string name="wifi_ca_cert_failed_to_install_ca_cert" msgid="4864192219789736195">"Instalacija certifikata nije uspjela."</string>
     <string name="wifi_tofu_invalid_cert_chain_title" msgid="332710627417595752">"Nije se moguće povezati s mrežom <xliff:g id="VALUE">%1$s</xliff:g>"</string>
     <string name="wifi_tofu_invalid_cert_chain_message" msgid="7047987920029432392">"Lanac potvrda servera je nevažeći."</string>
-    <string name="wifi_tofu_invalid_cert_chain_ok_text" msgid="9098567577510279854">"UREDU"</string>
+    <string name="wifi_tofu_invalid_cert_chain_ok_text" msgid="9098567577510279854">"Uredu"</string>
     <string name="wifi_ca_cert_dialog_preT_title" msgid="6916320484037009061">"Nije moguće potvrditi ovu mrežu"</string>
     <string name="wifi_ca_cert_dialog_preT_continue_text" msgid="9118713368838029797">"Ostanite povezani"</string>
     <string name="wifi_ca_cert_dialog_preT_abort_text" msgid="1331309662999405224">"Prekini vezu odmah"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Dozvoli"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nemoj dozvoliti"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"WiFi je uključen u načinu rada u avionu"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ako ostavite WiFi uključen, telefon će zapamtiti da ga ostavi uključenog sljedeći put kada budete u načinu rada u avionu"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ako ostavite WiFi uključen, uređaj će zapamtiti da treba biti uključen sljedeći put kada budete u načinu rada u avionu"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WiFi ostaje uključen"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon pamti da zadrži WiFi u načinu rada u avionu. Isključite WiFi ako ne želite da ostane uključen."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Uređaj pamti da WiFi treba biti uključen u načinu rada u avionu. Isključite WiFi ako ne želite da ostane uključen."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nedostupna mreža"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administrator je onemogućio <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zatvori"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Promijeniti WiFi mrežu?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Izgleda da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana s internetom. Prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Izgleda da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana s internetom. Prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> je niskog kvaliteta. Prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Prebaci"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nemoj prebaciti"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ca/strings.xml b/service/ServiceWifiResources/res/values-ca/strings.xml
index 20d5176..86c4fe2 100644
--- a/service/ServiceWifiResources/res/values-ca/strings.xml
+++ b/service/ServiceWifiResources/res/values-ca/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permet"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"No permetis"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi activada en mode d\'avió"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Si tens activada la Wi‑Fi, el telèfon recordarà mantenir-la així la pròxima vegada que utilitzis el mode d\'avió"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Si tens activada la Wi‑Fi, el dispositiu recordarà mantenir-la així la pròxima vegada que utilitzis el mode d\'avió"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"La Wi‑Fi es manté activada"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"El telèfon recorda mantenir la Wi‑Fi activada en mode d\'avió. Desactiva la Wi‑Fi si no vols que es quedi activada."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"El dispositiu recorda mantenir la Wi‑Fi activada en mode d\'avió. Desactiva la Wi‑Fi si no vols que es quedi activada."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Xarxa no disponible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"L\'administrador ha desactivat <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Tanca"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vols canviar de xarxa Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Sembla que <xliff:g id="SSID_0">%1$s</xliff:g> no té connexió a Internet. Vols canviar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Sembla que <xliff:g id="SSID_0">%1$s</xliff:g> no té connexió a Internet. Vols canviar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> és de qualitat baixa. Vols canviar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Canvia"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"No canviïs"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-cs/strings.xml b/service/ServiceWifiResources/res/values-cs/strings.xml
index 26f2d6d..9964035 100644
--- a/service/ServiceWifiResources/res/values-cs/strings.xml
+++ b/service/ServiceWifiResources/res/values-cs/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Povolit"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nepovolovat"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Zapnutá Wi-Fi v režimu Letadlo"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Pokud Wi-Fi necháte zapnutou, telefon si zapamatuje, že ji má příště v režimu Letadlo ponechat zapnutou"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Pokud Wi-Fi necháte zapnutou, zařízení si zapamatuje, že ji má příště v režimu Letadlo ponechat zapnutou"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi zůstane zapnutá"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon si pamatuje, že má v režimu Letadlo ponechat zapnutou Wi-Fi. Pokud nechcete, aby Wi-Fi zůstala zapnutá, vypněte ji."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Zařízení si pamatuje, že má v režimu Letadlo ponechat zapnutou Wi-Fi. Pokud nechcete, aby Wi-Fi zůstala zapnutá, vypněte ji."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nedostupná síť"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Funkce <xliff:g id="SSID">%1$s</xliff:g> byla deaktivována administrátorem."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zavřít"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Přepnout sítě Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Zdá se, že síť <xliff:g id="SSID_0">%1$s</xliff:g> není připojena k internetu. Přepnout na síť <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Zdá se, že síť <xliff:g id="SSID_0">%1$s</xliff:g> není připojena k internetu. Přepnout na síť <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Síť <xliff:g id="SSID_0">%1$s</xliff:g> není kvalitní. Přepnout na síť <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Přepnout"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nepřepínat"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-da/strings.xml b/service/ServiceWifiResources/res/values-da/strings.xml
index 8b25166..5d08fa5 100644
--- a/service/ServiceWifiResources/res/values-da/strings.xml
+++ b/service/ServiceWifiResources/res/values-da/strings.xml
@@ -100,11 +100,11 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32766" msgid="2335996367705677670">"<xliff:g id="SSID">%1$s</xliff:g>: EAP-godkendelsesfejl 32766"</string>
   <string-array name="wifi_eap_error_message_code_32766_carrier_overrides">
-    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>: Verizon Wi-Fi Access er ikke tilgængeligt i dit område. Prøv igen senere eller fra en anden placering (fejl = 32766)."</item>
+    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>: Verizon Wi-Fi Access er ikke tilgængeligt i dit område. Prøv igen senere eller fra en anden lokation (fejl = 32766)."</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32767" msgid="7094289719914089006">"<xliff:g id="SSID">%1$s</xliff:g>: EAP-godkendelsesfejl 32767"</string>
   <string-array name="wifi_eap_error_message_code_32767_carrier_overrides">
-    <item msgid="3756793972687282940">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>: Du kan ikke oprette forbindelse til Verizon Wi-Fi Access. Prøv igen senere eller fra et andet sted. (Fejl = 32767)"</item>
+    <item msgid="3756793972687282940">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>: Du kan ikke oprette forbindelse til Verizon Wi-Fi Access. Prøv igen senere eller fra en anden lokation. (Fejl = 32767)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_16384" msgid="575394783233092922">"<xliff:g id="SSID">%1$s</xliff:g>: EAP-godkendelsesfejl 16384"</string>
   <string-array name="wifi_eap_error_message_code_16384_carrier_overrides">
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Tillad"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Tillad ikke"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi aktiveret i flytilstand"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Hvis du holder Wi-Fi aktiveret, sørger din telefon for, at Wi-Fi forbliver aktiveret, næste gang du sætter den til flytilstand"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Hvis du holder Wi-Fi aktiveret, sørger din enhed for, at Wi-Fi forbliver aktiveret, næste gang du slår flytilstand til"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi forbliver aktiv"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Din telefon holder Wi-Fi aktiveret i flytilstand. Deaktiver Wi-Fi, hvis du ikke vil have, at det forbliver aktiveret."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Din enhed beholder Wi-Fi aktiveret i flytilstand. Deaktiver Wi-Fi, hvis du ikke vil have, at det forbliver aktiveret."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Utilgængeligt netværk"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> er blevet deaktiveret af din administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Luk"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vil du skifte Wi-Fi-netværk?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> lader ikke til at have forbindelse til internettet. Vil du skifte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> lader ikke til at have forbindelse til internettet. Vil du skifte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> er af dårlig kvalitet. Vil du skifte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Skift"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Skift ikke"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-de/strings.xml b/service/ServiceWifiResources/res/values-de/strings.xml
index 7e3f2d6..1f5c2bd 100644
--- a/service/ServiceWifiResources/res/values-de/strings.xml
+++ b/service/ServiceWifiResources/res/values-de/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Zulassen"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nicht zulassen"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"WLAN im Flugzeugmodus"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wenn das WLAN aktiviert bleibt, lässt es dein Smartphone eingeschaltet, wenn du das nächste Mal in den Flugmodus wechselst"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wenn du WLAN nicht ausschaltest, bleibt es eingeschaltet, wenn du das nächste Mal in den Flugmodus wechselst"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WLAN bleibt eingeschaltet"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Dein Smartphone lässt das WLAN im Flugmodus eingeschaltet. Schalte das WLAN aus, wenn du das nicht möchtest."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Dein Gerät lässt das WLAN im Flugmodus eingeschaltet. Schalte das WLAN aus, wenn du das nicht möchtest."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nicht verfügbares Netzwerk"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Die Funktion „<xliff:g id="SSID">%1$s</xliff:g>“ wurde von deinem Administrator deaktiviert."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Schließen"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"WLAN wechseln?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ist anscheinend nicht mit dem Internet verbunden. Zu <xliff:g id="SSID_1">%2$s</xliff:g> wechseln?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ist anscheinend nicht mit dem Internet verbunden. Zu <xliff:g id="SSID_1">%2$s</xliff:g> wechseln?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Die Qualität von <xliff:g id="SSID_0">%1$s</xliff:g> ist schlecht. Zu <xliff:g id="SSID_1">%2$s</xliff:g> wechseln?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Wechseln"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nicht wechseln"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-el/strings.xml b/service/ServiceWifiResources/res/values-el/strings.xml
index 8d0408b..09ae52a 100644
--- a/service/ServiceWifiResources/res/values-el/strings.xml
+++ b/service/ServiceWifiResources/res/values-el/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Να επιτρέπεται"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Να μην επιτρέπεται"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ενεργό σε λειτουργία πτήσης"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Αν διατηρήσετε το Wi‑Fi ενεργοποιημένο, το τηλέφωνό σας θα θυμάται να το διατηρήσει ενεργοποιημένο την επόμενη φορά που θα βρεθεί σε λειτουργία πτήσης"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Αν διατηρήσετε το Wi‑Fi ενεργοποιημένο, η συσκευή σας θα θυμάται να το διατηρήσει ενεργοποιημένο την επόμενη φορά που θα βρεθεί σε λειτουργία πτήσης"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Το Wi‑Fi παραμένει ενεργό"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Το τηλέφωνο θυμάται να διατηρεί ενεργοποιημένο το Wi‑Fi σε λειτουργία πτήσης. Απενεργοποιήστε το Wi‑Fi, αν δεν επιθυμείτε να είναι ενεργοποιημένο."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Η συσκευή θυμάται να διατηρεί ενεργοποιημένο το Wi‑Fi σε λειτουργία πτήσης. Απενεργοποιήστε το Wi‑Fi, αν δεν επιθυμείτε να είναι ενεργοποιημένο."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Μη διαθέσιμο δίκτυο"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Το <xliff:g id="SSID">%1$s</xliff:g> έχει απενεργοποιηθεί από τον διαχειριστή σας."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Κλείσιμο"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Εναλλαγή δικτύων Wi‑Fi;"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Το <xliff:g id="SSID_0">%1$s</xliff:g> φαίνεται ότι δεν είναι συνδεδεμένο στο διαδίκτυο. Εναλλαγή σε <xliff:g id="SSID_1">%2$s</xliff:g>;"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Το <xliff:g id="SSID_0">%1$s</xliff:g> φαίνεται ότι δεν είναι συνδεδεμένο στο διαδίκτυο. Εναλλαγή σε <xliff:g id="SSID_1">%2$s</xliff:g>;"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Το <xliff:g id="SSID_0">%1$s</xliff:g> είναι χαμηλής ποιότητας. Εναλλαγή σε <xliff:g id="SSID_1">%2$s</xliff:g>;"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Εναλλαγή"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Να μην γίνει εναλλαγή"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-en-rAU/strings.xml b/service/ServiceWifiResources/res/values-en-rAU/strings.xml
index ae7aa19..c5540fe 100644
--- a/service/ServiceWifiResources/res/values-en-rAU/strings.xml
+++ b/service/ServiceWifiResources/res/values-en-rAU/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Allow"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Don\'t allow"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi on in aeroplane mode"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"If you keep Wi‑Fi on, your phone will remember to keep it on the next time that you\'re in aeroplane mode"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"If you keep Wi‑Fi on, your device will remember to keep it on the next time you\'re in aeroplane mode"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi stays on"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Your phone remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Your device remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Unavailable network"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is disabled by your administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Close"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Switch Wi‑Fi networks?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is low quality. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Switch"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Don\'t switch"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-en-rCA/strings.xml b/service/ServiceWifiResources/res/values-en-rCA/strings.xml
index 72fe89b..b89fb28 100644
--- a/service/ServiceWifiResources/res/values-en-rCA/strings.xml
+++ b/service/ServiceWifiResources/res/values-en-rCA/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Allow"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Don\'t allow"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi on in Airplane mode"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"If you keep Wi‑Fi on, your phone will remember to keep it on the next time you\'re in Airplane mode"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"If you keep Wi‑Fi on, your device will remember to keep it on the next time you\'re in airplane mode"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi stays on"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Your phone remembers to keep Wi‑Fi on in Airplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Your device remembers to keep Wi‑Fi on in airplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Unavailable Network"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is disabled by your administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Close"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Switch Wi‑Fi networks?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is low quality. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Switch"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Don\'t switch"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-en-rGB/strings.xml b/service/ServiceWifiResources/res/values-en-rGB/strings.xml
index ae7aa19..c5540fe 100644
--- a/service/ServiceWifiResources/res/values-en-rGB/strings.xml
+++ b/service/ServiceWifiResources/res/values-en-rGB/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Allow"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Don\'t allow"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi on in aeroplane mode"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"If you keep Wi‑Fi on, your phone will remember to keep it on the next time that you\'re in aeroplane mode"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"If you keep Wi‑Fi on, your device will remember to keep it on the next time you\'re in aeroplane mode"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi stays on"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Your phone remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Your device remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Unavailable network"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is disabled by your administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Close"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Switch Wi‑Fi networks?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is low quality. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Switch"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Don\'t switch"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-en-rIN/strings.xml b/service/ServiceWifiResources/res/values-en-rIN/strings.xml
index ae7aa19..c5540fe 100644
--- a/service/ServiceWifiResources/res/values-en-rIN/strings.xml
+++ b/service/ServiceWifiResources/res/values-en-rIN/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Allow"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Don\'t allow"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi on in aeroplane mode"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"If you keep Wi‑Fi on, your phone will remember to keep it on the next time that you\'re in aeroplane mode"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"If you keep Wi‑Fi on, your device will remember to keep it on the next time you\'re in aeroplane mode"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi stays on"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Your phone remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Your device remembers to keep Wi‑Fi on in aeroplane mode. Turn off Wi‑Fi if you don\'t want it to stay on."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Unavailable network"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is disabled by your administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Close"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Switch Wi‑Fi networks?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> does not appear to be connected to the Internet. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is low quality. Switch to <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Switch"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Don\'t switch"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-en-rXC/strings.xml b/service/ServiceWifiResources/res/values-en-rXC/strings.xml
index 3df2c31..d5e5c56 100644
--- a/service/ServiceWifiResources/res/values-en-rXC/strings.xml
+++ b/service/ServiceWifiResources/res/values-en-rXC/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎Allow‎‏‎‎‏‎"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎Don\'t allow‎‏‎‎‏‎"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎Wi‑Fi on in airplane mode‎‏‎‎‏‎"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎If you keep Wi‑Fi on, your phone will remember to keep it on the next time you\'re in airplane mode‎‏‎‎‏‎"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎If you keep Wi‑Fi on, your device will remember to keep it on the next time you\'re in airplane mode‎‏‎‎‏‎"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎Wi‑Fi stays on‎‏‎‎‏‎"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎Your phone remembers to keep Wi‑Fi on in airplane mode. Turn off Wi‑Fi if you don\'t want it to stay on.‎‏‎‎‏‎"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎Your device remembers to keep Wi‑Fi on in airplane mode. Turn off Wi‑Fi if you don\'t want it to stay on.‎‏‎‎‏‎"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎Unavailable Network‎‏‎‎‏‎"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="SSID">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is disabled by your administrator.‎‏‎‎‏‎"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎Close‎‏‎‎‏‎"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎Switch Wi‑Fi networks?‎‏‎‎‏‎"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="SSID_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ does not appear to be connected to the internet. Switch to ‎‏‎‎‏‏‎<xliff:g id="SSID_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="SSID_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ does not appear to be connected to the internet. Switch to ‎‏‎‎‏‏‎<xliff:g id="SSID_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="SSID_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is low quality. Switch to ‎‏‎‎‏‏‎<xliff:g id="SSID_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‎Switch‎‏‎‎‏‎"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎Don\'t switch‎‏‎‎‏‎"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-es-rUS/strings.xml b/service/ServiceWifiResources/res/values-es-rUS/strings.xml
index 1bc4f00..66ddc08 100644
--- a/service/ServiceWifiResources/res/values-es-rUS/strings.xml
+++ b/service/ServiceWifiResources/res/values-es-rUS/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"No permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi activado en modo de avión"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Si mantienes el Wi-Fi activado, tu teléfono recordará mantenerlo activado la próxima vez que actives el modo de avión"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Si mantienes el Wi-Fi activado, el dispositivo recordará mantenerlo activado la próxima vez que actives el modo de avión"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"El Wi-Fi permanece activado"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"El teléfono dejará activado el Wi-Fi en el modo de avión. Desactiva el Wi-Fi si no quieres permanecer conectado."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"El dispositivo dejará activado el Wi-Fi en el modo de avión. Desactiva el Wi-Fi si no quieres que permanezca activado."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Red no disponible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Tu administrador inhabilitó <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Cerrar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"¿Quieres cambiar de red Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Al parecer, <xliff:g id="SSID_0">%1$s</xliff:g> no tiene conexión a Internet. ¿Quieres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Al parecer, <xliff:g id="SSID_0">%1$s</xliff:g> no tiene conexión a Internet. ¿Quieres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> es de baja calidad. ¿Quieres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Cambiar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"No cambiar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-es/strings.xml b/service/ServiceWifiResources/res/values-es/strings.xml
index 6b37588..6302a34 100644
--- a/service/ServiceWifiResources/res/values-es/strings.xml
+++ b/service/ServiceWifiResources/res/values-es/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"No permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi activado en modo Avión"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Si dejas el Wi-Fi activado, tu teléfono se acordará de dejarlo así la próxima vez que uses el modo Avión"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Si dejas el Wi-Fi activado, tu dispositivo se acordará de mantenerlo así la próxima vez que uses el modo Avión"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"El Wi‑Fi se mantiene activado"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Tu teléfono dejará el Wi-Fi activado en modo Avión. Desactiva el Wi-Fi si no quieres que se quede activado."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Tu dispositivo se acordará de mantener activado el Wi-Fi en modo Avión. Desactiva el Wi-Fi si no quieres que permanezca activado."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Red no disponible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Tu administrador ha inhabilitado <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Cerrar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"¿Cambiar de red Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Parece que <xliff:g id="SSID_0">%1$s</xliff:g> no tiene conexión a Internet. ¿Cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Parece que <xliff:g id="SSID_0">%1$s</xliff:g> no tiene conexión a Internet. ¿Cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> tiene baja calidad. ¿Cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Cambiar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"No cambiar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-et/strings.xml b/service/ServiceWifiResources/res/values-et/strings.xml
index 9519d7b..6ad483c 100644
--- a/service/ServiceWifiResources/res/values-et/strings.xml
+++ b/service/ServiceWifiResources/res/values-et/strings.xml
@@ -68,7 +68,7 @@
     <string name="wifi_cannot_connect_with_randomized_mac_message" msgid="4834133226521813352">"Puudutage privaatsusseadete muutmiseks ja uuesti proovimiseks"</string>
     <string name="wifi_disable_mac_randomization_dialog_title" msgid="2054540994993681606">"Kas muuta privaatsusseadet?"</string>
     <string name="wifi_disable_mac_randomization_dialog_message" msgid="8874064864332248988">"Ühendamiseks peab <xliff:g id="SSID">%1$s</xliff:g> kasutama teie seadme MAC-aadressi, mis on teie kordumatu ID. Praegu kasutab teie privaatsusseade selle võrgu puhul juhuslikku ID-d. \n\nSee muudatus võib võimaldada läheduses asuvatel seadmetel jälgida teie seadme asukohta."</string>
-    <string name="wifi_disable_mac_randomization_dialog_confirm_text" msgid="6954419863076751626">"Muuda seadet"</string>
+    <string name="wifi_disable_mac_randomization_dialog_confirm_text" msgid="6954419863076751626">"Muuda sätet"</string>
     <string name="wifi_disable_mac_randomization_dialog_success" msgid="5849155828154391387">"Seadet värskendati. Proovige uuesti ühendada."</string>
     <string name="wifi_disable_mac_randomization_dialog_failure" msgid="2894643619143813096">"Privaatsusseadet ei saa muuta"</string>
     <string name="wifi_disable_mac_randomization_dialog_network_not_found" msgid="7359256966900782004">"Võrku ei leitud"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Luba"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ära luba"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"WiFi on lennukirežiimis sisse lülitatud"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Kui hoiate WiFi sisselülitatuna, jätab telefon teie valiku meelde ja kasutab seda järgmisel korral lennukirežiimi aktiveerimisel."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Kui hoiate WiFi sisselülitatuna, jätab seade teie valiku meelde ja kasutab seda järgmisel korral lennukirežiimi aktiveerimisel."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WiFi jääb sisselülitatuks"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Teie telefon hoiab WiFi lennukirežiimis sisselülitatuna. Lülitage WiFi välja, kui te ei soovi, et see oleks sisse lülitatud."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Teie seade hoiab WiFi lennukirežiimis sisselülitatuna. Lülitage WiFi välja, kui te ei soovi, et see oleks sisse lülitatud."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Võrk ei ole saadaval"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administraator on funktsiooni <xliff:g id="SSID">%1$s</xliff:g> keelanud."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Sule"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Kas soovite vahetada WiFi-võrku?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ei näi olevat Internetiga ühendatud. Kas soovite aktiveerida operaatori <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ei näi olevat internetiga ühendatud. Kas lülituda võrgule <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> on halva kvaliteediga. Kas lülituda võrgule <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Vaheta"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ära vaheta"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-eu/strings.xml b/service/ServiceWifiResources/res/values-eu/strings.xml
index 427655f..c1463a4 100644
--- a/service/ServiceWifiResources/res/values-eu/strings.xml
+++ b/service/ServiceWifiResources/res/values-eu/strings.xml
@@ -33,11 +33,11 @@
     <string name="wifi_suggestion_action_allow_app" msgid="7757859972144671588">"Eman baimena"</string>
     <string name="wifi_suggestion_action_disallow_app" msgid="4565857699629860726">"Ez, eskerrik asko"</string>
     <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"<xliff:g id="CARRIERNAME">%s</xliff:g> operadorearen wifi-sarera konektatu nahi duzu?"</string>
-    <string name="wifi_suggestion_imsi_privacy_content" msgid="4266931269306079184">"Sare hauek SIM ID bat jasotzen dute, gailuaren kokapenaren jarraipena egin ahal izateko"</string>
+    <string name="wifi_suggestion_imsi_privacy_content" msgid="4266931269306079184">"Sare hauek SIM identifikatzaile bat jasotzen dute, gailuaren kokapenaren jarraipena egin ahal izateko"</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier" msgid="3888538126440442636">"Konektatu"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier" msgid="3225397664735676024">"Ez konektatu"</string>
     <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_title" msgid="4407415300707014525">"Konexioa berretsi nahi duzu?"</string>
-    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"Konektatzen bazara, baliteke <xliff:g id="CARRIERNAME">%s</xliff:g> operadorearen wifi-sareek zure SIM txartelarekin lotutako ID esklusiboa atzitzea edo partekatzea. Horrela, baliteke zure gailuaren kokapenaren jarraipena egiteko aukera izatea."</string>
+    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"Konektatzen bazara, baliteke <xliff:g id="CARRIERNAME">%s</xliff:g> operadorearen wifi-sareek zure SIM txartelarekin lotutako identifikatzaile esklusiboa atzitzea edo partekatzea. Horrela, baliteke zure gailuaren kokapenaren jarraipena egiteko aukera izatea."</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation" msgid="2168947026413431603">"Konektatu"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation" msgid="5156881939985876066">"Ez konektatu"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="3868826648004934540">"Wi‑Fi konexioa automatikoki aktibatuko da"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Eman baimena"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ez eman baimenik"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wifia aktibatuta hegaldi moduan"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wifia aktibatuta utziz gero, hura aktibatuta mantentzeaz gogoratuko da telefonoa hegaldi modua erabiltzen duzun hurrengoan"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wifia aktibatuta utziz gero, hura aktibatuta mantentzeaz gogoratuko da gailua hegaldi modua erabiltzen duzun hurrengoan"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wifia aktibatuta mantentzen da"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Hegaldi moduan, wifia aktibatuta mantentzeaz gogoratzen da telefonoa. Halakorik nahi ez baduzu, desaktiba ezazu zuk zeuk."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Hegaldi moduan, wifia aktibatuta mantentzeaz gogoratzen da gailua. Halakorik nahi ez baduzu, desaktiba ezazu zuk zeuk."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Sarea ez dago erabilgarri"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administratzaileak desgaitu du <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Itxi"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wifi-sarea aldatu nahi duzu?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ez dago konektatuta Internetera. <xliff:g id="SSID_1">%2$s</xliff:g> sarera aldatu nahi duzu?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ez dago konektatuta Internetera. <xliff:g id="SSID_1">%2$s</xliff:g> sarera aldatu nahi duzu?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> kalitate txikikoa da. <xliff:g id="SSID_1">%2$s</xliff:g> sarera aldatu nahi duzu?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Bai, aldatu nahi dut"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ez, ez dut aldatu nahi"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-fa/strings.xml b/service/ServiceWifiResources/res/values-fa/strings.xml
index 037e6d7..f4c8426 100644
--- a/service/ServiceWifiResources/res/values-fa/strings.xml
+++ b/service/ServiceWifiResources/res/values-fa/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"اجازه دادن"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"اجازه ندادن"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"‏‫Wi‑Fi در «حالت هواپیما» روشن باشد"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"‏اگر Wi-Fi را روشن نگه دارید، تلفنتان به‌یاد خواهد داشت تا دفعه بعدی که در «حالت هواپیما» هستید آن را روشن نگه دارد"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"‏اگر Wi-Fi را روشن نگه دارید، دستگاهتان به‌یاد خواهد داشت تا دفعه بعدی که در «حالت هواپیما» هستید آن را روشن نگه دارد"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"‏‫Wi-Fi روشن بماند"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"‏تلفنتان به‌یاد می‌آورد که Wi-Fi را در حالت هواپیما روشن نگه دارد. اگر نمی‌خواهید Wi-Fi روشن بماند، آن را خاموش کنید."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"‏دستگاهتان به‌یاد می‌آورد که Wi-Fi را در حالت هواپیما روشن نگه دارد. اگر نمی‌خواهید Wi-Fi روشن بماند، آن را خاموش کنید."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"شبکه غیرقابل‌دسترس"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"سرپرست شما <xliff:g id="SSID">%1$s</xliff:g> را غیرفعال کرده است."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"بستن"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"‏شبکه‌های Wi-Fi جابه‌جا شود؟"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"ظاهراً <xliff:g id="SSID_0">%1$s</xliff:g> به اینترنت متصل نیست. به <xliff:g id="SSID_1">%2$s</xliff:g> جابه‌جا شوید؟"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ظاهراً <xliff:g id="SSID_0">%1$s</xliff:g> به اینترنت متصل نیست. به <xliff:g id="SSID_1">%2$s</xliff:g> جابه‌جا می‌شوید؟"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> کیفیت پایینی دارد. به <xliff:g id="SSID_1">%2$s</xliff:g> جابه‌جا می‌شوید؟"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"جابه‌جا شود"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"جابه‌جا نشود"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-fi/strings.xml b/service/ServiceWifiResources/res/values-fi/strings.xml
index 0b61a9c..4c1dbf4 100644
--- a/service/ServiceWifiResources/res/values-fi/strings.xml
+++ b/service/ServiceWifiResources/res/values-fi/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Salli"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Älä salli"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi päällä lentokonetilassa"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Jos pidät Wi-Fi-yhteyden päällä, puhelin pitää sen päällä, kun seuraavan kerran olet lentokonetilassa"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Jos pidät Wi-Fi-yhteyden päällä, laite pitää sen päällä, kun seuraavan kerran olet lentokonetilassa"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi pysyy päällä"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Puhelimen Wi-Fi-yhteys pysyy päällä lentokonetilassa. Voit halutessasi laittaa Wi-Fi-yhteyden pois päältä."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Laitteen Wi-Fi-yhteys pysyy päällä lentokonetilassa. Voit halutessasi laittaa Wi-Fi-yhteyden pois päältä."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Verkko ei ole käytettävissä"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> on järjestelmänvalvojan käytöstä poistama."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Sulje"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vaihdetaanko Wi-Fi-verkkoja?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ei näytä olevan yhteydessä internetiin. Otetaanko <xliff:g id="SSID_1">%2$s</xliff:g> käyttöön?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ei näytä olevan yhteydessä internetiin. Otetaanko <xliff:g id="SSID_1">%2$s</xliff:g> käyttöön?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> on heikkolaatuinen. Otetaanko <xliff:g id="SSID_1">%2$s</xliff:g> käyttöön?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Vaihda"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Älä vaihda"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-fr-rCA/strings.xml b/service/ServiceWifiResources/res/values-fr-rCA/strings.xml
index 7c85fae..a0a7495 100644
--- a/service/ServiceWifiResources/res/values-fr-rCA/strings.xml
+++ b/service/ServiceWifiResources/res/values-fr-rCA/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Autoriser"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ne pas autoriser"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi activé en mode Avion"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Si vous laissez le Wi-Fi activé, votre téléphone se rappellera qu\'il doit le laisser activé la prochaine fois que vous serez en mode Avion"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Si vous laissez le Wi-Fi activé, votre appareil se souviendra qu\'il doit le laisser activé la prochaine fois que vous serez en mode Avion"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Le Wi‑Fi reste activé"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Votre téléphone se rappelle de garder le Wi-Fi activé en mode Avion. Désactivez le Wi-Fi si vous ne voulez pas qu\'il reste allumé."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Votre appareil se souvient de garder le Wi-Fi activé en mode Avion. Désactivez le Wi-Fi si vous ne voulez pas qu\'il reste allumé."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Réseau indisponible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Fonctionnalité <xliff:g id="SSID">%1$s</xliff:g> désactivée par votre administrateur."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Fermer"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Changer de réseau Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ne semble pas être connecté à Internet. Passer à <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ne semble pas être connecté à Internet. Passer à <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> est de faible qualité. Passer à <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Changer"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ne pas changer"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-fr/strings.xml b/service/ServiceWifiResources/res/values-fr/strings.xml
index eb69be7..181f2a6 100644
--- a/service/ServiceWifiResources/res/values-fr/strings.xml
+++ b/service/ServiceWifiResources/res/values-fr/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Autoriser"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ne pas autoriser"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi activé en mode Avion"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Si vous laissez le Wi-Fi activé, votre téléphone s\'en souviendra et le Wi-Fi restera activé la prochaine fois que vous serez en mode Avion"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Si vous laissez le Wi-Fi activé, votre appareil s\'en souviendra et le Wi-Fi restera activé la prochaine fois que vous serez en mode Avion"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Le Wi‑Fi reste activé"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Le Wi‑Fi de votre téléphone restera activé en mode Avion. Désactivez le Wi-Fi si ce paramètre ne vous convient pas."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Le Wi‑Fi de votre appareil restera activé en mode Avion, mais vous pouvez le désactiver."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Réseau indisponible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> est désactivé par votre administrateur."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Fermer"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Changer de réseau Wi‑Fi ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ne semble pas être connecté à Internet. Passer à <xliff:g id="SSID_1">%2$s</xliff:g> ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ne semble pas être connecté à Internet. Passer à <xliff:g id="SSID_1">%2$s</xliff:g> ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> est de basse qualité Passer à <xliff:g id="SSID_1">%2$s</xliff:g> ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Changer"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ne pas changer"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-gl/strings.xml b/service/ServiceWifiResources/res/values-gl/strings.xml
index 63a5745..bdadda5 100644
--- a/service/ServiceWifiResources/res/values-gl/strings.xml
+++ b/service/ServiceWifiResources/res/values-gl/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Non permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"A wifi está activada no modo avión"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Se mantés a wifi activada, o teléfono lembrará que ten que deixala nese estado a próxima vez que esteas no modo avión"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Se mantés a wifi activada, o dispositivo lembrará que ten que deixala nese estado a próxima vez que esteas no modo avión"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"A wifi permanece activada"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"O teléfono lembrará manter a wifi activada no modo avión. Desactívaa se non queres que permaneza activada."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"O dispositivo lembrará manter a wifi activada no modo avión. Se non queres que permaneza nese estado, desactívaa."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rede non dispoñible"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"O teu administrador desactivou a rede <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Pechar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Queres cambiar de rede wifi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Parece que <xliff:g id="SSID_0">%1$s</xliff:g> non ten conexión a Internet. Queres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Parece que <xliff:g id="SSID_0">%1$s</xliff:g> non ten conexión a Internet. Queres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> é de baixa calidade. Queres cambiar a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Cambiar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Non cambiar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-gu/strings.xml b/service/ServiceWifiResources/res/values-gu/strings.xml
index b948216..4a5a38d 100644
--- a/service/ServiceWifiResources/res/values-gu/strings.xml
+++ b/service/ServiceWifiResources/res/values-gu/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"મંજૂરી આપો"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"મંજૂરી આપશો નહીં"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"એરપ્લેન મોડમાં વાઇ-ફાઇ ચાલુ રહેશે"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"જો તમે વાઇ-ફાઇ ચાલુ રાખો, તો તમે જ્યારે આગલી વખતે એરપ્લેન મોડ પર જશો, ત્યારે તમારો ફોન તેને ચાલુ રાખવાનું યાદ રાખશે"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"જો તમે વાઇ-ફાઇ ચાલુ રાખો, તો તમે જ્યારે આગલી વખતે એરપ્લેન મોડ પર જશો, ત્યારે તમારું ડિવાઇસ તેને ચાલુ રાખવાનું યાદ રાખશે"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"વાઇ-ફાઇ ચાલુ રહેશે"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"તમારો ફોન વાઇ-ફાઇને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. જો તમે વાઇ-ફાઇ ચાલુ રાખવા માગતા ન હો, તો તેને બંધ કરો."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"તમારું ડિવાઇસ વાઇ-ફાઇને એરપ્લેન મોડમાં ચાલુ રાખવાનું યાદ રાખે છે. જો તમે વાઇ-ફાઇ ચાલુ રાખવા માગતા ન હો, તો તેને બંધ કરો."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"નેટવર્ક ઉપલબ્ધ નથી"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>ને તમારા ઍડમિનિસ્ટ્રેટર દ્વારા બંધ કરવામાં આવ્યું છે."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"બંધ કરો"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"વાઇ-ફાઇ નેટવર્ક સ્વિચ કરીએ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"એવું લાગે છે કે <xliff:g id="SSID_0">%1$s</xliff:g> ઇન્ટરનેટ સાથે કનેક્ટેડ નથી. <xliff:g id="SSID_1">%2$s</xliff:g> પર સ્વિચ કરીએ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"એવું લાગે છે કે <xliff:g id="SSID_0">%1$s</xliff:g> ઇન્ટરનેટ સાથે કનેક્ટેડ નથી. <xliff:g id="SSID_1">%2$s</xliff:g> પર સ્વિચ કરીએ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g>ની ક્વૉલિટી ઓછી છે. <xliff:g id="SSID_1">%2$s</xliff:g> પર સ્વિચ કરીએ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"સ્વિચ કરો"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"સ્વિચ કરશો નહીં"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-hi/strings.xml b/service/ServiceWifiResources/res/values-hi/strings.xml
index 9eca5c1..f9ad27a 100644
--- a/service/ServiceWifiResources/res/values-hi/strings.xml
+++ b/service/ServiceWifiResources/res/values-hi/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"अनुमति दें"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"अनुमति न दें"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"हवाई जहाज़ मोड में वाई-फ़ाई चालू रहेगा"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"वाई-फ़ाई चालू रखने पर आपका फ़ोन, अगली बार हवाई जहाज़ मोड चालू होने पर भी वाई-फ़ाई चालू रखेगा"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"वाई-फ़ाई चालू रखने पर, अगली बार फ़्लाइट मोड में भी आपके डिवाइस का वाई-फ़ाई चालू रहेगा"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"वाई-फ़ाई चालू रहेगा"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"हवाई जहाज़ मोड में भी आपका फ़ोन वाई-फ़ाई चालू रखता है. अगर वाई-फ़ाई चालू नहीं रखना है, तो उसे बंद कर दें."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"फ़्लाइट मोड में भी आपके डिवाइस का वाई-फ़ाई चालू रहता है. अगर वाई-फ़ाई चालू नहीं रखना है, तो उसे बंद कर दें."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"नेटवर्क उपलब्ध नहीं है"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"आपके एडमिन ने <xliff:g id="SSID">%1$s</xliff:g> को बंद किया हुआ है."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"बंद करें"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"क्या आपको वाई-फ़ाई नेटवर्क पर स्विच करना है?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"ऐसा लगता है कि <xliff:g id="SSID_0">%1$s</xliff:g>, इंटरनेट से कनेक्ट नहीं है. क्या आपको <xliff:g id="SSID_1">%2$s</xliff:g> पर स्विच करना है?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ऐसा लगता है कि <xliff:g id="SSID_0">%1$s</xliff:g>, इंटरनेट से कनेक्ट नहीं है. क्या आपको <xliff:g id="SSID_1">%2$s</xliff:g> पर स्विच करना है?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> की क्वालिटी हल्की है. क्या आपको <xliff:g id="SSID_1">%2$s</xliff:g> पर स्विच करना है?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"स्विच करें"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"स्विच न करें"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-hr/strings.xml b/service/ServiceWifiResources/res/values-hr/strings.xml
index bba5fc0..92e6d14 100644
--- a/service/ServiceWifiResources/res/values-hr/strings.xml
+++ b/service/ServiceWifiResources/res/values-hr/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Dopusti"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nemoj dopustiti"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi je uključen u načinu rada u zrakoplovu"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ako Wi-Fi ostane uključen, telefon će zapamtiti da treba ostati uključen u načinu rada u zrakoplovu"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ako Wi-Fi ostane uključen, uređaj će zapamtiti da treba ostati uključen u načinu rada u zrakoplovu"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi ostaje uključen"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon će zapamtiti da Wi‑Fi treba ostati uključen u načinu rada u zrakoplovu. Isključite Wi-Fi ako ne želite da ostane uključen."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Uređaj će zapamtiti da Wi‑Fi treba ostati uključen u načinu rada u zrakoplovu. Isključite Wi-Fi ako ne želite da ostane uključen."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Mreža nije dostupna"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Značajku <xliff:g id="SSID">%1$s</xliff:g> onemogućio je vaš administrator."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zatvori"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Želite li promijeniti Wi‑Fi mrežu?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Čini se da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana s internetom. Želite li se prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Čini se da mreža <xliff:g id="SSID_0">%1$s</xliff:g> nije povezana s internetom. Želite li se prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Slaba veza mreže <xliff:g id="SSID_0">%1$s</xliff:g>. Želite li se prebaciti na mrežu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Prebaci"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ne prebacuj"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-hu/strings.xml b/service/ServiceWifiResources/res/values-hu/strings.xml
index 8511a71..983ef13 100644
--- a/service/ServiceWifiResources/res/values-hu/strings.xml
+++ b/service/ServiceWifiResources/res/values-hu/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Engedélyezés"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Tiltás"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"A Wi‑Fi bekapcsolva marad Repülős üzemmódban"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ha bekapcsolva tartja a Wi-Fi-t, a telefon emlékezni fog arra, hogy a következő alkalommal, amikor Repülős üzemmódban van, bekapcsolva tartsa a funkciót"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ha bekapcsolva tartja a Wi-Fi-t, az eszköz emlékezni fog arra, hogy a következő alkalommal, amikor Repülős üzemmódban van, bekapcsolva tartsa a funkciót"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"A Wi‑Fi bekapcsolva marad"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"A telefon bekapcsolva tartja a Wi‑Fi-t Repülős üzemmódban. Kapcsolja ki a Wi-Fi-t, ha nem szeretné, hogy bekapcsolva maradjon."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Az eszköz bekapcsolva tartja a Wi‑Fi-t Repülős üzemmódban. Kapcsolja ki a Wi-Fi-t, ha nem szeretné, hogy bekapcsolva maradjon."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Hozzáférhetetlen hálózat"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> letiltva a rendszergazda által."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Bezárás"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Szeretne Wi‑Fi-hálózatot váltani?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Úgy tűnik, a(z) <xliff:g id="SSID_0">%1$s</xliff:g> nem csatlakozik az internethez. Szeretne a következőre váltani: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Úgy tűnik, a(z) <xliff:g id="SSID_0">%1$s</xliff:g> nem csatlakozik az internethez. Szeretne a következőre váltani: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"A(z) <xliff:g id="SSID_0">%1$s</xliff:g> gyenge minőségű. Szeretne a következőre váltani: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Váltás"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nincs váltás"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-hy/strings.xml b/service/ServiceWifiResources/res/values-hy/strings.xml
index 8d9b518..2a771d8 100644
--- a/service/ServiceWifiResources/res/values-hy/strings.xml
+++ b/service/ServiceWifiResources/res/values-hy/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Թույլատրել"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Չթույլատրել"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Ավիառեժիմում Wi‑Fi-ը միացված կլինի"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Եթե Wi-Fi-ը միացված թողնեք, հաջորդ անգամ այն ավտոմատ միացված կմնա ավիառեժիմում"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Եթե Wi-Fi-ը միացված թողնեք, հաջորդ անգամ այն ավտոմատ միացված կմնա ավիառեժիմում"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi-ը մնում է միացված"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Ավիառեժիմում Wi‑Fi-ը միացված կմնա։ Ցանկության դեպքում կարող եք անջատել Wi-Fi-ը։"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Ավիառեժիմում Wi‑Fi-ը միացված կմնա։ Ցանկության դեպքում կարող եք անջատել Wi-Fi-ը։"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Անհասանելի ցանց"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Ադմինիստրատորն անջատել է <xliff:g id="SSID">%1$s</xliff:g> գործառույթը։"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Փակել"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Անցնե՞լ մյուս Wi-Fi ցանցին"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ցանցը կարծես թե միացած չէ ինտերնետին։ Անցնե՞լ <xliff:g id="SSID_1">%2$s</xliff:g> ցանցին։"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ցանցը կարծես թե միացած չէ ինտերնետին։ Անցնե՞լ <xliff:g id="SSID_1">%2$s</xliff:g> ցանցին։"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ցանցի որակը ցածր է։ Անցնե՞լ <xliff:g id="SSID_1">%2$s</xliff:g> ցանցին։"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Անցնել"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Չանցնել"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-in/strings.xml b/service/ServiceWifiResources/res/values-in/strings.xml
index 72d47b8..514dfca 100644
--- a/service/ServiceWifiResources/res/values-in/strings.xml
+++ b/service/ServiceWifiResources/res/values-in/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Izinkan"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Jangan izinkan"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi aktif dalam mode pesawat"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Jika Wi-Fi tetap diaktifkan, ponsel akan ingat untuk tetap mengaktifkannya saat berikutnya ponsel Anda disetel ke mode pesawat"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Jika Wi-Fi tetap diaktifkan, perangkat akan ingat untuk tetap mengaktifkannya saat berikutnya perangkat disetel ke mode pesawat"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi tetap aktif"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Ponsel akan ingat untuk tetap mengaktifkan Wi-Fi dalam mode pesawat. Nonaktifkan jika Anda tidak ingin Wi-Fi terus aktif."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Perangkat akan mengingat untuk tetap mengaktifkan Wi-Fi dalam mode pesawat. Nonaktifkan jika Anda tidak ingin Wi-Fi terus aktif."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Jaringan Tidak Tersedia"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> dinonaktifkan oleh administrator Anda."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Tutup"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Alihkan jaringan Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> tampaknya tidak terhubung ke internet. Alihkan ke <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> tampaknya tidak terhubung ke internet. Alihkan ke <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Kualitas sinyal <xliff:g id="SSID_0">%1$s</xliff:g> rendah. Alihkan ke <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Alihkan"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Jangan alihkan"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-is/strings.xml b/service/ServiceWifiResources/res/values-is/strings.xml
index 71f31cf..15818b8 100644
--- a/service/ServiceWifiResources/res/values-is/strings.xml
+++ b/service/ServiceWifiResources/res/values-is/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Leyfa"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ekki leyfa"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Kveikt á Wi‑Fi í flugstillingu"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ef þú hefur kveikt á Wi-Fi mun síminn muna að hafa kveikt á því næst þegar þú stillir á flugstillingu"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ef þú hefur kveikt á Wi-Fi mun tækið muna að hafa kveikt á því næst þegar þú stillir á flugstillingu"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Áfram kveikt á Wi‑Fi"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Síminn man að hafa kveikt á Wi-Fi í flugstillingu. Slökktu á Wi-Fi ef þú vilt ekki hafa kveikt á því."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Tækið man að hafa kveikt á Wi-Fi í flugstillingu. Slökktu á Wi-Fi ef þú vilt ekki hafa kveikt á því."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Netkerfi ekki tiltækt"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Stjórnandinn hefur slökkt á <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Loka"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Skipta um WiFi-net?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> virðist ekki vera tengt netinu. Skipta yfir í <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> virðist ekki vera með nettengingu. Skipta yfir í <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> er í lélegum gæðum. Skipta yfir í <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Skipta"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ekki skipta"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-it/strings.xml b/service/ServiceWifiResources/res/values-it/strings.xml
index f185977..1143108 100644
--- a/service/ServiceWifiResources/res/values-it/strings.xml
+++ b/service/ServiceWifiResources/res/values-it/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifiResourcesAppLabel" product="default" msgid="3120115613525263696">"Risorse Wi-Fi sistema"</string>
-    <string name="wifi_available_title" msgid="3899472737467127635">"Stabilisci la connessione per aprire la rete Wi‑Fi"</string>
+    <string name="wifi_available_title" msgid="3899472737467127635">"Connettiti alla rete Wi-Fi aperta"</string>
     <string name="wifi_available_title_connecting" msgid="7233590022728579868">"Connessione alla rete Wi-Fi"</string>
     <string name="wifi_available_title_connected" msgid="6329493859989844201">"Connessione alla rete Wi-Fi stabilita"</string>
     <string name="wifi_available_title_failed_to_connect" msgid="4840833680513368639">"Impossibile connettersi alla rete Wi-Fi"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Consenti"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Non consentire"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi attivo in modalità aereo"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Se tieni attivo il Wi-fi, il telefono ricorderà di mantenerlo attivo la prossima volta che sarai in modalità aereo"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Se tieni attivo il Wi‑Fi, il dispositivo memorizza che dovrà tenerlo attivo la prossima volta che sarai in modalità aereo"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Il Wi-Fi rimane attivo"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Il telefono ricorda di mantenere attivo il Wi‑Fi in modalità aereo. Disattiva il Wi-Fi se non vuoi che resti attivo."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Il dispositivo memorizza che deve tenere attivo il Wi‑Fi in modalità aereo. Disattiva il Wi-Fi se non vuoi tenerlo attivo."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rete non disponibile"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"L\'amministratore ha disattivato la funzionalità <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Chiudi"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Cambiare rete Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> non sembra essere connessa a internet. Passare a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> non sembra essere connessa a internet. Vuoi passare a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> di bassa qualità. Vuoi passare a <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Passa"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Non effettuare il passaggio"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-iw/strings.xml b/service/ServiceWifiResources/res/values-iw/strings.xml
index daa1571..e129112 100644
--- a/service/ServiceWifiResources/res/values-iw/strings.xml
+++ b/service/ServiceWifiResources/res/values-iw/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"אישור"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"אין אישור"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"‏Wi‑Fi מופעל במצב טיסה"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"‏אם חיבור ה-Wi‑Fi נשאר מופעל, הטלפון יזכור להשאיר אותו מופעל בפעם הבאה שהוא יועבר למצב טיסה"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"‏אם חיבור ה-Wi‑Fi נשאר מופעל, המכשיר יזכור להשאיר אותו מופעל בפעם הבאה שהוא יועבר למצב טיסה"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"‏חיבור ה-Wi‑Fi יישאר מופעל"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"‏חיבור ה-Wi‑Fi בטלפון יישאר מופעל במצב טיסה. אפשר להשבית את ה-Wi-Fi אם לא רוצים שהוא יפעל."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"‏חיבור ה-Wi‑Fi במכשיר יישאר מופעל במצב טיסה. אפשר להשבית את ה-Wi-Fi אם לא רוצים שהוא יפעל."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"רשת לא זמינה"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"האדמין השבית את <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"סגירה"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"‏להחליף בין רשתות ה-Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"נראה ש<xliff:g id="SSID_0">%1$s</xliff:g> לא מחובר לאינטרנט. לעבור ל<xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"נראה שאין חיבור של <xliff:g id="SSID_0">%1$s</xliff:g> לאינטרנט. לעבור אל <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"האיכות של <xliff:g id="SSID_0">%1$s</xliff:g> היא נמוכה. לעבור אל <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"מעבר"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"לא לעבור"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ja/strings.xml b/service/ServiceWifiResources/res/values-ja/strings.xml
index 37ce2b8..ccc60f3 100644
--- a/service/ServiceWifiResources/res/values-ja/strings.xml
+++ b/service/ServiceWifiResources/res/values-ja/strings.xml
@@ -119,7 +119,7 @@
     <item msgid="2705387186478280792">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : Verizon Wi-Fi への接続中に問題が発生しました(エラー = 不明)。"</item>
   </string-array>
     <string name="wifi_softap_auto_shutdown_timeout_expired_title" msgid="4896534374569504484">"アクセス ポイントが OFF になりました"</string>
-    <string name="wifi_softap_auto_shutdown_timeout_expired_summary" msgid="7975476698140267728">"デバイスは接続されていません。タップすると変更できます。"</string>
+    <string name="wifi_softap_auto_shutdown_timeout_expired_summary" msgid="7975476698140267728">"デバイスは接続されていません。タップで変更できます。"</string>
     <string name="wifi_sim_required_title" msgid="2262227800991155459">"Wi-Fi が切断されました"</string>
     <string name="wifi_sim_required_message" msgid="284812212346125745">"<xliff:g id="SSID">%1$s</xliff:g> に接続するには、<xliff:g id="CARRIER_NAME">%2$s</xliff:g> の SIM を挿入します"</string>
     <string name="wifi_interface_priority_title" msgid="5117627874976875544">"<xliff:g id="APP">%1$s</xliff:g> がネットワーク リソースの使用を求めています"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"許可"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"許可しない"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"機内モードで Wi-Fi を ON"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi-Fi を ON にしておくと、次に機内モードになったときも ON のままになります"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi-Fi を ON にしておくと、次に機内モードになったときも ON のままになります"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi を ON のままにする"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"機内モードでも、スマートフォンの Wi-Fi は ON のままになります。Wi-Fi を ON にしたくない場合は OFF にしてください。"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"機内モードでも、デバイスの Wi-Fi は ON のままになります。Wi-Fi を ON にしたくない場合は OFF にしてください。"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"使用できないネットワーク"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> は管理者によって無効にされています。"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"閉じる"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi-Fi ネットワークに切り替えますか?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> がインターネットに接続できないようです。<xliff:g id="SSID_1">%2$s</xliff:g> に切り替えますか?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> がインターネットに接続できないようです。<xliff:g id="SSID_1">%2$s</xliff:g> に切り替えますか?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> は低品質です。<xliff:g id="SSID_1">%2$s</xliff:g> に切り替えますか?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"切り替える"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"切り替えない"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ka/strings.xml b/service/ServiceWifiResources/res/values-ka/strings.xml
index 253635c..4528330 100644
--- a/service/ServiceWifiResources/res/values-ka/strings.xml
+++ b/service/ServiceWifiResources/res/values-ka/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"დაშვება"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"აკრძალვა"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ჩართულია თვითმფრინავის რეჟიმში"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"თუ Wi‑Fi-ს ჩართულს დატოვებთ, თქვენი ტელეფონი დაიმახსოვრებს და ჩართულს დატოვებს, როდესაც შემდგომში თვითმფრინავის რეჟიმში იქნებით"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"თუ Wi‑Fi-ს ჩართულს დატოვებთ, თქვენი მოწყობილობა ამას დაიმახსოვრებს და ჩართულს დატოვებს მას, როდესაც შემდეგ ჯერზე თვითმფრინავის რეჟიმში იქნებით"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi კვლავ ჩართულია"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"თქვენს ტელეფონს ემახსოვრება, რომ Wi‑Fi ჩართული უნდა იყოს თვითმფრინავის რეჟიმში. Wi‑Fi if-ის გამორთვა, თუ არ გსურთ ჩართული დატოვოთ."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"თქვენს მოწყობილობას ახსოვს, რომ Wi‑Fi ჩართული დატოვოს თვითმფრინავის რეჟიმში. გამორთეთ Wi‑Fi, თუ არ გსურთ, რომ ის ჩართული იყოს."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ქსელი მიუწვდომელია"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> გათიშულია თქვენი ადმინისტრატორის მიერ."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"დახურვა"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"გსურთ Wi‑Fi ქსელების გადართვა?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"სავარაუდოდ, <xliff:g id="SSID_0">%1$s</xliff:g> არ არის ინტერნეტთან დაკავშირებული. გსურთ <xliff:g id="SSID_1">%2$s</xliff:g>-ზე გადართვა?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"სავარაუდოდ, <xliff:g id="SSID_0">%1$s</xliff:g> არ არის ინტერნეტთან დაკავშირებული. გსურთ <xliff:g id="SSID_1">%2$s</xliff:g>-ზე გადართვა?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> დაბალი ხარისხისაა. გსურთ <xliff:g id="SSID_1">%2$s</xliff:g>-ზე გადართვა?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"გადართვა"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"არ გადაირთოს"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-kk/strings.xml b/service/ServiceWifiResources/res/values-kk/strings.xml
index 28767d2..003869d 100644
--- a/service/ServiceWifiResources/res/values-kk/strings.xml
+++ b/service/ServiceWifiResources/res/values-kk/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Рұқсат беру"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Рұқсат бермеу"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ұшақ режимінде қосулы болады"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi‑Fi қосулы болса, ол келесіде телефондағы ұшақ режимінде қосулы болады."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi‑Fi-ды қосулы қалдырсаңыз, келесі жолы ұшақ режиміне ауысқанда да ол қосылып тұрады."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi қосулы тұрады"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Телефондағы Wi‑Fi ұшақ режимінде қосулы болады. Wi‑Fi желісінің қосулы тұрғанын қаламасаңыз, оны өшіріңіз."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Wi‑Fi ұшақ режимінде қосылып тұрады. Қаласаңыз, оны өшіріп қоюыңызға болады."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Қолжетімсіз желі"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> әкімші тарапынан өшірілді."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Жабу"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi желілерін ауыстыру керек пе?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> интернетке қосылмаған сияқты. <xliff:g id="SSID_1">%2$s</xliff:g> желісіне ауысу керек пе?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> интернетке қосылмаған сияқты. <xliff:g id="SSID_1">%2$s</xliff:g> желісіне ауысу керек пе?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> сапасы нашар. <xliff:g id="SSID_1">%2$s</xliff:g> желісіне ауысу керек пе?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Ауысу"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ауыспау"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-km/strings.xml b/service/ServiceWifiResources/res/values-km/strings.xml
index ba06243..318c11b 100644
--- a/service/ServiceWifiResources/res/values-km/strings.xml
+++ b/service/ServiceWifiResources/res/values-km/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"អនុញ្ញាត"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"មិនអនុញ្ញាត"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"បើក ​​Wi-Fi នៅក្នុងមុខងារពេល​ជិះ​យន្តហោះ"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ប្រសិនបើអ្នកបន្តបើក Wi‑Fi នោះទូរសព្ទរបស់អ្នកនឹងចាំថាត្រូវបន្តបើកវា នៅលើកក្រោយដែលអ្នកស្ថិតក្នុងមុខងារពេលជិះយន្តហោះ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ប្រសិនបើអ្នក​បន្តបើក Wi-Fi នោះឧបករណ៍​របស់អ្នកនឹង​ចាំថាត្រូវបន្តបើកវា នៅលើកក្រោយ​ដែលអ្នកស្ថិតក្នុង​មុខងារពេលជិះយន្តហោះ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi នៅ​តែ​បើក"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ទូរសព្ទរបស់អ្នកចាំថាត្រូវបន្តបើក Wi-Fi នៅក្នុងមុខងារពេលជិះយន្តហោះ។ បិទ Wi‑Fi ប្រសិនបើអ្នកមិនចង់ឱ្យវាបន្តបើក។"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ឧបករណ៍របស់អ្នកចាំថាត្រូវបន្តបើក Wi-Fi នៅក្នុងមុខងារពេល​ជិះ​យន្តហោះ។ បិទ Wi‑Fi ប្រសិនបើអ្នក​មិនចង់ឱ្យបន្តបើកទេ។"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"បណ្ដាញ​មិន​អាច​ប្រើ​បាន"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> ត្រូវបានបិទ​ដោយអ្នកគ្រប់គ្រង​របស់អ្នក។"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"បិទ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ប្ដូរបណ្ដាញ Wi‑Fi ឬ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ហាក់ដូចជាមិនបានភ្ជាប់អ៊ីនធឺណិតទេ។ ប្ដូរទៅ <xliff:g id="SSID_1">%2$s</xliff:g> ឬ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ហាក់ដូចជាមិនបានភ្ជាប់អ៊ីនធឺណិតទេ។ ប្ដូរទៅ <xliff:g id="SSID_1">%2$s</xliff:g> ឬ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> មានគុណភាពទាប។ ប្ដូរទៅ <xliff:g id="SSID_1">%2$s</xliff:g> ឬ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ប្ដូរ"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"កុំ​ប្ដូរ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-kn/strings.xml b/service/ServiceWifiResources/res/values-kn/strings.xml
index 7d85254..fba1a5d 100644
--- a/service/ServiceWifiResources/res/values-kn/strings.xml
+++ b/service/ServiceWifiResources/res/values-kn/strings.xml
@@ -32,12 +32,12 @@
     <string name="wifi_suggestion_content" msgid="6985149577828091835">"<xliff:g id="NAME">%s</xliff:g> ಸೂಚಿಸಿರುವ ನೆಟ್‌ವರ್ಕ್‌ಗಳು. ಸಾಧನಗಳು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಗೊಳ್ಳಬಹುದು."</string>
     <string name="wifi_suggestion_action_allow_app" msgid="7757859972144671588">"ಅನುಮತಿಸಿ"</string>
     <string name="wifi_suggestion_action_disallow_app" msgid="4565857699629860726">"ಬೇಡ"</string>
-    <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"<xliff:g id="CARRIERNAME">%s</xliff:g> ವೈ-ಫೈ ಗೆ ಕನೆಕ್ಟ್ ಮಾಡುವುದೇ?"</string>
+    <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"<xliff:g id="CARRIERNAME">%s</xliff:g> ವೈ-ಫೈ ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಬೇಕೆ?"</string>
     <string name="wifi_suggestion_imsi_privacy_content" msgid="4266931269306079184">"ಸಾಧನದ ಸ್ಥಳವನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಲು ಬಳಸಬಹುದಾದ SIM ಐಡಿಯನ್ನು ಈ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಸ್ವೀಕರಿಸುತ್ತವೆ"</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier" msgid="3888538126440442636">"ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier" msgid="3225397664735676024">"ಕನೆಕ್ಟ್ ಮಾಡಬೇಡಿ"</string>
     <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_title" msgid="4407415300707014525">"ಕನೆಕ್ಷನ್ ಅನ್ನು ಖಚಿತಪಡಿಸಬೇಕೇ?"</string>
-    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"ನೀವು ಕನೆಕ್ಟ್ ಮಾಡಿದರೆ, <xliff:g id="CARRIERNAME">%s</xliff:g> ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ನಿಮ್ಮ SIM ಗೆ ಸಂಬಂಧಿಸಿದ ಅನನ್ಯ ಐಡಿಗೆ ಪ್ರವೇಶ ಪಡೆಯಬಹುದು ಅಥವಾ ಅದನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು. ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಲು ಇದು ಅವಕಾಶ ನೀಡಬಹುದು."</string>
+    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"ನೀವು ಕನೆಕ್ಟ್ ಮಾಡಿದರೆ, <xliff:g id="CARRIERNAME">%s</xliff:g> ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ನಿಮ್ಮ SIM ಗೆ ಸಂಬಂಧಿಸಿದ ಅನನ್ಯ ಐಡಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಪಡೆಯಬಹುದು ಅಥವಾ ಅದನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು. ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಲು ಇದು ಅವಕಾಶ ನೀಡಬಹುದು."</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation" msgid="2168947026413431603">"ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation" msgid="5156881939985876066">"ಕನೆಕ್ಟ್ ಮಾಡಬೇಡಿ"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="3868826648004934540">"ವೈ‑ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗುತ್ತದೆ"</string>
@@ -84,7 +84,7 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32762" msgid="2381670648753465737">"<xliff:g id="SSID">%1$s</xliff:g> : EAP ಪ್ರಮಾಣೀಕರಣ ದೋಷ 32762"</string>
   <string-array name="wifi_eap_error_message_code_32762_carrier_overrides">
-    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ Verizon ವೈ-ಫೈ ಪ್ರವೇಶ ಖಾತೆಯಲ್ಲಿ ಸಮಸ್ಯೆಯಿದೆ. ನಮ್ಮನ್ನು ಸಂಪರ್ಕಿಸಲು 800-922-0204 ಗೆ ಕರೆ ಮಾಡಿ. (ದೋಷ = 32762)"</item>
+    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ Verizon ವೈ-ಫೈ ಆ್ಯಕ್ಸೆಸ್ ಖಾತೆಯಲ್ಲಿ ಸಮಸ್ಯೆಯಿದೆ. ನಮ್ಮನ್ನು ಸಂಪರ್ಕಿಸಲು 800-922-0204 ಗೆ ಕರೆ ಮಾಡಿ. (ದೋಷ = 32762)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32763" msgid="4467733260757049969">"<xliff:g id="SSID">%1$s</xliff:g> : EAP ಪ್ರಮಾಣೀಕರಣ ದೋಷ 32763"</string>
   <string-array name="wifi_eap_error_message_code_32763_carrier_overrides">
@@ -96,11 +96,11 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32765" msgid="2167528358066037980">"<xliff:g id="SSID">%1$s</xliff:g> : EAP ಪ್ರಮಾಣೀಕರಣ ದೋಷ 32765"</string>
   <string-array name="wifi_eap_error_message_code_32765_carrier_overrides">
-    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ Verizon ವೈ-ಫೈ ಪ್ರವೇಶ ಖಾತೆಯಲ್ಲಿ ಸಮಸ್ಯೆಯಿದೆ. ನಮ್ಮನ್ನು ಸಂಪರ್ಕಿಸಲು 800-922-0204 ಗೆ ಕರೆ ಮಾಡಿ. (ದೋಷ = 32765)"</item>
+    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ Verizon ವೈ-ಫೈ ಆ್ಯಕ್ಸೆಸ್ ಖಾತೆಯಲ್ಲಿ ಸಮಸ್ಯೆಯಿದೆ. ನಮ್ಮನ್ನು ಸಂಪರ್ಕಿಸಲು 800-922-0204 ಗೆ ಕರೆ ಮಾಡಿ. (ದೋಷ = 32765)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32766" msgid="2335996367705677670">"<xliff:g id="SSID">%1$s</xliff:g> : EAP ಪ್ರಮಾಣೀಕರಣ ದೋಷ 32766"</string>
   <string-array name="wifi_eap_error_message_code_32766_carrier_overrides">
-    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ ಸ್ಥಳದಿಂದ Verizon ವೈ-ಫೈ ಪ್ರವೇಶ ಲಭ್ಯವಿಲ್ಲ. ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಬೇರೊಂದು ಸ್ಥಳದಿಂದ ಪ್ರಯತ್ನಿಸಿ. (ದೋಷ = 32766)"</item>
+    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : ನಿಮ್ಮ ಸ್ಥಳದಿಂದ Verizon ವೈ-ಫೈ ಆ್ಯಕ್ಸೆಸ್ ಲಭ್ಯವಿಲ್ಲ. ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಬೇರೊಂದು ಸ್ಥಳದಿಂದ ಪ್ರಯತ್ನಿಸಿ. (ದೋಷ = 32766)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32767" msgid="7094289719914089006">"<xliff:g id="SSID">%1$s</xliff:g> : EAP ಪ್ರಮಾಣೀಕರಣ ದೋಷ 32767"</string>
   <string-array name="wifi_eap_error_message_code_32767_carrier_overrides">
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ಅನುಮತಿಸಿ"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ಅನುಮತಿಸಬೇಡಿ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿ ವೈ-ಫೈ ಆನ್ ಆಗಿದೆ"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ನೀವು ವೈ-ಫೈ ಆನ್ ಮಾಡಿದರೆ, ಮುಂದಿನ ಬಾರಿ ನೀವು ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿರುವಾಗ ಅದನ್ನು ಆನ್ ಮಾಡಲು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿಸಿಕೊಳ್ಳುತ್ತದೆ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ನೀವು ವೈ-ಫೈ ಆನ್ ಮಾಡಿದರೆ, ಮುಂದಿನ ಬಾರಿ ನೀವು ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿರುವಾಗ ಅದನ್ನು ಆನ್ ಮಾಡಲು ನಿಮ್ಮ ಸಾಧನ ನೆನಪಿನಲ್ಲಿರಿಸಿಕೊಳ್ಳುತ್ತದೆ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"ವೈ-ಫೈ ಆನ್ ಇರುತ್ತದೆ"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ವೈ-ಫೈ ಅನ್ನು ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿ ಇರಿಸಿಕೊಳ್ಳಲು ನಿಮ್ಮ ಫೋನ್ ನೆನಪಿನಲ್ಲಿರಿಸಿಕೊಳ್ಳುತ್ತದೆ. ವೈ-ಫೈ ಆನ್ ಆಗಿರಲು ನೀವು ಬಯಸದಿದ್ದರೆ ಅದನ್ನು ಆಫ್ ಮಾಡಿ."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ವೈ-ಫೈ ಅನ್ನು ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿ ಆನ್ ಆಗಿರಿಸಿಕೊಳ್ಳುವುದನ್ನು ನಿಮ್ಮ ಸಾಧನ ನೆನಪಿನಲ್ಲಿರಿಸಿಕೊಳ್ಳುತ್ತದೆ. ವೈ-ಫೈ ಆನ್ ಆಗಿರಲು ನೀವು ಬಯಸದಿದ್ದರೆ ಅದನ್ನು ಆಫ್ ಮಾಡಿ."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ನೆಟ್‌ವರ್ಕ್ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ಮುಚ್ಚಿರಿ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಬದಲಿಸಬೇಕೆ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ಇಂಟರ್ನೆಟ್‌ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಂತೆ ತೋರುತ್ತಿಲ್ಲ. <xliff:g id="SSID_1">%2$s</xliff:g> ಗೆ ಬದಲಿಸಬೇಕೇ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ಇಂಟರ್ನೆಟ್‌ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಂತೆ ತೋರುತ್ತಿಲ್ಲ. <xliff:g id="SSID_1">%2$s</xliff:g> ಗೆ ಬದಲಿಸಬೇಕೇ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ಕಡಿಮೆ ಗುಣಮಟ್ಟ ಹೊಂದಿದೆ. <xliff:g id="SSID_1">%2$s</xliff:g> ಗೆ ಬದಲಿಸಬೇಕೇ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ಬದಲಿಸಿ"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"ಬದಲಿಸಬೇಡಿ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ko/strings.xml b/service/ServiceWifiResources/res/values-ko/strings.xml
index 01ebc51..747167e 100644
--- a/service/ServiceWifiResources/res/values-ko/strings.xml
+++ b/service/ServiceWifiResources/res/values-ko/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"허용"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"허용 안함"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"비행기 모드에서 Wi-Fi 사용"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi-Fi를 켜진 상태로 유지하면 다음에 비행기 모드를 사용할 때도 Wi-Fi 연결이 유지됩니다."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi-Fi를 켜진 상태로 유지하면 다음에 비행기 모드를 사용할 때도 Wi-Fi 연결이 유지됩니다."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi가 켜진 상태로 유지됨"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"휴대전화가 비행기 모드에서 Wi-Fi를 켜진 상태로 유지합니다. 유지하지 않으려면 Wi-Fi를 사용 중지하세요."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"비행기 모드에서 기기의 Wi-Fi가 켜진 상태로 유지됩니다. 유지하지 않으려면 Wi-Fi를 사용 중지하세요."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"사용할 수 없는 네트워크"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>이(가) 관리자에 의해 사용 중지되었습니다."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"닫기"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi 네트워크를 전환할까요?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> 네트워크가 인터넷에 연결되어 있지 않은 것 같습니다. <xliff:g id="SSID_1">%2$s</xliff:g> 네트워크로 전환할까요?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> 네트워크가 인터넷에 연결되어 있지 않은 것 같습니다. <xliff:g id="SSID_1">%2$s</xliff:g> 네트워크로 전환할까요?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g>의 품질이 낮습니다. <xliff:g id="SSID_1">%2$s</xliff:g> 네트워크로 전환할까요?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"전환"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"전환 안 함"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ky/strings.xml b/service/ServiceWifiResources/res/values-ky/strings.xml
index 63aa2ad..f574c1a 100644
--- a/service/ServiceWifiResources/res/values-ky/strings.xml
+++ b/service/ServiceWifiResources/res/values-ky/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Уруксат берүү"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Тыюу салуу"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi учак режиминде күйгүзүлөт"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Эгер Wi-Fi күйүк бойдон калса, кийинки жолу учак режимине өткөнүңүздө телефонуңуз аны эстеп калат"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Эгер Wi-Fi күйүк бойдон калса, кийинки жолу учак режимине өткөнүңүздө түзмөгүңүз аны эстеп калат"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi күйгөн бойдон калат"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Телефонуңуз учак режиминде Wi‑Fi\'га туташкан бойдон калат. Кааласаңыз, Wi‑Fi\'ды өчүрүп койсоңуз болот."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Түзмөгүңүз учак режиминде Wi‑Fi\'га туташкан бойдон калат. Кааласаңыз, Wi‑Fi\'ды өчүрүп койсоңуз болот."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Жеткиликсиз тармак"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> администраторуңуз тарабынан өчүрүлдү."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Жабуу"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi тармактарына которуласызбы?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> Интернетке туташпай турат окшойт. <xliff:g id="SSID_1">%2$s</xliff:g> тармагына которуласызбы?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> Интернетке туташпай турат окшойт. <xliff:g id="SSID_1">%2$s</xliff:g> тармагына которуласызбы?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> байланышы начар. <xliff:g id="SSID_1">%2$s</xliff:g> тармагына которуласызбы?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Которгуч"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Которулбасын"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-lo/strings.xml b/service/ServiceWifiResources/res/values-lo/strings.xml
index 08201d8..f362fa7 100644
--- a/service/ServiceWifiResources/res/values-lo/strings.xml
+++ b/service/ServiceWifiResources/res/values-lo/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ອະນຸຍາດ"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ບໍ່ອະນຸຍາດ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ເປີດຢູ່ໃນໂໝດຢູ່ໃນຍົນ"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ຫາກທ່ານເປີດ Wi‑Fi ໄວ້, ໂທລະສັບຂອງທ່ານຈະຈື່ວ່າຕ້ອງເປີດ Wi‑Fi ໃນເທື່ອຕໍ່ໄປທີ່ທ່ານຢູ່ໃນໂໝດຢູ່ໃນຍົນ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ຫາກທ່ານເປີດ Wi-Fi ປະໄວ້, ອຸປະກອນຂອງທ່ານຈະຈື່ວ່າຕ້ອງເປີດມັນໃນເທື່ອຕໍ່ໄປທີ່ທ່ານຢູ່ໃນໂໝດຢູ່ໃນຍົນ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi ເປີດຢູ່"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ໂທລະສັບຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Wi-Fi ໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ປິດ Wi‑Fi ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເປີດມັນໄວ້."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ອຸປະກອນຂອງທ່ານຈື່ວ່າຈະຕ້ອງເປີດ Wi-Fi ປະໄວ້ໃນໂໝດຢູ່ໃນຍົນ. ປິດ Wi‑Fi ຫາກທ່ານບໍ່ຕ້ອງການໃຫ້ເປີດປະໄວ້."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ບໍ່ສາມາດໃຊ້ເຄືອຂ່າຍໄດ້"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ປິດ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ສະຫຼັບເຄືອຂ່າຍ Wi-Fi ບໍ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ປາກົດວ່າບໍ່ໄດ້ເຊື່ອມຕໍ່ກັບອິນເຕີເນັດ. ສະຫຼັບໄປຫາ <xliff:g id="SSID_1">%2$s</xliff:g> ບໍ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ປາກົດວ່າ <xliff:g id="SSID_0">%1$s</xliff:g> ຈະບໍ່ໄດ້ເຊື່ອມຕໍ່ກັບອິນເຕີເນັດ. ສະຫຼັບໄປໃຊ້ <xliff:g id="SSID_1">%2$s</xliff:g> ບໍ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ມີຄຸນນະພາບຕ່ຳ. ສະຫຼັບໄປໃຊ້ <xliff:g id="SSID_1">%2$s</xliff:g> ບໍ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ສະຫຼັບ"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"ຢ່າສະຫຼັບ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-lt/strings.xml b/service/ServiceWifiResources/res/values-lt/strings.xml
index 987b0d4..8580219 100644
--- a/service/ServiceWifiResources/res/values-lt/strings.xml
+++ b/service/ServiceWifiResources/res/values-lt/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Leisti"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Neleisti"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"„Wi‑Fi“ ryšys įjungtas lėktuvo režimu"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Jei paliksite „Wi‑Fi“ ryšį įjungtą, telefonas, prisimins palikti jį įjungtą, kai kitą kartą įjungsite lėktuvo režimą"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Jei paliksite „Wi‑Fi“ ryšį įjungtą, įrenginys prisimins palikti jį įjungtą, kai kitą kartą įjungsite lėktuvo režimą"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"„Wi‑Fi“ lieka įjungtas"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonas prisimena, kad lėktuvo režimu reikia palikti įjungtą „Wi‑Fi“ ryšį. Išjunkite „Wi‑Fi“, jei nenorite, kad jis liktų įjungtas."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Įrenginys prisimena, kad lėktuvo režimu reikia palikti įjungtą „Wi‑Fi“ ryšį. Išjunkite „Wi‑Fi“, jei nenorite, kad jis liktų įjungtas."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nepasiekiamas tinklas"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"„<xliff:g id="SSID">%1$s</xliff:g>“ išjungė administratorius."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Uždaryti"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Perjungti „Wi‑Fi“ tinklus?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Panašu, kad „<xliff:g id="SSID_0">%1$s</xliff:g>“ neprijungtas prie interneto. Perjungti į „<xliff:g id="SSID_1">%2$s</xliff:g>“?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Panašu, kad „<xliff:g id="SSID_0">%1$s</xliff:g>“ neprijungtas prie interneto. Perjungti į „<xliff:g id="SSID_1">%2$s</xliff:g>“?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"„<xliff:g id="SSID_0">%1$s</xliff:g>“ prastos kokybės. Perjungti į „<xliff:g id="SSID_1">%2$s</xliff:g>“?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Perjungti"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Neperjungti"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-lv/strings.xml b/service/ServiceWifiResources/res/values-lv/strings.xml
index f25c290..68da378 100644
--- a/service/ServiceWifiResources/res/values-lv/strings.xml
+++ b/service/ServiceWifiResources/res/values-lv/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Atļaut"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Neatļaut"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ieslēgts lidojuma režīmā"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ja Wi‑Fi savienojums paliks ieslēgts, tālrunī tas paliks ieslēgts arī nākamreiz, kad ieslēgsiet lidojuma režīmu."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ja Wi-Fi savienojums paliks ieslēgts, ierīcē tas paliks ieslēgts arī nākamreiz, kad ieslēgsiet lidojuma režīmu."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi joprojām ieslēgts"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Tālrunī joprojām būs ieslēgts Wi-Fi savienojums lidojuma režīmā. Izslēdziet Wi‑Fi savienojumu, ja nevēlaties, lai tas paliktu ieslēgts."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Lidojuma režīmā ierīcē joprojām būs ieslēgts Wi-Fi savienojums. Izslēdziet Wi-Fi savienojumu, ja nevēlaties, lai tas paliktu ieslēgts."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nepieejams tīkls"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administrators atspējoja tīklu <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Aizvērt"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vai pāriet uz citu Wi-Fi tīklu?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Šķiet, tīklā <xliff:g id="SSID_0">%1$s</xliff:g> nav savienojuma ar internetu. Vai vēlaties pāriet uz tīklu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Šķiet, tīklā <xliff:g id="SSID_0">%1$s</xliff:g> nav savienojuma ar internetu. Vai vēlaties pāriet uz tīklu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Tīklam <xliff:g id="SSID_0">%1$s</xliff:g> ir zema kvalitāte. Vai vēlaties pāriet uz tīklu <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Pāriet"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nepāriet"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-mk/strings.xml b/service/ServiceWifiResources/res/values-mk/strings.xml
index 447347a..513b33d 100644
--- a/service/ServiceWifiResources/res/values-mk/strings.xml
+++ b/service/ServiceWifiResources/res/values-mk/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Дозволи"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Не дозволувај"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi е вклучено во авионски режим"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ако го оставите Wi‑Fi вклучено, телефонот ќе запомни да го остави вклучено до следниот пат кога ќе бидете во авионски режим"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ако го оставите Wi‑Fi вклучено, уредот ќе запомни да го остави вклучено до следниот пат кога ќе бидете во авионски режим"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi останува вклучено"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Телефонот помни да го остави Wi‑Fi вклучено во авионски режим. Исклучете го Wi‑Fi ако не сакате да остане вклучено."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Уредотд помни да го остави Wi‑Fi вклучено во авионски режим. Исклучете го Wi‑Fi ако не сакате да остане вклучено."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Недостапна мрежа"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> е оневозможенa од вашиот администратор."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Затвори"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Да се смени Wi-Fi-мрежата?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Се чини дека <xliff:g id="SSID_0">%1$s</xliff:g> не е поврзан со интернет. Да се префрли на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Се чини дека <xliff:g id="SSID_0">%1$s</xliff:g> не е поврзан со интернет. Да се префрли на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> е со лош квалитет. Да се префрли на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Префрли"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Не префрлај"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ml/strings.xml b/service/ServiceWifiResources/res/values-ml/strings.xml
index 76249eb..ca56e78 100644
--- a/service/ServiceWifiResources/res/values-ml/strings.xml
+++ b/service/ServiceWifiResources/res/values-ml/strings.xml
@@ -40,10 +40,10 @@
     <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"നിങ്ങൾ കണക്റ്റ് ചെയ്യുകയാണെങ്കിൽ, സിമ്മുമായി ബന്ധപ്പെട്ട തനത് ഐഡി <xliff:g id="CARRIERNAME">%s</xliff:g> വൈഫൈ നെറ്റ്‌വർക്കുകൾ ആക്സസ് ചെയ്യുകയോ പങ്കിടുകയോ ചെയ്തേക്കാം. നിങ്ങളുടെ ഉപകരണ ലൊക്കേഷൻ ട്രാക്ക് ചെയ്യാൻ ഇത് അനുവദിച്ചേക്കാം."</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation" msgid="2168947026413431603">"കണക്റ്റ് ചെയ്യുക"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation" msgid="5156881939985876066">"കണക്റ്റ് ചെയ്യരുത്"</string>
-    <string name="wifi_wakeup_onboarding_title" msgid="3868826648004934540">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string>
+    <string name="wifi_wakeup_onboarding_title" msgid="3868826648004934540">"വൈഫൈ സ്വയമേവ ഓണാകും"</string>
     <string name="wifi_wakeup_onboarding_subtext" msgid="5705886295837387430">"നിങ്ങൾ ഉയർന്ന നിലവാരമുള്ള സംരക്ഷിക്കപ്പെട്ട നെറ്റ്‌വർക്കിനരികിലെത്തുമ്പോൾ"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="6209706680391785825">"തിരികെ ഓണാക്കരുത്"</string>
-    <string name="wifi_wakeup_enabled_title" msgid="5043486751612595850">"വൈഫൈ സ്വമേധയാ ഓണായി"</string>
+    <string name="wifi_wakeup_enabled_title" msgid="5043486751612595850">"വൈഫൈ സ്വയമേവ ഓണായി"</string>
     <string name="wifi_wakeup_enabled_content" msgid="3911262526267025882">"നിങ്ങൾ സംരക്ഷിച്ചിട്ടുള്ള ഒരു നെറ്റ്‌വർക്കിന് സമീപമാണ്: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
     <string name="wifi_watchdog_network_disabled" msgid="5769226742956006362">"Wi-Fi-ലേക്ക് കണക്‌റ്റുചെയ്യാൻ കഴിഞ്ഞില്ല"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="1725243835135539125">" എന്നതിന്റെ ഇന്റർനെറ്റ് കണക്ഷൻ മോശമാണ്."</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"അനുവദിക്കുക"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"അനുവദിക്കരുത്"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"ഫ്ലൈറ്റ് മോഡിൽ വൈഫൈ ഓണാണ്"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"വൈഫൈ ഓണാക്കി വച്ചാൽ, അടുത്ത തവണ നിങ്ങൾ ഫ്ലൈറ്റ് മോഡിൽ ആയിരിക്കുമ്പോൾ നിങ്ങളുടെ ഫോൺ അത് ഓണാക്കി വയ്ക്കാൻ ഓർക്കും"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"വൈഫൈ ഓണാക്കി വച്ചാൽ, അടുത്ത തവണ നിങ്ങൾ ഫ്ലൈറ്റ് മോഡിൽ ആയിരിക്കുമ്പോൾ നിങ്ങളുടെ ഉപകരണം അത് ഓണാക്കി വയ്ക്കാൻ ഓർക്കും"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"വൈഫൈ ഓണായിരിക്കും"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ വൈഫൈ ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഫോൺ ഓർമ്മിക്കും. വൈഫൈ ഓണാക്കി വയ്ക്കാൻ താൽപ്പര്യമില്ലെങ്കിൽ അത് ഓഫാക്കുക."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ഫ്ലൈറ്റ് മോഡിലായിരിക്കുമ്പോൾ വൈഫൈ ഓണാക്കി വയ്ക്കാൻ നിങ്ങളുടെ ഉപകരണം ഓർമ്മിക്കും. വൈഫൈ ഓണാക്കി വയ്ക്കാൻ താൽപ്പര്യമില്ലെങ്കിൽ അത് ഓഫാക്കുക."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ലഭ്യമല്ലാത്ത നെറ്റ്‌വർക്ക്"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> നിങ്ങളുടെ അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"അടയ്ക്കുക"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"വൈഫൈ നെറ്റ്‌വർക്കുകൾ മാറണോ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ഇന്റർനെറ്റിൽ കണക്‌റ്റ് ചെയ്‌തതായി തോന്നുന്നില്ല. <xliff:g id="SSID_1">%2$s</xliff:g> എന്നതിലേക്ക് മാറണോ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ഇന്റർനെറ്റിൽ കണക്‌റ്റ് ചെയ്‌തതായി തോന്നുന്നില്ല. <xliff:g id="SSID_1">%2$s</xliff:g> എന്നതിലേക്ക് മാറണോ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> കുറഞ്ഞ നിലവാരമാണ്. <xliff:g id="SSID_1">%2$s</xliff:g> എന്നതിലേക്ക് മാറണോ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"മാറുക"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"മാറരുത്"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-mn/strings.xml b/service/ServiceWifiResources/res/values-mn/strings.xml
index 42cf1e6..69b1376 100644
--- a/service/ServiceWifiResources/res/values-mn/strings.xml
+++ b/service/ServiceWifiResources/res/values-mn/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Зөвшөөр"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Бүү зөвшөөр"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi нислэгийн горимд асаалттай"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Хэрэв та Wi-Fi-аа асаалттай байлгавал таныг дараагийн удаа нислэгийн горимд байх үед утас тань үүнийг асаалттай байлгахыг санана"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Хэрэв та Wi-Fi-аа асаалттай байлгавал таныг дараагийн удаа нислэгийн горимд байх үед төхөөрөмж тань үүнийг асаалттай байлгахыг санана"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi асаалттай байна"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Таны утас Wi-Fi-г нислэгийн горимд асаалттай байлгахыг санана. Хэрэв та үүнийг асаалттай байлгахыг хүсэхгүй байвал Wi-Fi-г унтраана уу."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Таны төхөөрөмж Wi-Fi-г нислэгийн горимд асаалттай байлгахыг санана. Хэрэв та үүнийг асаалттай байлгахыг хүсэхгүй байвал Wi-Fi-г унтраана уу."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Боломжгүй сүлжээ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>-г танай администратор идэвхгүй болгосон."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Хаах"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi-Fi сүлжээнүүдийг сэлгэх үү?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> интернэтэд холбогдоогүй бололтой. <xliff:g id="SSID_1">%2$s</xliff:g> руу сэлгэх үү?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> интернэтэд холбогдоогүй бололтой. <xliff:g id="SSID_1">%2$s</xliff:g> руу сэлгэх үү?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> чанар багатай байна. <xliff:g id="SSID_1">%2$s</xliff:g> руу сэлгэх үү?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Сэлгэх"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Бүү сэлгэ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-mr/strings.xml b/service/ServiceWifiResources/res/values-mr/strings.xml
index 2ab703e..57fd585 100644
--- a/service/ServiceWifiResources/res/values-mr/strings.xml
+++ b/service/ServiceWifiResources/res/values-mr/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"अनुमती द्या"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"अनुमती देऊ नका"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"विमान मोडमध्ये वाय-फाय सुरू आहे"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"तुम्ही वाय-फाय सुरू ठेवल्यास, पुढील वेळी विमान मोडमध्ये असाल, तेव्हा तुमचा फोन ते सुरू ठेवण्याचे लक्षात ठेवेल"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"तुम्ही वाय-फाय सुरू ठेवल्यास, पुढील वेळी विमान मोडमध्ये असाल, तेव्हा तुमचे डिव्हाइस ते सुरू ठेवण्याचे लक्षात ठेवेल"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"वाय-फाय सुरू राहते"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"तुमचा फोन विमान मोडमध्ये वाय-फाय सुरू ठेवण्याचे लक्षात ठेवतो. तुम्हाला सुरू ठेवायचे नसल्यास, वाय-फाय बंद करा."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"तुमचे डिव्हाइस विमान मोडमध्ये वाय-फाय सुरू ठेवण्याचे लक्षात ठेवते. तुम्हाला वाय-फाय सुरू ठेवायचा नसल्यास तो बंद करा."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"उपलब्ध नसलेले नेटवर्क"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"तुमच्या अ‍ॅडमिनिस्ट्रेटर ने <xliff:g id="SSID">%1$s</xliff:g> बंद केले आहे."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"बंद करा"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"वाय-फाय नेटवर्क स्विच करायचे आहेत का?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> इंटरनेटशी कनेक्ट केलेले दिसत नाही. <xliff:g id="SSID_1">%2$s</xliff:g> वर स्विच करायचे आहे का?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> इंटरनेटशी कनेक्ट केलेले दिसत नाही. <xliff:g id="SSID_1">%2$s</xliff:g> वर स्विच करायचे आहे का?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ही गुणवत्ता कमी आहे. <xliff:g id="SSID_1">%2$s</xliff:g> वर स्विच करायचे आहे का?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"स्विच करा"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"स्विच करू नका"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ms/strings.xml b/service/ServiceWifiResources/res/values-ms/strings.xml
index 7182913..bbb0c84 100644
--- a/service/ServiceWifiResources/res/values-ms/strings.xml
+++ b/service/ServiceWifiResources/res/values-ms/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Benarkan"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Jangan benarkan"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi dihidupkan dalam mod pesawat"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Jika anda terus menghidupkan Wi-Fi, telefon anda akan ingat untuk membiarkan Wi-Fi hidup pada kali seterusnya telefon anda berada dalam mod pesawat"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Jika anda terus menghidupkan Wi-Fi, peranti anda akan kekal menghidupkan Wi-Fi pada kali seterusnya peranti anda dalam mod pesawat"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi terus dihidupkan"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon anda diingatkan untuk terus menghidupkan Wi-Fi dalam mod pesawat. Matikan Wi‑Fi jika anda tidak mahu Wi‑Fi sentiasa hidup."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Peranti anda diingatkan untuk terus menghidupkan Wi-Fi dalam mod pesawat. Matikan Wi‑Fi jika anda tidak mahu Wi‑Fi sentiasa hidup."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rangkaian Tidak Tersedia"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> dilumpuhkan oleh pentadbir anda."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Tutup"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Tukar rangkaian Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Nampaknya <xliff:g id="SSID_0">%1$s</xliff:g> tidak disambungkan kepada Internet. Tukar kepada <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Nampaknya <xliff:g id="SSID_0">%1$s</xliff:g> tidak disambungkan kepada Internet. Tukar kepada <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> berkualiti rendah. Tukar kepada <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Tukar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Jangan tukar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-my/strings.xml b/service/ServiceWifiResources/res/values-my/strings.xml
index 24290c5..6bba39c 100644
--- a/service/ServiceWifiResources/res/values-my/strings.xml
+++ b/service/ServiceWifiResources/res/values-my/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ခွင့်ပြုရန်"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ခွင့်မပြုပါ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"လေယာဉ်ပျံမုဒ်တွင် Wi‑Fi ပွင့်နေသည်"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi‑Fi ဆက်ဖွင့်ထားပါက နောက်တစ်ကြိမ်လေယာဉ်ပျံမုဒ် သုံးချိန်တွင် ၎င်းဆက်ဖွင့်ရန် သင့်ဖုန်းက မှတ်ထားမည်။"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi‑Fi ဆက်ဖွင့်ထားပါက နောက်တစ်ကြိမ်လေယာဉ်ပျံမုဒ် သုံးချိန်တွင် ၎င်းဆက်ဖွင့်ရန် သင့်စက်က မှတ်ထားမည်"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi ဆက်ပွင့်နေသည်"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"လေယာဉ်ပျံမုဒ်သုံးစဉ် Wi-Fi ဆက်ဖွင့်ထားရန် သင့်ဖုန်းက မှတ်မိသည်။ Wi‑Fi ဆက်ဖွင့်မထားလိုပါက ပိတ်နိုင်သည်။"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"လေယာဉ်ပျံမုဒ်သုံးစဉ် Wi-Fi ဆက်ဖွင့်ထားရန် သင့်စက်က မှတ်မိသည်။ Wi‑Fi ဆက်ဖွင့်မထားလိုပါက ပိတ်နိုင်သည်။"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ကွန်ရက်မရနိုင်ပါ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"သင့်စီမံခန့်ခွဲသူက <xliff:g id="SSID">%1$s</xliff:g> ကို ပိတ်ထားသည်။"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ပိတ်ရန်"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi ကွန်ရက်များကို ပြောင်းမလား။"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ကို အင်တာနက်နှင့် ချိတ်ဆက်ထားပုံမရပါ။ <xliff:g id="SSID_1">%2$s</xliff:g> သို့ ပြောင်းမလား။"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ကို အင်တာနက်နှင့် ချိတ်ဆက်ထားပုံမရပါ။ <xliff:g id="SSID_1">%2$s</xliff:g> သို့ ပြောင်းမလား။"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> သည် အရည်အသွေးနိမ့်သည်။ <xliff:g id="SSID_1">%2$s</xliff:g> သို့ ပြောင်းမလား။"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ပြောင်းရန်"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"မပြောင်းပါနှင့်"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-nb/strings.xml b/service/ServiceWifiResources/res/values-nb/strings.xml
index 8b6ba27..48f7814 100644
--- a/service/ServiceWifiResources/res/values-nb/strings.xml
+++ b/service/ServiceWifiResources/res/values-nb/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Tillat"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ikke tillat"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wifi på i flymodus"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Hvis du lar wifi være på, husker telefonen dette til den neste gangen du bruker flymodus"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Hvis du lar wifi være på, husker enheten dette til neste gang du bruker flymodus"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wifi holdes på"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonen husker å la wifi være påslått i flymodus. Du kan slå av wifi hvis du ikke vil la det være på."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Enheten husker at wifi skal være på i flymodus. Slå av wifi hvis du ikke vil at det skal være på."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Utilgjengelig nettverk"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administratoren din har deaktivert <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Lukk"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vil du bytte wifi-nettverk?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ser ikke ut til å være koblet til internett. Vil du bytte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ser ikke ut til å være koblet til internett. Vil du bytte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> er av lav kvalitet. Vil du bytte til <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Bytt"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ikke bytt"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ne/strings.xml b/service/ServiceWifiResources/res/values-ne/strings.xml
index 16b6975..6d4f3b6 100644
--- a/service/ServiceWifiResources/res/values-ne/strings.xml
+++ b/service/ServiceWifiResources/res/values-ne/strings.xml
@@ -29,7 +29,7 @@
     <string name="notification_channel_network_available" msgid="8454366142428864948">"नेटवर्क उपलब्ध छ"</string>
     <string name="notification_channel_apm_alerts" msgid="6775293475896473489">"APM सम्बन्धी अलर्टहरू"</string>
     <string name="wifi_suggestion_title" msgid="2564179935989099139">"सिफारिस गरिएका Wi‑Fi नेटवर्कहरूलाई अनुमति दिनुहोस्?"</string>
-    <string name="wifi_suggestion_content" msgid="6985149577828091835">"<xliff:g id="NAME">%s</xliff:g> ले सिफारिस गरेका नेटवर्कहरू। यन्त्र स्वतः जडान हुन सक्छ।"</string>
+    <string name="wifi_suggestion_content" msgid="6985149577828091835">"<xliff:g id="NAME">%s</xliff:g> ले सिफारिस गरेका नेटवर्कहरू। यन्त्र अटो कनेक्ट हुन सक्छ।"</string>
     <string name="wifi_suggestion_action_allow_app" msgid="7757859972144671588">"अनुमति दिनुहोस्"</string>
     <string name="wifi_suggestion_action_disallow_app" msgid="4565857699629860726">"पर्दैन, धन्यवाद"</string>
     <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"<xliff:g id="CARRIERNAME">%s</xliff:g> Wi-Fi मा कनेक्ट गर्ने हो?"</string>
@@ -67,7 +67,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> ले तपाईंको डिभाइसको म्याक एड्रेस अर्थात् एउटा अद्वितीय पहिचानकर्ता प्रयोग गर्नु पर्ने हुन्छ। हाल, यो नेटवर्कमा लागू हुने तपाईंको गोपनीयतासम्बन्धी सेटिङले अनियमित पहिचानकर्ताको प्रयोग गर्छ। \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>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"अनुमति दिनुहोस्"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"अनुमति नदिनुहोस्"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"हवाइजहाज मोडमा Wi-Fi अन रहन्छ"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"तपाईंले Wi‑Fi अन राख्नुभयो भने तपाईंले फेरि हवाइजहाज मोड सेट गर्दा फोनले Wi-Fi अन राख्नु पर्ने कुरा याद राख्छ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"तपाईंले Wi-Fi अन राखिराख्नुभयो भने तपाईंले आफ्नो डिभाइस अर्को पटक हवाइजहाज मोडमा लैजाँदा तपाईंको डिभाइसले Wi-Fi अन राख्नु पर्ने कुरा याद गर्छ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi अन रहन्छ"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"हवाइजहाज मोडमा पनि तपाईंको फोनमा Wi-Fi अन नै रहने छ। तपाईं Wi‑Fi अन भइनरहोस् भन्ने चाहनुहुन्छ भने अफ गर्नुहोस्।"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"तपाईंको डिभाइस अर्को पटक हवाइजहाज मोडमा लैजाँदा तपाईंको फोनले Wi-Fi अन राख्नु पर्ने कुरा याद गर्छ। तपाईं Wi‑Fi अन भइनरहोस् भन्ने चाहनुहुन्छ भने Wi-Fi अफ गर्नुहोस्।"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"नेटवर्क उपलब्ध छैन"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"तपाईंका एड्मिनले <xliff:g id="SSID">%1$s</xliff:g> अफ गर्नुभएको छ।"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"बन्द गर्नुहोस्"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi नेटवर्क बदल्ने हो?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> इन्टरनेटमा कनेक्ट छैन जस्तो देखिन्छ। नेटवर्क बदलेर <xliff:g id="SSID_1">%2$s</xliff:g> प्रयोग गर्न थाल्ने हो?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> इन्टरनेटमा कनेक्ट छैन जस्तो देखिन्छ। नेटवर्क बदलेर <xliff:g id="SSID_1">%2$s</xliff:g> प्रयोग गर्न थाल्ने हो?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> न्यून गुणस्तरको छ। नेटवर्क बदलेर <xliff:g id="SSID_1">%2$s</xliff:g> प्रयोग गर्न थाल्ने हो?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"बदल्नुहोस्"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"नबदल्नुहोस्"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-nl/strings.xml b/service/ServiceWifiResources/res/values-nl/strings.xml
index 69a3dbe..9742475 100644
--- a/service/ServiceWifiResources/res/values-nl/strings.xml
+++ b/service/ServiceWifiResources/res/values-nl/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Toestaan"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Niet toestaan"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wifi aan in vliegtuigmodus"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Als je wifi laat aanstaan, onthoudt je telefoon dit en blijft de wifi aanstaan als je de vliegtuigmodus weer aanzet"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Als je wifi laat aanstaan, onthoudt je apparaat dit en blijft de wifi aanstaan als je de vliegtuigmodus weer aanzet"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wifi blijft aan"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"De wifi op je telefoon blijft aan in de vliegtuigmodus. Zet wifi uit als je niet wilt dat dit aan blijft."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"De wifi op je apparaat blijft aan in de vliegtuigmodus. Zet wifi uit als je niet wilt dat dit aan blijft."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Niet-beschikbaar netwerk"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> is uitgezet door je beheerder."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Sluiten"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Schakelen tussen wifi-netwerken?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> heeft geen verbinding met internet. Overschakelen naar <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> heeft geen verbinding met internet. Overschakelen naar <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> is van lage kwaliteit. Overschakelen naar <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Overschakelen"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Niet overschakelen"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-or/strings.xml b/service/ServiceWifiResources/res/values-or/strings.xml
index da1d991..71fb148 100644
--- a/service/ServiceWifiResources/res/values-or/strings.xml
+++ b/service/ServiceWifiResources/res/values-or/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"ଏୟାରପ୍ଲେନ ମୋଡରେ ୱାଇ-ଫାଇ ଚାଲୁ ଅଛି"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ଯଦି ଆପଣ ୱାଇ-ଫାଇ ଚାଲୁ ରଖନ୍ତି, ତେବେ ଆପଣ ପରବର୍ତ୍ତୀ ଥର ଏୟାରପ୍ଲେନ ମୋଡରେ ଥିବା ସମୟରେ ଆପଣଙ୍କ ଫୋନ ଏହାକୁ ଚାଲୁ ରଖିବାକୁ ମନେ ରଖିବ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ଯଦି ଆପଣ ୱାଇ-ଫାଇ ଚାଲୁ ରଖନ୍ତି, ତେବେ ଆପଣ ପରବର୍ତ୍ତୀ ଥର ଏୟାରପ୍ଲେନ ମୋଡରେ ଥିବା ସମୟରେ ଆପଣଙ୍କ ଡିଭାଇସ ଏହାକୁ ଚାଲୁ ରଖିବା ପାଇଁ ମନେ ରଖିବ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"ୱାଇ-ଫାଇ ଚାଲୁ ରହିବ"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ଆପଣଙ୍କ ଫୋନ ଏୟାରପ୍ଲେନ ମୋଡରେ ୱାଇ-ଫାଇ ଚାଲୁ ରଖିବାକୁ ମନେ ରଖେ। ଯଦି ଆପଣ ୱାଇ-ଫାଇ ଚାଲୁ ରଖିବାକୁ ଚାହାଁନ୍ତି ନାହିଁ ତେବେ ୱାଇ-ଫାଇ ବନ୍ଦ କରନ୍ତୁ।"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ଆପଣଙ୍କ ଡିଭାଇସ ଏୟାରପ୍ଲେନ ମୋଡରେ ୱାଇ-ଫାଇ ଚାଲୁ ରଖିବାକୁ ମନେ ରଖେ। ଯଦି ଆପଣ ୱାଇ-ଫାଇ ଚାଲୁ ରହୁ ବୋଲି ଚାହୁଁନାହାଁନ୍ତି ତେବେ ୱାଇ-ଫାଇ ବନ୍ଦ କରନ୍ତୁ।"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ଅନୁପଲବ୍ଧ ନେଟୱାର୍କ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଆଡମିନିଷ୍ଟ୍ରେଟରଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି।"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ୱାଇ-ଫାଇ ନେଟୱାର୍କକୁ ସ୍ୱିଚ କରିବେ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ଇଣ୍ଟରନେଟ ସହ କନେକ୍ଟ ହୋଇଥିବା ପରି ଲାଗୁନାହିଁ। <xliff:g id="SSID_1">%2$s</xliff:g>କୁ ସ୍ୱିଚ କରିବେ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ଇଣ୍ଟରନେଟ ସହ କନେକ୍ଟ ହୋଇଥିବା ପରି ଲାଗୁନାହିଁ। <xliff:g id="SSID_1">%2$s</xliff:g>କୁ ସୁଇଚ କରିବେ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g>ର ଗୁଣବତ୍ତା ନିମ୍ନ ଅଟେ। <xliff:g id="SSID_1">%2$s</xliff:g>କୁ ସୁଇଚ କରିବେ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ସ୍ୱିଚ କରନ୍ତୁ"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"ସ୍ୱିଚ କରନ୍ତୁ ନାହିଁ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-pa/strings.xml b/service/ServiceWifiResources/res/values-pa/strings.xml
index b3310fc..d01fe9c 100644
--- a/service/ServiceWifiResources/res/values-pa/strings.xml
+++ b/service/ServiceWifiResources/res/values-pa/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ਆਗਿਆ ਦਿਓ"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ਆਗਿਆ ਨਾ ਦਿਓ"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਵਾਈ-ਫਾਈ ਚਾਲੂ ਹੈ"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ਜੇ ਤੁਸੀਂ ਵਾਈ-ਫਾਈ ਨੂੰ ਚਾਲੂ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਤੁਹਾਡਾ ਫ਼ੋਨ ਇਸਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖੇਗਾ"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ਜੇ ਤੁਸੀਂ ਵਾਈ-ਫਾਈ ਨੂੰ ਚਾਲੂ ਰੱਖਦੇ ਹੋ, ਤਾਂ ਅਗਲੀ ਵਾਰ ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਤੁਹਾਡਾ ਫ਼ੋਨ ਇਸਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖੇਗਾ"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"ਵਾਈ-ਫਾਈ ਚਾਲੂ ਰਹੇਗਾ"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਵਾਈ-ਫਾਈ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਚਾਲੂ ਨਹੀਂ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਵਾਈ-ਫਾਈ ਨੂੰ ਬੰਦ ਕਰੋ।"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਵਿੱਚ ਵਾਈ-ਫਾਈ ਨੂੰ ਚਾਲੂ ਰੱਖਣਾ ਯਾਦ ਰੱਖਦਾ ਹੈ। ਜੇ ਤੁਸੀਂ ਇਸਨੂੰ ਚਾਲੂ ਨਹੀਂ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਵਾਈ-ਫਾਈ ਨੂੰ ਬੰਦ ਕਰੋ।"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ <xliff:g id="SSID">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ।"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ਬੰਦ ਕਰੋ"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"ਕੀ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ ਵਿਚਕਾਰ ਸਵਿੱਚ ਕਰਨਾ ਹੈ?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"ਲੱਗਦਾ ਹੈ <xliff:g id="SSID_0">%1$s</xliff:g> ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੈ। ਕੀ <xliff:g id="SSID_1">%2$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰਨਾ ਹੈ?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ਇੰਝ ਜਾਪਦਾ ਹੈ ਕਿ <xliff:g id="SSID_0">%1$s</xliff:g> ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੈ। ਕੀ <xliff:g id="SSID_1">%2$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰਨਾ ਹੈ?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ਦੀ ਕੁਆਲਿਟੀ ਠੀਕ-ਠਾਕ ਹੈ। ਕੀ <xliff:g id="SSID_1">%2$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰਨਾ ਹੈ?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"ਸਵਿੱਚ ਨਾ ਕਰੋ"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-pl/strings.xml b/service/ServiceWifiResources/res/values-pl/strings.xml
index 406b824..2f2b3b8 100644
--- a/service/ServiceWifiResources/res/values-pl/strings.xml
+++ b/service/ServiceWifiResources/res/values-pl/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Zezwól"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nie zezwalaj"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi włączone w trybie samolotowym"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Jeśli pozostawisz funkcję Wi‑Fi włączoną, telefon zachowa się podobnie przy kolejnym przejściu w tryb samolotowy"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Jeśli pozostawisz funkcję Wi‑Fi włączoną, urządzenie zachowa się podobnie przy kolejnym przejściu w tryb samolotowy"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi pozostanie włączone"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Wi-Fi na telefonie pozostaje włączone w trybie samolotowym. Wyłącz Wi‑Fi, jeśli nie chcesz, aby ta funkcja pozostała uruchomiona."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Wi-Fi na urządzeniu pozostaje włączone w trybie samolotowym. Wyłącz Wi‑Fi, jeśli nie chcesz, aby ta funkcja pozostała uruchomiona."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Niedostępna sieć"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administrator wyłączył sieć <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zamknij"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Przełączyć sieci Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Wygląda na to, że sieć <xliff:g id="SSID_0">%1$s</xliff:g> nie jest podłączona do internetu. Przełączyć na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Wygląda na to, że sieć <xliff:g id="SSID_0">%1$s</xliff:g> nie jest podłączona do internetu. Przełączyć na: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> jest niskiej jakości. Przełączyć na: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Przełącz"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nie przełączaj"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-pt-rBR/strings.xml b/service/ServiceWifiResources/res/values-pt-rBR/strings.xml
index 7db6562..71aa053 100644
--- a/service/ServiceWifiResources/res/values-pt-rBR/strings.xml
+++ b/service/ServiceWifiResources/res/values-pt-rBR/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Não permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi ativado no modo avião"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Se você escolher manter o Wi‑Fi ligado, na próxima vez que você estiver em modo avião, o Wi-Fi do smartphone vai continuar ativado"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Se você deixar o Wi-Fi ativado, ele vai continuar assim na próxima vez que o modo avião for usado"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"O Wi-Fi fica ativado"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"O smartphone vai manter o Wi-Fi ligado no modo avião. Desligue o Wi‑Fi se você não quer que ele fique ativado."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"O dispositivo vai manter o Wi-Fi ativado no modo avião. Ele poderá ser desativado manualmente se você preferir."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rede indisponível"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"O recurso <xliff:g id="SSID">%1$s</xliff:g> foi desativado pelo administrador."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Fechar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Trocar de rede Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"O dispositivo <xliff:g id="SSID_0">%1$s</xliff:g> parece não estar conectado à Internet. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"O dispositivo <xliff:g id="SSID_0">%1$s</xliff:g> parece não estar conectado à Internet. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> tem baixa qualidade. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Trocar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Não trocar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-pt-rPT/strings.xml b/service/ServiceWifiResources/res/values-pt-rPT/strings.xml
index 74f9343..3804873 100644
--- a/service/ServiceWifiResources/res/values-pt-rPT/strings.xml
+++ b/service/ServiceWifiResources/res/values-pt-rPT/strings.xml
@@ -32,11 +32,11 @@
     <string name="wifi_suggestion_content" msgid="6985149577828091835">"Redes sugeridas por <xliff:g id="NAME">%s</xliff:g>. O dispositivo pode estabelecer ligação automaticamente."</string>
     <string name="wifi_suggestion_action_allow_app" msgid="7757859972144671588">"Permitir"</string>
     <string name="wifi_suggestion_action_disallow_app" msgid="4565857699629860726">"Não, obrigado"</string>
-    <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"Pretende estabelecer ligação à rede Wi-Fi do operador <xliff:g id="CARRIERNAME">%s</xliff:g>?"</string>
+    <string name="wifi_suggestion_imsi_privacy_title" msgid="8969261812845304079">"Quer estabelecer ligação à rede Wi-Fi do operador <xliff:g id="CARRIERNAME">%s</xliff:g>?"</string>
     <string name="wifi_suggestion_imsi_privacy_content" msgid="4266931269306079184">"Estas redes recebem um ID do SIM que pode ser utilizado para monitorizar a localização dos dispositivos."</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier" msgid="3888538126440442636">"Ligar"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier" msgid="3225397664735676024">"Não ligar"</string>
-    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_title" msgid="4407415300707014525">"Pretende confirmar a ligação?"</string>
+    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_title" msgid="4407415300707014525">"Quer confirmar a ligação?"</string>
     <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"Caso proceda à ligação, as redes Wi-Fi do operador <xliff:g id="CARRIERNAME">%s</xliff:g> podem partilhar ou aceder a um ID exclusivo associado ao seu SIM. Esta ação pode permitir que a localização do seu dispositivo seja monitorizada."</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation" msgid="2168947026413431603">"Ligar"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation" msgid="5156881939985876066">"Não ligar"</string>
@@ -48,7 +48,7 @@
     <string name="wifi_watchdog_network_disabled" msgid="5769226742956006362">"Não foi possível ligar a Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="1725243835135539125">" tem uma ligação à internet fraca."</string>
     <string name="wifi_connect_alert_title" msgid="2368200646665663612">"Permitir ligação?"</string>
-    <string name="wifi_connect_alert_message" msgid="7226456300982080746">"A app %1$s pretende estabelecer ligação à rede Wi-Fi %2$s"</string>
+    <string name="wifi_connect_alert_message" msgid="7226456300982080746">"A app %1$s quer estabelecer ligação à rede Wi-Fi %2$s"</string>
     <string name="wifi_connect_default_application" msgid="8917703737222707062">"Uma app"</string>
     <string name="accept" msgid="8346431649376483879">"Aceitar"</string>
     <string name="decline" msgid="4172251727603762084">"Recusar"</string>
@@ -66,7 +66,7 @@
     <string name="dlg_ok" msgid="254496739491689405">"OK"</string>
     <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Não é possível ligar a <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_cannot_connect_with_randomized_mac_message" msgid="4834133226521813352">"Toque para alterar as definições de privacidade e tente novamente."</string>
-    <string name="wifi_disable_mac_randomization_dialog_title" msgid="2054540994993681606">"Pretende alterar a definição de privacidade?"</string>
+    <string name="wifi_disable_mac_randomization_dialog_title" msgid="2054540994993681606">"Quer alterar a definição de privacidade?"</string>
     <string name="wifi_disable_mac_randomization_dialog_message" msgid="8874064864332248988">"Para associar, o <xliff:g id="SSID">%1$s</xliff:g> tem de utilizar o endereço MAC do seu dispositivo, um identificador único. Atualmente, a sua definição de privacidade para esta rede utiliza um identificador aleatorizado. \n\nEsta alteração pode permitir que a localização do seu dispositivo seja monitorizada por dispositivos próximos."</string>
     <string name="wifi_disable_mac_randomization_dialog_confirm_text" msgid="6954419863076751626">"Alterar definição"</string>
     <string name="wifi_disable_mac_randomization_dialog_success" msgid="5849155828154391387">"Definição atualizada. Experimente estabelecer ligação novamente."</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Não permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi ativado no modo de avião"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Se mantiver o Wi‑Fi ativado, o telemóvel vai lembrar-se de o manter ativado da próxima vez que estiver no modo de avião"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Se mantiver o Wi‑Fi ativado, o dispositivo vai lembrar-se de o manter ativado da próxima vez que estiver no modo de avião"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"O Wi‑Fi mantém-se ativado"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"O seu telemóvel mantém o Wi-Fi ativado no modo de avião. Desative o Wi-Fi se não quiser que fique ativado."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"O seu dispositivo lembra-se de manter o Wi-Fi ativado no modo de avião. Desative o Wi-Fi se não quiser que fique ativado."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rede indisponível"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"O administrador desativou a funcionalidade <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Fechar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Mudar de rede Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> não parece ter uma ligação à Internet. Mudar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> não parece ter uma ligação à Internet. Mudar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> tem baixa qualidade. Mudar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Mudar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Não mudar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-pt/strings.xml b/service/ServiceWifiResources/res/values-pt/strings.xml
index 7db6562..71aa053 100644
--- a/service/ServiceWifiResources/res/values-pt/strings.xml
+++ b/service/ServiceWifiResources/res/values-pt/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permitir"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Não permitir"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi-Fi ativado no modo avião"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Se você escolher manter o Wi‑Fi ligado, na próxima vez que você estiver em modo avião, o Wi-Fi do smartphone vai continuar ativado"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Se você deixar o Wi-Fi ativado, ele vai continuar assim na próxima vez que o modo avião for usado"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"O Wi-Fi fica ativado"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"O smartphone vai manter o Wi-Fi ligado no modo avião. Desligue o Wi‑Fi se você não quer que ele fique ativado."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"O dispositivo vai manter o Wi-Fi ativado no modo avião. Ele poderá ser desativado manualmente se você preferir."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rede indisponível"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"O recurso <xliff:g id="SSID">%1$s</xliff:g> foi desativado pelo administrador."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Fechar"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Trocar de rede Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"O dispositivo <xliff:g id="SSID_0">%1$s</xliff:g> parece não estar conectado à Internet. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"O dispositivo <xliff:g id="SSID_0">%1$s</xliff:g> parece não estar conectado à Internet. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> tem baixa qualidade. Trocar para <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Trocar"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Não trocar"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ro/strings.xml b/service/ServiceWifiResources/res/values-ro/strings.xml
index 3f395e2..c150310 100644
--- a/service/ServiceWifiResources/res/values-ro/strings.xml
+++ b/service/ServiceWifiResources/res/values-ro/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Permite"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nu permite"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi activat în modul Avion"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Dacă păstrezi Wi‑Fi activat, telefonul tău va reține să-l păstreze activat data viitoare când ești în modul Avion"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Dacă păstrezi Wi‑Fi activat, dispozitivul tău va reține să-l păstreze activat data viitoare când ești în modul Avion"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Funcția Wi‑Fi rămâne activată"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonul reține să păstreze funcția Wi-Fi activată în modul Avion. Dezactivează Wi‑Fi dacă nu vrei să rămână activat."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Dispozitivul reține să păstreze funcția Wi-Fi activată în modul Avion. Dezactivează Wi‑Fi dacă nu vrei să rămână activat."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rețea indisponibilă"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Administratorul a dezactivat <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Închide"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Schimbi rețeaua Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> nu pare să se fi conectat la internet. Comuți la <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> nu pare să se fi conectat la internet. Comuți la <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> are o calitate redusă. Comuți la <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Comută"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Nu comuta"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ru/strings.xml b/service/ServiceWifiResources/res/values-ru/strings.xml
index 794aaba..d4629ef 100644
--- a/service/ServiceWifiResources/res/values-ru/strings.xml
+++ b/service/ServiceWifiResources/res/values-ru/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Разрешить"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Не разрешать"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi будет включен в режиме полета"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Если не отключить функцию Wi-Fi, в следующий раз она останется включенной в режиме полета."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Если не отключить функцию Wi-Fi, в следующий раз она останется включенной в режиме полета."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Функция Wi‑Fi остается включенной"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Функция Wi‑Fi останется включенной в режиме полета. Вы можете отключить ее, если хотите."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Функция Wi‑Fi останется включенной в режиме полета. Вы можете отключить ее, если хотите."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Сеть недоступна"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Администратор отключил функцию \"<xliff:g id="SSID">%1$s</xliff:g>\"."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Закрыть"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Перейти на другую сеть Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Сеть <xliff:g id="SSID_0">%1$s</xliff:g> не подключена к интернету. Перейти на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Сеть <xliff:g id="SSID_0">%1$s</xliff:g> не подключена к интернету. Перейти на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"У сети <xliff:g id="SSID_0">%1$s</xliff:g> низкое качество. Перейти на <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Перейти"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Нет"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-si/strings.xml b/service/ServiceWifiResources/res/values-si/strings.xml
index 86dd174..2e65f86 100644
--- a/service/ServiceWifiResources/res/values-si/strings.xml
+++ b/service/ServiceWifiResources/res/values-si/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"ඉඩ දෙන්න"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ඉඩ නොදෙන්න"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"ගුවන්යානා ප්‍රකාරය තුළ Wi‑Fi සක්‍රීයයි"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"ඔබ Wi‑Fi ක්‍රියාත්මක කර තබා ගන්නේ නම්, ඔබ අහස්යානා ආකාරයේ සිටින මීළඟ වතාවේ එය ක්‍රියාත්මක කිරීමට ඔබේ දුරකථනයට මතක තිබෙනු ඇත."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"ඔබ Wi‑Fi ක්‍රියාත්මක කර තබා ගන්නේ නම්, ඔබ අහස්යානා ආකාරයේ සිටින මීළඟ වතාවේ එය ක්‍රියාත්මක කිරීමට ඔබේ උපාංගයට මතක තිබෙනු ඇත."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi ක්‍රියාත්මකව පවතී"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"ඔබේ දුරකථනය අහස්යානා ආකාරයේ දී Wi‑Fi ක්‍රියාත්මකව තබා ගැනීමට මතක තබා ගනියි. ඔබට Wi‑Fi ක්‍රියාත්මක වීමට අවශ්‍ය නොවේ නම් එය ක්‍රියාවිරහිත කරන්න."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"ඔබේ උපාංගය අහස්යානා ආකාරයේ දී Wi‑Fi ක්‍රියාත්මකව තබා ගැනීමට මතක තබා ගනියි. ඔබට Wi‑Fi ක්‍රියාත්මක වීමට අවශ්‍ය නොවේ නම් එය ක්‍රියාවිරහිත කරන්න."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"ලබා ගත නොහැකි ජාලය"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> ඔබේ පරිපාලක විසින් අබල කර ඇත."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"වසන්න"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi-Fi ජාල මාරු කරන්නද?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> අන්තර්ජාලයට සම්බන්ධ වී ඇති බව නොපෙනේ. <xliff:g id="SSID_1">%2$s</xliff:g> වෙත මාරු කරන්නද?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> අන්තර්ජාලයට සම්බන්ධ වී ඇති බව නොපෙනේ. <xliff:g id="SSID_1">%2$s</xliff:g> ලෙස මාරු කරන්න ද?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> හි ගුණාත්මක බව අඩුයි. <xliff:g id="SSID_1">%2$s</xliff:g> ලෙස මාරු කරන්න ද?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"මාරු කරන්න"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"මාරු නොකරන්න"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sk/strings.xml b/service/ServiceWifiResources/res/values-sk/strings.xml
index d10d942..a1cea84 100644
--- a/service/ServiceWifiResources/res/values-sk/strings.xml
+++ b/service/ServiceWifiResources/res/values-sk/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Povoliť"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Nepovoliť"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Sieť Wi‑Fi bude v režime v lietadle zapnutá"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ak ponecháte sieť Wi‑Fi zapnutú, váš telefón si zapamätá, že ju má ponechať zapnutú pri ďalšom aktivovaní režimu v lietadle"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ak ponecháte sieť Wi‑Fi zapnutú, vaše zariadenie si zapamätá, že ju má ponechať zapnutú pri ďalšom aktivovaní režimu v lietadle"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Pripojenie Wi‑Fi zostane zapnuté"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefón si zapamätá, aby v režime v lietadle nevypínal sieť Wi‑Fi. Vypnite ju, ak nechcete, aby zostala zapnutá."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Zariadenie si pamätá, aby v režime v lietadle nevypínalo sieť Wi‑Fi. Ak ju nechcete ponechať zapnutú, vypnite ju."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Nedostupná sieť"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Funkcia <xliff:g id="SSID">%1$s</xliff:g> je deaktivovaná správcom."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zavrieť"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Chcete prepnúť siete Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> zrejme nemá internetové pripojenie. Chcete prepnúť na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> zrejme nemá internetové pripojenie. Chcete prepnúť na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> má nízku kvalitu. Chcete prepnúť na <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Prepnúť"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Neprepnúť"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sl/strings.xml b/service/ServiceWifiResources/res/values-sl/strings.xml
index 3a68375..ab99da0 100644
--- a/service/ServiceWifiResources/res/values-sl/strings.xml
+++ b/service/ServiceWifiResources/res/values-sl/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Dovoli"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ne dovoli"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi je vklopljen v načinu za letalo."</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Če pustite Wi-Fi vklopljen, bo telefon ob naslednjem preklopu na način za letalo pustil Wi-Fi vklopljen."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Če pustite Wi-Fi vklopljen, bo naprava ob naslednjem preklopu na način za letalo pustila Wi-Fi vklopljen."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi ostane vklopljen"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefon v načinu za letalo pusti Wi-Fi vklopljen. Če ne želite, da ostane vklopljen, izklopite vmesnik Wi‑Fi."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Naprava v načinu za letalo pusti Wi-Fi vklopljen. Če ne želite, da Wi‑Fi ostane vklopljen, ga izklopite."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Omrežje ni na voljo"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Omrežje <xliff:g id="SSID">%1$s</xliff:g> je onemogočil skrbnik."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Zapri"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Želite preklopiti omrežje Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Videti je, da omrežje <xliff:g id="SSID_0">%1$s</xliff:g> ni povezano v internet. Želite preklopiti na omrežje <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Videti je, da omrežje <xliff:g id="SSID_0">%1$s</xliff:g> ni povezano v internet. Želite preklopiti na omrežje <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Omrežje <xliff:g id="SSID_0">%1$s</xliff:g> je nizke kakovosti. Želite preklopiti na omrežje <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Preklopi"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ne preklopi"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sq/strings.xml b/service/ServiceWifiResources/res/values-sq/strings.xml
index 9cb7851..92fe6fa 100644
--- a/service/ServiceWifiResources/res/values-sq/strings.xml
+++ b/service/ServiceWifiResources/res/values-sq/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Lejo"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Mos lejo"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi është në modalitetin e aeroplanit"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Nëse e mban Wi-Fi të aktivizuar, telefoni yt do të kujtohet ta mbajë atë të aktivizuar herën tjetër që të jesh në modalitetin e aeroplanit"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Nëse e mban Wi-Fi të aktivizuar, pajisja jote do të kujtohet ta mbajë të aktivizuar herën tjetër që të jesh në modalitetin e aeroplanit"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi qëndron aktiv"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefoni yt kujtohet që ta mbajë Wi-Fi të aktivizuar në modalitetin e aeroplanit. Çaktivizo Wi‑Fi nëse nuk dëshiron që të qëndrojë e aktivizuar."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Pajisja jote kujtohet që ta mbajë Wi-Fi të aktivizuar në modalitetin e aeroplanit. Çaktivizo Wi‑Fi nëse nuk dëshiron që të qëndrojë e aktivizuar."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Rrjeti i padisponueshëm"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> është çaktivizuar nga administratori yt."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Mbyll"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Të ndërrohen rrjetet Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> nuk duket të jetë lidhur me internetin. Të kalohet në <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> nuk duket të jetë lidhur me internetin. Të kalohet në <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> është me cilësi të ulët. Të kalohet në <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Kalo"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Mos kalo"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sr/strings.xml b/service/ServiceWifiResources/res/values-sr/strings.xml
index b71ce77..4b4491e 100644
--- a/service/ServiceWifiResources/res/values-sr/strings.xml
+++ b/service/ServiceWifiResources/res/values-sr/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Дозволи"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Не дозволи"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"WiFi је укључен у режиму рада у авиону"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Ако одлучите да не искључујете WiFi, телефон ће запамтити да га не искључује следећи пут када будете у режиму рада у авиону"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Ако одлучите да не искључујете WiFi, уређај ће запамтити да га не искључује следећи пут када будете у режиму рада у авиону"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WiFi остаје укључен"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Телефон памти да не треба да искључује WiFi у режиму рада у авиону. Искључите WiFi ако не желите да остане укључен."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Уређај памти да не треба да искључује WiFi у режиму рада у авиону. Искључите WiFi ако не желите да остане укључен."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Недоступна мрежа"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Администратор је онемогућио <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Затвори"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Желите да промените WiFi мрежу?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Изгледа да мрежа <xliff:g id="SSID_0">%1$s</xliff:g> није повезана на интернет. Желите да пребаците на мрежу <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Изгледа да мрежа <xliff:g id="SSID_0">%1$s</xliff:g> није повезана на интернет. Желите да се пребаците на: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> је лошег квалитета. Желите да се пребаците на: <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Пребаци"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Не пребацуј"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sv/strings.xml b/service/ServiceWifiResources/res/values-sv/strings.xml
index c0299b7..07f76a1 100644
--- a/service/ServiceWifiResources/res/values-sv/strings.xml
+++ b/service/ServiceWifiResources/res/values-sv/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Tillåt"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Tillåt inte"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wifi är aktiverat i flygplansläget"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Om du håller wifi aktiverat kommer telefonen ihåg att hålla det aktiverat nästa gång du använder flygplansläge."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Om du håller wifi aktiverat kommer enheten ihåg att hålla det aktiverat nästa gång du använder flygplansläge."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wifi inaktiveras inte"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonen håller wifi aktiverat i flygplansläge. Inaktivera wifi om du inte vill att wifi ska hållas aktiverat."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Enheten kommer ihåg att hålla wifi aktiverat i flygplansläge. Inaktivera wifi om du inte vill att wifi ska hållas aktiverat."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Otillgängligt nätverk"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> har inaktiverats av administratören."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Stäng"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Vill du byta wifi-nätverk?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> verkar inte vara ansluten till internet. Vill du byta till <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> verkar inte vara ansluten till internet. Vill du byta till <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> håller låg kvalitet. Vill du byta till <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Byt"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Byt inte"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-sw/strings.xml b/service/ServiceWifiResources/res/values-sw/strings.xml
index dc66784..30af5ea 100644
--- a/service/ServiceWifiResources/res/values-sw/strings.xml
+++ b/service/ServiceWifiResources/res/values-sw/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Ruhusu"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Usiruhusu"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi itawashwa katika hali ya ndegeni"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Usipozima Wi-Fi, simu yako itakumbuka kuiwasha wakati mwingine unapokuwa katika hali ya ndegeni"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Usipozima Wi-Fi, kifaa chako kitakumbuka kuiwasha wakati mwingine utakapokuwa katika hali ya ndegeni"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi itaendelea kuwaka"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Simu yako itaendelea kuwasha Wi-Fi inapokuwa katika hali ya ndegeni. Zima Wi-Fi iwapo hutaki iendelee kuwaka."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Kifaa chako kitaendelea kuwasha Wi-Fi ukiwa katika hali ya ndegeni. Zima Wi-Fi iwapo hutaki iendelee kuwaka."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Mtandao Haupatikani"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> umezimwa na msimamizi wako."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Funga"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Ungependa kubadili mitandao ya Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> haionekani kuwa imeunganishwa kwenye intaneti. Ungependa kubadilisha utumie <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Inaonekana <xliff:g id="SSID_0">%1$s</xliff:g> haijaunganishwa kwenye intaneti. Ungependa kubadilisha utumie <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> ni ya ubora wa chini. Ungependa kubadilisha utumie <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Badilisha"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Usibadili"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ta/strings.xml b/service/ServiceWifiResources/res/values-ta/strings.xml
index c8508d3..d565756 100644
--- a/service/ServiceWifiResources/res/values-ta/strings.xml
+++ b/service/ServiceWifiResources/res/values-ta/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"அனுமதி"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"அனுமதிக்க வேண்டாம்"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"விமானப் பயன்முறையில் வைஃபை இயக்கத்திலேயே இருக்கும்"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"வைஃபையை இயக்கத்தில் வைத்திருந்தால், அடுத்த முறை நீங்கள் விமானப் பயன்முறையைப் பயன்படுத்தும்போது உங்கள் மொபைல் வைஃபையை இயக்கத்தில் வைக்கும்"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"நீங்கள் வைஃபையை இயக்கத்தில் வைத்திருந்தால், அடுத்த முறை நீங்கள் விமானப் பயன்முறையில் இருக்கும்போது உங்கள் சாதனம் அதை இயக்கத்திலேயே வைத்திருக்கும்"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"வைஃபை இயக்கத்திலேயே இருக்கும்"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"உங்கள் மொபைல் விமானப் பயன்முறையில் இருக்கும்போது வைஃபையை இயக்கத்திலேயே வைத்திருக்கும். வைஃபையைப் பயன்படுத்த விரும்பவில்லை என்றால் அதை முடக்கவும்."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"உங்கள் சாதனம் விமானப் பயன்முறையில் வைஃபையை இயக்கத்திலேயே வைத்திருக்கும். வைஃபையை இயக்கத்தில் வைத்திருக்க விரும்பவில்லை எனில் நீங்கள் அதை முடக்கலாம்."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"நெட்வொர்க் கிடைக்கவில்லை"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> உங்கள் நிர்வாகியால் முடக்கப்பட்டுள்ளது."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"மூடுக"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"வைஃபை நெட்வொர்க்குகளை மாற்றவா?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"இணையதளத்தில் இணைப்பதற்காக <xliff:g id="SSID_0">%1$s</xliff:g> காட்டப்படவில்லை. <xliff:g id="SSID_1">%2$s</xliff:g>க்கு மாற்றவா?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> இணையத்துடன் இணைக்கப்படவில்லை. <xliff:g id="SSID_1">%2$s</xliff:g>க்கு மாற்றவா?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> தரம் குறைவாக உள்ளது. <xliff:g id="SSID_1">%2$s</xliff:g>க்கு மாற்றவா?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"மாற்று"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"மாற்ற வேண்டாம்"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-te/strings.xml b/service/ServiceWifiResources/res/values-te/strings.xml
index 597ce18..0de30c8 100644
--- a/service/ServiceWifiResources/res/values-te/strings.xml
+++ b/service/ServiceWifiResources/res/values-te/strings.xml
@@ -65,7 +65,7 @@
     <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="2226863827636191980">"ఫోన్ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>కి కనెక్ట్ అయినప్పుడు అది Wi-Fi నుండి తాత్కాలికంగా డిస్‌కనెక్ట్ చేయబడుతుంది"</string>
     <string name="dlg_ok" msgid="254496739491689405">"సరే"</string>
     <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_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_confirm_text" msgid="6954419863076751626">"సెట్టింగ్‌ను మార్చండి"</string>
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"అనుమతించండి"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"అనుమతించవద్దు"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"విమానం మోడ్‌లో Wi‑Fi ఆన్ చేయబడింది"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"మీరు Wi‑Fiని ఆన్‌లో ఉంచినట్లయితే, మీరు తదుపరిసారి విమానం మోడ్‌లో ఉన్నప్పుడు దాన్ని ఆన్‌లో ఉంచాలని మీ ఫోన్ గుర్తుంచుకుంటుంది"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"మీరు Wi‑Fiని ఆన్‌లో ఉంచినట్లయితే, మీరు తదుపరిసారి విమానం మోడ్‌లో ఉన్నప్పుడు దాన్ని ఆన్‌లో ఉంచాలని మీ పరికరం గుర్తుంచుకుంటుంది"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi ఆన్‌లోనే ఉంటుంది"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"మీ ఫోన్ విమానం మోడ్‌లో Wi‑Fiని ఆన్‌లో ఉంచాలని గుర్తుంచుకుంటుంది. Wi‑Fi ఆన్‌లో ఉండకూడదనుకుంటే దాన్ని ఆఫ్ చేయండి."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"మీ పరికరం విమానం మోడ్‌లో Wi‑Fiని ఆన్‌లో ఉంచాలని గుర్తుంచుకుంటుంది. Wi‑Fi ఆన్‌లో ఉండకూడదనుకుంటే దాన్ని ఆఫ్ చేయండి."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"నెట్‌వర్క్ అందుబాటులో లేదు"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>ను మీ అడ్మినిస్ట్రేటర్ డిజేబుల్ చేశారు."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"మూసివేయండి"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Wi‑Fi నెట్‌వర్క్‌లను మార్చలా?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> ఇంటర్నెట్‌కు కనెక్ట్ అయి లేనట్లు కనిపిస్తోంది. <xliff:g id="SSID_1">%2$s</xliff:g>‌కు మారాలా?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> ఇంటర్నెట్‌కు కనెక్ట్ అయి లేనట్లు కనిపిస్తోంది. <xliff:g id="SSID_1">%2$s</xliff:g>‌కు మారాలా?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> తక్కువ క్వాలిటీలో ఉంది. <xliff:g id="SSID_1">%2$s</xliff:g>‌కు మారాలా?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"మారండి"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"మారద్దు"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-th/strings.xml b/service/ServiceWifiResources/res/values-th/strings.xml
index 2eeb1c3..8d2ed19 100644
--- a/service/ServiceWifiResources/res/values-th/strings.xml
+++ b/service/ServiceWifiResources/res/values-th/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"อนุญาต"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"ไม่อนุญาต"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi จะเปิดในโหมดบนเครื่องบิน"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"หากเปิด Wi-Fi ไว้ โทรศัพท์จะจำว่าต้องเปิด Wi-Fi ในครั้งถัดไปที่คุณอยู่ในโหมดบนเครื่องบิน"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"หากเปิด Wi-Fi ไว้ อุปกรณ์จะจำว่าต้องเปิด Wi-Fi ในครั้งถัดไปที่คุณอยู่ในโหมดบนเครื่องบิน"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi-Fi เปิดอยู่"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"โทรศัพท์จำว่าจะต้องเปิด Wi-Fi ในโหมดบนเครื่องบิน ปิด Wi-Fi หากคุณไม่ต้องการให้เปิดไว้"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"อุปกรณ์จำว่าจะต้องเปิด Wi-Fi ไว้ในโหมดบนเครื่องบิน ปิด Wi-Fi หากคุณไม่ต้องการให้เปิดไว้"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"เครือข่ายไม่พร้อมใช้งาน"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"ผู้ดูแลระบบปิดใช้ <xliff:g id="SSID">%1$s</xliff:g>"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"ปิด"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"เปลี่ยนเครือข่าย Wi-Fi ไหม"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"ดูเหมือนว่า <xliff:g id="SSID_0">%1$s</xliff:g> จะไม่ได้เชื่อมต่ออินเทอร์เน็ต เปลี่ยนเป็น <xliff:g id="SSID_1">%2$s</xliff:g> ไหม"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ดูเหมือนว่า <xliff:g id="SSID_0">%1$s</xliff:g> จะไม่ได้เชื่อมต่ออินเทอร์เน็ต เปลี่ยนเป็น <xliff:g id="SSID_1">%2$s</xliff:g> ไหม"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> มีคุณภาพต่ำ เปลี่ยนเป็น <xliff:g id="SSID_1">%2$s</xliff:g> ไหม"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"เปลี่ยน"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"ไม่เปลี่ยน"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-tl/strings.xml b/service/ServiceWifiResources/res/values-tl/strings.xml
index 97da1e3..9966aec 100644
--- a/service/ServiceWifiResources/res/values-tl/strings.xml
+++ b/service/ServiceWifiResources/res/values-tl/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Payagan"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Huwag payagan"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"I-on ang Wi‑Fi habang nasa airplane mode"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Kung papanatilihin mong naka-on ang Wi‑Fi, tatandaan ng iyong telepono na panatilihin itong naka-on sa susunod na nasa airplane mode ka"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Kung papanatilihin mong naka-on ang Wi‑Fi, tatandaan ng iyong device na panatilihin itong naka-on sa susunod na nasa airplane mode ka"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Mananatiling naka-on ang Wi‑Fi"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Tinatandaan ng iyong telepono na panatilihing naka-on ang Wi-Fi habang nasa airplane mode. I-off ang Wi‑Fi kung ayaw mo itong manatiling naka-on."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Tinatandaan ng iyong device na panatilihing naka-on ang Wi-Fi habang nasa airplane mode. I-off ang Wi‑Fi kung ayaw mo itong manatiling naka-on."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Hindi Available na Network"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Na-disable ng iyong administrator ang <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Isara"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Lumipat ng Wi‑Fi network?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Mukhang hindi nakakonekta sa internet ang <xliff:g id="SSID_0">%1$s</xliff:g>. Lumipat sa <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Mukhang hindi nakakonekta sa internet ang <xliff:g id="SSID_0">%1$s</xliff:g>. Lumipat sa <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"Mababa ang kalidad ng <xliff:g id="SSID_0">%1$s</xliff:g>. Lumipat sa <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Lumipat"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Huwag lumipat"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-tr/strings.xml b/service/ServiceWifiResources/res/values-tr/strings.xml
index 04adf33..1550480 100644
--- a/service/ServiceWifiResources/res/values-tr/strings.xml
+++ b/service/ServiceWifiResources/res/values-tr/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"İzin ver"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"İzin verme"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Uçak modundayken kablosuz bağlantı açık"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Kablosuz bağlantıyı açık tutarsanız telefonunuz, daha sonra tekrar uçak modunda olduğunuzda kablosuz bağlantıyı açık tutar"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Kablosuz bağlantıyı açık tutarsanız cihazınız, daha sonra tekrar uçak modunda olduğunuzda kablosuz bağlantıyı açık tutar"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Kablosuz bağlantı açık kalır"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefonunuz, uçak modundayken kablosuz bağlantıyı açık tutmayı hatırlar. Açık kalmasını istemiyorsanız kablosuz bağlantıyı kapatın."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Cihazınız, uçak modundayken kablosuz bağlantıyı açık tutmayı hatırlar. Açık kalmasını istemiyorsanız kablosuz bağlantıyı kapatın."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Kullanılamayan Ağ"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g>, yöneticiniz tarafından devre dışı bırakıldı."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Kapat"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Kablosuz ağa geçişiyapılsın mı?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> internete bağlı değil. <xliff:g id="SSID_1">%2$s</xliff:g> geçişi yapılsın mı?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> internete bağlı değil. <xliff:g id="SSID_1">%2$s</xliff:g> ağına geçilsin mi?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> bağlantı kalitesi düşük. <xliff:g id="SSID_1">%2$s</xliff:g> ağına geçilsin mi?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Geçiş yap"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Geçiş yapma"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-uk/strings.xml b/service/ServiceWifiResources/res/values-uk/strings.xml
index 1654b0c..2afa400 100644
--- a/service/ServiceWifiResources/res/values-uk/strings.xml
+++ b/service/ServiceWifiResources/res/values-uk/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Дозволити"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Не дозволяти"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Ви ввімкнули Wi‑Fi у режимі польоту"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Якщо ви не вимкнете Wi‑Fi на телефоні, ця функція залишатиметься ввімкненою під час наступного використання режиму польоту"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Якщо ви не вимкнете Wi‑Fi на пристрої, ця функція залишатиметься ввімкненою під час наступного використання режиму польоту"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi не буде вимкнено"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"У режимі польоту функція Wi‑Fi на телефоні залишатиметься ввімкненою. Якщо захочете вимкнути її, вимкніть Wi‑Fi."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"У режимі польоту функція Wi‑Fi на пристрої залишатиметься ввімкненою. За бажання її можна вимкнути."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Недоступна мережа"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Адміністратор вимкнув мережу <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Закрити"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Перемкнутися на іншу мережу Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Схоже, у мережі <xliff:g id="SSID_0">%1$s</xliff:g> немає інтернет-зʼєднання. Перемкнутися на мережу <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Схоже, у мережі <xliff:g id="SSID_0">%1$s</xliff:g> немає інтернет-зʼєднання. Перемкнутися на мережу <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"У мережі <xliff:g id="SSID_0">%1$s</xliff:g> поганий сигнал. Перемкнутися на мережу <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Перемкнутися"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Не перемикатися"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-ur/strings.xml b/service/ServiceWifiResources/res/values-ur/strings.xml
index 099d3a1..9305ac6 100644
--- a/service/ServiceWifiResources/res/values-ur/strings.xml
+++ b/service/ServiceWifiResources/res/values-ur/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"اجازت دیں"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"اجازت نہ دیں"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"‏ہوائی جہاز وضع میں Wi‑Fi آن ہے"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"‏اگر آپ Wi-Fi کو آن رکھتے ہیں تو آپ کا فون آپ کے اگلی مرتبہ ہوائی جہاز وضع میں ہونے پر اسے آن رکھنا یاد رکھے گا"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"‏اگر آپ Wi-Fi کو آن رکھتے ہیں تو آپ کا آلہ آپ کے اگلی مرتبہ ہوائی جہاز وضع میں ہونے پر اسے آن رکھنا یاد رکھے گا"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"‏Wi‑Fi آن رہتا ہے"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"‏آپ کا فون ہوائی جہاز وضع میں Wi‑Fi کو آن رکھنا یاد رکھتا ہے۔ اگر آپ نہیں چاہتے ہیں کہ Wi-Fi آن رہے تو اسے آف کریں۔"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"‏آپ کا آلہ ہوائی جہاز وضع میں Wi‑Fi کو آن رکھنا یاد رکھتا ہے۔ اگر آپ نہیں چاہتے ہیں کہ Wi-Fi آن رہے تو اسے آف کریں۔"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"غیر دستیاب نیٹ ورک"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> کو آپ کے منتظم کے ذریعے مسدود کر دیا گیا ہے۔"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"بند کریں"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"‏Wi-Fi نیٹ ورکس کو سوئچ کریں؟"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"ایسا لگتا ہے کہ <xliff:g id="SSID_0">%1$s</xliff:g> انٹرنیٹ سے منسلک نہیں ہے۔ <xliff:g id="SSID_1">%2$s</xliff:g> پر سوئچ کریں؟"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"ایسا لگتا ہے کہ <xliff:g id="SSID_0">%1$s</xliff:g> انٹرنیٹ سے منسلک نہیں ہے۔ <xliff:g id="SSID_1">%2$s</xliff:g> پر سوئچ کریں؟"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> کی کوالٹی ادنی ہے۔ <xliff:g id="SSID_1">%2$s</xliff:g> پر سوئچ کریں؟"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"سوئچ کریں"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"سوئچ نہ کریں"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-uz/strings.xml b/service/ServiceWifiResources/res/values-uz/strings.xml
index cb081b7..fa24256 100644
--- a/service/ServiceWifiResources/res/values-uz/strings.xml
+++ b/service/ServiceWifiResources/res/values-uz/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Ruxsat berish"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Rad etish"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi parvoz rejimida yoniq"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Wi-Fi yoniq qolsa, telefon keyingi safar parvoz rejimida ham uni yoniq qoldiradi."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Wi-Fi yoniq qolsa, qurilmangiz keyingi safar parvoz rejimida ham uni yoniq qoldiradi."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi yoniq turadi"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Telefoningiz parvoz rejimida Wi‑Fi yoqilganini eslab qoladi. Yoniq qolmasligi uchun Wi-Fi aloqasini oʻchiring."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Qurilmangiz parvoz rejimida Wi‑Fi yoqilganini eslab qoladi. Yoniq qolmasligi uchun Wi-Fi aloqasini oʻchiring."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Tarmoq mavjud emas"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> administrator tomonidan faolsizlantirilgan."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Yopish"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Boshqa Wi-Fi tarmoqqa almashtirilsinmi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> internetga ulanmagan. <xliff:g id="SSID_1">%2$s</xliff:g>ga almashsinmi?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> internetga ulanmagan. <xliff:g id="SSID_1">%2$s</xliff:g>ga almashsinmi?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> aloqasi sust. <xliff:g id="SSID_1">%2$s</xliff:g>ga almashsinmi?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Almashish"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Almashmasin"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-vi/strings.xml b/service/ServiceWifiResources/res/values-vi/strings.xml
index e9b6eb4..fe43095 100644
--- a/service/ServiceWifiResources/res/values-vi/strings.xml
+++ b/service/ServiceWifiResources/res/values-vi/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Cho phép"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Không cho phép"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"Wi‑Fi bật ở chế độ trên máy bay"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Nếu bạn không tắt Wi‑Fi, điện thoại sẽ luôn bật Wi‑Fi vào lần tiếp theo bạn dùng chế độ trên máy bay"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Nếu bạn không tắt Wi‑Fi, thiết bị sẽ luôn bật Wi‑Fi vào lần tiếp theo bạn dùng chế độ trên máy bay"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi ‑ Fi luôn được bật"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Điện thoại của bạn sẽ luôn bật Wi-Fi ở chế độ trên máy bay. Nếu không muốn như vậy thì bạn có thể tắt Wi-Fi."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Thiết bị của bạn sẽ luôn bật Wi-Fi ở chế độ trên máy bay. Nếu không muốn như vậy thì bạn có thể tắt Wi-Fi."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Không có kết nối mạng"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"Quản trị viên của bạn đã tắt tính năng <xliff:g id="SSID">%1$s</xliff:g>."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Đóng"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Chuyển mạng Wi‑Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"Có vẻ như <xliff:g id="SSID_0">%1$s</xliff:g> không kết nối với Internet. Chuyển sang <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"Có vẻ như <xliff:g id="SSID_0">%1$s</xliff:g> không kết nối với Internet. Chuyển sang <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> có chất lượng thấp Chuyển sang <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Chuyển"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Không chuyển"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-zh-rCN/strings.xml b/service/ServiceWifiResources/res/values-zh-rCN/strings.xml
index d8dffa1..737a627 100644
--- a/service/ServiceWifiResources/res/values-zh-rCN/strings.xml
+++ b/service/ServiceWifiResources/res/values-zh-rCN/strings.xml
@@ -55,7 +55,7 @@
     <string name="ok" msgid="847575529546290102">"确定"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="6552639940428040869">"已发出邀请"</string>
     <string name="wifi_p2p_invitation_to_connect_title" msgid="8917157937652519251">"连接邀请"</string>
-    <string name="wifi_p2p_invitation_seconds_remaining" msgid="4214101104506918344">"{0,plural, =1{# 秒后接受。}other{# 秒后接受。}}"</string>
+    <string name="wifi_p2p_invitation_seconds_remaining" msgid="4214101104506918344">"{0,plural, =1{# 秒内接受。}other{# 秒内接受。}}"</string>
     <string name="wifi_p2p_from_message" msgid="5921308150192756898">"发件人:"</string>
     <string name="wifi_p2p_to_message" msgid="3809923305696994787">"收件人:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"输入所需的PIN码:"</string>
@@ -84,7 +84,7 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32762" msgid="2381670648753465737">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 身份验证错误 32762"</string>
   <string-array name="wifi_eap_error_message_code_32762_carrier_overrides">
-    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 帐号出现了问题。请拨打 800-922-0204 与我们联系。(错误 32762)"</item>
+    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 账号出现了问题。请拨打 800-922-0204 与我们联系。(错误 32762)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32763" msgid="4467733260757049969">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 身份验证错误 32763"</string>
   <string-array name="wifi_eap_error_message_code_32763_carrier_overrides">
@@ -96,7 +96,7 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32765" msgid="2167528358066037980">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 身份验证错误 32765"</string>
   <string-array name="wifi_eap_error_message_code_32765_carrier_overrides">
-    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 帐号出现了问题。请拨打 800-922-0204 与我们联系。(错误 32765)"</item>
+    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 账号出现了问题。请拨打 800-922-0204 与我们联系。(错误 32765)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32766" msgid="2335996367705677670">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 身份验证错误 32766"</string>
   <string-array name="wifi_eap_error_message_code_32766_carrier_overrides">
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"允许"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"不允许"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"飞行模式时开启 WLAN"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"如果您不关闭 WLAN,那么您下次进入飞行模式时手机将记住保持 WLAN 开启"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"如果您不关闭 WLAN,那么您下次进入飞行模式时设备将记住保持 WLAN 开启"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"WLAN 保持开启状态"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"在飞行模式下手机将记住保持 WLAN 开启。如果您不想让 WLAN 保持开启,请将其关闭。"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"在飞行模式下设备将记住保持 WLAN 开启。如果您不想让 WLAN 保持开启,请关闭 WLAN。"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"网络无法使用"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"<xliff:g id="SSID">%1$s</xliff:g> 已被您的管理员停用。"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"关闭"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"要切换到 WLAN 网络吗?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g>似乎并未连接到互联网。要切换到<xliff:g id="SSID_1">%2$s</xliff:g>吗?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g>似乎并未连接到互联网。要切换到<xliff:g id="SSID_1">%2$s</xliff:g>吗?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g>质量不佳。要切换到<xliff:g id="SSID_1">%2$s</xliff:g>吗?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"切换"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"不切换"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-zh-rHK/strings.xml b/service/ServiceWifiResources/res/values-zh-rHK/strings.xml
index 5004287..00009b2 100644
--- a/service/ServiceWifiResources/res/values-zh-rHK/strings.xml
+++ b/service/ServiceWifiResources/res/values-zh-rHK/strings.xml
@@ -37,14 +37,14 @@
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_carrier" msgid="3888538126440442636">"連接"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_carrier" msgid="3225397664735676024">"不要連線"</string>
     <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_title" msgid="4407415300707014525">"確定要連線嗎?"</string>
-    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"連線後,「<xliff:g id="CARRIERNAME">%s</xliff:g>」的 Wi‑Fi 網絡就能存取或分享與 SIM 卡相關的獨有 ID,有心人或許可藉此追蹤您裝置的位置。"</string>
+    <string name="wifi_suggestion_imsi_privacy_exemption_confirmation_content" msgid="9211241189147807136">"連線後,「<xliff:g id="CARRIERNAME">%s</xliff:g>」的 Wi‑Fi 網絡就能存取或分享與 SIM 卡相關的獨有 ID,有心人或許可藉此追蹤你裝置的位置。"</string>
     <string name="wifi_suggestion_action_allow_imsi_privacy_exemption_confirmation" msgid="2168947026413431603">"連線"</string>
     <string name="wifi_suggestion_action_disallow_imsi_privacy_exemption_confirmation" msgid="5156881939985876066">"不要連線"</string>
     <string name="wifi_wakeup_onboarding_title" msgid="3868826648004934540">"Wi-Fi 將會自動開啟"</string>
-    <string name="wifi_wakeup_onboarding_subtext" msgid="5705886295837387430">"當您位於已儲存的高品質網絡信號範圍內時"</string>
+    <string name="wifi_wakeup_onboarding_subtext" msgid="5705886295837387430">"當你位於已儲存的高品質網絡信號範圍內時"</string>
     <string name="wifi_wakeup_onboarding_action_disable" msgid="6209706680391785825">"不要重新開啟"</string>
     <string name="wifi_wakeup_enabled_title" msgid="5043486751612595850">"已自動開啟 Wi‑Fi"</string>
-    <string name="wifi_wakeup_enabled_content" msgid="3911262526267025882">"您附近有已儲存的網絡:<xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
+    <string name="wifi_wakeup_enabled_content" msgid="3911262526267025882">"你附近有已儲存的網絡:<xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string>
     <string name="wifi_watchdog_network_disabled" msgid="5769226742956006362">"無法連線至 Wi-Fi"</string>
     <string name="wifi_watchdog_network_disabled_detailed" msgid="1725243835135539125">" 互聯網連線欠佳。"</string>
     <string name="wifi_connect_alert_title" msgid="2368200646665663612">"允許連線?"</string>
@@ -67,7 +67,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>
@@ -80,15 +80,15 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32761" msgid="3962610712123291591">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32761"</string>
   <string-array name="wifi_eap_error_message_code_32761_carrier_overrides">
-    <item msgid="5173744897521799785">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:您未訂閱 Verizon Wi-Fi Access。請致電 800-922-0204 與我們聯絡 (錯誤 = 32761)"</item>
+    <item msgid="5173744897521799785">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:你未訂閱 Verizon Wi-Fi Access。請致電 800-922-0204 與我們聯絡 (錯誤 = 32761)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32762" msgid="2381670648753465737">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32762"</string>
   <string-array name="wifi_eap_error_message_code_32762_carrier_overrides">
-    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 帳戶發生問題。請致電 800-922-0204 與我們聯絡 (錯誤 = 32762)"</item>
+    <item msgid="2911560823350042826">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:你的 Verizon Wi-Fi Access 帳戶發生問題。請致電 800-922-0204 與我們聯絡 (錯誤 = 32762)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32763" msgid="4467733260757049969">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32763"</string>
   <string-array name="wifi_eap_error_message_code_32763_carrier_overrides">
-    <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:您已連線至 Verizon Wi-Fi Access (錯誤 = 32763)"</item>
+    <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:你已連線至 Verizon Wi-Fi Access (錯誤 = 32763)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32764" msgid="7349538467012877101">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32764"</string>
   <string-array name="wifi_eap_error_message_code_32764_carrier_overrides">
@@ -96,15 +96,15 @@
   </string-array>
     <string name="wifi_eap_error_message_code_32765" msgid="2167528358066037980">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32765"</string>
   <string-array name="wifi_eap_error_message_code_32765_carrier_overrides">
-    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:您的 Verizon Wi-Fi Access 帳戶發生問題。請致電 800-922-0204 與我們聯絡 (錯誤 = 32765)"</item>
+    <item msgid="7454136618636618962">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:你的 Verizon Wi-Fi Access 帳戶發生問題。請致電 800-922-0204 與我們聯絡 (錯誤 = 32765)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32766" msgid="2335996367705677670">"<xliff:g id="SSID">%1$s</xliff:g> : EAP 驗證錯誤 32766"</string>
   <string-array name="wifi_eap_error_message_code_32766_carrier_overrides">
-    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:您目前的所在地點無法使用 Verizon Wi-Fi Access,請稍後再試。您亦可換個地點,然後再試一次 (錯誤 = 32766)"</item>
+    <item msgid="5876210184761573755">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:你目前的所在地點無法使用 Verizon Wi-Fi Access,請稍後再試。你亦可換個地點,然後再試一次 (錯誤 = 32766)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_32767" msgid="7094289719914089006">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 驗證錯誤 32767"</string>
   <string-array name="wifi_eap_error_message_code_32767_carrier_overrides">
-    <item msgid="3756793972687282940">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:連線至 Verizon Wi-Fi Access 時發生問題。請稍後再試。您亦可換個地點,然後再試一次 (錯誤 = 32767)"</item>
+    <item msgid="3756793972687282940">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g>:連線至 Verizon Wi-Fi Access 時發生問題。請稍後再試。你亦可換個地點,然後再試一次 (錯誤 = 32767)"</item>
   </string-array>
     <string name="wifi_eap_error_message_code_16384" msgid="575394783233092922">"<xliff:g id="SSID">%1$s</xliff:g>:EAP 驗證錯誤 16384"</string>
   <string-array name="wifi_eap_error_message_code_16384_carrier_overrides">
@@ -158,18 +158,18 @@
     <string name="wifi_ca_cert_notification_preT_continue_text" msgid="1525418430746943670">"仍要連線"</string>
     <string name="wifi_ca_cert_notification_preT_abort_text" msgid="8307996031461071854">"不要連線"</string>
     <string name="wifi_enable_request_dialog_title" msgid="3577459145316177148">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」開啟 Wi‑Fi 嗎?"</string>
-    <string name="wifi_enable_request_dialog_message" msgid="6395169178524938278">"您可以在「快速設定」中關閉 Wi-Fi"</string>
+    <string name="wifi_enable_request_dialog_message" msgid="6395169178524938278">"你可以在「快速設定」中關閉 Wi-Fi"</string>
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"允許"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"不允許"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"在飛行模式下開啟 Wi-Fi"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"如果您不關閉 Wi-Fi,下次手機進入飛行模式時,Wi-Fi 將保持開啟"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"如果你不關閉 Wi-Fi,下次裝置進入飛行模式時,Wi-Fi 將保持開啟"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"保持 Wi-Fi 連線"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"手機會記得在飛行模式下保持 Wi-Fi 開啟。如果您不希望保持開啟,請關閉 Wi-Fi。"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"裝置會記得在飛行模式下保持 Wi-Fi 開啟。如果你不希望保持開啟,請關閉 Wi-Fi。"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"無法使用網絡"</string>
-    <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"您的管理員已停用「<xliff:g id="SSID">%1$s</xliff:g>」。"</string>
+    <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"你的管理員已停用「<xliff:g id="SSID">%1$s</xliff:g>」。"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"關閉"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"要切換至 Wi-Fi 網絡嗎?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"「<xliff:g id="SSID_0">%1$s</xliff:g>」似乎尚未連線至互聯網。要切換至「<xliff:g id="SSID_1">%2$s</xliff:g>」嗎?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"「<xliff:g id="SSID_0">%1$s</xliff:g>」似乎未連線至互聯網。要切換至「<xliff:g id="SSID_1">%2$s</xliff:g>」嗎?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"「<xliff:g id="SSID_0">%1$s</xliff:g>」品質偏低,要切換至「<xliff:g id="SSID_1">%2$s</xliff:g>」嗎?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"切換"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"不要切換"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-zh-rTW/strings.xml b/service/ServiceWifiResources/res/values-zh-rTW/strings.xml
index d0781fb..90a9b2d 100644
--- a/service/ServiceWifiResources/res/values-zh-rTW/strings.xml
+++ b/service/ServiceWifiResources/res/values-zh-rTW/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"允許"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"不允許"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"在飛航模式下保持 Wi-Fi 開啟狀態"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"如果你不關閉 Wi-Fi,下次手機進入飛航模式時,Wi-Fi 將保持開啟"</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"如果你不關閉 Wi-Fi,下次裝置進入飛航模式時,Wi-Fi 將保持開啟狀態"</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"Wi‑Fi 會保持開啟狀態"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"手機會記得在飛航模式下保持 Wi-Fi 開啟。如果不要保持開啟,請關閉 Wi-Fi。"</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"裝置會記得在飛航模式下讓 Wi-Fi 保持開啟狀態。如果不要保持開啟狀態,請關閉 Wi-Fi。"</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"網路無法使用"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"你的系統管理員已停用 <xliff:g id="SSID">%1$s</xliff:g>。"</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"關閉"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"是否要切換至 Wi‑Fi 網路?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"<xliff:g id="SSID_0">%1$s</xliff:g> 似乎並未連上網際網路,是否要切換至 <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"<xliff:g id="SSID_0">%1$s</xliff:g> 似乎並未連上網際網路,是否要切換至 <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"<xliff:g id="SSID_0">%1$s</xliff:g> 品質不佳,是否要切換至 <xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"切換"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"不要切換"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values-zu/strings.xml b/service/ServiceWifiResources/res/values-zu/strings.xml
index e7092cf..23c754f 100644
--- a/service/ServiceWifiResources/res/values-zu/strings.xml
+++ b/service/ServiceWifiResources/res/values-zu/strings.xml
@@ -162,14 +162,14 @@
     <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Vumela"</string>
     <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ungavumeli"</string>
     <string name="wifi_enabled_apm_first_time_title" msgid="4814302384637588804">"I-Wi‑Fi ivuliwe kumodi yendiza"</string>
-    <string name="wifi_enabled_apm_first_time_message" msgid="5443101896157496353">"Uma ugcina i-Wi‑Fi ivuliwe, ifoni yakho izokhumbula ukuyigcina ivuliwe ngesikhathi esilandelayo uma ukumodi yendiza."</string>
+    <string name="wifi_enabled_apm_first_time_message" msgid="6416193199042203037">"Uma ugcina i-Wi‑Fi ivuliwe, idivayisi yakho izokhumbula ukuyigcina ivuliwe ngesikhathi esilandelayo uma ukumodi yendiza."</string>
     <string name="apm_enabled_first_time_title" msgid="2534167413190488009">"I-Wi‑Fi ihlala ivuliwe"</string>
-    <string name="apm_enabled_first_time_message" msgid="4753441005253327841">"Ifoni yakho ikhumbula ukugcina i-Wi‑Fi ivuliwe kumodi yendiza. Vala i-Wi-Fi uma ungafuni ukuthi ihlale ivuliwe."</string>
+    <string name="apm_enabled_first_time_message" msgid="6093911536874954410">"Ifoni yakho ikhumbula ukugcina i-Wi‑Fi ivuliwe kumodi yendiza. Vala i-Wi-Fi uma ungafuni ukuthi ihlale ivuliwe."</string>
     <string name="wifi_network_disabled_by_admin_title" msgid="9057697656855227293">"Inethiwekhi Engatholakali"</string>
     <string name="wifi_network_disabled_by_admin_message" msgid="7830360441333155462">"I-<xliff:g id="SSID">%1$s</xliff:g> ikhutshazwe umlawuli wakho."</string>
     <string name="wifi_network_disabled_by_admin_button" msgid="3350310756265122689">"Vala"</string>
-    <string name="wifi_network_switch_dialog_title" msgid="4387595800316985421">"Shintsha Amanethiwekhi we-Wi-Fi?"</string>
-    <string name="wifi_network_switch_dialog_message" msgid="1122837977780086429">"I-<xliff:g id="SSID_0">%1$s</xliff:g> ayibonakali ixhumeke ku-inthanethi. Shintshela ku-<xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_no_internet" msgid="4116969009784041242">"I-<xliff:g id="SSID_0">%1$s</xliff:g> ayibonakali ixhumeke ku-inthanethi. Shintshela ku-<xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
+    <string name="wifi_network_switch_dialog_title_bad_internet" msgid="6013385270249705547">"I-<xliff:g id="SSID_0">%1$s</xliff:g> yikhwalithi ephansi. Shintshela ku-<xliff:g id="SSID_1">%2$s</xliff:g>?"</string>
     <string name="wifi_network_switch_dialog_positive_button" msgid="7845507995422722454">"Shintsha"</string>
     <string name="wifi_network_switch_dialog_negative_button" msgid="1496665895689498806">"Ungashintshi"</string>
 </resources>
diff --git a/service/ServiceWifiResources/res/values/config.xml b/service/ServiceWifiResources/res/values/config.xml
index e2da916..14b83eb 100644
--- a/service/ServiceWifiResources/res/values/config.xml
+++ b/service/ServiceWifiResources/res/values/config.xml
@@ -33,6 +33,13 @@
     <!-- boolean indicating whether the WiFi chipset has 6GHz band support -->
     <bool translatable="false" name ="config_wifi6ghzSupport">false</bool>
 
+    <!-- Boolean indicating whether the device supports AFC.
+            Note that this flag should remain disabled unless the device is allowed to
+            use AFC by the regulatory rules.
+            Note that enabling AFC will result in the device's location being shared
+            with the AFC server. -->
+    <bool translatable="false" name ="config_wifiAfcSupported">false</bool>
+
     <!-- boolean indicating whether the WiFi chipset has 60GHz band support -->
     <bool translatable="false" name ="config_wifi60ghzSupport">false</bool>
 
@@ -263,6 +270,10 @@
     <!-- Integer for maximum number of channels to use in initial partial scan. If equals to 0, means add all available channels for networks -->
     <integer translatable="false" name="config_wifiInitialPartialScanChannelMaxCount">10</integer>
 
+    <!-- Configure the max number of new channels to add into the initial partial scan list per network.
+         If equals to 0, it means there's no limit on the max number of channels to include per network.-->
+    <integer translatable="false" name="config_wifiInitialPartialScanMaxNewChannelsPerNetwork">0</integer>
+
     <!-- Integer for maximum age for scan results used to identify channels for partial initial
          scan in minutes -->
     <integer translatable="false" name="config_wifiInitialPartialScanChannelCacheAgeMins">14400</integer>
@@ -585,6 +596,9 @@
     <integer translatable="false" name="config_wifiDisableReasonAuthenticationNoSubscriptionThreshold"> 1 </integer>
     <integer translatable="false" name="config_wifiDisableReasonConsecutiveFailuresThreshold"> 1 </integer>
 
+    <!-- Integer for maximum value of temporarily disabled duration -->
+    <integer translatable="false" name="config_wifiDisableTemporaryMaximumDurationMs"> 64800000 </integer>
+
     <!-- Base duration to set a network disabled after validation failure happen-->
     <integer translatable="false" name="config_wifiDisableReasonAssociationRejectionDurationMs"> 300000 </integer>
     <integer translatable="false" name="config_wifiDisableReasonAuthenticationFailureDurationMs"> 300000 </integer>
@@ -873,6 +887,21 @@
     <string-array translatable="false" name="config_wifiDisableReasonAuthenticationFailureCarrierSpecificDurationMs_carrier_overrides">
         <item><xliff:g id="carrier_id_prefix">:::1839:::</xliff:g>14400000</item>
     </string-array>
+
+    <!-- Configure the Carrier specific override for EAP failure disable behavior starting from Android 14.
+         Should be a comma separated list of the following information:
+           - Carrier ID
+           - EAP failure code
+           - display notification or not (0=false, 1=true)
+           - EAP profile failure threshold
+           - EAP profile disable duration in minutes -->
+    <string-array translatable="false" name="config_wifiEapFailureConfig">
+        <item>1839, 0,     0, 1, 240 </item>
+        <item>1839, 16384, 0, 1, 5 </item>
+        <item>1839, 1026,  0, 1, 240 </item>
+        <item>1839, 1031,  1, 1, 1440 </item>
+        <item>1839, 16385, 0, 3, 120 </item>
+    </string-array>
     <!-- Flush ANQP cache on Wi-Fi toggle off event -->
     <bool translatable="false" name="config_wifiFlushAnqpCacheOnWifiToggleOffEvent">true</bool>
 
@@ -961,6 +990,9 @@
          name. It is STRONGLY RECOMMENDED to be set to false -->
     <bool translatable="false" name="config_wifiAllowInsecureEnterpriseConfigurationsForSettingsAndSUW">false</bool>
 
+    <!-- Configuration that allows Multi-internet to connect simultaneously to both 5GHz high and 5GHz low -->
+    <bool translatable="false" name="config_wifiAllowMultiInternetConnectDual5GFrequency">false</bool>
+
     <!-- Indicate the max lines for connectivity local log based on the device ram size -->
     <integer translatable="false" name="config_wifiConnectivityLocalLogMaxLinesLowRam">256</integer>
     <integer translatable="false" name="config_wifiConnectivityLocalLogMaxLinesHighRam">512</integer>
@@ -1137,6 +1169,18 @@
         -->
     </string-array>
 
+    <!-- Lists available AFC servers for each country, based on country code. Each country may
+     have more than one server listed. If a country is not present in the list, then AFC is not
+     available in that country, and the device will behave as if AFC is disabled.
+     Entries should be in the format:
+     <item>countryCode,url1,url2,url3...</item> -->
+    <string-array translatable="false" name="config_wifiAfcServerUrlsForCountry">
+        <!-- Below is a sample configuration for this list:
+            <item>US,https://example.com/,https://google.com/</item>
+            <item>CA,https://example.com/</item>
+        -->
+    </string-array>
+
     <!-- ACTION_FILE_BUG_DEEPLINK intent name -->
     <string name="config_wifiBugreportDeepLink" translatable="false">com.google.android.apps.betterbug.intent.FILE_BUG_DEEPLINK</string>
 
@@ -1197,9 +1241,36 @@
          configured, the default range is 192.168.49.128 to 192.168.49.254 -->
     <string translatable="false" name="config_wifiP2pGoEapolIpAddressRangeStart"/>
     <string translatable="false" name="config_wifiP2pGoEapolIpAddressRangeEnd"/>
+    <!-- Boolean indicating whether to disable firmware roaming when the device goes into idle mode.
+         true: firmware roaming will be disabled when the device goes into idle mode, and then
+               re-activated when the device exits idle mode.
+         false: firmware roaming will not be affected. -->
+    <bool translatable="false" name ="config_wifiDisableFirmwareRoamingInIdleMode">false</bool>
     <!-- Boolean indicating whether the framework updates country from scan results when there is
          no telephony country code. This is applied to all generic cases and might require the
          regulatory approval (for example, FCC pre-approval is required according to "594280 D01
          Software Configuration Control v02r01").-->
     <bool translatable="false" name ="config_wifiUpdateCountryCodeFromScanResultGeneric">false</bool>
+    <!-- Boolean indication whether enable VDBG for more detailed log when Wi-Fi aware verbose
+    logging is enabled -->
+    <bool translatable="false" name ="config_aware_vdbg_enable_on_verbose_logging">false</bool>
+    <!-- Boolean indicating the Hal support for mapping WPA3 transition mode to WPA3 for Softap in
+         6GHz band. WPA3 transition mode or SAE+WPA_PSK key management(AKM) is not allowed in 6GHz.
+         By default, framework removes 6GHz band for WPA3_SAE_TRANSITION security type.
+         Enabling this config allows framework to send the 6GHz band to Hostapd HAL for
+         WPA3_SAE_TRANSITION security type. Only enable this flag if Hostapd HAL has the
+         implementation to convert WPA3 transition mode to WPA3 only. otherwise driver/firmware may
+         fail to set up the AP.-->
+    <bool translatable="false" name="config_wifiSofapHalMapWpa3TransitionModeToWpa3OnlyIn6GHzBand">false</bool>
+    <!-- Boolean indicating whether the device should remain connected to a network without DHCP.
+        If true, the IpClient provisioning timeout will be set to infinite, which means that the
+        connection will not be torn down if the DHCP server does not respond. As a result the device
+        will be discoverable and reachable via the local-only IPv6 address. -->
+    <bool translatable="false" name ="config_wifiRemainConnectedAfterIpProvisionTimeout">false</bool>
+    <!-- Boolean indicating whether to disable disconnects on NUD failures. Such a configuration
+         is limited to WAPI and to a specific country code.
+     true: do not disconnect IF the connection type is WAPI and in the specific country code
+           regulatory domain.
+     false: disconnect on NUD failures (normal/default action). -->
+    <bool translatable="false" name ="config_wifiDisableNudDisconnectsForWapiInSpecificCc">false</bool>
 </resources>
diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml
index 0cc0281..0504a5e 100644
--- a/service/ServiceWifiResources/res/values/overlayable.xml
+++ b/service/ServiceWifiResources/res/values/overlayable.xml
@@ -96,6 +96,7 @@
           <item type="bool" name="config_wifiEnable6ghzPscScanning" />
           <item type="bool" name="config_wifiEnablePartialInitialScan" />
           <item type="integer" name="config_wifiInitialPartialScanChannelMaxCount" />
+          <item type="integer" name="config_wifiInitialPartialScanMaxNewChannelsPerNetwork" />
           <item type="integer" name="config_wifiInitialPartialScanChannelCacheAgeMins" />
           <item type="bool" name="config_wifi_framework_use_single_radio_chain_scan_results_network_selection" />
           <item type="bool" name="config_wifiAllowLinkingUnknownDefaultGatewayConfigurations" />
@@ -114,6 +115,7 @@
           <item type="bool" name="config_wifiSoftap24ghzSupported" />
           <item type="bool" name="config_wifiSoftap5ghzSupported" />
           <item type="bool" name="config_wifiSoftap6ghzSupported" />
+          <item type="bool" name="config_wifiAfcSupported" />
           <item type="bool" name="config_wifiSoftap60ghzSupported" />
           <item type="bool" name="config_wifiSoftapAutoAppendLowerBandsToBandConfigurationEnabled" />
           <item type="bool" name="config_wifiSoftapHeSuBeamformerSupported" />
@@ -165,6 +167,7 @@
           <item type="integer" name="config_wifiDisableReasonByWrongPasswordThreshold" />
           <item type="integer" name="config_wifiDisableReasonAuthenticationNoSubscriptionThreshold" />
           <item type="integer" name="config_wifiDisableReasonConsecutiveFailuresThreshold" />
+          <item type="integer" name="config_wifiDisableTemporaryMaximumDurationMs" />
 
           <item type="integer" name="config_wifiDisableReasonAssociationRejectionDurationMs" />
           <item type="integer" name="config_wifiDisableReasonAuthenticationFailureDurationMs" />
@@ -255,6 +258,7 @@
           <item type="array" name="config_wifiDisableReasonAuthenticationFailureCarrierSpecificThreshold_carrier_overrides" />
           <item type="integer" name="config_wifiDisableReasonAuthenticationFailureCarrierSpecificDurationMs" />
           <item type="array" name="config_wifiDisableReasonAuthenticationFailureCarrierSpecificDurationMs_carrier_overrides" />
+          <item type="array" name="config_wifiEapFailureConfig" />
           <item type="bool" name="config_wifiAllowMultipleNetworksOnSameAwareNdi"/>
           <item type="integer" name="config_wifiAwareInstantCommunicationModeDurationMillis" />
           <item type="bool" name="config_wifiSupportChannelOnDataPath"/>
@@ -274,6 +278,7 @@
           <item type="integer" name="config_wifiMaxNumWifiConfigurations" />
           <item type="integer" name="config_wifiMaxNumWifiConfigurationsAddedByAllApps" />
           <item type="bool" name="config_wifiAllowInsecureEnterpriseConfigurationsForSettingsAndSUW" />
+          <item type="bool" name="config_wifiAllowMultiInternetConnectDual5GFrequency" />
           <item type="integer" name="config_wifiConnectivityLocalLogMaxLinesLowRam" />
           <item type="integer" name="config_wifiConnectivityLocalLogMaxLinesHighRam" />
           <item type="integer" name="config_wifiClientModeImplNumLogRecs" />
@@ -307,6 +312,7 @@
           <item type="bool" name="config_wifiAwareNdpSecurityUpdateOnSameNdi" />
           <item type="array" name="config_wifiBackgroundRttThrottleExceptionList" />
           <item type="array" name="config_wifiCharsetsForSsidTranslation" />
+          <item type="array" name="config_wifiAfcServerUrlsForCountry" />
           <item type="string" name="config_wifiBugreportDeepLink" />
           <item type="bool" name="config_wifiWaitForDestroyedListeners" />
           <item type="integer" name="config_wifiNetworkSpecifierMaxPreferredChannels" />
@@ -324,7 +330,12 @@
           <item type="bool" name="config_wifiP2pGoIpAddressAllocationInEapolFrames" />
           <item type="string"  name="config_wifiP2pGoEapolIpAddressRangeStart" />
           <item type="string"  name="config_wifiP2pGoEapolIpAddressRangeEnd" />
+          <item type="bool" name="config_wifiDisableFirmwareRoamingInIdleMode" />
           <item type="bool" name="config_wifiUpdateCountryCodeFromScanResultGeneric" />
+          <item type="bool" name ="config_aware_vdbg_enable_on_verbose_logging" />
+          <item type="bool" name="config_wifiSofapHalMapWpa3TransitionModeToWpa3OnlyIn6GHzBand" />
+          <item type="bool" name ="config_wifiRemainConnectedAfterIpProvisionTimeout" />
+          <item type="bool" name ="config_wifiDisableNudDisconnectsForWapiInSpecificCc" />
 
           <!-- Params from config.xml that can be overlayed -->
 
@@ -471,6 +482,7 @@
           <item type="style" name="wifi_section" />
           <item type="style" name="wifi_dialog" />
           <item type="style" name="wifi_p2p_invitation_received_dialog" />
+          <item type="style" name="wifi_p2p_dialog_timeout" />
           <item type="style" name="wifi_p2p_dialog_row_label" />
           <item type="style" name="wifi_p2p_dialog_row_content" />
           <item type="style" name="wifi_p2p_dialog_enter_pin_message" />
diff --git a/service/ServiceWifiResources/res/values/styles.xml b/service/ServiceWifiResources/res/values/styles.xml
index e3ad161..714d10f 100644
--- a/service/ServiceWifiResources/res/values/styles.xml
+++ b/service/ServiceWifiResources/res/values/styles.xml
@@ -46,6 +46,8 @@
     <style name="wifi_dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
     <style name="wifi_p2p_invitation_received_dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
 
+    <style name="wifi_p2p_dialog_timeout" parent="@style/wifi_item" />
+
     <style name="wifi_p2p_dialog_row_label" parent="@style/wifi_item_label" />
 
     <style name="wifi_p2p_dialog_row_content" parent="@style/wifi_item_content">
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java
index 8a0f125..9b776bd 100644
--- a/service/java/com/android/server/wifi/ActiveModeWarden.java
+++ b/service/java/com/android/server/wifi/ActiveModeWarden.java
@@ -139,6 +139,7 @@
     private final DppManager mDppManager;
     private final UserManager mUserManager;
     private final LastCallerInfoManager mLastCallerInfoManager;
+    private final WifiGlobals mWifiGlobals;
 
     private WifiServiceImpl.SoftApCallbackInternal mSoftApCallback;
     private WifiServiceImpl.SoftApCallbackInternal mLohsCallback;
@@ -156,6 +157,7 @@
             WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN;
     /** Cache to store the external scorer for primary and secondary (MBB) client mode manager. */
     @Nullable private Pair<IBinder, IWifiConnectedNetworkScorer> mClientModeManagerScorer;
+    private int mScorerUid;
 
     @Nullable
     private ConcreteClientModeManager mLastPrimaryClientModeManager = null;
@@ -173,6 +175,9 @@
     @GuardedBy("mServiceApiLock")
     private WifiInfo mCurrentConnectionInfo = new WifiInfo();
 
+    @GuardedBy("mServiceApiLock")
+    private final ArraySet<WorkSource> mRequestWs = new ArraySet<>();
+
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
      * {@link WifiManager#WIFI_STATE_DISABLING},
@@ -208,6 +213,17 @@
         }
     }
 
+    /**
+     * Get the request WorkSource for secondary CMM
+     *
+     * @return the WorkSources of the current secondary CMMs
+     */
+    public Set<WorkSource> getSecondaryRequestWs() {
+        synchronized (mServiceApiLock) {
+            return new ArraySet<>(mRequestWs);
+        }
+    }
+
     private String getWifiStateName() {
         switch (mWifiState.get()) {
             case WIFI_STATE_DISABLING:
@@ -235,6 +251,17 @@
     }
 
     /**
+     * Notify changes in PowerManager#isDeviceIdleMode
+     */
+    public void onIdleModeChanged(boolean isIdle) {
+        // only client mode managers need to get notified for now to consider enabling/disabling
+        // firmware roaming
+        for (ClientModeManager cmm : mClientModeManagers) {
+            cmm.onIdleModeChanged(isIdle);
+        }
+    }
+
+    /**
      * Called from WifiServiceImpl to register a callback for notifications from SoftApManager
      */
     public void registerSoftApCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) {
@@ -356,7 +383,8 @@
             WifiPermissionsUtil wifiPermissionsUtil,
             WifiMetrics wifiMetrics,
             ExternalScoreUpdateObserverProxy externalScoreUpdateObserverProxy,
-            DppManager dppManager) {
+            DppManager dppManager,
+            WifiGlobals wifiGlobals) {
         mWifiInjector = wifiInjector;
         mLooper = looper;
         mHandler = new Handler(looper);
@@ -376,6 +404,7 @@
         mGraveyard = new Graveyard();
         mUserManager = mWifiInjector.getUserManager();
         mLastCallerInfoManager = mWifiInjector.getLastCallerInfoManager();
+        mWifiGlobals = wifiGlobals;
 
         wifiNative.registerStatusListener(isReady -> {
             if (!isReady && !mIsShuttingdown) {
@@ -405,8 +434,8 @@
                     }
                     if (newPrimaryClientModeManager != null && mClientModeManagerScorer != null) {
                         newPrimaryClientModeManager.setWifiConnectedNetworkScorer(
-                                mClientModeManagerScorer.first,
-                                mClientModeManagerScorer.second);
+                                mClientModeManagerScorer.first, mClientModeManagerScorer.second,
+                                mScorerUid);
                     }
                 });
     }
@@ -475,7 +504,7 @@
      * WifiManager.WifiConnectedNetworkScorer)}
      */
     public boolean setWifiConnectedNetworkScorer(IBinder binder,
-            IWifiConnectedNetworkScorer scorer) {
+            IWifiConnectedNetworkScorer scorer, int callerUid) {
         try {
             scorer.onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy);
         } catch (RemoteException e) {
@@ -483,7 +512,9 @@
             return false;
         }
         mClientModeManagerScorer = Pair.create(binder, scorer);
-        return getPrimaryClientModeManager().setWifiConnectedNetworkScorer(binder, scorer);
+        mScorerUid = callerUid;
+        return getPrimaryClientModeManager().setWifiConnectedNetworkScorer(binder, scorer,
+                callerUid);
     }
 
     /**
@@ -491,6 +522,7 @@
      */
     public void clearWifiConnectedNetworkScorer() {
         mClientModeManagerScorer = null;
+        mScorerUid = Process.WIFI_UID;
         getPrimaryClientModeManager().clearWifiConnectedNetworkScorer();
     }
 
@@ -1272,12 +1304,33 @@
         }
     }
 
+    private List<ConcreteClientModeManager> getClientModeManagersPrimaryLast() {
+        List<ConcreteClientModeManager> primaries = new ArrayList<>();
+        List<ConcreteClientModeManager> others = new ArrayList<>();
+        for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
+            if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
+                primaries.add(clientModeManager);
+            } else {
+                others.add(clientModeManager);
+            }
+        }
+        if (primaries.size() > 1) {
+            Log.wtf(TAG, "More than 1 primary CMM detected when turning off Wi-Fi");
+            mWifiDiagnostics.takeBugReport("Wi-Fi ActiveModeWarden bugreport",
+                    "Multiple primary CMMs detected when turning off Wi-Fi.");
+        }
+        List<ConcreteClientModeManager> result = new ArrayList<>();
+        result.addAll(others);
+        result.addAll(primaries);
+        return result;
+    }
+
     /**
      * Method to stop all client mode mangers.
      */
     private void stopAllClientModeManagers() {
         Log.d(TAG, "Shutting down all client mode managers");
-        for (ConcreteClientModeManager clientModeManager : mClientModeManagers) {
+        for (ConcreteClientModeManager clientModeManager : getClientModeManagersPrimaryLast()) {
             clientModeManager.stop();
         }
     }
@@ -1361,6 +1414,11 @@
         ConcreteClientModeManager manager = mWifiInjector.makeClientModeManager(
                 listener, requestorWs, role, mVerboseLoggingEnabled);
         mClientModeManagers.add(manager);
+        if (ROLE_CLIENT_SECONDARY_LONG_LIVED.equals(role) || ROLE_CLIENT_LOCAL_ONLY.equals(role)) {
+            synchronized (mServiceApiLock) {
+                mRequestWs.add(new WorkSource(requestorWs));
+            }
+        }
         return true;
     }
 
@@ -1374,6 +1432,14 @@
             @NonNull WorkSource requestorWs) {
         Log.d(TAG, "Switching role for additional ClientModeManager to role: " + role);
         ClientListener listener = new ClientListener(externalRequestListener);
+        synchronized (mServiceApiLock) {
+            mRequestWs.remove(manager.getRequestorWs());
+        }
+        if (ROLE_CLIENT_SECONDARY_LONG_LIVED.equals(role) || ROLE_CLIENT_LOCAL_ONLY.equals(role)) {
+            synchronized (mServiceApiLock) {
+                mRequestWs.add(new WorkSource(requestorWs));
+            }
+        }
         manager.setRole(role, requestorWs, listener);
         return true;
     }
@@ -1446,8 +1512,8 @@
     @VisibleForTesting
     Collection<ActiveModeManager> getActiveModeManagers() {
         ArrayList<ActiveModeManager> activeModeManagers = new ArrayList<>();
-        activeModeManagers.addAll(mClientModeManagers);
         activeModeManagers.addAll(mSoftApManagers);
+        activeModeManagers.addAll(getClientModeManagersPrimaryLast());
         return activeModeManagers;
     }
 
@@ -1624,6 +1690,12 @@
 
         private void onStoppedOrStartFailure(ConcreteClientModeManager clientModeManager) {
             mClientModeManagers.remove(clientModeManager);
+            if (ROLE_CLIENT_SECONDARY_LONG_LIVED.equals(clientModeManager.getPreviousRole())
+                    || ROLE_CLIENT_LOCAL_ONLY.equals(clientModeManager.getPreviousRole())) {
+                synchronized (mServiceApiLock) {
+                    mRequestWs.remove(clientModeManager.getRequestorWs());
+                }
+            }
             mGraveyard.inter(clientModeManager);
             updateClientScanMode();
             updateBatteryStats();
@@ -2330,6 +2402,13 @@
 
             private void handleAdditionalClientModeManagerRequest(
                     @NonNull AdditionalClientModeManagerRequestInfo requestInfo) {
+                if (mWifiState.get() == WIFI_STATE_DISABLING
+                        || mWifiState.get() == WIFI_STATE_DISABLED) {
+                    // Do no allow getting secondary CMM when wifi is being disabled or disabled.
+                    requestInfo.listener.onAnswer(null);
+                    return;
+                }
+
                 ClientModeManager primaryManager = getPrimaryClientModeManagerNullable();
                 // TODO(b/228529090): Remove this special code once root cause is resolved.
                 // Special case for holders with ENTER_CAR_MODE_PRIORITIZED. Only give them the
@@ -2488,6 +2567,15 @@
                                 // airplane mode toggle message to disable airplane mode.
                                 deferMessage(msg);
                             } else {
+                                if (!hasPrimaryOrScanOnlyModeManager()) {
+                                    // SoftAp was enabled during airplane mode and caused
+                                    // WifiController to be in EnabledState without
+                                    // a primary client mode manager.
+                                    // Defer to the default state to handle the airplane mode toggle
+                                    // which may result in enabling wifi if necessary.
+                                    log("airplane mode toggled - and no primary manager");
+                                    return NOT_HANDLED;
+                                }
                                 // when airplane mode is toggled off, but wifi is on, we can keep it
                                 // on
                                 log("airplane mode toggled - and airplane mode is off. return "
@@ -2672,6 +2760,14 @@
             // It doesn't relate the vendor HAL, set if overlay enables it.
             additionalFeatureSet |= WifiManager.WIFI_FEATURE_STA_BRIDGED_AP;
         }
+        if (mWifiGlobals.isWepSupported()) {
+            additionalFeatureSet |= WifiManager.WIFI_FEATURE_WEP;
+        }
+
+        if (!mWifiGlobals.isWpaPersonalDeprecated()) {
+            // The WPA didn't be deprecated, set it.
+            additionalFeatureSet |= WifiManager.WIFI_FEATURE_WPA_PERSONAL;
+        }
         mSupportedFeatureSet.set(
                 (supportedFeatureSet | concurrencyFeatureSet | additionalFeatureSet)
                         & ~excludedFeatureSet);
@@ -2731,7 +2827,7 @@
      */
     public @NonNull WifiInfo getConnectionInfo() {
         synchronized (mServiceApiLock) {
-            return mCurrentConnectionInfo;
+            return new WifiInfo(mCurrentConnectionInfo);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/AfcClient.java b/service/java/com/android/server/wifi/AfcClient.java
new file mode 100644
index 0000000..1682e6a
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcClient.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.server.wifi.entitlement.http.HttpClient;
+import com.android.server.wifi.entitlement.http.HttpConstants.RequestMethod;
+import com.android.server.wifi.entitlement.http.HttpRequest;
+import com.android.server.wifi.entitlement.http.HttpResponse;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class that queries the AFC server with HTTP post requests and returns the HTTP response result.
+ */
+public class AfcClient {
+    private static final String TAG = "WifiAfcClient";
+    public static final int REASON_NO_URL_SPECIFIED = 0;
+    public static final int REASON_AFC_RESPONSE_CODE_ERROR = 1;
+    public static final int REASON_SERVICE_ENTITLEMENT_FAILURE = 2;
+    public static final int REASON_JSON_FAILURE = 3;
+    public static final int REASON_UNDEFINED_FAILURE = 4;
+    @IntDef(prefix = { "REASON_" }, value = {
+            REASON_NO_URL_SPECIFIED,
+            REASON_AFC_RESPONSE_CODE_ERROR,
+            REASON_SERVICE_ENTITLEMENT_FAILURE,
+            REASON_JSON_FAILURE,
+            REASON_UNDEFINED_FAILURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FailureReasonCode {}
+    static final int CONNECT_TIMEOUT_SECS = 30;
+    static final int LOW_FREQUENCY = ScanResult.BAND_6_GHZ_START_FREQ_MHZ;
+
+    // TODO(b/291774201): Update max frequency since server is currently returning errors with
+    //  higher values than 6425.
+    static final int HIGH_FREQUENCY = 6425;
+    static final String SERIAL_NUMBER = "ABCDEFG";
+    static final String NRA = "FCC";
+    static final String ID = "EFGHIJK";
+    static final String RULE_SET_ID = "US_47_CFR_PART_15_SUBPART_E";
+    static final String VERSION = "1.2";
+    private static String sServerUrl;
+    private static HashMap<String, String> sRequestProperties;
+    private final Handler mBackgroundHandler;
+    private int mRequestId;
+
+    AfcClient(Handler backgroundHandler) {
+        mRequestId = 0;
+        sRequestProperties = new HashMap<>();
+        mBackgroundHandler = backgroundHandler;
+    }
+
+    /**
+     * Query the AFC server using the background thread and post the response to the callback on
+     * the Wi-Fi handler thread.
+     */
+    public void queryAfcServer(@NonNull AfcLocation afcLocation, @NonNull Handler wifiHandler,
+            @NonNull Callback callback) {
+        // If no URL is provided, then fail to proceed with sending request to AFC server
+        if (sServerUrl == null) {
+            wifiHandler.post(() -> callback.onFailure(REASON_NO_URL_SPECIFIED,
+                    "No Server URL was provided through command line argument. Must "
+                            + "provide URL through configure-afc-server wifi shell command."
+            ));
+            return;
+        }
+
+        HttpRequest httpRequest = getAfcHttpRequestObject(afcLocation);
+        mBackgroundHandler.post(() -> {
+            try {
+                HttpResponse httpResponse = HttpClient.request(httpRequest);
+                JSONObject httpResponseBodyJSON = new JSONObject(httpResponse.body());
+
+                AfcServerResponse serverResponse = AfcServerResponse
+                        .fromSpectrumInquiryResponse(getHttpResponseCode(httpResponse),
+                                getAvailableSpectrumInquiryResponse(httpResponseBodyJSON,
+                                        mRequestId));
+                if (serverResponse == null) {
+                    wifiHandler.post(() -> callback.onFailure(REASON_JSON_FAILURE,
+                            "Encountered JSON error when parsing AFC server's "
+                                    + "response."
+                    ));
+                } else if (serverResponse.getAfcChannelAllowance() == null) {
+                    wifiHandler.post(() -> callback.onFailure(
+                            REASON_AFC_RESPONSE_CODE_ERROR, "Response code server error. "
+                                    + "HttpResponseCode=" + serverResponse.getHttpResponseCode()
+                                    + ", AfcServerResponseCode=" + serverResponse
+                                    .getAfcResponseCode() + ", Short Description: "
+                                    + serverResponse.getAfcResponseDescription()));
+                } else {
+                    // Post the server response to the callback
+                    wifiHandler.post(() -> callback.onResult(serverResponse, afcLocation));
+                }
+            } catch (ServiceEntitlementException e) {
+                wifiHandler.post(() -> callback.onFailure(REASON_SERVICE_ENTITLEMENT_FAILURE,
+                        "Encountered Service Entitlement Exception when sending "
+                                + "request to server. " + e));
+            } catch (JSONException e) {
+                wifiHandler.post(() -> callback.onFailure(REASON_JSON_FAILURE,
+                        "Encountered JSON error when parsing HTTP response." + e
+                ));
+            } catch (Exception e) {
+                wifiHandler.post(() -> callback.onFailure(REASON_UNDEFINED_FAILURE,
+                        "Encountered unexpected error when parsing AFC server's response."
+                                + e));
+            } finally {
+                // Increment the request ID by 1 for the next request
+                mRequestId++;
+            }
+        });
+    }
+
+    /**
+     * Create and return the HttpRequest object that queries the AFC server for the afcLocation
+     * object.
+     */
+    public HttpRequest getAfcHttpRequestObject(AfcLocation afcLocation) {
+        HttpRequest.Builder httpRequestBuilder = HttpRequest.builder()
+                .setUrl(sServerUrl)
+                .setRequestMethod(RequestMethod.POST)
+                .setTimeoutInSec(CONNECT_TIMEOUT_SECS);
+        for (Map.Entry<String, String> requestProperty : sRequestProperties.entrySet()) {
+            httpRequestBuilder.addRequestProperty(requestProperty.getKey(),
+                    requestProperty.getValue());
+        }
+        JSONObject jsonRequestObject = getAfcRequestJSONObject(afcLocation);
+        httpRequestBuilder.setPostData(jsonRequestObject);
+        return httpRequestBuilder.build();
+    }
+
+    /**
+     * Get the AFC request JSON object used to query the AFC server.
+     */
+    private JSONObject getAfcRequestJSONObject(AfcLocation afcLocation) {
+        try {
+            JSONObject requestObject = new JSONObject();
+            JSONArray inquiryRequests = new JSONArray();
+            JSONObject inquiryRequest = new JSONObject();
+
+            inquiryRequest.put("requestId", String.valueOf(mRequestId));
+
+            JSONObject deviceDescriptor = new JSONObject();
+            deviceDescriptor.put("serialNumber", AfcClient.SERIAL_NUMBER);
+            JSONObject certificationId = new JSONObject();
+            certificationId.put("nra", AfcClient.NRA);
+            certificationId.put("id", AfcClient.ID);
+            deviceDescriptor.put("certificationId", certificationId);
+            deviceDescriptor.put("rulesetIds", AfcClient.RULE_SET_ID);
+            inquiryRequest.put("deviceDescriptor", deviceDescriptor);
+
+            JSONObject location = afcLocation.toJson();
+            inquiryRequest.put("location", location);
+
+            JSONArray inquiredFrequencyRange = new JSONArray();
+            JSONObject range = new JSONObject();
+            range.put("lowFrequency", AfcClient.LOW_FREQUENCY);
+            range.put("highFrequency", AfcClient.HIGH_FREQUENCY);
+            inquiredFrequencyRange.put(range);
+            inquiryRequest.put("inquiredFrequencyRange", inquiredFrequencyRange);
+
+            inquiryRequests.put(0, inquiryRequest);
+
+            requestObject.put("version", AfcClient.VERSION);
+            requestObject.put("availableSpectrumInquiryRequests", inquiryRequests);
+
+            return requestObject;
+        } catch (JSONException e) {
+            Log.e(TAG, "Encountered error when building JSON object: " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Parses the AFC server's HTTP response and finds an Available Spectrum Inquiry Response with
+     * a matching request ID.
+     *
+     * @param httpResponseJSON the response of the AFC server
+     * @return the Available Spectrum Inquiry Response as a JSON Object with a request ID matching
+     * the ID used in the request, or null if no object with a matching ID is found.
+     */
+    private JSONObject getAvailableSpectrumInquiryResponse(JSONObject httpResponseJSON,
+            int requestId) {
+        if (httpResponseJSON == null) {
+            return null;
+        }
+        String requestIdString = Integer.toString(requestId);
+
+        try {
+            JSONArray spectrumInquiryResponses = httpResponseJSON.getJSONArray(
+                    "availableSpectrumInquiryResponses");
+
+            // iterate through responses to find one with a matching request ID
+            for (int i = 0, numResponses = spectrumInquiryResponses.length();
+                    i < numResponses; i++) {
+                JSONObject spectrumInquiryResponse = spectrumInquiryResponses.getJSONObject(i);
+                if (requestIdString.equals(spectrumInquiryResponse.getString("requestId"))) {
+                    return spectrumInquiryResponse;
+                }
+            }
+
+            Log.e(TAG, "Did not find an available spectrum inquiry response with request ID: "
+                    + requestIdString);
+        } catch (JSONException e) {
+            Log.e(TAG, "Error occurred while parsing available spectrum inquiry response: "
+                    + e);
+        }
+        return null;
+    }
+
+    /**
+     * @return the http response code, or 500 if it does not exist
+     */
+    private int getHttpResponseCode(HttpResponse httpResponse) {
+        return httpResponse == null ? 500 : httpResponse.responseCode();
+    }
+
+    /**
+     * Set the server URL used to query the AFC server.
+     */
+    public void setServerURL(String url) {
+        sServerUrl = url;
+    }
+
+    /**
+     * Sets the request properties Map through copying the Map created from the configure-afc-server
+     * wifi-shell command.
+     * @param requestProperties A map with key and value Strings for the HTTP header's request
+     *                          property fields. Each key has a corresponding value.
+     */
+    public void setRequestPropertyPairs(Map<String, String> requestProperties) {
+        sRequestProperties = new HashMap<>();
+        sRequestProperties.putAll(requestProperties);
+    }
+
+    /**
+     * Get the server URL for this AfcClient.
+     */
+    public String getServerURL() {
+        return sServerUrl;
+    }
+
+    /**
+     * Get the Map with <key, value> header fields for the HTTP request properties.
+     */
+    public Map<String, String> getRequestProperties() {
+        return sRequestProperties;
+    }
+
+    /**
+     * Callback which will be called after AfcResponse retrieval.
+     */
+    public interface Callback {
+        /**
+         * Indicates that an AfcServerResponse was received successfully.
+         */
+        void onResult(AfcServerResponse serverResponse, AfcLocation afcLocation);
+        /**
+         * Indicate a failure happens when receiving the AfcServerResponse.
+         * @param reasonCode The failure reason code.
+         * @param description The description of the failure.
+         */
+        void onFailure(@FailureReasonCode int reasonCode, String description);
+    }
+}
diff --git a/service/java/com/android/server/wifi/AfcEllipseLocation.java b/service/java/com/android/server/wifi/AfcEllipseLocation.java
new file mode 100644
index 0000000..cb2eafd
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcEllipseLocation.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.location.Location;
+import android.util.Log;
+
+import org.json.JSONObject;
+
+import java.util.Random;
+
+/**
+ * Helper class to store a coarse ellipse location used to send to the AFC server. Randomizes the
+ * center point of the ellipse with a center leeway parameter.
+ */
+public class AfcEllipseLocation extends AfcLocation {
+    private static final String TAG = "WifiAfcEllipseLocation";
+    static final double ONE_DEGREE_LONGITUDE_IN_METERS = 111139.0; // Used in double division
+    static final int DEFAULT_SEMI_MINOR_AXIS_METERS = 500;
+    static final int DEFAULT_SEMI_MAJOR_AXIS_METERS = 500;
+    static final float DEFAULT_ORIENTATION = 90f;
+    static final double DEFAULT_CENTER_LEEWAY_DEGREES = 0.001;
+    static final int MIN_LONGITUDE = -180;
+    static final int MAX_LONGITUDE = 180;
+    static final int MIN_LATITUDE = -90;
+    static final int MAX_LATITUDE = 90;
+    double mLatitude;
+    double mLongitude;
+    int mSemiMajorAxis;
+    int mSemiMinorAxis;
+    double mOrientation;
+    double mCenterLeeway;
+
+    /**
+     * @param semiMinorAxisMeters The length of the semi major axis of an ellipse which is a
+     *                            positive integer in meters.
+     * @param semiMajorAxisMeters The length of the semi minor axis of an ellipse which is a
+     *                            positive integer in meters.
+     * @param orientation         The orientation of the majorAxis field in decimal degrees,
+     *                            measured clockwise from True North.
+     * @param centerLeeway        The max amount in degrees that the longitude and latitude
+     *                            coordinates of the generated ellipse will be randomly shifted +-
+     *                            by. This variable should be reasonably smaller than the longitude
+     *                            and latitude ranges. Each 0.001 degree is roughly 111 meters.
+     * @param random              The random variable to randomize the center ellipse coordinates.
+     * @param location            The geographic coordinates within which the AP or Fixed Client
+     *                            Device is located and the AfcEllipseLocation is centered around.
+     */
+    public AfcEllipseLocation(int semiMinorAxisMeters, int semiMajorAxisMeters, double orientation,
+            double centerLeeway, Random random, Location location) {
+        super(location);
+
+        mSemiMajorAxis = semiMajorAxisMeters;
+        mSemiMinorAxis = semiMinorAxisMeters;
+        mCenterLeeway = centerLeeway;
+        mOrientation = orientation;
+
+        // The ellipse longitude point is in between the location longitude point +- mCenterLeeway
+        mLongitude = random.nextDouble() * (2 * mCenterLeeway) + (location.getLongitude()
+                - mCenterLeeway);
+
+        // Adjust for allowed range for longitude which is -180 to 180.
+        if (mLongitude < MIN_LONGITUDE) {
+            mLongitude = MIN_LONGITUDE;
+        } else if (mLongitude > MAX_LONGITUDE) {
+            mLongitude = MAX_LONGITUDE;
+        }
+
+        // The ellipse latitude point is in between the location latitude point +- mCenterLeeway
+        mLatitude = random.nextDouble() * (2 * mCenterLeeway) + (location.getLatitude()
+                - mCenterLeeway);
+
+        // Adjust for allowed range for latitude which is -90 to 90.
+        if (mLatitude < MIN_LATITUDE) {
+            mLatitude = MIN_LATITUDE;
+        } else if (mLatitude > MAX_LATITUDE) {
+            mLatitude = MAX_LATITUDE;
+        }
+    }
+
+    /**
+     * Create a location JSONObject that has the ellipse fields for AFC server queries.
+     */
+    @Override
+    public JSONObject toJson() {
+        try {
+            JSONObject location = new JSONObject();
+            JSONObject ellipse = new JSONObject();
+            JSONObject center = new JSONObject();
+
+            center.put("latitude", mLatitude);
+            center.put("longitude", mLongitude);
+            ellipse.put("center", center);
+            ellipse.put("majorAxis", mSemiMajorAxis);
+            ellipse.put("minorAxis", mSemiMinorAxis);
+            ellipse.put("orientation", mOrientation);
+            location.put("ellipse", ellipse);
+
+            JSONObject elevation = new JSONObject();
+            elevation.put("height", mHeight);
+            elevation.put("verticalUncertainty", mVerticalUncertainty);
+            elevation.put("height_type", mHeightType);
+            location.put("elevation", elevation);
+            location.put("indoorDeployment", mLocationType);
+
+            return location;
+        } catch (Exception e) {
+            Log.e(TAG, "Encountered error when building JSON object: " + e);
+            return null;
+        }
+    }
+
+    /**
+     * Determine if location comparingLocation is within bounds of this AfcEllipseLocation object.
+     */
+    @Override
+    public AfcLocationUtil.InBoundsCheckResult checkLocation(Location comparingLocation) {
+        double comparingLatitude = comparingLocation.getLatitude();
+        double comparingLongitude = comparingLocation.getLongitude();
+        AfcLocationUtil.InBoundsCheckResult inBoundsResult;
+
+        double semiMinorAxisDegrees = mSemiMinorAxis / ONE_DEGREE_LONGITUDE_IN_METERS;
+        double semiMajorAxisDegrees = mSemiMajorAxis / ONE_DEGREE_LONGITUDE_IN_METERS;
+
+        // Adjust comparingLongitude if the ellipse goes above the maximum longitude or below the
+        // minimum longitude
+        if (mLongitude + semiMajorAxisDegrees > MAX_LONGITUDE && comparingLongitude < 0) {
+            comparingLongitude = comparingLongitude + 360;
+        } else if (mLongitude - semiMajorAxisDegrees < MIN_LONGITUDE && comparingLongitude > 0) {
+            comparingLongitude = comparingLongitude - 360;
+        }
+
+        // Equation for circles and ellipses on the cartesian plane that have the major axis
+        // centered on the x-axis. Divide axes by 111139 to convert from meters to degrees which is
+        // the same units as coordinates.
+        double checkPoint = Math.pow((comparingLongitude - mLongitude) / semiMajorAxisDegrees, 2)
+                + Math.pow((comparingLatitude - mLatitude) / semiMinorAxisDegrees, 2);
+
+        if (checkPoint > 1.001) {
+            inBoundsResult = AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION;
+        } else if (checkPoint < 0.999) {
+            inBoundsResult = AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION;
+        } else {
+            inBoundsResult = AfcLocationUtil.InBoundsCheckResult.ON_BORDER;
+        }
+        return inBoundsResult;
+    }
+}
diff --git a/service/java/com/android/server/wifi/AfcLocation.java b/service/java/com/android/server/wifi/AfcLocation.java
new file mode 100644
index 0000000..84a8e9d
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcLocation.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.location.Location;
+
+import org.json.JSONObject;
+
+/**
+ * Helper class to store a coarse location used to send to the AFC server.
+ */
+public abstract class AfcLocation {
+    // Reference level for the value of the height field.
+    // "AGL": Above Ground Level. Antenna height as measured relative to the local
+    // ground level.
+    // "AMSL": Above Mean Sea Level. Antenna height as measured with respect to
+    // WGS84 datum.
+    enum HeightType {
+        AGL,
+        AMSL;
+    }
+
+    // Indicator for whether the deployment of the AP or Fixed Client device is located indoors,
+    // outdoor, or is unknown.
+    enum LocationType {
+        LOCATION_TYPE_UNKNOWN,
+        INDOOR,
+        OUTDOOR;
+    }
+
+    int mLocationType = LocationType.LOCATION_TYPE_UNKNOWN.ordinal();
+    double mHeight;
+    double mVerticalUncertainty;
+    String mHeightType;
+
+    public AfcLocation(Location location) {
+        mHeight = location.getAltitude();
+        mVerticalUncertainty = location.getVerticalAccuracyMeters();
+        mHeightType = AfcEllipseLocation.HeightType.AMSL.name();
+    }
+
+    /**
+     * Converts this AfcLocation class to JSON format needed for AFC queries.
+     */
+    public abstract JSONObject toJson();
+
+    /**
+     * Determine if location comparingLocation is within bounds of this AfcLocation object.
+     */
+    public abstract AfcLocationUtil.InBoundsCheckResult checkLocation(Location comparingLocation);
+}
diff --git a/service/java/com/android/server/wifi/AfcLocationUtil.java b/service/java/com/android/server/wifi/AfcLocationUtil.java
new file mode 100644
index 0000000..9c8ca73
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcLocationUtil.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+
+import android.location.Location;
+
+import java.util.Random;
+
+/**
+ * Class that converts a Location object to a new AfcLocation and checks if a Location object is in
+ * bounds of an AfcLocation object.
+ */
+public class AfcLocationUtil {
+    // Enum that has values for inside, on the border, and outside the ellipse
+    public enum InBoundsCheckResult {
+        INSIDE_AFC_LOCATION,
+        ON_BORDER,
+        OUTSIDE_AFC_LOCATION,
+    }
+
+    /**
+     * Return a new AfcEllipseLocation object with default values.
+     */
+    public AfcLocation createAfcLocation(Location location) {
+        return new AfcEllipseLocation(AfcEllipseLocation.DEFAULT_SEMI_MINOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_SEMI_MAJOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_ORIENTATION,
+                AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES,
+                new Random(), location
+        );
+    }
+
+    /**
+     * Determine if location comparingLocation is within bounds of the afcLocation object.
+     */
+    public AfcLocationUtil.InBoundsCheckResult checkLocation(AfcLocation afcLocation,
+            Location comparingLocation) {
+        return afcLocation.checkLocation(comparingLocation);
+    }
+}
diff --git a/service/java/com/android/server/wifi/AfcManager.java b/service/java/com/android/server/wifi/AfcManager.java
new file mode 100644
index 0000000..8c49fdb
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcManager.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.wifi.WifiContext;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.HandlerExecutor;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.server.wifi.hal.WifiChip;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class that handles interactions with the AFC server and passing its response to the driver
+ */
+public class AfcManager {
+    private static final String TAG = "WifiAfcManager";
+    private static final long MINUTE_IN_MILLIS = 60 * 1000;
+    private static final long LOCATION_MIN_TIME_MILLIS = MINUTE_IN_MILLIS;
+    private static final float LOCATION_MIN_DISTANCE_METERS = 200;
+    private final HandlerThread mWifiHandlerThread;
+    private final WifiContext mContext;
+    private final WifiNative mWifiNative;
+    private final WifiGlobals mWifiGlobals;
+    private final Clock mClock;
+    private final LocationListener mLocationListener;
+    private final AfcClient mAfcClient;
+    private final AfcLocationUtil mAfcLocationUtil;
+    private final AfcClient.Callback mCallback;
+    private Location mLastKnownLocation;
+    private String mLastKnownCountryCode;
+    private LocationManager mLocationManager;
+    private long mLastAfcServerQueryTime;
+    private String mProviderForLocationRequest = "";
+    private boolean mIsAfcSupportedForCurrentCountry = false;
+    private boolean mVerboseLoggingEnabled = false;
+    private AfcLocation mLastAfcLocationInSuccessfulQuery;
+    private AfcServerResponse mLatestAfcServerResponse;
+    private String mAfcServerUrl;
+    private String mServerUrlSetFromShellCommand;
+    private Map<String, String> mServerRequestPropertiesSetFromShellCommand;
+
+    public AfcManager(WifiContext context, WifiInjector wifiInjector) {
+        mContext = context;
+        mClock = wifiInjector.getClock();
+
+        mWifiHandlerThread = wifiInjector.getWifiHandlerThread();
+        mWifiGlobals = wifiInjector.getWifiGlobals();
+        mWifiNative = wifiInjector.getWifiNative();
+        mAfcLocationUtil = wifiInjector.getAfcLocationUtil();
+        mAfcClient = wifiInjector.getAfcClient();
+
+        mLocationListener = new LocationListener() {
+            @Override
+            public void onLocationChanged(Location location) {
+                onLocationChange(location, false);
+            }
+        };
+
+        mCallback = new AfcClient.Callback() {
+            // Cache the server response and pass the AFC channel allowance to the driver.
+            @Override
+            public void onResult(AfcServerResponse serverResponse, AfcLocation afcLocation) {
+                mLatestAfcServerResponse = serverResponse;
+                mLastAfcLocationInSuccessfulQuery = afcLocation;
+
+                boolean allowanceSetSuccessfully = setAfcChannelAllowance(mLatestAfcServerResponse
+                        .getAfcChannelAllowance());
+
+                if (mVerboseLoggingEnabled) {
+                    Log.i(TAG, "The AFC Client Query was successful and had the response:\n"
+                            + serverResponse);
+                }
+
+                if (!allowanceSetSuccessfully) {
+                    Log.e(TAG, "The AFC allowed channels and frequencies were not set "
+                            + "successfully in the driver.");
+                }
+            }
+
+            @Override
+            public void onFailure(int reasonCode, String description) {
+                Log.e(TAG, "Reason Code: " + reasonCode + ", Description: " + description);
+            }
+        };
+    }
+
+    /**
+     * This method starts listening to location changes which help determine whether to query the
+     * AFC server. This should only be called when AFC is available in the country that the device
+     * is in.
+     */
+    private void listenForLocationChanges() {
+        List<String> locationProviders = mLocationManager.getProviders(true);
+
+        // find a provider we can use for location updates, then request to listen for updates
+        for (String provider : locationProviders) {
+            if (isAcceptableProviderForLocationUpdates(provider)) {
+                try {
+                    mLocationManager.requestLocationUpdates(
+                            provider,
+                            LOCATION_MIN_TIME_MILLIS,
+                            LOCATION_MIN_DISTANCE_METERS,
+                            mLocationListener,
+                            mWifiHandlerThread.getLooper()
+                    );
+                } catch (Exception e) {
+                    Log.e(TAG, e.toString());
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Stops listening to location changes for the current location listener.
+     */
+    private void stopListeningForLocationChanges() {
+        mLocationManager.removeUpdates(mLocationListener);
+    }
+
+    /**
+     * Returns whether this is a location provider we want to use when requesting location updates.
+     */
+    private boolean isAcceptableProviderForLocationUpdates(String provider) {
+        // We don't want to actively initiate a location fix here (with gps or network providers).
+        return LocationManager.PASSIVE_PROVIDER.equals(provider);
+    }
+
+    /**
+     * Perform a re-query if the server hasn't been queried before, if the expiration time
+     * of the last successful AfcResponse has expired, or if the location parameter is outside the
+     * bounds of the AfcLocation from the last successful AFC server query.
+     *
+     * @param location the device's current location.
+     * @param isCalledFromShellCommand whether this method is being called from a shell command.
+     *                                 Used to bypass the flag in the overlay for AFC being enabled
+     *                                 or disabled.
+     */
+    public void onLocationChange(Location location, boolean isCalledFromShellCommand) {
+        mLastKnownLocation = location;
+
+        if (!mIsAfcSupportedForCurrentCountry && !isCalledFromShellCommand) {
+            return;
+        }
+
+        if (location == null) {
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "Location is null");
+            }
+            return;
+        }
+
+        // If there was no prior successful query, then query the server.
+        if (mLastAfcLocationInSuccessfulQuery == null) {
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "There is no prior successful query so a new query of the server is"
+                        + " executed.");
+            }
+            queryServerAndInformDriver(location, isCalledFromShellCommand);
+            return;
+        }
+
+        // If the expiration time of the last successful Afc response has expired, then query the
+        // server.
+        if (mClock.getWallClockMillis() >= mLatestAfcServerResponse.getAfcChannelAllowance()
+                .availabilityExpireTimeMs) {
+            queryServerAndInformDriver(location, isCalledFromShellCommand);
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "The availability expiration time of the last query has expired"
+                        + " so a new query of the AFC server is executed.");
+            }
+            return;
+        }
+
+        AfcLocationUtil.InBoundsCheckResult inBoundsResult = mAfcLocationUtil.checkLocation(
+                mLastAfcLocationInSuccessfulQuery, location);
+
+        // Query the AFC server if the new parameter location is outside the AfcLocation
+        // boundary.
+        if (inBoundsResult == AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION) {
+            queryServerAndInformDriver(location, isCalledFromShellCommand);
+
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "The location is outside the bounds of the Afc location object so a"
+                        + " query of the AFC server is executed with a new AfcLocation object.");
+            }
+        } else {
+            // Don't query since the location parameter is either inside the AfcLocation boundary
+            // or on the border.
+            if (mVerboseLoggingEnabled) {
+                Log.i(TAG, "The current location is " + inBoundsResult + " so a query "
+                        + "will not be executed.");
+            }
+        }
+    }
+
+    /**
+     * Sends the allowed AFC channels and frequencies to the driver.
+     */
+    private boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        return mWifiNative.setAfcChannelAllowance(afcChannelAllowance);
+    }
+
+    /**
+     * Query the AFC server to get allowed AFC frequencies and channels, then update the driver with
+     * these values.
+     *
+     * @param location the location used to construct the location boundary sent to the server.
+     * @param isCalledFromShellCommand whether this method is being called from a shell command.
+     */
+    private void queryServerAndInformDriver(Location location, boolean isCalledFromShellCommand) {
+        mLastAfcServerQueryTime = mClock.getElapsedSinceBootMillis();
+
+        if (isCalledFromShellCommand) {
+            if (mServerUrlSetFromShellCommand == null) {
+                Log.e(TAG, "The AFC server URL has not been set. Please use the "
+                        + "configure-afc-server shell command to set the server URL before "
+                        + "attempting to query the server from a shell command.");
+                return;
+            }
+
+            mAfcClient.setServerURL(mServerUrlSetFromShellCommand);
+            mAfcClient.setRequestPropertyPairs(mServerRequestPropertiesSetFromShellCommand);
+        } else {
+            mAfcClient.setServerURL(mAfcServerUrl);
+        }
+
+        // Convert the Location object to an AfcLocation object
+        AfcLocation afcLocationForQuery = mAfcLocationUtil.createAfcLocation(location);
+
+        mAfcClient.queryAfcServer(afcLocationForQuery, new Handler(mWifiHandlerThread.getLooper()),
+                mCallback);
+    }
+
+    /**
+     * On a country code change, check if AFC is supported in this country. If it is, start
+     * listening to location updates if we aren't already, and query the AFC server. If it isn't,
+     * stop listening to location updates and send an AfcChannelAllowance object with empty
+     * frequency and channel lists to the driver.
+     */
+    public void onCountryCodeChange(String countryCode) {
+        if (!mWifiGlobals.isAfcSupportedOnDevice() || countryCode.equals(mLastKnownCountryCode)) {
+            return;
+        }
+        mLastKnownCountryCode = countryCode;
+        List<String> afcServerUrlsForCountry = mWifiGlobals.getAfcServerUrlsForCountry(countryCode);
+
+        if (afcServerUrlsForCountry == null || afcServerUrlsForCountry.size() == 0) {
+            mAfcServerUrl = null;
+        } else {
+            mAfcServerUrl = afcServerUrlsForCountry.get(0);
+        }
+
+        // if AFC support has not changed, we do not need to do anything else
+        if (mIsAfcSupportedForCurrentCountry == (afcServerUrlsForCountry != null)) return;
+
+        mIsAfcSupportedForCurrentCountry = !mIsAfcSupportedForCurrentCountry;
+        getLocationManager();
+
+        if (mLocationManager == null) {
+            Log.e(TAG, "Location Manager should not be null.");
+            return;
+        }
+
+        if (!mIsAfcSupportedForCurrentCountry) {
+            stopListeningForLocationChanges();
+
+            // send driver AFC allowance with empty frequency and channel arrays
+            WifiChip.AfcChannelAllowance afcChannelAllowance = new WifiChip.AfcChannelAllowance();
+            afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
+            afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
+            setAfcChannelAllowance(afcChannelAllowance);
+            return;
+        }
+
+        listenForLocationChanges();
+        getProviderForLocationRequest();
+        if (mProviderForLocationRequest.isEmpty()) return;
+
+        mLocationManager.getCurrentLocation(
+                mProviderForLocationRequest, null,
+                new HandlerExecutor(new Handler(mWifiHandlerThread.getLooper())),
+                currentLocation -> {
+                    mLastKnownLocation = currentLocation;
+
+                    if (currentLocation == null) {
+                        Log.e(TAG, "Current location is null.");
+                        return;
+                    }
+
+                    queryServerAndInformDriver(currentLocation, false);
+                });
+    }
+
+    /**
+     * Returns the preferred provider to use for getting the current location, or an empty string
+     * if none are present.
+     */
+    private String getProviderForLocationRequest() {
+        if (!mProviderForLocationRequest.isEmpty() || mLocationManager == null) {
+            return mProviderForLocationRequest;
+        }
+
+        // Order in which location providers are preferred. A lower index means a higher preference.
+        String[] preferredProvidersInOrder;
+        // FUSED_PROVIDER is only available from API level 31 onwards
+        if (SdkLevel.isAtLeastS()) {
+            preferredProvidersInOrder = new String[] { LocationManager.FUSED_PROVIDER,
+                    LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER };
+        } else {
+            preferredProvidersInOrder = new String[] { LocationManager.NETWORK_PROVIDER,
+                    LocationManager.GPS_PROVIDER };
+        }
+
+        List<String> availableLocationProviders = mLocationManager.getProviders(true);
+
+        // return the first preferred provider that is available
+        for (String preferredProvider : preferredProvidersInOrder) {
+            if (availableLocationProviders.contains(preferredProvider)) {
+                mProviderForLocationRequest = preferredProvider;
+                break;
+            }
+        }
+        return mProviderForLocationRequest;
+    }
+
+    /**
+     * Set the server URL and request properties map used to query the AFC server. This is called
+     * from the configure-afc-server Wi-Fi shell command.
+     *
+     * @param url the URL of the AFC server
+     * @param requestProperties A map with key and value Strings for the HTTP header's request
+     *                          property fields.
+     */
+    public void setServerUrlAndRequestPropertyPairs(@NonNull String url,
+            @NonNull Map<String, String> requestProperties) {
+        mServerUrlSetFromShellCommand = url;
+        mServerRequestPropertiesSetFromShellCommand = new HashMap<>();
+        mServerRequestPropertiesSetFromShellCommand.putAll(requestProperties);
+    }
+
+    /**
+     * Dump the internal state of AfcManager.
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("Dump of AfcManager");
+        if (!mWifiGlobals.isAfcSupportedOnDevice()) {
+            pw.println("AfcManager - AFC is not supported on this device.");
+            return;
+        }
+        pw.println("AfcManager - AFC is supported on this device.");
+
+        if (!mIsAfcSupportedForCurrentCountry) {
+            pw.println("AfcManager - AFC is not available in this country, with country code: "
+                    + mLastKnownCountryCode);
+        } else {
+            pw.println("AfcManager - AFC is available in this country, with country code: "
+                    + mLastKnownCountryCode);
+        }
+
+        pw.println("AfcManager - Last time the server was queried: " + mLastAfcServerQueryTime);
+    }
+
+    /**
+     * Enable verbose logging in AfcManager.
+     */
+    public void enableVerboseLogging(boolean verboseLoggingEnabled) {
+        mVerboseLoggingEnabled = verboseLoggingEnabled;
+    }
+
+    @VisibleForTesting
+    LocationManager getLocationManager() {
+        if (mLocationManager == null) {
+            mLocationManager = mContext.getSystemService(LocationManager.class);
+        }
+        return mLocationManager;
+    }
+
+    @VisibleForTesting
+    Location getLastKnownLocation() {
+        return mLastKnownLocation;
+    }
+
+    @VisibleForTesting
+    long getLastAfcServerQueryTime() {
+        return mLastAfcServerQueryTime;
+    }
+
+    @VisibleForTesting
+    AfcLocation getLastAfcLocationInSuccessfulQuery() {
+        return mLastAfcLocationInSuccessfulQuery;
+    }
+
+    @VisibleForTesting
+    public void setIsAfcSupportedInCurrentCountry(boolean isAfcSupported) {
+        mIsAfcSupportedForCurrentCountry = isAfcSupported;
+    }
+
+    @VisibleForTesting
+    @Nullable
+    String getAfcServerUrl() {
+        return mAfcServerUrl;
+    }
+}
diff --git a/service/java/com/android/server/wifi/AfcServerResponse.java b/service/java/com/android/server/wifi/AfcServerResponse.java
new file mode 100644
index 0000000..2a002a6
--- /dev/null
+++ b/service/java/com/android/server/wifi/AfcServerResponse.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.server.wifi.hal.WifiChip;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class stores information about the response of the AFC Server to a query
+ */
+public class AfcServerResponse {
+    private static final String TAG = "WifiAfcServerResponse";
+    static final int SUCCESS_AFC_RESPONSE_CODE = 0;
+    static final int SUCCESS_HTTP_RESPONSE_CODE = 200;
+
+    /*
+     * The value represented by the AFC response code is mapped out as follows, taken from the AFC
+     * server specs:
+     * -1: General Failure
+     * 0: SUCCESS
+     * 100 – 199: General errors related to the protocol
+     * 300 – 399: Error events specific to message exchanges for the Available Spectrum Inquiry
+     */
+    private int mAfcResponseCode;
+    // HTTP response code is 200 if the request succeeded, otherwise the request failed
+    private int mHttpResponseCode;
+    private String mAfcResponseDescription;
+    private WifiChip.AfcChannelAllowance mAfcChannelAllowance;
+
+    /**
+     * Parses the spectrum inquiry response object received from the AFC server into an
+     * AfcServerResponse object. Returns null if the available spectrum inquiry response is
+     * incorrectly formatted.
+     *
+     * @param httpResponseCode the HTTP response code of the AFC server.
+     * @param spectrumInquiryResponse the available spectrum inquiry response to parse.
+     * @return the parsed response object as an AfcServerResponse, or null if the input JSON is
+     * incorrectly formatted.
+     */
+    public static AfcServerResponse fromSpectrumInquiryResponse(int httpResponseCode,
+            JSONObject spectrumInquiryResponse) {
+        AfcServerResponse afcServerResponse = new AfcServerResponse();
+
+        if (spectrumInquiryResponse == null) {
+            return null;
+        }
+
+        afcServerResponse.setHttpResponseCode(httpResponseCode);
+        afcServerResponse.setAfcResponseCode(parseAfcResponseCode(spectrumInquiryResponse));
+        afcServerResponse.setAfcResponseDescription(getResponseShortDescriptionFromJSON(
+                spectrumInquiryResponse));
+
+        // no need to keep parsing if either the AFC or HTTP codes indicate a failed request
+        if (afcServerResponse.mAfcResponseCode != SUCCESS_AFC_RESPONSE_CODE
+                || afcServerResponse.mHttpResponseCode != SUCCESS_HTTP_RESPONSE_CODE) {
+            return afcServerResponse;
+        }
+
+        // parse the available frequencies and channels, and their expiration time
+        try {
+            WifiChip.AfcChannelAllowance afcChannelAllowance = new WifiChip.AfcChannelAllowance();
+            afcChannelAllowance.availableAfcFrequencyInfos = parseAvailableFrequencyInfo(
+                    spectrumInquiryResponse.optJSONArray("availableFrequencyInfo"));
+            afcChannelAllowance.availableAfcChannelInfos = parseAvailableChannelInfo(
+                    spectrumInquiryResponse.optJSONArray("availableChannelInfo"));
+
+            // expiry time is required as we know the response was successful
+            String availabilityExpireTimeString = spectrumInquiryResponse.getString(
+                    "availabilityExpireTime");
+            afcChannelAllowance.availabilityExpireTimeMs =
+                    convertExpireTimeStringToTimestamp(availabilityExpireTimeString);
+
+            afcServerResponse.setAfcChannelAllowance(afcChannelAllowance);
+        } catch (JSONException | NullPointerException e) {
+            Log.e(TAG, e.toString());
+            return null;
+        }
+        return afcServerResponse;
+    }
+
+    /**
+     * Parses the available frequencies of an AfcServerResponse.
+     */
+    private static List<WifiChip.AvailableAfcFrequencyInfo> parseAvailableFrequencyInfo(
+            JSONArray availableFrequencyInfoJSON) {
+        if (availableFrequencyInfoJSON != null) {
+            List<WifiChip.AvailableAfcFrequencyInfo> availableFrequencyInfo = new ArrayList<>();
+
+            try {
+                for (int i = 0; i < availableFrequencyInfoJSON.length(); ++i) {
+                    JSONObject frequencyRange = availableFrequencyInfoJSON.getJSONObject(i)
+                            .getJSONObject("frequencyRange");
+
+                    WifiChip.AvailableAfcFrequencyInfo availableFrequency = new WifiChip
+                            .AvailableAfcFrequencyInfo();
+                    availableFrequency.startFrequencyMhz =
+                            frequencyRange.getInt("lowFrequency");
+                    availableFrequency.endFrequencyMhz =
+                            frequencyRange.getInt("highFrequency");
+                    availableFrequency.maxPsdDbmPerMhz =
+                            availableFrequencyInfoJSON.getJSONObject(i).getInt("maxPsd");
+
+                    availableFrequencyInfo.add(availableFrequency);
+                }
+                return availableFrequencyInfo;
+            } catch (JSONException | NullPointerException e) {
+                Log.e(TAG, "Error occurred when parsing available AFC frequency info.");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parses the available channels of an AfcServerResponse.
+     */
+    private static List<WifiChip.AvailableAfcChannelInfo> parseAvailableChannelInfo(
+            JSONArray availableChannelInfoJSON) {
+        if (availableChannelInfoJSON != null) {
+            List<WifiChip.AvailableAfcChannelInfo> availableChannelInfo = new ArrayList<>();
+
+            try {
+                for (int i = 0; i < availableChannelInfoJSON.length(); ++i) {
+                    int globalOperatingClass = availableChannelInfoJSON
+                            .getJSONObject(i).getInt("globalOperatingClass");
+
+                    for (int j = 0; j < availableChannelInfoJSON.getJSONObject(i).getJSONArray(
+                            "channelCfi").length(); ++j) {
+                        WifiChip.AvailableAfcChannelInfo availableChannel = new WifiChip
+                                .AvailableAfcChannelInfo();
+
+                        availableChannel.globalOperatingClass = globalOperatingClass;
+                        availableChannel.channelCfi = availableChannelInfoJSON.getJSONObject(i)
+                                        .getJSONArray("channelCfi").getInt(j);
+                        availableChannel.maxEirpDbm = availableChannelInfoJSON.getJSONObject(i)
+                                        .getJSONArray("maxEirp").getInt(j);
+                        availableChannelInfo.add(availableChannel);
+                    }
+                }
+                return availableChannelInfo;
+            } catch (JSONException | NullPointerException e) {
+                Log.e(TAG, "Error occurred when parsing available AFC channel info.");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Converts a date string in the format: YYYY-MM-DDThh:mm:ssZ to a Unix timestamp.
+     *
+     * @param availabilityExpireTime the expiration time of the AFC frequency and channel
+     * availability.
+     * @return the expiration time as a Unix timestamp in milliseconds.
+     */
+    static long convertExpireTimeStringToTimestamp(String availabilityExpireTime) {
+        // Sometimes the expiration time is incorrectly formatted and includes appended
+        // milliseconds after the seconds section, so we want to remove these before converting to
+        // a timestamp
+        if (availabilityExpireTime.length() > 20) {
+            availabilityExpireTime = availabilityExpireTime.substring(0, 19) + "Z";
+        }
+
+        return Instant.parse(availabilityExpireTime).toEpochMilli();
+    }
+
+    /**
+     * Returns the AFC response code for an available spectrum inquiry response or Integer.MIN_VALUE
+     * if it does not exist.
+     */
+    static int parseAfcResponseCode(JSONObject spectrumInquiryResponse) {
+        try {
+            // parse the response code and return it. Throws an error if a field does not exist.
+            int responseCode = spectrumInquiryResponse.getJSONObject("response")
+                    .getInt("responseCode");
+
+            return responseCode;
+        } catch (JSONException | NullPointerException e) {
+            Log.e(TAG, "The available spectrum inquiry response does not contain a response "
+                    + "code field.");
+
+            // Currently the internal Google AFC server doesn't provide an AFC response code of 0
+            // for successful queries.
+            // TODO (b/294594249): If the server is modified to correctly provide an AFC response
+            //  code of 0 upon a successful query, handle this case accordingly.
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the short description of an available spectrum inquiry response, or an empty string
+     * if it does not exist.
+     */
+    static String getResponseShortDescriptionFromJSON(JSONObject spectrumInquiryResponse) {
+        try {
+            // parse and return the response description. Throws an error if a field does not exist.
+            return spectrumInquiryResponse.getJSONObject("response").getString("shortDescription");
+        } catch (JSONException | NullPointerException e) {
+            return "";
+        }
+    }
+
+    /**
+     * Sets the AFC response code associated with an available spectrum inquiry response.
+     */
+    public void setAfcResponseCode(int afcResponseCode) {
+        mAfcResponseCode = afcResponseCode;
+    }
+
+    /**
+     * Sets the description associated with an available spectrum inquiry response.
+     */
+    public void setAfcResponseDescription(String afcResponseDescription) {
+        mAfcResponseDescription = afcResponseDescription;
+    }
+
+    /**
+     * Sets the AFC channel and frequency allowance.
+     */
+    public void setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        mAfcChannelAllowance = afcChannelAllowance;
+    }
+
+    /**
+     * Sets the AFC server HTTP response code.
+     */
+    public void setHttpResponseCode(int httpResponseCode) {
+        mHttpResponseCode = httpResponseCode;
+    }
+
+    /**
+     * Returns the AFC response code of the available spectrum inquiry response.
+     */
+    public int getAfcResponseCode() {
+        return mAfcResponseCode;
+    }
+
+    /**
+     * Returns the HTTP response code of the AFC server's response.
+     */
+    public int getHttpResponseCode() {
+        return mHttpResponseCode;
+    }
+
+    /**
+     * Returns the AFC response description.
+     */
+    public String getAfcResponseDescription() {
+        return mAfcResponseDescription;
+    }
+
+    /**
+     * Returns the available channels and frequencies received from the AFC query.
+     */
+    @Nullable
+    public WifiChip.AfcChannelAllowance getAfcChannelAllowance() {
+        return mAfcChannelAllowance;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+
+        if (mAfcChannelAllowance != null) {
+            // print frequency info
+            if (mAfcChannelAllowance.availableAfcFrequencyInfos != null) {
+                sbuf.append("Available frequencies:\n");
+
+                for (WifiChip.AvailableAfcFrequencyInfo frequency :
+                        mAfcChannelAllowance.availableAfcFrequencyInfos) {
+                    sbuf.append("   [" + frequency.startFrequencyMhz + ", "
+                            + frequency.endFrequencyMhz + "], maxPsd: " + frequency.maxPsdDbmPerMhz
+                            + "dBm per MHZ\n");
+                }
+            }
+
+            // print channel info
+            if (mAfcChannelAllowance.availableAfcChannelInfos != null) {
+                sbuf.append("Available channels:\n");
+
+                for (WifiChip.AvailableAfcChannelInfo channel :
+                        mAfcChannelAllowance.availableAfcChannelInfos) {
+                    sbuf.append("   Global operating class: " + channel.globalOperatingClass
+                            + ", Cfi: " + channel.channelCfi + " maxEirp: " + channel.maxEirpDbm
+                            + "dBm\n");
+                }
+            }
+
+            sbuf.append("Availability expiration time (ms): "
+                    + mAfcChannelAllowance.availabilityExpireTimeMs + "\n");
+        }
+
+        sbuf.append("HTTP response code: " + mHttpResponseCode + "\n");
+        sbuf.append("AFC response code: " + mAfcResponseCode + "\n");
+        sbuf.append("AFC response short description: " + mAfcResponseDescription + "\n");
+        return sbuf.toString();
+    }
+}
diff --git a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
index d68029c..c4d43c5 100644
--- a/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
+++ b/service/java/com/android/server/wifi/AvailableNetworkNotifier.java
@@ -263,6 +263,7 @@
 
         if (mState != STATE_NO_NOTIFICATION) {
             mWifiNotificationManager.cancel(mSystemMessageNotificationId);
+            Log.i(mTag, "notification canceled");
 
             if (mRecommendedNetwork != null) {
                 Log.d(mTag, "Notification with state="
@@ -434,6 +435,7 @@
 
     private void postNotification(Notification notification) {
         mWifiNotificationManager.notify(mSystemMessageNotificationId, notification);
+        Log.i(mTag, "notification send");
     }
 
     private void handleConnectToNetworkAction() {
diff --git a/service/java/com/android/server/wifi/ClientMode.java b/service/java/com/android/server/wifi/ClientMode.java
index 9129dc4..1427a8d 100644
--- a/service/java/com/android/server/wifi/ClientMode.java
+++ b/service/java/com/android/server/wifi/ClientMode.java
@@ -92,7 +92,12 @@
      */
     void setLinkLayerStatsPollingInterval(int newIntervalMs);
 
-    boolean setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer);
+    /**
+     * See {@link android.net.wifi.WifiManager#setWifiConnectedNetworkScorer(Executor,
+     * WifiManager.WifiConnectedNetworkScorer)}
+     */
+    boolean setWifiConnectedNetworkScorer(IBinder binder, IWifiConnectedNetworkScorer scorer,
+            int callerUid);
 
     void clearWifiConnectedNetworkScorer();
 
@@ -203,6 +208,9 @@
 
     void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status);
 
+    /** returns whether the current network is labeled as local-only due to ip provision timeout */
+    boolean isIpProvisioningTimedOut();
+
     /** Result callback for {@link #probeLink(LinkProbeCallback, int)} */
     interface LinkProbeCallback extends WifiNl80211Manager.SendMgmtFrameCallback {
 
@@ -355,4 +363,9 @@
      * @return true if connection is MLO, otherwise false.
      */
     boolean isMlo();
+
+    /**
+     * Notify changes in PowerManager#isDeviceIdleMode
+     */
+    void onIdleModeChanged(boolean isIdle);
 }
diff --git a/service/java/com/android/server/wifi/ClientModeDefaults.java b/service/java/com/android/server/wifi/ClientModeDefaults.java
index 1692a68..115dfaf 100644
--- a/service/java/com/android/server/wifi/ClientModeDefaults.java
+++ b/service/java/com/android/server/wifi/ClientModeDefaults.java
@@ -73,7 +73,7 @@
     default void setLinkLayerStatsPollingInterval(int newIntervalMs) { }
 
     default boolean setWifiConnectedNetworkScorer(
-            IBinder binder, IWifiConnectedNetworkScorer scorer) {
+            IBinder binder, IWifiConnectedNetworkScorer scorer, int callerUid) {
         // don't fail the public API when wifi is off.
         return true;
     }
@@ -207,6 +207,10 @@
         return true;
     }
 
+    default boolean isIpProvisioningTimedOut() {
+        return false;
+    }
+
     default boolean isSupplicantTransientState() {
         return false;
     }
@@ -276,4 +280,7 @@
     default boolean isMlo() {
         return false;
     }
+
+    @Override
+    default void onIdleModeChanged(boolean isIdle) { }
 }
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 35f8896..34981aa 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -23,6 +23,7 @@
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_UNWANTED_LOW_RSSI;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_TDLS;
 import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE;
 
@@ -42,10 +43,8 @@
 import android.app.ActivityManager;
 import android.app.BroadcastOptions;
 import android.app.admin.SecurityLog;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.CaptivePortalData;
 import android.net.ConnectivityManager;
 import android.net.DhcpResultsParcelable;
@@ -199,9 +198,12 @@
     private static final int IPCLIENT_STARTUP_TIMEOUT_MS = 2_000;
     private static final int IPCLIENT_SHUTDOWN_TIMEOUT_MS = 60_000; // 60 seconds
     private static final int NETWORK_AGENT_TEARDOWN_DELAY_MS = 5_000; // Max teardown delay.
+    private static final int DISASSOC_AP_BUSY_DISABLE_DURATION_MS = 5 * 60 * 1000; // 5 minutes
     @VisibleForTesting public static final long CONNECTING_WATCHDOG_TIMEOUT_MS = 30_000; // 30 secs.
+    public static final int PROVISIONING_TIMEOUT_FILS_CONNECTION_MS = 36_000; // 36 secs.
     @VisibleForTesting
     public static final String ARP_TABLE_PATH = "/proc/net/arp";
+    private final WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
 
     private boolean mVerboseLoggingEnabled = false;
 
@@ -271,6 +273,7 @@
     private final ConcreteClientModeManager mClientModeManager;
 
     private final WifiNotificationManager mNotificationManager;
+    private final WifiConnectivityHelper mWifiConnectivityHelper;
     private final QosPolicyRequestHandler mQosPolicyRequestHandler;
 
     private boolean mFailedToResetMacAddress = false;
@@ -290,6 +293,11 @@
     private int mPowerSaveDisableRequests = 0; // mask based on @PowerSaveClientType
     private boolean mIsUserSelected = false;
     private boolean mCurrentConnectionDetectedCaptivePortal;
+    // Overrides StatsLog disconnect reason logging for NETWORK_DISCONNECTION_EVENT with specific
+    // framework initiated disconnect reason code.
+    private int mFrameworkDisconnectReasonOverride;
+
+    private boolean mIpProvisioningTimedOut = false;
 
     private String getTag() {
         return TAG + "[" + mId + ":" + (mInterfaceName == null ? "unknown" : mInterfaceName) + "]";
@@ -401,6 +409,7 @@
 
     private volatile IpClientManager mIpClient;
     private IpClientCallbacksImpl mIpClientCallbacks;
+    private static int sIpClientCallbacksIndex = 0;
 
     private final WifiNetworkFactory mNetworkFactory;
     private final UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
@@ -444,6 +453,9 @@
     static final int CMD_ONESHOT_RSSI_POLL                              = BASE + 84;
     /* Enable suspend mode optimizations in the driver */
     static final int CMD_SET_SUSPEND_OPT_ENABLED                        = BASE + 86;
+    /* L3 provisioning timed out*/
+    static final int CMD_IP_PROVISIONING_TIMEOUT                        = BASE + 87;
+
 
     /**
      * Watchdog for protecting against b/16823537
@@ -554,7 +566,8 @@
 
     @VisibleForTesting
     static final int CMD_PRE_DHCP_ACTION                                = BASE + 255;
-    private static final int CMD_PRE_DHCP_ACTION_COMPLETE               = BASE + 256;
+    @VisibleForTesting
+    static final int CMD_PRE_DHCP_ACTION_COMPLETE                       = BASE + 256;
     private static final int CMD_POST_DHCP_ACTION                       = BASE + 257;
 
     private static final int CMD_CONNECT_NETWORK                        = BASE + 258;
@@ -582,6 +595,11 @@
     private static final int SUSPEND_DUE_TO_HIGH_PERF = 1 << 1;
     private static final int SUSPEND_DUE_TO_SCREEN = 1 << 2;
 
+    /* Timeout after which a network connection stuck in L3 provisioning is considered as local
+    only. It should be at least equal to ProvisioningConfiguration#DEFAULT_TIMEOUT_MS */
+    @VisibleForTesting
+    public static final long WAIT_FOR_L3_PROVISIONING_TIMEOUT_MS = 18000;
+
     /** @see #isRecentlySelectedByTheUser */
     @VisibleForTesting
     public static final int LAST_SELECTED_NETWORK_EXPIRATION_AGE_MILLIS = 30 * 1000;
@@ -725,7 +743,8 @@
             @NonNull WifiInjector wifiInjector,
             @NonNull WifiSettingsConfigStore settingsConfigStore,
             boolean verboseLoggingEnabled,
-            @NonNull WifiNotificationManager wifiNotificationManager) {
+            @NonNull WifiNotificationManager wifiNotificationManager,
+            @NonNull WifiConnectivityHelper wifiConnectivityHelper) {
         super(TAG, looper);
         mWifiMetrics = wifiMetrics;
         mClock = clock;
@@ -770,6 +789,7 @@
         mActivityManager = context.getSystemService(ActivityManager.class);
 
         mLastBssid = null;
+        mIpProvisioningTimedOut = false;
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         mLastSimBasedConnectionCarrierName = null;
@@ -803,6 +823,7 @@
         mTelephonyManager = telephonyManager;
         mSettingsConfigStore = settingsConfigStore;
         updateInterfaceCapabilities();
+        mWifiDeviceStateChangeManager = wifiInjector.getWifiDeviceStateChangeManager();
 
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
@@ -832,6 +853,7 @@
         enableVerboseLogging(verboseLoggingEnabled);
 
         mNotificationManager = wifiNotificationManager;
+        mWifiConnectivityHelper = wifiConnectivityHelper;
         mInsecureEapNetworkHandlerCallbacksImpl =
                 new InsecureEapNetworkHandler.InsecureEapNetworkHandlerCallbacks() {
                 @Override
@@ -1020,6 +1042,11 @@
     class IpClientCallbacksImpl extends IpClientCallbacks {
         private final ConditionVariable mWaitForCreationCv = new ConditionVariable(false);
         private final ConditionVariable mWaitForStopCv = new ConditionVariable(false);
+        private int mIpClientCallbacksIndex;
+
+        private IpClientCallbacksImpl() {
+            mIpClientCallbacksIndex = ++sIpClientCallbacksIndex;
+        }
 
         @Override
         public void onIpClientCreated(IIpClient ipClient) {
@@ -1030,103 +1057,89 @@
             // If IpClient is still not ready after blocking wait, async wait (for when wait is
             // long). Will drop all connection requests until IpClient is ready. Other requests
             // will still be processed.
-            sendMessageAtFrontOfQueue(CMD_IPCLIENT_CREATED,
+            sendMessageAtFrontOfQueue(CMD_IPCLIENT_CREATED, mIpClientCallbacksIndex, 0,
                     new IpClientManager(ipClient, getName()));
             mWaitForCreationCv.open();
         }
 
         @Override
         public void onPreDhcpAction() {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_PRE_DHCP_ACTION);
+            sendMessage(CMD_PRE_DHCP_ACTION, mIpClientCallbacksIndex);
         }
 
         @Override
         public void onPostDhcpAction() {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_POST_DHCP_ACTION);
+            sendMessage(CMD_POST_DHCP_ACTION, mIpClientCallbacksIndex);
         }
 
         @Override
         public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
-            if (mIpClientCallbacks != this) return;
             if (dhcpResults != null) {
-                sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
+                sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, mIpClientCallbacksIndex, 0, dhcpResults);
             } else {
-                sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
+                sendMessage(CMD_IPV4_PROVISIONING_FAILURE, mIpClientCallbacksIndex);
             }
         }
 
         @Override
         public void onProvisioningSuccess(LinkProperties newLp) {
-            if (mIpClientCallbacks != this) return;
             addPasspointInfoToLinkProperties(newLp);
             mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
-            sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
-            sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
+            sendMessage(CMD_UPDATE_LINKPROPERTIES, mIpClientCallbacksIndex, 0, newLp);
+            sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL, mIpClientCallbacksIndex);
         }
 
         @Override
         public void onProvisioningFailure(LinkProperties newLp) {
-            if (mIpClientCallbacks != this) return;
             mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST);
-            sendMessage(CMD_IP_CONFIGURATION_LOST);
+            sendMessage(CMD_IP_CONFIGURATION_LOST, mIpClientCallbacksIndex);
         }
 
         @Override
         public void onLinkPropertiesChange(LinkProperties newLp) {
-            if (mIpClientCallbacks != this) return;
             addPasspointInfoToLinkProperties(newLp);
-            sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
+            sendMessage(CMD_UPDATE_LINKPROPERTIES, mIpClientCallbacksIndex, 0, newLp);
         }
 
         @Override
         public void onReachabilityLost(String logMsg) {
-            if (mIpClientCallbacks != this) return;
             mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
-            sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
+            sendMessage(CMD_IP_REACHABILITY_LOST, mIpClientCallbacksIndex, 0, logMsg);
         }
 
         @Override
         public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_IP_REACHABILITY_FAILURE, lossInfo);
+            sendMessage(CMD_IP_REACHABILITY_FAILURE, mIpClientCallbacksIndex, 0, lossInfo);
         }
 
         @Override
         public void installPacketFilter(byte[] filter) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_INSTALL_PACKET_FILTER, filter);
+            sendMessage(CMD_INSTALL_PACKET_FILTER, mIpClientCallbacksIndex, 0, filter);
         }
 
         @Override
         public void startReadPacketFilter() {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_READ_PACKET_FILTER);
+            sendMessage(CMD_READ_PACKET_FILTER, mIpClientCallbacksIndex);
         }
 
         @Override
         public void setFallbackMulticastFilter(boolean enabled) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_SET_FALLBACK_PACKET_FILTERING, enabled);
+            sendMessage(CMD_SET_FALLBACK_PACKET_FILTERING, mIpClientCallbacksIndex, 0, enabled);
         }
 
         @Override
         public void setNeighborDiscoveryOffload(boolean enabled) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_CONFIG_ND_OFFLOAD, (enabled ? 1 : 0));
+            sendMessage(CMD_CONFIG_ND_OFFLOAD, mIpClientCallbacksIndex, (enabled ? 1 : 0));
         }
 
         @Override
         public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_START_FILS_CONNECTION, 0, 0, packets);
+            sendMessage(CMD_START_FILS_CONNECTION, mIpClientCallbacksIndex, 0, packets);
         }
 
         @Override
         public void setMaxDtimMultiplier(int multiplier) {
-            if (mIpClientCallbacks != this) return;
-            sendMessage(CMD_SET_MAX_DTIM_MULTIPLIER, multiplier);
+            sendMessage(CMD_SET_MAX_DTIM_MULTIPLIER, mIpClientCallbacksIndex, multiplier);
         }
 
         @Override
@@ -1142,6 +1155,10 @@
         boolean awaitShutdown() {
             return mWaitForStopCv.block(IPCLIENT_SHUTDOWN_TIMEOUT_MS);
         }
+
+        int getCallbackIndex() {
+            return mIpClientCallbacksIndex;
+        }
     }
 
     private void stopIpClient() {
@@ -1191,6 +1208,7 @@
             // The current connected or connecting network has been removed, trigger a disconnect.
             if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
                 // Disconnect and let autojoin reselect a new network
+                mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_REMOVED;
                 sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_REMOVED);
                 // Log disconnection here, since the network config won't exist when the
                 // disconnection event is received.
@@ -1210,9 +1228,9 @@
         }
 
         @Override
-        public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig) {
-
-            if (WifiConfigurationUtil.hasCredentialChanged(oldConfig, newConfig)) {
+        public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig,
+                boolean hasCredentialChanged) {
+            if (hasCredentialChanged) {
                 // Clear invalid cached data.
                 mWifiNative.removeNetworkCachedData(oldConfig.networkId);
                 mWifiBlocklistMonitor.handleNetworkRemoved(newConfig.SSID);
@@ -1234,6 +1252,7 @@
                 mWifiInfo.setTrusted(newConfig.trusted);
                 if (!newConfig.trusted) {
                     Log.w(getTag(), "Network marked untrusted, triggering disconnect");
+                    mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED;
                     sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED);
                     return;
                 }
@@ -1241,6 +1260,7 @@
 
             if (isMetered) {
                 Log.w(getTag(), "Network marked metered, triggering disconnect");
+                mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_METERED;
                 sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_METERED);
                 return;
             }
@@ -1256,6 +1276,7 @@
             if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
             if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
                 // Disconnect and let autojoin reselect a new network
+                mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_TEMP_DISABLED;
                 sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_TEMPORARY_DISABLED);
             }
 
@@ -1270,6 +1291,7 @@
             if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
             if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) {
                 // Disconnect and let autojoin reselect a new network
+                mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_PERM_DISABLED;
                 sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_PERMANENT_DISABLED);
             }
         }
@@ -1289,6 +1311,7 @@
             if (configuration != null && configuration.subscriptionId == subscriptionId
                     && configuration.carrierMerged == merged) {
                 Log.i(getTag(), "Carrier network offload disabled, triggering disconnect");
+                mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_CARRIER_OFFLOAD_DISABLED;
                 sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_CARRIER_OFFLOAD_DISABLED);
             }
             mWifiConnectivityManager.clearCachedCandidates();
@@ -1565,11 +1588,13 @@
         }
         mLastLinkLayerStatsUpdate = mClock.getWallClockMillis();
         WifiLinkLayerStats stats = null;
-        if (isPrimary()) {
-            stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName);
-        } else {
-            if (mVerboseLoggingEnabled) {
-                Log.w(getTag(), "Can't getWifiLinkLayerStats on secondary iface");
+        if (isLinkLayerStatsSupported()) {
+            if (isPrimary()) {
+                stats = mWifiNative.getWifiLinkLayerStats(mInterfaceName);
+            } else {
+                if (mVerboseLoggingEnabled) {
+                    Log.w(getTag(), "Can't getWifiLinkLayerStats on secondary iface");
+                }
             }
         }
         if (stats != null) {
@@ -1588,6 +1613,10 @@
         return stats;
     }
 
+    private boolean isLinkLayerStatsSupported() {
+        return (getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0;
+    }
+
     /**
      * Update interface capabilities
      * This method is used to update some of interface capabilities defined in overlay
@@ -1849,6 +1878,8 @@
      * Disconnect from Access Point
      */
     public void disconnect() {
+        mFrameworkDisconnectReasonOverride =
+                WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_GENERAL;
         sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_GENERIC);
     }
 
@@ -2320,7 +2351,7 @@
                 sb.append(" enabled=" + (boolean) msg.obj);
                 break;
             case CMD_SET_MAX_DTIM_MULTIPLIER:
-                sb.append(" maximum multiplier=" + msg.arg1);
+                sb.append(" maximum multiplier=" + msg.arg2);
                 break;
             case CMD_ROAM_WATCHDOG_TIMER:
                 sb.append(" ");
@@ -2533,6 +2564,12 @@
         return mClientModeManager.getRole() == ROLE_CLIENT_LOCAL_ONLY;
     }
 
+    /** Check whether this connection is for local only network due to Ip Provisioning Timeout. */
+    @Override
+    public boolean isIpProvisioningTimedOut() {
+        return mIpProvisioningTimedOut;
+    }
+
     /**
      * Check if originaly requested as local only for ClientModeManager before fallback.
      * A secondary role could fallback to primary due to hardware support limit.
@@ -2659,8 +2696,8 @@
                     + " RxLinkSpeed=" + newRxLinkSpeed);
         }
 
-        /* Set link specific signal poll results */
-        for (MloLink link : mWifiInfo.getAffiliatedMloLinks()) {
+        /* Set link specific signal poll results for associated links */
+        for (MloLink link : mWifiInfo.getAssociatedMloLinks()) {
             int linkId = link.getLinkId();
             link.setRssi(pollResults.getRssi(linkId));
             link.setTxLinkSpeedMbps(pollResults.getTxLinkSpeed(linkId));
@@ -2708,6 +2745,7 @@
                 Log.wtf(getTag(), "Error! +ve value RSSI: " + newRssi);
                 newRssi -= 256;
             }
+            int oldRssi = mWifiInfo.getRssi();
             mWifiInfo.setRssi(newRssi);
             /*
              * Rather than sending the raw RSSI out every time it
@@ -2723,6 +2761,12 @@
             if (newSignalLevel != mLastSignalLevel) {
                 sendRssiChangeBroadcast(newRssi);
                 updateNetworkCapabilities = true;
+            } else if (newRssi != oldRssi
+                    && mWifiGlobals.getVerboseLoggingLevel()
+                    != WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED) {
+                // Send the raw RSSI if verbose logging is enabled by the user so we can show
+                // granular RSSI changes in Settings.
+                sendRssiChangeBroadcast(newRssi);
             }
             updateLinkBandwidthAndCapabilities(stats, updateNetworkCapabilities, txBytes,
                     rxBytes);
@@ -3044,7 +3088,12 @@
         if (matchingScanResult != null && matchingScanResult.getApMldMacAddress() != null) {
             mWifiInfo.setApMldMacAddress(matchingScanResult.getApMldMacAddress());
             mWifiInfo.setApMloLinkId(matchingScanResult.getApMloLinkId());
-            mWifiInfo.setAffiliatedMloLinks(matchingScanResult.getAffiliatedMloLinks());
+            // Deep copy the affiliated links to avoid any overwrite in future from WifiInfo.
+            List<MloLink> deepCopyAffiliatedMloLinks = new ArrayList<>();
+            for (MloLink link : matchingScanResult.getAffiliatedMloLinks()) {
+                deepCopyAffiliatedMloLinks.add(new MloLink(link, NetworkCapabilities.REDACT_NONE));
+            }
+            mWifiInfo.setAffiliatedMloLinks(deepCopyAffiliatedMloLinks);
         } else {
             mWifiInfo.setApMldMacAddress(null);
             mWifiInfo.setApMloLinkId(MloLink.INVALID_MLO_LINK_ID);
@@ -3064,6 +3113,36 @@
                 && currentState != SupplicantState.COMPLETED;
     }
 
+    // Return true if link frequency is changed.
+    private boolean updateAssociatedMloLinksFromLinksInfoWhenBssFreqChanged(int bssFreq) {
+        boolean isLinkFrequencyChanged = false;
+        // retrieve mlo links info with updated frequencies from HAL
+        WifiNative.ConnectionMloLinksInfo mloLinksInfo = mWifiNative.getConnectionMloLinksInfo(
+                mInterfaceName);
+        if (mloLinksInfo == null) {
+            return false;
+        }
+        for (int i = 0; i < mloLinksInfo.links.length; i++) {
+            MloLink link = mWifiInfo.getAffiliatedMloLink(mloLinksInfo.links[i].getLinkId());
+            if (link != null && mloLinksInfo.links[i].getFrequencyMHz() == bssFreq) {
+                int linkCurFreq = ScanResult.convertChannelToFrequencyMhzIfSupported(
+                        link.getChannel(), link.getBand());
+                if (linkCurFreq != ScanResult.UNSPECIFIED && bssFreq != linkCurFreq) {
+                    if (link.getRssi() == mWifiInfo.getRssi()
+                            && ScanResult.convertChannelToFrequencyMhzIfSupported(link.getChannel(),
+                            link.getBand()) == mWifiInfo.getFrequency()) {
+                        mWifiInfo.setFrequency(bssFreq);
+                    }
+                    isLinkFrequencyChanged = true;
+                    link.setChannel(ScanResult.convertFrequencyMhzToChannelIfSupported(bssFreq));
+                    link.setBand(ScanResult.toBand(bssFreq));
+                    break;
+                }
+            }
+        }
+        return isLinkFrequencyChanged;
+    }
+
     private SupplicantState handleSupplicantStateChange(StateChangeResult stateChangeResult) {
         SupplicantState state = stateChangeResult.state;
         mWifiScoreCard.noteSupplicantStateChanging(mWifiInfo, state);
@@ -3146,6 +3225,12 @@
             // identify WPA3 Enterprise and WEP, so the original configuration
             // is still necessary.
             WifiConfiguration tmp = new WifiConfiguration(config);
+            SecurityParams securityParams =
+                    config.getNetworkSelectionStatus().getLastUsedSecurityParams();
+            tmp.requirePmf = securityParams.isRequirePmf();
+            tmp.allowedProtocols = securityParams.getAllowedProtocols();
+            tmp.allowedPairwiseCiphers = securityParams.getAllowedPairwiseCiphers();
+            tmp.allowedGroupCiphers = securityParams.getAllowedGroupCiphers();
             tmp.setSecurityParams(connectionInfo.keyMgmtMask);
             mWifiInfo.setCurrentSecurityType(tmp.getDefaultSecurityParams().getSecurityType());
             // Update the last used security params.
@@ -3333,9 +3418,18 @@
      *                         defined in /frameworks/proto_logging/stats/atoms.proto
      */
     private void handleNetworkDisconnect(boolean newConnectionInProgress, int disconnectReason) {
-        mWifiMetrics.reportNetworkDisconnect(mInterfaceName, disconnectReason,
+        if (newConnectionInProgress) {
+            mFrameworkDisconnectReasonOverride = mIsUserSelected
+                    ? WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NEW_CONNECTION_USER
+                    : WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NEW_CONNECTION_OTHERS;
+        }
+        int wifiStatsLogDisconnectReason = mFrameworkDisconnectReasonOverride != 0
+                ? mFrameworkDisconnectReasonOverride : disconnectReason;
+        mFrameworkDisconnectReasonOverride = 0;
+        mWifiMetrics.reportNetworkDisconnect(mInterfaceName, wifiStatsLogDisconnectReason,
                 mWifiInfo.getRssi(),
-                mWifiInfo.getLinkSpeed());
+                mWifiInfo.getLinkSpeed(),
+                mWifiInfo.getLastRssiUpdateMillis());
 
         if (mVerboseLoggingEnabled) {
             Log.v(getTag(), "handleNetworkDisconnect: newConnectionInProgress: "
@@ -3372,6 +3466,14 @@
             mWifiConfigManager.setRecentFailureAssociationStatus(
                     mWifiInfo.getNetworkId(),
                     WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY);
+            // Block the current BSS temporarily since it cannot handle more stations
+            WifiConfiguration config = getConnectedWifiConfigurationInternal();
+            if (config == null) {
+                config = getConnectingWifiConfigurationInternal();
+            }
+            mWifiBlocklistMonitor.blockBssidForDurationMs(mLastBssid, config,
+                    DISASSOC_AP_BUSY_DISABLE_DURATION_MS,
+                    WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA, mWifiInfo.getRssi());
         }
 
         mWifiScoreReport.stopConnectedNetworkScorer();
@@ -3393,6 +3495,7 @@
         clearLinkProperties();
 
         mLastBssid = null;
+        mIpProvisioningTimedOut = false;
         mLastLinkLayerStats = null;
         registerDisconnected();
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -3442,7 +3545,7 @@
         // Update link layer stats
         getWifiLinkLayerStats();
 
-        if (mWifiP2pConnection.isConnected()) {
+        if (mWifiP2pConnection.isConnected() && !mWifiP2pConnection.isP2pInWaitingState()) {
             // P2P discovery breaks DHCP, so shut it down in order to get through this.
             // Once P2P service receives this message and processes it accordingly, it is supposed
             // to send arg2 (i.e. CMD_PRE_DHCP_ACTION_COMPLETE) in a new Message.what back to
@@ -3480,8 +3583,10 @@
         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
         setPowerSave(POWER_SAVE_CLIENT_DHCP, true);
 
-        mWifiP2pConnection.sendMessage(
-                WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
+        if (mWifiP2pConnection.isConnected()) {
+            mWifiP2pConnection.sendMessage(
+                    WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
+        }
 
         // Set the coexistence mode back to its default value
         mWifiNative.setBluetoothCoexistenceMode(
@@ -3618,7 +3723,7 @@
      * the current connection attempt has concluded.
      */
     private void reportConnectionAttemptEnd(int level2FailureCode, int connectivityFailureCode,
-            int level2FailureReason) {
+            int level2FailureReason, int statusCode) {
         // if connected, this should be non-null.
         WifiConfiguration configuration = getConnectedWifiConfigurationInternal();
         if (configuration == null) {
@@ -3698,7 +3803,7 @@
             frequency = candidate.frequency;
         }
         mWifiMetrics.endConnectionEvent(mInterfaceName, level2FailureCode,
-                connectivityFailureCode, level2FailureReason, frequency);
+                connectivityFailureCode, level2FailureReason, frequency, statusCode);
         mWifiConnectivityManager.handleConnectionAttemptEnded(
                 mClientModeManager, level2FailureCode, level2FailureReason, bssid,
                 configuration);
@@ -3860,11 +3965,12 @@
         /* DHCP times out after about 30 seconds, we do a
          * disconnect thru supplicant, we will let autojoin retry connecting to the network
          */
+        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_IP_PROVISIONING_FAILURE;
         mWifiNative.disconnect(mInterfaceName);
         updateCurrentConnectionInfo();
     }
 
-    private void handleIpReachabilityLost() {
+    private void handleIpReachabilityLost(int lossReason) {
         mWifiBlocklistMonitor.handleBssidConnectionFailure(mWifiInfo.getBSSID(),
                 getConnectedWifiConfiguration(),
                 WifiBlocklistMonitor.REASON_ABNORMAL_DISCONNECT, mWifiInfo.getRssi());
@@ -3872,6 +3978,14 @@
         mWifiInfo.setInetAddress(null);
         mWifiInfo.setMeteredHint(false);
 
+        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NUD_FAILURE_GENERIC;
+        if (lossReason == ReachabilityLossReason.ROAM) {
+            mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NUD_FAILURE_ROAM;
+        } else if (lossReason == ReachabilityLossReason.CONFIRM) {
+            mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NUD_FAILURE_CONFIRM;
+        } else if (lossReason == ReachabilityLossReason.ORGANIC) {
+            mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NUD_FAILURE_ORGANIC;
+        }
         // Disconnect via supplicant, and let autojoin retry connecting to the network.
         mWifiNative.disconnect(mInterfaceName);
         updateCurrentConnectionInfo();
@@ -3884,7 +3998,7 @@
      * This method is invoked only upon receiving reachability failure post roaming or triggered
      * from Wi-Fi RSSI polling or organic kernel probe.
      */
-    private void processIpReachabilityFailure() {
+    private void processIpReachabilityFailure(int lossReason) {
         WifiConfiguration config = getConnectedWifiConfigurationInternal();
         if (config == null) {
             // special case for IP reachability lost which happens right after linked network
@@ -3892,10 +4006,13 @@
             // the connected configuration to be null.
             config = getConnectingWifiConfigurationInternal();
         }
-        if (config == null) {
+        if (config == null || (config.getIpAssignment() == IpConfiguration.IpAssignment.STATIC)) {
             // config could be null if it had been removed from WifiConfigManager. In this case
-            // we should simply disconnect.
-            handleIpReachabilityLost();
+            // we should simply disconnect. And if IP reachability failures come from static IP
+            // case(e.g. a misconfigured default gateway IP address), refreshing L3 provisioning
+            // doesn't help improve the situation and also introduces a loop, it's better to
+            // disconnect and disable the auto-rejoin on that network.
+            handleIpReachabilityLost(lossReason);
             return;
         }
         final NetworkAgentConfig naConfig = getNetworkAgentConfigInternal(config);
@@ -3917,12 +4034,17 @@
             mQosPolicyRequestHandler.setNetworkAgent(mNetworkAgent);
         }
 
+        // Shutdown IpClient and cleanup IpClientCallbacks instance before transition to
+        // WaitBeforeL3ProvisioningState, in order to prevent WiFi state machine from
+        // processing the posted message sent from the legacy IpClientCallbacks instance,
+        // see b/286338765.
+        maybeShutdownIpclient();
         transitionTo(mWaitBeforeL3ProvisioningState);
 
         updateCurrentConnectionInfo();
     }
 
-    private void processLegacyIpReachabilityLost() {
+    private void processLegacyIpReachabilityLost(int lossReason) {
         mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
         mWifiMetrics.logWifiIsUnusableEvent(mInterfaceName,
                 WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
@@ -3930,31 +4052,50 @@
                 WifiUsabilityStats.LABEL_BAD,
                 WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1);
         if (mWifiGlobals.getIpReachabilityDisconnectEnabled()) {
-            handleIpReachabilityLost();
+            handleIpReachabilityLost(lossReason);
         } else {
             logd("CMD_IP_REACHABILITY_LOST but disconnect disabled -- ignore");
         }
     }
 
+    private boolean shouldIgnoreNudDisconnectForWapiInCn() {
+        // temporary work-around for <=U devices
+        if (SdkLevel.isAtLeastV()) return false;
+
+        if (!mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc() || !"CN".equalsIgnoreCase(
+                mWifiInjector.getWifiCountryCode().getCountryCode())) {
+            return false;
+        }
+        WifiConfiguration config = getConnectedWifiConfigurationInternal();
+        return config != null && (config.allowedProtocols.get(WifiConfiguration.Protocol.WAPI)
+                || config.getAuthType() == WifiConfiguration.KeyMgmt.NONE);
+    }
+
     private void handleIpReachabilityFailure(@NonNull ReachabilityLossInfoParcelable lossInfo) {
         if (lossInfo == null) {
             Log.e(getTag(), "lossInfo should never be null");
-            processLegacyIpReachabilityLost();
+            processLegacyIpReachabilityLost(-1);
             return;
         }
 
         switch (lossInfo.reason) {
             case ReachabilityLossReason.ROAM:
-                processIpReachabilityFailure();
+                processIpReachabilityFailure(lossInfo.reason);
                 break;
             case ReachabilityLossReason.CONFIRM:
-            case ReachabilityLossReason.ORGANIC:
+            case ReachabilityLossReason.ORGANIC: {
+                if (shouldIgnoreNudDisconnectForWapiInCn()) {
+                    logd("CMD_IP_REACHABILITY_FAILURE but disconnect disabled for WAPI -- ignore");
+                    return;
+                }
+
                 if (mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()) {
-                    processIpReachabilityFailure();
+                    processIpReachabilityFailure(lossInfo.reason);
                 } else {
-                    processLegacyIpReachabilityLost();
+                    processLegacyIpReachabilityLost(lossInfo.reason);
                 }
                 break;
+            }
             default:
                 logd("Invalid failure reason " + lossInfo.reason + "from onIpReachabilityFailure");
         }
@@ -4149,6 +4290,7 @@
         mTargetBssid = SUPPLICANT_BSSID_ANY;
         mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastBssid = null;
+        mIpProvisioningTimedOut = false;
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         mLastSimBasedConnectionCarrierName = null;
@@ -4189,6 +4331,10 @@
         mWifiNative.enableStaAutoReconnect(mInterfaceName, false);
         // STA has higher priority over P2P
         mWifiNative.setConcurrencyPriority(true);
+        if (mClientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
+            // Loads the firmware roaming info which gets used in WifiBlocklistMonitor
+            mWifiConnectivityHelper.getFirmwareRoamingInfo();
+        }
 
         // Retrieve and store the factory MAC address (on first bootup).
         retrieveFactoryMacAddressAndStoreIfNecessary();
@@ -4348,7 +4494,7 @@
             reportConnectionAttemptEnd(
                     WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
                     WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
         }
     }
 
@@ -4372,23 +4518,30 @@
         mIpClient = null;
     }
 
+    // Always use "arg1" to take the current IpClient callbacks index to check if the callbacks
+    // come from the current mIpClientCallbacks instance.
+    private boolean isFromCurrentIpClientCallbacks(Message msg) {
+        if (mIpClientCallbacks == null || msg == null) return false;
+        return mIpClientCallbacks.getCallbackIndex() == msg.arg1;
+    }
+
     /********************************************************
      * HSM states
      *******************************************************/
 
     class ConnectableState extends RunnerState {
         private boolean mIsScreenStateChangeReceiverRegistered = false;
-        BroadcastReceiver mScreenStateChangeReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
-                    sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
-                } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
-                    sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
-                }
-            }
-        };
+        WifiDeviceStateChangeManager.StateChangeCallback mScreenStateChangeReceiver =
+                new WifiDeviceStateChangeManager.StateChangeCallback() {
+                    @Override
+                    public void onScreenStateChanged(boolean screenOn) {
+                        if (screenOn) {
+                            sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
+                        } else {
+                            sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
+                        }
+                    }
+                };
 
         ConnectableState(int threshold) {
             super(threshold, mWifiInjector.getWifiHandlerLocalLog());
@@ -4415,11 +4568,9 @@
             mIpClient = ipClientManager;
             setupClientMode();
 
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_SCREEN_ON);
-            filter.addAction(Intent.ACTION_SCREEN_OFF);
             if (!mIsScreenStateChangeReceiverRegistered) {
-                mContext.registerReceiver(mScreenStateChangeReceiver, filter);
+                mWifiDeviceStateChangeManager.registerStateChangeCallback(
+                        mScreenStateChangeReceiver);
                 mIsScreenStateChangeReceiverRegistered = true;
             }
             // Learn the initial state of whether the screen is on.
@@ -4451,7 +4602,8 @@
                 loge("Failed to remove networks on exiting connect mode");
             }
             if (mIsScreenStateChangeReceiverRegistered) {
-                mContext.unregisterReceiver(mScreenStateChangeReceiver);
+                mWifiDeviceStateChangeManager.unregisterStateChangeCallback(
+                        mScreenStateChangeReceiver);
                 mIsScreenStateChangeReceiverRegistered = false;
             }
 
@@ -4469,6 +4621,7 @@
         public boolean processMessageImpl(Message message) {
             switch (message.what) {
                 case CMD_IPCLIENT_CREATED:
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if (mIpClient != null) {
                         loge("Setup connectable state again when IpClient is ready?");
                     } else {
@@ -4493,6 +4646,7 @@
                     if (mWifiP2pConnection.shouldTemporarilyDisconnectWifi()) {
                         mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT,
                                 StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_P2P_REQUESTED_DISCONNECT;
                         mWifiNative.disconnect(mInterfaceName);
                     } else {
                         mWifiNative.reconnect(mInterfaceName);
@@ -4597,6 +4751,7 @@
                     break;
                 }
                 case CMD_START_FILS_CONNECTION: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if (mIpClient == null) {
                         logd("IpClient is not ready, START_FILS_CONNECTION dropped");
                         break;
@@ -4743,6 +4898,7 @@
                     if (mTermsAndConditionsUrl == null) {
                         loge("Disconnecting from Passpoint network due to an issue with the "
                                 + "Terms and Conditions URL");
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_PASSPOINT_TAC;
                         sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_PASSPOINT_TAC);
                     }
                     break;
@@ -4754,12 +4910,14 @@
                     break;
                 }
                 case CMD_CONFIG_ND_OFFLOAD: {
-                    final boolean enabled = (message.arg1 > 0);
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
+                    final boolean enabled = (message.arg2 > 0);
                     mWifiNative.configureNeighborDiscoveryOffload(mInterfaceName, enabled);
                     break;
                 }
                 // Link configuration (IP address, DNS, ...) changes notified via netlink
                 case CMD_UPDATE_LINKPROPERTIES: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     updateLinkProperties((LinkProperties) message.obj);
                     break;
                 }
@@ -4773,6 +4931,7 @@
                     break;
                 }
                 case CMD_INSTALL_PACKET_FILTER: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if (mContext.getResources().getBoolean(
                             R.bool.config_wifiEnableApfOnNonPrimarySta)
                             || isPrimary()) {
@@ -4783,6 +4942,7 @@
                     break;
                 }
                 case CMD_READ_PACKET_FILTER: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     byte[] packetFilter = null;
                     if (mContext.getResources().getBoolean(
                             R.bool.config_wifiEnableApfOnNonPrimarySta)
@@ -4797,6 +4957,7 @@
                     break;
                 }
                 case CMD_SET_FALLBACK_PACKET_FILTERING: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if ((boolean) message.obj) {
                         mWifiNative.startFilteringMulticastV4Packets(mInterfaceName);
                     } else {
@@ -4805,7 +4966,8 @@
                     break;
                 }
                 case CMD_SET_MAX_DTIM_MULTIPLIER: {
-                    final int maxMultiplier = message.arg1;
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
+                    final int maxMultiplier = message.arg2;
                     final boolean success =
                             mWifiNative.setDtimMultiplier(mInterfaceName, maxMultiplier);
                     if (mVerboseLoggingEnabled) {
@@ -4882,6 +5044,7 @@
 
         switch (message.what) {
             case CMD_PRE_DHCP_ACTION:
+                if (!isFromCurrentIpClientCallbacks(message)) break;
                 handlePreDhcpSetup();
                 break;
             case CMD_PRE_DHCP_ACTION_COMPLETE:
@@ -4911,10 +5074,20 @@
         // Defensive copy to avoid mutating the passed argument
         final WifiConfiguration conf = new WifiConfiguration(currentWifiConfiguration);
         conf.BSSID = currentBssid;
+
+        int band = WifiNetworkSpecifier.getBand(mWifiInfo.getFrequency());
+
+        if (!isPrimary() && mWifiGlobals.isSupportMultiInternetDual5G()
+                && band == ScanResult.WIFI_BAND_5_GHZ) {
+            if (mWifiInfo.getFrequency() <= ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ) {
+                band = ScanResult.WIFI_BAND_5_GHZ_LOW;
+            } else {
+                band = ScanResult.WIFI_BAND_5_GHZ_HIGH;
+            }
+        }
+
         WifiNetworkAgentSpecifier wns =
-                new WifiNetworkAgentSpecifier(conf,
-                        WifiNetworkSpecifier.getBand(mWifiInfo.getFrequency()),
-                        matchLocationSensitiveInformation);
+                new WifiNetworkAgentSpecifier(conf, band, matchLocationSensitiveInformation);
         return wns;
     }
 
@@ -5031,6 +5204,8 @@
         final VcnNetworkPolicyResult vcnNetworkPolicy =
                 mVcnManager.applyVcnNetworkPolicy(networkCapabilities, mLinkProperties);
         if (vcnNetworkPolicy.isTeardownRequested()) {
+            mFrameworkDisconnectReasonOverride =
+                    WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_VNC_REQUEST;
             sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_VCN_REQUEST);
         }
         final NetworkCapabilities vcnCapability = vcnNetworkPolicy.getNetworkCapabilities();
@@ -5477,6 +5652,7 @@
                     if (config == null) {
                         logw("Connected to unknown networkId " + mLastNetworkId
                                 + ", disconnecting...");
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_UNKNOWN_NETWORK;
                         sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_UNKNOWN_NETWORK);
                         break;
                     }
@@ -5531,7 +5707,7 @@
                             // network. Consider that older releases might not be able to have
                             // the vendor partition updated, only update to native service on T
                             // or newer.
-                            if (SdkLevel.isAtLeastT()) {
+                            if (mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)) {
                                 mWifiNative.setEapAnonymousIdentity(mInterfaceName,
                                         anonymousIdentity, updateToNativeService);
                             }
@@ -5595,7 +5771,7 @@
                         log("OOB pseudonym is not applied.");
                         break;
                     }
-                    if (SdkLevel.isAtLeastT()) {
+                    if (mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)) {
                         log("send OOB pseudonym to supplicant");
                         String pseudonym = (String) message.obj;
                         mWifiNative.setEapAnonymousIdentity(mInterfaceName,
@@ -5658,7 +5834,8 @@
                         }
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
-                                WifiMetricsProto.ConnectionEvent.HLF_NONE, level2FailureReason);
+                                WifiMetricsProto.ConnectionEvent.HLF_NONE, level2FailureReason,
+                                eventInfo.reasonCode);
                     }
                     handleNetworkDisconnect(newConnectionInProgress, eventInfo.reasonCode);
                     if (!newConnectionInProgress) {
@@ -5730,6 +5907,7 @@
                     // If TOFU is not supported and the network is already connected, this will
                     // disconnect the network.
                     if (disconnectRequired) {
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED;
                         sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED);
                     }
                     break;
@@ -5838,9 +6016,20 @@
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_NETWORK_NOT_FOUND,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
                         handleNetworkDisconnect(false,
                                 WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__UNSPECIFIED);
+                        // TODO(b/302728081): remove the code once the issue is resolved.
+                        if (mDeviceConfigFacade.isAbnormalDisconnectionBugreportEnabled()
+                                && mTargetWifiConfiguration.enterpriseConfig != null
+                                && mTargetWifiConfiguration.enterpriseConfig
+                                        .isAuthenticationSimBased()
+                                && mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(
+                                        mTargetWifiConfiguration.carrierId)) {
+                            String bugTitle = "Wi-Fi BugReport: suspicious NETWORK_NOT_FOUND";
+                            String bugDetail = "Detect abnormal NETWORK_NOT_FOUND error";
+                            mWifiDiagnostics.takeBugReport(bugTitle, bugDetail);
+                        }
                         transitionTo(mDisconnectedState); // End of connection attempt.
                     }
                     break;
@@ -5887,7 +6076,7 @@
                                     ? WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT
                                     : WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            level2FailureReason);
+                            level2FailureReason, timedOut ? 0 : statusCode);
 
                     if (level2FailureReason != WifiMetricsProto.ConnectionEvent
                             .ASSOCIATION_REJECTION_AP_UNABLE_TO_HANDLE_NEW_STA
@@ -5988,7 +6177,7 @@
                     reportConnectionAttemptEnd(
                             WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            level2FailureReason);
+                            level2FailureReason, errorCode);
                     if (reasonCode != WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD && reasonCode
                             != WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE) {
                         mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
@@ -6084,7 +6273,7 @@
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_NO_RESPONSE,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
                         handleNetworkDisconnect(false,
                                 WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__CONNECTING_WATCHDOG_TIMER);
                         transitionTo(mDisconnectedState);
@@ -6110,10 +6299,11 @@
                 }
                 case WifiMonitor.TOFU_CERTIFICATE_EVENT: {
                     if (null == mTargetWifiConfiguration) break;
+                    int networkId = message.arg1;
                     final int certificateDepth = message.arg2;
                     final CertificateEventInfo eventInfo = (CertificateEventInfo) message.obj;
                     if (!mInsecureEapNetworkHandler.addPendingCertificate(
-                            mTargetWifiConfiguration.SSID, certificateDepth, eventInfo)) {
+                            networkId, certificateDepth, eventInfo)) {
                         Log.d(TAG, "Cannot set pending cert.");
                     }
                     // Launch user approval upon receiving the server certificate and disconnect
@@ -6121,6 +6311,7 @@
                             .startUserApprovalIfNecessary(mIsUserSelected)) {
                         // In the TOFU flow, the user approval dialog is now displayed and the
                         // network remains disconnected and disabled until it is approved.
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED;
                         sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED);
                         mLeafCertSent = true;
                     }
@@ -6145,6 +6336,14 @@
 
         @Override
         public void enterImpl() {
+            final WifiConfiguration config = getConnectedWifiConfigurationInternal();
+            if (config == null) {
+                logw("Connected to a network that's already been removed " + mLastNetworkId
+                        + ", disconnecting...");
+                sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_UNKNOWN_NETWORK);
+                return;
+            }
+
             mRssiPollToken++;
             if (mEnableRssiPolling) {
                 if (isPrimary()) {
@@ -6153,11 +6352,8 @@
                 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
             }
             sendNetworkChangeBroadcast(DetailedState.CONNECTING);
-
             // If this network was explicitly selected by the user, evaluate whether to inform
             // ConnectivityService of that fact so the system can treat it appropriately.
-            final WifiConfiguration config = getConnectedWifiConfigurationInternal();
-
             final NetworkAgentConfig naConfig = getNetworkAgentConfigInternal(config);
             final NetworkCapabilities nc = getCapabilities(
                     getConnectedWifiConfigurationInternal(), getConnectedBssidInternal());
@@ -6221,6 +6417,7 @@
 
             switch (message.what) {
                 case CMD_PRE_DHCP_ACTION: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     handlePreDhcpSetup();
                     break;
                 }
@@ -6231,6 +6428,7 @@
                     break;
                 }
                 case CMD_POST_DHCP_ACTION: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     handlePostDhcpSetup();
                     // We advance to mL3ConnectedState because IpClient will also send a
                     // CMD_IPV4_PROVISIONING_SUCCESS message, which calls handleIPv4Success(),
@@ -6239,11 +6437,13 @@
                     break;
                 }
                 case CMD_IPV4_PROVISIONING_SUCCESS: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     handleIPv4Success((DhcpResultsParcelable) message.obj);
                     sendNetworkChangeBroadcastWithCurrentState();
                     break;
                 }
                 case CMD_IPV4_PROVISIONING_FAILURE: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     handleIPv4Failure();
                     mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
                             getConnectingSsidInternal(),
@@ -6253,29 +6453,31 @@
                     break;
                 }
                 case CMD_IP_CONFIGURATION_SUCCESSFUL: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if (getConnectedWifiConfigurationInternal() == null || mNetworkAgent == null) {
                         // The current config may have been removed while we were connecting,
                         // trigger a disconnect to clear up state.
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_REMOVED;
                         mWifiNative.disconnect(mInterfaceName);
                     } else {
                         handleSuccessfulIpConfiguration();
-                        sendConnectedState();
                         transitionTo(mL3ConnectedState);
                     }
                     break;
                 }
                 case CMD_IP_CONFIGURATION_LOST: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     // Get Link layer stats so that we get fresh tx packet counters.
                     getWifiLinkLayerStats();
                     handleIpConfigurationLost();
                     reportConnectionAttemptEnd(
                             WifiMetrics.ConnectionEvent.FAILURE_DHCP,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
                     mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
                             getConnectingSsidInternal(),
                             (mLastBssid == null) ? mTargetBssid : mLastBssid,
@@ -6287,6 +6489,7 @@
                     break;
                 }
                 case CMD_IP_REACHABILITY_LOST: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
                     mWifiDiagnostics.triggerBugReportDataCapture(
                             WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST);
@@ -6296,13 +6499,14 @@
                             WifiUsabilityStats.LABEL_BAD,
                             WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1);
                     if (mWifiGlobals.getIpReachabilityDisconnectEnabled()) {
-                        handleIpReachabilityLost();
+                        handleIpReachabilityLost(-1);
                     } else {
                         logd("CMD_IP_REACHABILITY_LOST but disconnect disabled -- ignore");
                     }
                     break;
                 }
                 case CMD_IP_REACHABILITY_FAILURE: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     mWifiDiagnostics.triggerBugReportDataCapture(
                             WifiDiagnostics.REPORT_REASON_REACHABILITY_FAILURE);
                     handleIpReachabilityFailure((ReachabilityLossInfoParcelable) message.obj);
@@ -6312,6 +6516,7 @@
                     if (mWifiP2pConnection.shouldTemporarilyDisconnectWifi()) {
                         mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT,
                                 StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
+                        mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_P2P_REQUESTED_DISCONNECT;
                         mWifiNative.disconnect(mInterfaceName);
                     }
                     break;
@@ -6320,6 +6525,7 @@
                     NetworkConnectionEventInfo connectionInfo =
                             (NetworkConnectionEventInfo) message.obj;
                     mLastNetworkId = connectionInfo.networkId;
+                    mWifiMetrics.onRoamComplete();
                     handleNetworkConnectionEventInfo(
                             getConnectedWifiConfigurationInternal(), connectionInfo);
                     mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
@@ -6345,8 +6551,17 @@
                 case WifiMonitor.BSS_FREQUENCY_CHANGED_EVENT: {
                     int newFrequency = message.arg1;
                     if (newFrequency > 0) {
-                        if (mWifiInfo.getFrequency() != newFrequency) {
+                        boolean isNeedUpdate = false;
+                        if (isMlo()) {
+                            if (updateAssociatedMloLinksFromLinksInfoWhenBssFreqChanged(
+                                    newFrequency)) {
+                                isNeedUpdate = true;
+                            }
+                        } else if (mWifiInfo.getFrequency() != newFrequency) {
                             mWifiInfo.setFrequency(newFrequency);
+                            isNeedUpdate = true;
+                        }
+                        if (isNeedUpdate) {
                             updateCurrentConnectionInfo();
                             updateCapabilities();
                         }
@@ -6653,7 +6868,7 @@
     // messages in this state except CMD_IPCLIENT_CREATED, recreation of a new IpClient
     // guarantees the out-of-date callbacks will be ignored, otherwise, it's not easy to
     // differentiate callbacks which comes from new IpClient or legacy IpClient. So far
-    // only transit to this state iff IP reachability gets lost post roam.
+    // only transit to this state iff IP reachability gets lost due to NUD failure.
     class WaitBeforeL3ProvisioningState extends RunnerState {
         WaitBeforeL3ProvisioningState(int threshold) {
             super(threshold, mWifiInjector.getWifiHandlerLocalLog());
@@ -6662,7 +6877,6 @@
         @Override
         public void enterImpl() {
             // Recreate a new IpClient instance.
-            maybeShutdownIpclient();
             makeIpClient();
 
             // Given that {@link IpClientCallbacks#awaitCreation} is invoked when making a
@@ -6681,6 +6895,7 @@
         public boolean processMessageImpl(Message message) {
             switch(message.what) {
                 case CMD_IPCLIENT_CREATED: {
+                    if (!isFromCurrentIpClientCallbacks(message)) break;
                     mIpClient = (IpClientManager) message.obj;
                     transitionTo(mL3ProvisioningState);
                     break;
@@ -6718,6 +6933,17 @@
     }
 
     class L3ProvisioningState extends RunnerState {
+
+        private void onL3ProvisioningTimeout() {
+            logi("l3Provisioning Timeout, Configuring the network as local-only");
+            mIpProvisioningTimedOut = true;
+            mWifiConnectivityManager.handleConnectionStateChanged(
+                    mClientModeManager,
+                    WifiConnectivityManager.WIFI_STATE_CONNECTED);
+            mWifiConfigManager.setIpProvisioningTimedOut(mLastNetworkId, true);
+            sendNetworkChangeBroadcast(DetailedState.CONNECTED);
+        };
+
         L3ProvisioningState(int threshold) {
             super(threshold, mWifiInjector.getWifiHandlerLocalLog());
         }
@@ -6725,10 +6951,18 @@
         @Override
         public void enterImpl() {
             startL3Provisioning();
+            if (mContext.getResources().getBoolean(
+                    R.bool.config_wifiRemainConnectedAfterIpProvisionTimeout)) {
+                sendMessageDelayed(obtainMessage(CMD_IP_PROVISIONING_TIMEOUT),
+                        WAIT_FOR_L3_PROVISIONING_TIMEOUT_MS);
+            }
         }
 
         @Override
         public void exitImpl() {
+            mIpProvisioningTimedOut = false;
+            removeMessages(CMD_IP_PROVISIONING_TIMEOUT);
+            mWifiConfigManager.setIpProvisioningTimedOut(mLastNetworkId, false);
         }
 
         @Override
@@ -6753,6 +6987,10 @@
                     handleStatus = NOT_HANDLED;
                     break;
                 }
+                case CMD_IP_PROVISIONING_TIMEOUT: {
+                    onL3ProvisioningTimeout();
+                    break;
+                }
                 default: {
                     handleStatus = NOT_HANDLED;
                     break;
@@ -6882,7 +7120,7 @@
                                 WifiMetrics.ConnectionEvent.FAILURE_ROAM_TIMEOUT,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
                                 WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN,
-                                mWifiInfo.getFrequency());
+                                mWifiInfo.getFrequency(), 0);
                         mRoamFailCount++;
                         handleNetworkDisconnect(false,
                                 WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__ROAM_WATCHDOG_TIMER);
@@ -6913,7 +7151,7 @@
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
 
                         // We must clear the config BSSID, as the wifi chipset may decide to roam
                         // from this point on and having the BSSID specified by QNS would cause
@@ -6986,7 +7224,7 @@
             reportConnectionAttemptEnd(
                     WifiMetrics.ConnectionEvent.FAILURE_NONE,
                     WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
             mWifiConnectivityManager.handleConnectionStateChanged(
                     mClientModeManager,
                     WifiConnectivityManager.WIFI_STATE_CONNECTED);
@@ -7012,6 +7250,7 @@
             // So only record success here.
             mWifiMetrics.noteFirstL3ConnectionAfterBoot(true);
             updateCurrentConnectionInfo();
+            sendConnectedState();
         }
 
         @Override
@@ -7088,10 +7327,16 @@
                                         mClientModeManager);
                                 mWifiConfigManager.incrementNetworkNoInternetAccessReports(
                                         config.networkId);
-                                // If this was not recently selected by the user, update network
-                                // selection status to temporarily disable the network.
-                                if (!isRecentlySelectedByTheUser(config)
+                                if (!config.getNetworkSelectionStatus()
+                                        .hasEverValidatedInternetAccess()
                                         && !config.noInternetAccessExpected) {
+                                    mWifiConfigManager.updateNetworkSelectionStatus(
+                                            config.networkId,
+                                            DISABLED_NO_INTERNET_PERMANENT);
+                                } else if (!isRecentlySelectedByTheUser(config)
+                                        && !config.noInternetAccessExpected) {
+                                    // If this was not recently selected by the user, update network
+                                    // selection status to temporarily disable the network.
                                     if (config.getNetworkSelectionStatus()
                                             .getNetworkSelectionDisableReason()
                                             != DISABLED_NO_INTERNET_PERMANENT) {
@@ -7138,7 +7383,8 @@
                                 mTermsAndConditionsUrl = null;
                                 LinkProperties newLp = new LinkProperties(mLinkProperties);
                                 addPasspointInfoToLinkProperties(newLp);
-                                sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
+                                sendMessage(CMD_UPDATE_LINKPROPERTIES,
+                                        mIpClientCallbacks.getCallbackIndex(), 0, newLp);
                                 mWifiMetrics
                                         .incrementTotalNumberOfPasspointAcceptanceOfTermsAndConditions();
                             }
@@ -7160,7 +7406,8 @@
                     reportConnectionAttemptEnd(
                             WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN,
+                            eventInfo.reasonCode);
                     if (unexpectedDisconnectedReason(eventInfo.reasonCode)) {
                         mWifiDiagnostics.triggerBugReportDataCapture(
                                 WifiDiagnostics.REPORT_REASON_UNEXPECTED_DISCONNECT);
@@ -7227,7 +7474,7 @@
                         reportConnectionAttemptEnd(
                                 WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
                                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
+                                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
                         mMessageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
                         break;
                     }
@@ -7873,6 +8120,8 @@
                 ? mLastL2KeyAndGroupHint.second : null;
         final Layer2Information layer2Info = new Layer2Information(l2Key, groupHint,
                 currentBssid);
+        final boolean mRemainConnectedAfterIpProvisionTimeout = mContext.getResources().getBoolean(
+                R.bool.config_wifiRemainConnectedAfterIpProvisionTimeout);
 
         if (isFilsConnection) {
             stopIpClient();
@@ -7883,11 +8132,13 @@
             setConfigurationsPriorToIpClientProvisioning(config);
             final ProvisioningConfiguration.Builder prov =
                     new ProvisioningConfiguration.Builder()
-                    .withPreDhcpAction()
-                    .withPreconnection()
-                    .withDisplayName(config.SSID)
-                    .withCreatorUid(config.creatorUid)
-                    .withLayer2Information(layer2Info);
+                            .withPreDhcpAction()
+                            .withPreconnection()
+                            .withDisplayName(config.SSID)
+                            .withCreatorUid(config.creatorUid)
+                            .withLayer2Information(layer2Info)
+                            .withProvisioningTimeoutMs(mRemainConnectedAfterIpProvisionTimeout ? 0 :
+                                    PROVISIONING_TIMEOUT_FILS_CONNECTION_MS);
             if (mContext.getResources().getBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta)
                     || isPrimary()) {
                 // unclear if the native layer will return the correct non-capabilities if APF is
@@ -7961,6 +8212,9 @@
                 prov.withRandomMacAddress();
             }
             prov.withDhcpOptions(convertToInternalDhcpOptions(options));
+            if (mRemainConnectedAfterIpProvisionTimeout) {
+                prov.withProvisioningTimeoutMs(0);
+            }
             mIpClient.startProvisioning(prov.build());
         }
 
@@ -8004,8 +8258,8 @@
 
     @Override
     public boolean setWifiConnectedNetworkScorer(IBinder binder,
-            IWifiConnectedNetworkScorer scorer) {
-        return mWifiScoreReport.setWifiConnectedNetworkScorer(binder, scorer);
+            IWifiConnectedNetworkScorer scorer, int callerUid) {
+        return mWifiScoreReport.setWifiConnectedNetworkScorer(binder, scorer, callerUid);
     }
 
     @Override
@@ -8078,6 +8332,54 @@
         return status == WifiNative.SET_FIRMWARE_ROAMING_SUCCESS;
     }
 
+    private void considerChangingFirmwareRoaming(boolean isIdle) {
+        if (mClientModeManager.getRole() != ROLE_CLIENT_PRIMARY) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Idle mode changed: iface " + mInterfaceName + " is not primary.");
+            }
+            return;
+        }
+        if (!mWifiGlobals.isDisableFirmwareRoamingInIdleMode()
+                || !mWifiConnectivityHelper.isFirmwareRoamingSupported()) {
+            // feature not enabled, or firmware roaming not supported - no need to continue.
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Idle mode changed: iface " + mInterfaceName
+                        + " firmware roaming not supported");
+            }
+            return;
+        }
+        if (isIdle) {
+            // disable firmware roaming if in idle mode
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Idle mode changed: iface " + mInterfaceName
+                        + " disabling roaming");
+            }
+            enableRoaming(false);
+            return;
+        }
+        // Exiting idle mode so re-enable firmware roaming, but only if the current use-case is
+        // not the local-only use-case. The local-only use-case requires firmware roaming to be
+        // always disabled.
+        WifiConfiguration config = getConnectedWifiConfigurationInternal();
+        if (config == null) {
+            config = getConnectingWifiConfigurationInternal();
+        }
+        if (config != null && getClientRoleForMetrics(config)
+                == WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_LOCAL_ONLY) {
+            return;
+        }
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "Idle mode changed: iface " + mInterfaceName
+                    + " enabling roaming");
+        }
+        enableRoaming(true);
+    }
+
+    @Override
+    public void onIdleModeChanged(boolean isIdle) {
+        considerChangingFirmwareRoaming(isIdle);
+    }
+
     @Override
     public boolean setCountryCode(String countryCode) {
         return mWifiNative.setStaCountryCode(mInterfaceName, countryCode);
diff --git a/service/java/com/android/server/wifi/ConcreteClientModeManager.java b/service/java/com/android/server/wifi/ConcreteClientModeManager.java
index 0e1e23b..79ea6e8 100644
--- a/service/java/com/android/server/wifi/ConcreteClientModeManager.java
+++ b/service/java/com/android/server/wifi/ConcreteClientModeManager.java
@@ -407,6 +407,13 @@
                 if (!mWifiNative.switchClientInterfaceToScanMode(
                         mClientInterfaceName, mTargetRoleChangeInfo.requestorWs)) {
                     mModeListener.onStartFailure(ConcreteClientModeManager.this);
+                    updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN,
+                            WifiManager.WIFI_STATE_DISABLING);
+                    updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED,
+                            WifiManager.WIFI_STATE_UNKNOWN);
+                    takeBugReportInterfaceFailureIfNeeded(
+                            "Wi-Fi BugReport (STA interface failure):",
+                            "Fail to switch to scan-only mode in started state");
                 } else {
                     mStateMachine.sendMessage(
                             ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE,
@@ -1416,8 +1423,8 @@
 
     @Override
     public boolean setWifiConnectedNetworkScorer(
-            IBinder binder, IWifiConnectedNetworkScorer scorer) {
-        return getClientMode().setWifiConnectedNetworkScorer(binder, scorer);
+            IBinder binder, IWifiConnectedNetworkScorer scorer, int callerUid) {
+        return getClientMode().setWifiConnectedNetworkScorer(binder, scorer, callerUid);
     }
 
     @Override
@@ -1594,6 +1601,11 @@
     }
 
     @Override
+    public boolean isIpProvisioningTimedOut() {
+        return getClientMode().isIpProvisioningTimedOut();
+    }
+
+    @Override
     public boolean isSupplicantTransientState() {
         return getClientMode().isSupplicantTransientState();
     }
@@ -1701,4 +1713,9 @@
     public boolean isMlo() {
         return getClientMode().isMlo();
     }
+
+    @Override
+    public void onIdleModeChanged(boolean isIdle) {
+        getClientMode().onIdleModeChanged(isIdle);
+    }
 }
diff --git a/service/java/com/android/server/wifi/DeviceConfigFacade.java b/service/java/com/android/server/wifi/DeviceConfigFacade.java
index a400755..f908046 100644
--- a/service/java/com/android/server/wifi/DeviceConfigFacade.java
+++ b/service/java/com/android/server/wifi/DeviceConfigFacade.java
@@ -25,6 +25,7 @@
 import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.wifi.flags.FeatureFlags;
 
 import java.util.Collections;
 import java.util.Optional;
@@ -40,6 +41,7 @@
     private final WifiMetrics mWifiMetrics;
 
     private static final String NAMESPACE = "wifi";
+    private final FeatureFlags mFeatureFlags;
 
     // Default values of fields
     @VisibleForTesting
@@ -225,6 +227,7 @@
         mContext = context;
         mWifiMetrics = wifiMetrics;
         mWifiHandler = handler;
+        mFeatureFlags = new com.android.wifi.flags.FeatureFlagsImpl();
         updateDeviceConfigFlags();
         DeviceConfig.addOnPropertiesChangedListener(
                 NAMESPACE,
@@ -409,13 +412,13 @@
         }
         mOobPseudonymEnabled = Optional.of(oobPseudonymEnabled);
         mApplicationQosPolicyApiEnabled = DeviceConfig.getBoolean(NAMESPACE,
-                "application_qos_policy_api_enabled", false);
-        mAdjustPollRssiIntervalEnabled = DeviceConfig.getBoolean(NAMESPACE,
-                "adjust_poll_rssi_interval_enabled", true);
+                "application_qos_policy_api_enabled", true);
+        mAdjustPollRssiIntervalEnabled =
+                DeviceConfig.getBoolean(NAMESPACE, "adjust_poll_rssi_interval_enabled", false);
         mSoftwarePnoEnabled = DeviceConfig.getBoolean(NAMESPACE,
                 "software_pno_enabled", false);
         mIncludePasspointSsidsInPnoScans = DeviceConfig.getBoolean(NAMESPACE,
-                "include_passpoint_ssids_in_pno_scans", false);
+                "include_passpoint_ssids_in_pno_scans", true);
         mHandleRssiOrganicKernelFailuresEnabled = DeviceConfig.getBoolean(NAMESPACE,
                 "handle_rssi_organic_kernel_failures_enabled", true);
     }
@@ -914,4 +917,8 @@
             Consumer<Boolean> listener) {
         mOobPseudonymFeatureFlagChangedListener = listener;
     }
+
+    public FeatureFlags getFeatureFlags() {
+        return mFeatureFlags;
+    }
 }
diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java
index 4aeb156..8ef3ce1 100644
--- a/service/java/com/android/server/wifi/DppManager.java
+++ b/service/java/com/android/server/wifi/DppManager.java
@@ -49,6 +49,7 @@
 import com.android.server.wifi.util.ApConfigUtil;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -147,16 +148,16 @@
         });
     }
 
-    private static String encodeStringToHex(String str) {
+    private static String encodeStringToUtf8Hex(String str) {
         if ((str.length() > 1) && (str.charAt(0) == '"') && (str.charAt(str.length() - 1) == '"')) {
             // Remove the surrounding quotes
             str = str.substring(1, str.length() - 1);
 
             // Convert to Hex
-            char[] charsArray = str.toCharArray();
+            byte[] bytesArray = str.getBytes(StandardCharsets.UTF_8);
             StringBuffer hexBuffer = new StringBuffer();
-            for (int i = 0; i < charsArray.length; i++) {
-                hexBuffer.append(Integer.toHexString((int) charsArray[i]));
+            for (int i = 0; i < bytesArray.length; i++) {
+                hexBuffer.append(Integer.toHexString(0xFF & bytesArray[i]));
             }
             return hexBuffer.toString();
         }
@@ -368,14 +369,14 @@
         String ssidEncoded;
         WifiSsid originalSsid = mWifiInjector.getSsidTranslator().getOriginalSsid(selectedNetwork);
         if (originalSsid != null) {
-            ssidEncoded = encodeStringToHex(originalSsid.toString());
+            ssidEncoded = encodeStringToUtf8Hex(originalSsid.toString());
         } else {
-            ssidEncoded = encodeStringToHex(selectedNetwork.SSID);
+            ssidEncoded = encodeStringToUtf8Hex(selectedNetwork.SSID);
         }
         String passwordEncoded = null;
 
         if (password != null) {
-            passwordEncoded = encodeStringToHex(selectedNetwork.preSharedKey);
+            passwordEncoded = encodeStringToUtf8Hex(selectedNetwork.preSharedKey);
         }
 
         if (!mWifiNative.startDppConfiguratorInitiator(mClientIfaceName,
diff --git a/service/java/com/android/server/wifi/EapFailureNotifier.java b/service/java/com/android/server/wifi/EapFailureNotifier.java
index 0eee70e..64143eb 100644
--- a/service/java/com/android/server/wifi/EapFailureNotifier.java
+++ b/service/java/com/android/server/wifi/EapFailureNotifier.java
@@ -58,6 +58,7 @@
     private final WifiNotificationManager mNotificationManager;
     private final FrameworkFacade mFrameworkFacade;
     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
+    private final WifiGlobals mWifiGlobals;
 
     // Unique ID associated with the notification.
     public static final int NOTIFICATION_ID = SystemMessage.NOTE_WIFI_EAP_FAILURE;
@@ -65,11 +66,13 @@
 
     public EapFailureNotifier(WifiContext context, FrameworkFacade frameworkFacade,
             WifiCarrierInfoManager wifiCarrierInfoManager,
-            WifiNotificationManager wifiNotificationManager) {
+            WifiNotificationManager wifiNotificationManager,
+            WifiGlobals wifiGlobals) {
         mContext = context;
         mFrameworkFacade = frameworkFacade;
         mWifiCarrierInfoManager = wifiCarrierInfoManager;
         mNotificationManager = wifiNotificationManager;
+        mWifiGlobals = wifiGlobals;
     }
 
     /**
@@ -87,10 +90,12 @@
             // only consider non-negative error codes for carrier-specific error messages.
             return null;
         }
+        int carrierId = config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID
+                ? mWifiCarrierInfoManager.getDefaultDataSimCarrierId()
+                : config.carrierId;
         WifiStringResourceWrapper sr = mContext.getStringResourceWrapper(
                 mWifiCarrierInfoManager.getBestMatchSubscriptionId(config),
-                config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID
-                        ? mWifiCarrierInfoManager.getDefaultDataSimCarrierId() : config.carrierId);
+                carrierId);
         String errorMessage = sr.getString(ERROR_MESSAGE_OVERLAY_PREFIX + errorCode, config.SSID);
         if (SdkLevel.isAtLeastT()) {
             if (errorMessage == null) {
@@ -105,7 +110,16 @@
         WifiBlocklistMonitor.CarrierSpecificEapFailureConfig eapFailureConfig =
                 new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(
                         sr.getInt(CONFIG_EAP_FAILURE_DISABLE_THRESHOLD, 1),
-                        sr.getInt(CONFIG_EAP_FAILURE_DISABLE_DURATION, -1));
+                        sr.getInt(CONFIG_EAP_FAILURE_DISABLE_DURATION, -1),
+                        true);
+        if (SdkLevel.isAtLeastU()) {
+            WifiBlocklistMonitor.CarrierSpecificEapFailureConfig carrierSpecificOverride =
+                    mWifiGlobals.getCarrierSpecificEapFailureConfig(carrierId, errorCode);
+            if (carrierSpecificOverride != null) {
+                eapFailureConfig = carrierSpecificOverride;
+            }
+        }
+
         StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
         for (StatusBarNotification activeNotification : activeNotifications) {
             if ((activeNotification.getId() == NOTIFICATION_ID)
@@ -114,7 +128,7 @@
             }
         }
 
-        if (showNotification) {
+        if (showNotification && eapFailureConfig.displayNotification) {
             showNotification(errorMessage, config.SSID);
         }
         return eapFailureConfig;
diff --git a/service/java/com/android/server/wifi/ExtendedWifiInfo.java b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
index 2f9f016..ec70b17 100644
--- a/service/java/com/android/server/wifi/ExtendedWifiInfo.java
+++ b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
@@ -17,6 +17,7 @@
 package com.android.server.wifi;
 
 import android.annotation.NonNull;
+import android.net.wifi.MloLink;
 import android.net.wifi.WifiInfo;
 
 /**
@@ -61,7 +62,19 @@
         long txretries = stats.retries_be + stats.retries_bk + stats.retries_vi + stats.retries_vo;
         long txbad = stats.lostmpdu_be + stats.lostmpdu_bk + stats.lostmpdu_vi + stats.lostmpdu_vo;
         long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
-        update(SOURCE_LLSTATS, txgood, txretries, txbad, rxgood, timeStamp);
+        updateWifiInfoRates(SOURCE_LLSTATS, txgood, txretries, txbad, rxgood, timeStamp);
+        // Process link stats if available.
+        if (stats.links == null) return;
+        for (WifiLinkLayerStats.LinkSpecificStats link : stats.links) {
+            updateMloRates(
+                    link.link_id,
+                    SOURCE_LLSTATS,
+                    link.txmpdu_be + link.txmpdu_bk + link.txmpdu_vi + link.txmpdu_vo,
+                    link.retries_be + link.retries_bk + link.retries_vi + link.retries_vo,
+                    link.lostmpdu_be + link.lostmpdu_bk + link.lostmpdu_vi + link.lostmpdu_vo,
+                    link.rxmpdu_be + link.rxmpdu_bk + link.rxmpdu_vi + link.rxmpdu_vo,
+                    timeStamp);
+        }
     }
 
     /**
@@ -69,11 +82,61 @@
      * at the Wifi HAL
      */
     public void updatePacketRates(long txPackets, long rxPackets, long timeStamp) {
-        update(SOURCE_TRAFFIC_COUNTERS, txPackets, 0, 0, rxPackets, timeStamp);
+        updateWifiInfoRates(SOURCE_TRAFFIC_COUNTERS, txPackets, 0, 0, rxPackets, timeStamp);
     }
 
-    private void update(int source, long txgood, long txretries, long txbad, long rxgood,
+    private void updateMloRates(
+            int linkId,
+            int source,
+            long txgood,
+            long txretries,
+            long txbad,
+            long rxgood,
             long timeStamp) {
+        MloLink link = getAffiliatedMloLink(linkId);
+        if (link == null) return;
+        if (source == mLastSource
+                && link.lastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
+                && link.lastPacketCountUpdateTimeStamp < timeStamp
+                && link.txBad <= txbad
+                && link.txSuccess <= txgood
+                && link.rxSuccess <= rxgood
+                && link.txRetries <= txretries) {
+            long timeDelta = timeStamp - link.lastPacketCountUpdateTimeStamp;
+            double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
+            double currentSampleWeight = 1.0 - lastSampleWeight;
+
+            link.setLostTxPacketsPerSecond(
+                    link.getLostTxPacketsPerSecond() * lastSampleWeight
+                            + (txbad - link.txBad) * 1000.0 / timeDelta * currentSampleWeight);
+            link.setSuccessfulTxPacketsPerSecond(
+                    link.getSuccessfulTxPacketsPerSecond() * lastSampleWeight
+                            + (txgood - link.txSuccess) * 1000.0 / timeDelta * currentSampleWeight);
+            link.setSuccessfulRxPacketsPerSecond(
+                    link.getSuccessfulRxPacketsPerSecond() * lastSampleWeight
+                            + (rxgood - link.rxSuccess) * 1000.0 / timeDelta * currentSampleWeight);
+            link.setRetriedTxPacketsRate(
+                    link.getRetriedTxPacketsPerSecond() * lastSampleWeight
+                            + (txretries - link.txRetries)
+                                    * 1000.0
+                                    / timeDelta
+                                    * currentSampleWeight);
+        } else {
+            link.setLostTxPacketsPerSecond(0);
+            link.setSuccessfulTxPacketsPerSecond(0);
+            link.setSuccessfulRxPacketsPerSecond(0);
+            link.setRetriedTxPacketsRate(0);
+            mLastSource = source;
+        }
+        link.txBad = txbad;
+        link.txSuccess = txgood;
+        link.rxSuccess = rxgood;
+        link.txRetries = txretries;
+        link.lastPacketCountUpdateTimeStamp = timeStamp;
+    }
+
+    private void updateWifiInfoRates(
+            int source, long txgood, long txretries, long txbad, long rxgood, long timeStamp) {
         if (source == mLastSource
                 && mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
                 && mLastPacketCountUpdateTimeStamp < timeStamp
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index 9735bf9..3e950d9 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -29,6 +29,7 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.net.NetworkInfo;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.p2p.WifiP2pManager;
@@ -53,6 +54,7 @@
 import com.android.server.wifi.hal.WifiRttController;
 import com.android.server.wifi.hal.WifiStaIface;
 import com.android.server.wifi.util.WorkSourceHelper;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import org.json.JSONArray;
@@ -77,6 +79,7 @@
 public class HalDeviceManager {
     private static final String TAG = "HalDevMgr";
     private static final boolean VDBG = false;
+    private final FeatureFlags mFeatureFlags;
     private boolean mDbg = false;
 
     public static final long CHIP_CAPABILITY_ANY = 0L;
@@ -151,6 +154,7 @@
         mContext = context;
         mClock = clock;
         mWifiInjector = wifiInjector;
+        mFeatureFlags = mWifiInjector.getDeviceConfigFacade().getFeatureFlags();
         mEventHandler = handler;
         mIWifiDeathRecipient = new WifiDeathRecipient();
         mWifiHal = getWifiHalMockable(context, wifiInjector);
@@ -320,7 +324,7 @@
             return null;
         }
         WifiStaIface staIface = (WifiStaIface) createIface(HDM_CREATE_IFACE_STA,
-                requiredChipCapabilities, destroyedListener, handler, requestorWs);
+                requiredChipCapabilities, destroyedListener, handler, requestorWs, null);
         if (staIface != null) {
             mClientModeManagers.put(getName(staIface), concreteClientModeManager);
         }
@@ -358,14 +362,14 @@
             long requiredChipCapabilities,
             @Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler,
             @NonNull WorkSource requestorWs, boolean isBridged,
-            @NonNull SoftApManager softApManager) {
+            @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) {
         if (softApManager == null) {
             Log.e(TAG, "Cannot create AP Iface with null SoftApManager");
             return null;
         }
         WifiApIface apIface = (WifiApIface) createIface(isBridged ? HDM_CREATE_IFACE_AP_BRIDGE
                 : HDM_CREATE_IFACE_AP, requiredChipCapabilities, destroyedListener,
-                handler, requestorWs);
+                handler, requestorWs, vendorData);
         if (apIface != null) {
             mSoftApManagers.put(getName(apIface), softApManager);
         }
@@ -380,7 +384,7 @@
             @Nullable InterfaceDestroyedListener destroyedListener,
             @Nullable Handler handler, @NonNull WorkSource requestorWs) {
         WifiP2pIface iface = (WifiP2pIface) createIface(HDM_CREATE_IFACE_P2P,
-                requiredChipCapabilities, destroyedListener, handler, requestorWs);
+                requiredChipCapabilities, destroyedListener, handler, requestorWs, null);
         if (iface == null) {
             return null;
         }
@@ -407,7 +411,7 @@
     public WifiNanIface createNanIface(@Nullable InterfaceDestroyedListener destroyedListener,
             @Nullable Handler handler, @NonNull WorkSource requestorWs) {
         return (WifiNanIface) createIface(HDM_CREATE_IFACE_NAN, CHIP_CAPABILITY_ANY,
-                destroyedListener, handler, requestorWs);
+                destroyedListener, handler, requestorWs, null);
     }
 
     /**
@@ -568,7 +572,7 @@
      * Replace the requestorWs info for the associated info.
      *
      * When a new iface is requested via
-     * {@link #createIface(int, long, InterfaceDestroyedListener, Handler, WorkSource, ConcreteClientModeManager)}, the clients
+     * {@link #createIface(int, long, InterfaceDestroyedListener, Handler, WorkSource, List)}, the clients
      * pass in a worksource which includes all the apps that triggered the iface creation. However,
      * this list of apps can change during the lifetime of the iface (as new apps request the same
      * iface or existing apps release their request for the iface). This API can be invoked multiple
@@ -812,7 +816,7 @@
             Log.d(TAG, "isItPossibleToCreateIface: createIfaceType=" + createIfaceType
                     + ", requiredChipCapabilities=" + requiredChipCapabilities);
         }
-        return reportImpactToCreateIface(createIfaceType, true, requiredChipCapabilities,
+        return getIfacesToDestroyForRequest(createIfaceType, true, requiredChipCapabilities,
                 requestorWs) != null;
     }
 
@@ -831,9 +835,8 @@
     }
 
     /**
-     * Returns the details of what it would take to create the provided Iface requested by the
-     * specified requestor. The details are the list of other interfaces which would have to be
-     * destroyed.
+     * Returns the list of interfaces that would be deleted to create the provided Iface requested
+     * by the specified requestor.
      *
      * Return types imply:
      * - null: interface cannot be created
@@ -848,14 +851,13 @@
      *                                 See the HAL for documentation.
      * @param requestorWs Requestor worksource. This will be used to determine priority of this
      *                    interface using rules based on the requestor app's context.
-     * @return the list of interfaces that would have to be destroyed and their worksource. The
-     * interface type is described using @HdmIfaceTypeForCreation.
+     * @return the list of interfaces that would have to be destroyed.
      */
-    public List<Pair<Integer, WorkSource>> reportImpactToCreateIface(
+    private List<WifiIfaceInfo> getIfacesToDestroyForRequest(
             @HdmIfaceTypeForCreation int createIfaceType, boolean queryForNewInterface,
             long requiredChipCapabilities, WorkSource requestorWs) {
         if (VDBG) {
-            Log.d(TAG, "reportImpactToCreateIface: ifaceType=" + createIfaceType
+            Log.d(TAG, "getIfacesToDestroyForRequest: ifaceType=" + createIfaceType
                     + ", requiredChipCapabilities=" + requiredChipCapabilities
                     + ", requestorWs=" + requestorWs);
         }
@@ -863,18 +865,18 @@
         IfaceCreationData creationData;
         synchronized (mLock) {
             if (!mWifiHal.isInitializationComplete()) {
-                Log.e(TAG, "reportImpactToCreateIface: Wifi Hal is not available");
+                Log.e(TAG, "getIfacesToDestroyForRequest: Wifi Hal is not available");
                 return null;
             }
-            WifiChipInfo[] chipInfos = getAllChipInfo();
+            WifiChipInfo[] chipInfos = getAllChipInfo(false);
             if (chipInfos == null) {
-                Log.e(TAG, "createIface: no chip info found");
+                Log.e(TAG, "getIfacesToDestroyForRequest: no chip info found");
                 stopWifi(); // major error: shutting down
                 return null;
             }
 
             if (!validateInterfaceCacheAndRetrieveRequestorWs(chipInfos)) {
-                Log.e(TAG, "createIface: local cache is invalid!");
+                Log.e(TAG, "getIfacesToDestroyForRequest: local cache is invalid!");
                 stopWifi(); // major error: shutting down
                 return null;
             }
@@ -895,53 +897,63 @@
             return null; // impossible to create requested interface
         }
 
-        List<Pair<Integer, WorkSource>> details = new ArrayList<>();
+        List<WifiIfaceInfo> ifaces = new ArrayList<>();
         boolean isModeConfigNeeded = !creationData.chipInfo.currentModeIdValid
                 || creationData.chipInfo.currentModeId != creationData.chipModeId;
         if (!isModeConfigNeeded && (creationData.interfacesToBeRemovedFirst == null
                 || creationData.interfacesToBeRemovedFirst.isEmpty())) {
             // can create interface w/o deleting any other interfaces
-            return details;
+            return ifaces;
         }
 
         if (isModeConfigNeeded) {
             if (VDBG) {
-                Log.d(TAG, "isItPossibleToCreateIfaceDetails: mode change from - "
+                Log.d(TAG, "getIfacesToDestroyForRequest: mode change from - "
                         + creationData.chipInfo.currentModeId + ", to - "
                         + creationData.chipModeId);
             }
             for (WifiIfaceInfo[] ifaceInfos: creationData.chipInfo.ifaces) {
-                for (WifiIfaceInfo ifaceInfo : ifaceInfos) {
-                    details.add(Pair.create(ifaceInfo.createType,
-                            ifaceInfo.requestorWsHelper.getWorkSource()));
-                }
+                ifaces.addAll(Arrays.asList(ifaceInfos));
             }
         } else {
-            for (WifiIfaceInfo ifaceInfo : creationData.interfacesToBeRemovedFirst) {
-                details.add(Pair.create(ifaceInfo.createType,
-                        ifaceInfo.requestorWsHelper.getWorkSource()));
-            }
+            ifaces.addAll(creationData.interfacesToBeRemovedFirst);
         }
 
-        return details;
+        return ifaces;
     }
 
     /**
-     * See {@link #reportImpactToCreateIface(int, boolean, long, WorkSource)}.
+     * Returns the details of what it would take to create the provided Iface requested by the
+     * specified requestor. The details are the list of other interfaces which would have to be
+     * destroyed.
      *
-     * @param ifaceType Type of iface requested.
+     * Return types imply:
+     * - null: interface cannot be created
+     * - empty list: interface can be crated w/o destroying any other interfaces
+     * - otherwise: a list of interfaces to be destroyed
+     *
+     * @param createIfaceType Type of iface requested.
      * @param queryForNewInterface True: request another interface of the specified type, False: if
      *                             there's already an interface of the specified type then no need
      *                             for further operation.
      * @param requestorWs Requestor worksource. This will be used to determine priority of this
      *                    interface using rules based on the requestor app's context.
-     * @return the list of interfaces that would have to be destroyed and their worksource.
+     * @return the list of interfaces that would have to be destroyed and their worksource. The
+     * interface type is described using @HdmIfaceTypeForCreation.
      */
     public List<Pair<Integer, WorkSource>> reportImpactToCreateIface(
-            @HdmIfaceTypeForCreation int ifaceType, boolean queryForNewInterface,
+            @HdmIfaceTypeForCreation int createIfaceType, boolean queryForNewInterface,
             WorkSource requestorWs) {
-        return reportImpactToCreateIface(ifaceType, queryForNewInterface, CHIP_CAPABILITY_ANY,
-                requestorWs);
+        List<WifiIfaceInfo> ifaces = getIfacesToDestroyForRequest(createIfaceType,
+                queryForNewInterface, CHIP_CAPABILITY_ANY, requestorWs);
+        if (ifaces == null) {
+            return null;
+        }
+        List<Pair<Integer, WorkSource>> impact = new ArrayList<>();
+        for (WifiIfaceInfo iface : ifaces) {
+            impact.add(new Pair<>(iface.createType, iface.requestorWsHelper.getWorkSource()));
+        }
+        return impact;
     }
 
     /**
@@ -950,14 +962,14 @@
      */
     public boolean creatingIfaceWillDeletePrivilegedIface(
             @HdmIfaceTypeForCreation int ifaceType, WorkSource requestorWs) {
-        List<Pair<Integer, WorkSource>> impact =
-                reportImpactToCreateIface(ifaceType, true, requestorWs);
-        if (impact == null) {
+        List<WifiIfaceInfo> ifaces = getIfacesToDestroyForRequest(ifaceType, true,
+                CHIP_CAPABILITY_ANY, requestorWs);
+        if (ifaces == null) {
             return false;
         }
-        for (Pair<Integer, WorkSource> pair : impact) {
-            if (mWifiInjector.makeWsHelper(pair.second).getRequestorWsPriority()
-                    == WorkSourceHelper.PRIORITY_PRIVILEGED) {
+        for (WifiIfaceInfo iface : ifaces) {
+            if (iface.requestorWsHelper.getRequestorWsPriority()
+                    == WorkSourceHelper.PRIORITY_PRIVILEGED && !isDisconnectedP2p(iface)) {
                 return true;
             }
         }
@@ -1123,12 +1135,12 @@
      * Get current information about all the chips in the system: modes, current mode (if any), and
      * any existing interfaces.
      *
-     * Intended to be called for any external iface support related queries. This information is
-     * cached to reduce performance overhead (unlike {@link #getAllChipInfo()}).
+     * <p>Intended to be called for any external iface support related queries. This information is
+     * cached to reduce performance overhead (unlike {@link #getAllChipInfo(boolean)}).
      */
     private WifiChipInfo[] getAllChipInfoCached() {
         if (mCachedWifiChipInfos == null) {
-            mCachedWifiChipInfos = getAllChipInfo();
+            mCachedWifiChipInfos = getAllChipInfo(false);
         }
         return mCachedWifiChipInfos;
     }
@@ -1137,10 +1149,10 @@
      * Get current information about all the chips in the system: modes, current mode (if any), and
      * any existing interfaces.
      *
-     * Intended to be called whenever we need to configure the chips - information is NOT cached (to
-     * reduce the likelihood that we get out-of-sync).
+     * <p>Intended to be called whenever we need to configure the chips - information is NOT cached
+     * (to reduce the likelihood that we get out-of-sync).
      */
-    private WifiChipInfo[] getAllChipInfo() {
+    private WifiChipInfo[] getAllChipInfo(boolean forceReadChipInfoFromDriver) {
         if (VDBG) Log.d(TAG, "getAllChipInfo");
 
         synchronized (mLock) {
@@ -1174,15 +1186,6 @@
                     return null;
                 }
 
-                StaticChipInfo staticChipInfo = staticChipInfoPerId.get(chipId);
-                List<WifiChip.ChipMode> chipModes = null;
-                if (staticChipInfo == null) {
-                    chipModes = chip.getAvailableModes();
-                    if (chipModes == null) {
-                        return null;
-                    }
-                }
-
                 WifiChip.Response<Integer> currentMode = chip.getMode();
                 if (currentMode.getStatusCode() != WifiHal.WIFI_STATUS_SUCCESS
                         && currentMode.getStatusCode() != WifiHal.WIFI_STATUS_ERROR_NOT_AVAILABLE) {
@@ -1296,10 +1299,15 @@
 
                 chipInfo.chip = chip;
                 chipInfo.chipId = chipId;
-                if (staticChipInfo != null) {
-                    chipInfo.availableModes = staticChipInfo.getAvailableModes();
-                } else {
+                StaticChipInfo staticChipInfo = staticChipInfoPerId.get(chipId);
+                if (forceReadChipInfoFromDriver || staticChipInfo == null) {
+                    List<WifiChip.ChipMode> chipModes = chip.getAvailableModes();
+                    if (chipModes == null) {
+                        return null;
+                    }
                     chipInfo.availableModes = new ArrayList<>(chipModes);
+                } else {
+                    chipInfo.availableModes = staticChipInfo.getAvailableModes();
                 }
                 chipInfo.currentModeIdValid =
                         currentMode.getStatusCode() == WifiHal.WIFI_STATUS_SUCCESS;
@@ -1450,7 +1458,7 @@
                         Log.d(TAG, "start IWifi succeeded after trying "
                                  + triedCount + " times");
                     }
-                    WifiChipInfo[] wifiChipInfos = getAllChipInfo();
+                    WifiChipInfo[] wifiChipInfos = getAllChipInfo(false);
                     if (wifiChipInfos == null) {
                         Log.e(TAG, "Started wifi but could not get current chip info.");
                     }
@@ -1593,7 +1601,7 @@
 
     private WifiHal.WifiInterface createIface(@HdmIfaceTypeForCreation int createIfaceType,
             long requiredChipCapabilities, InterfaceDestroyedListener destroyedListener,
-            Handler handler, WorkSource requestorWs) {
+            Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData) {
         if (mDbg) {
             Log.d(TAG, "createIface: createIfaceType=" + createIfaceType
                     + ", requiredChipCapabilities=" + requiredChipCapabilities
@@ -1606,7 +1614,7 @@
         }
 
         synchronized (mLock) {
-            WifiChipInfo[] chipInfos = getAllChipInfo();
+            WifiChipInfo[] chipInfos = getAllChipInfo(false);
             if (chipInfos == null) {
                 Log.e(TAG, "createIface: no chip info found");
                 stopWifi(); // major error: shutting down
@@ -1625,7 +1633,7 @@
 
             return createIfaceIfPossible(
                     chipInfos, createIfaceType, requiredChipCapabilities,
-                    destroyedListener, handler, requestorWs);
+                    destroyedListener, handler, requestorWs, vendorData);
         }
     }
 
@@ -1677,6 +1685,30 @@
                     }
                 }
             }
+            if (bestIfaceCreationProposal == null) {
+                List<String> createIfaceInfoString = new ArrayList<String>();
+                for (WifiChipInfo chipInfo : chipInfos) {
+                    for (int existingCreateType : CREATE_TYPES_BY_PRIORITY) {
+                        WifiIfaceInfo[] createTypeIfaces = chipInfo.ifaces[existingCreateType];
+                        for (WifiIfaceInfo intfInfo : createTypeIfaces) {
+                            if (intfInfo != null) {
+                                createIfaceInfoString.add(
+                                        "name="
+                                                + intfInfo.name
+                                                + " type="
+                                                + getIfaceTypeToString(intfInfo.createType));
+                            }
+                        }
+                    }
+                }
+                Log.i(
+                        TAG,
+                        "bestIfaceCreationProposal is null,"
+                                + " requestIface="
+                                + getIfaceTypeToString(createIfaceType)
+                                + ", existingIface="
+                                + createIfaceInfoString);
+            }
             return bestIfaceCreationProposal;
         }
     }
@@ -1702,7 +1734,7 @@
     private WifiHal.WifiInterface createIfaceIfPossible(
             WifiChipInfo[] chipInfos, @HdmIfaceTypeForCreation int createIfaceType,
             long requiredChipCapabilities, InterfaceDestroyedListener destroyedListener,
-            Handler handler, WorkSource requestorWs) {
+            Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData) {
         int targetHalIfaceType = HAL_IFACE_MAP.get(createIfaceType);
         if (VDBG) {
             Log.d(TAG, "createIfaceIfPossible: chipInfos=" + Arrays.deepToString(chipInfos)
@@ -1711,13 +1743,17 @@
                     + ", requiredChipCapabilities=" + requiredChipCapabilities
                     + ", requestorWs=" + requestorWs);
         }
+        if (vendorData != null && !vendorData.isEmpty()) {
+            Log.d(TAG, "Request includes vendor data. ifaceType=" + createIfaceType
+                    + ", vendorDataSize=" + vendorData.size());
+        }
         synchronized (mLock) {
             IfaceCreationData bestIfaceCreationProposal = getBestIfaceCreationProposal(chipInfos,
                     createIfaceType, requiredChipCapabilities, requestorWs);
 
             if (bestIfaceCreationProposal != null) {
                 WifiHal.WifiInterface iface = executeChipReconfiguration(bestIfaceCreationProposal,
-                        createIfaceType);
+                        createIfaceType, vendorData);
                 if (iface == null) {
                     // If the chip reconfiguration failed, we'll need to clean up internal state.
                     Log.e(TAG, "Teardown Wifi internal state");
@@ -1743,23 +1779,6 @@
                             Pair.create(cacheEntry.name, cacheEntry.type), cacheEntry);
                     return iface;
                 }
-            } else {
-                List<String> createIfaceInfoString = new ArrayList<String>();
-                for (WifiChipInfo chipInfo : chipInfos) {
-                    for (int existingCreateType : CREATE_TYPES_BY_PRIORITY) {
-                        WifiIfaceInfo[] createTypeIfaces = chipInfo.ifaces[existingCreateType];
-                        for (WifiIfaceInfo intfInfo : createTypeIfaces) {
-                            if (intfInfo != null) {
-                                createIfaceInfoString.add(
-                                        "name=" + intfInfo.name + " type=" + getIfaceTypeToString(
-                                                intfInfo.createType));
-                            }
-                        }
-                    }
-                }
-                Log.i(TAG, "bestIfaceCreationProposal is null," + " requestIface="
-                        + getIfaceTypeToString(createIfaceType) + ", existingIface="
-                        + createIfaceInfoString);
             }
         }
 
@@ -2034,21 +2053,9 @@
 
         // Allow FG apps to delete any disconnected P2P iface if they are older than
         // config_disconnectedP2pIfaceLowPriorityTimeoutMs.
-        int unusedP2pTimeoutMs = mContext.getResources().getInteger(
-                R.integer.config_disconnectedP2pIfaceLowPriorityTimeoutMs);
         if (newRequestorWsPriority > WorkSourceHelper.PRIORITY_BG
-                && existingCreateType == HDM_CREATE_IFACE_P2P
-                && !mIsP2pConnected
-                && unusedP2pTimeoutMs >= 0) {
-            InterfaceCacheEntry ifaceCacheEntry = mInterfaceInfoCache.get(
-                    Pair.create(existingIfaceInfo.name, getType(existingIfaceInfo.iface)));
-            if (ifaceCacheEntry != null && mClock.getElapsedSinceBootMillis()
-                    >= ifaceCacheEntry.creationTime + unusedP2pTimeoutMs) {
-                if (mDbg) {
-                    Log.i(TAG, "Allowed to delete disconnected P2P iface: " + ifaceCacheEntry);
-                }
-                return true;
-            }
+                && isDisconnectedP2p(existingIfaceInfo)) {
+            return true;
         }
 
         // Defer deletion decision to the InterfaceConflictManager dialog.
@@ -2070,14 +2077,23 @@
             if (requestedCreateType == existingCreateType) {
                 return false;
             }
-            // If both the requests are privileged, the new requestor wins. The exception is for
-            // backwards compatibility with P2P Settings, prefer SoftAP over P2P for when the user
-            // enables SoftAP with P2P Settings open.
+            // If both the requests are privileged, the new requestor wins unless it's P2P against
+            // AP (for when the user enables SoftAP with P2P Settings open) or primary STA
+            // (since P2P isn't supported without STA).
             if (newRequestorWsPriority == WorkSourceHelper.PRIORITY_PRIVILEGED) {
-                if (requestedCreateType == HDM_CREATE_IFACE_P2P
-                        && (existingCreateType == HDM_CREATE_IFACE_AP
-                        || existingCreateType == HDM_CREATE_IFACE_AP_BRIDGE)) {
-                    return false;
+                if (requestedCreateType == HDM_CREATE_IFACE_P2P) {
+                    if (existingCreateType == HDM_CREATE_IFACE_AP
+                            || existingCreateType == HDM_CREATE_IFACE_AP_BRIDGE) {
+                        return false;
+                    }
+                    if (existingCreateType == HDM_CREATE_IFACE_STA) {
+                        ConcreteClientModeManager cmm = mClientModeManagers.get(
+                                existingIfaceInfo.name);
+                        if (cmm != null && (cmm.getRole()
+                                == ActiveModeManager.ROLE_CLIENT_PRIMARY)) {
+                            return false;
+                        }
+                    }
                 }
                 return true;
             }
@@ -2102,17 +2118,19 @@
             @WorkSourceHelper.RequestorWsPriority int newRequestorWsPriority,
             @HdmIfaceTypeForCreation int existingCreateType,
             @WorkSourceHelper.RequestorWsPriority int existingRequestorWsPriority) {
-        return !canDeviceSupportCreateTypeCombo(
-                new SparseArray<Integer>() {{
-                    put(HDM_CREATE_IFACE_STA, 1);
-                    put(HDM_CREATE_IFACE_AP, 1);
-                }})
-                && (requestedCreateType == HDM_CREATE_IFACE_AP
-                || requestedCreateType == HDM_CREATE_IFACE_AP_BRIDGE)
+        return (requestedCreateType == HDM_CREATE_IFACE_AP
+                        || requestedCreateType == HDM_CREATE_IFACE_AP_BRIDGE)
                 && newRequestorWsPriority != WorkSourceHelper.PRIORITY_INTERNAL
                 && newRequestorWsPriority != WorkSourceHelper.PRIORITY_PRIVILEGED
                 && existingCreateType == HDM_CREATE_IFACE_STA
-                && existingRequestorWsPriority == WorkSourceHelper.PRIORITY_PRIVILEGED;
+                && existingRequestorWsPriority == WorkSourceHelper.PRIORITY_PRIVILEGED
+                && !canDeviceSupportCreateTypeCombo(
+                        new SparseArray<Integer>() {
+                            {
+                                put(HDM_CREATE_IFACE_STA, 1);
+                                put(HDM_CREATE_IFACE_AP, 1);
+                            }
+                        });
     }
 
     /**
@@ -2151,6 +2169,25 @@
         return true;
     }
 
+    private boolean isDisconnectedP2p(WifiIfaceInfo p2pInfo) {
+        int unusedP2pTimeoutMs = mContext.getResources().getInteger(
+                R.integer.config_disconnectedP2pIfaceLowPriorityTimeoutMs);
+        if (p2pInfo.createType == HDM_CREATE_IFACE_P2P
+                && !mIsP2pConnected
+                && unusedP2pTimeoutMs >= 0) {
+            InterfaceCacheEntry ifaceCacheEntry = mInterfaceInfoCache.get(
+                    Pair.create(p2pInfo.name, getType(p2pInfo.iface)));
+            if (ifaceCacheEntry != null && mClock.getElapsedSinceBootMillis()
+                    >= ifaceCacheEntry.creationTime + unusedP2pTimeoutMs) {
+                if (mDbg) {
+                    Log.i(TAG, "Allowed to delete disconnected P2P iface: " + ifaceCacheEntry);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Selects the interfaces of a given type and quantity to delete for a requested interface.
      * If the specified quantity of interfaces cannot be deleted, returns null.
@@ -2330,7 +2367,7 @@
      * Returns the newly created interface or a null on any error.
      */
     private WifiHal.WifiInterface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
-            @HdmIfaceTypeForCreation int createIfaceType) {
+            @HdmIfaceTypeForCreation int createIfaceType, @Nullable List<OuiKeyedData> vendorData) {
         if (mDbg) {
             Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData
                     + ", createIfaceType=" + createIfaceType);
@@ -2357,23 +2394,28 @@
                     }
                 }
 
-                boolean success = ifaceCreationData.chipInfo.chip.configureChip(
-                        ifaceCreationData.chipModeId);
-                if (!success) {
-                    Log.e(TAG, "executeChipReconfiguration: configureChip error");
-                    return null;
-                }
+                // Configure mode using the cached chip info, then reload chip info if needed
+                boolean configureChipSuccess =
+                        ifaceCreationData.chipInfo.chip.configureChip(ifaceCreationData.chipModeId);
                 if (!mIsConcurrencyComboLoadedFromDriver) {
-                    WifiChipInfo[] wifiChipInfos = getAllChipInfo();
+                    WifiChipInfo[] wifiChipInfos = getAllChipInfo(true);
                     if (wifiChipInfos != null) {
                         mCachedStaticChipInfos =
                                 convertWifiChipInfoToStaticChipInfos(wifiChipInfos);
                         saveStaticChipInfoToStore(mCachedStaticChipInfos);
-                        mIsConcurrencyComboLoadedFromDriver = true;
+                        if (configureChipSuccess) {
+                            // Successful chip configuration suggests that the modes are valid
+                            Log.i(TAG, "Chip info loaded successfully from the HAL");
+                            mIsConcurrencyComboLoadedFromDriver = true;
+                        }
                     } else {
-                        Log.e(TAG, "Configured chip but could not get current chip info.");
+                        Log.e(TAG, "Could not get current chip info.");
                     }
                 }
+                if (!configureChipSuccess) {
+                    Log.e(TAG, "executeChipReconfiguration: configureChip error");
+                    return null;
+                }
             } else {
                 // remove all interfaces on the delete list
                 for (WifiIfaceInfo ifaceInfo : ifaceCreationData.interfacesToBeRemovedFirst) {
@@ -2399,10 +2441,10 @@
                     iface = ifaceCreationData.chipInfo.chip.createStaIface();
                     break;
                 case HDM_CREATE_IFACE_AP_BRIDGE:
-                    iface = ifaceCreationData.chipInfo.chip.createBridgedApIface();
+                    iface = ifaceCreationData.chipInfo.chip.createBridgedApIface(vendorData);
                     break;
                 case HDM_CREATE_IFACE_AP:
-                    iface = ifaceCreationData.chipInfo.chip.createApIface();
+                    iface = ifaceCreationData.chipInfo.chip.createApIface(vendorData);
                     break;
                 case HDM_CREATE_IFACE_P2P:
                     iface = ifaceCreationData.chipInfo.chip.createP2pIface();
@@ -2600,6 +2642,10 @@
             //  When all wifi services (ie. WifiAware, WifiP2p) get moved to the wifi handler
             //  thread, remove this thread check and the Handler#post() and simply always
             //  invoke the callback directly.
+            if (mFeatureFlags.singleWifiThread()) {
+                action();
+                return;
+            }
             if (requestedToRunInCurrentThread()) {
                 // Already running on the same handler thread. Trigger listener synchronously.
                 action();
@@ -2752,7 +2798,7 @@
                 return null;
             }
 
-            WifiChipInfo[] chipInfos = getAllChipInfo();
+            WifiChipInfo[] chipInfos = getAllChipInfo(false);
             if (chipInfos == null) {
                 Log.d(TAG, "createRttControllerIfPossible: no chip info found - most likely chip "
                         + "not up yet");
@@ -2868,8 +2914,10 @@
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Dump of HalDeviceManager:");
-        pw.println("  mManagerStatusListeners: " + mManagerStatusListeners);
-        pw.println("  mInterfaceInfoCache: " + mInterfaceInfoCache);
-        pw.println("  mDebugChipsInfo: " + Arrays.toString(getAllChipInfo()));
+        synchronized (mLock) {
+            pw.println("  mManagerStatusListeners: " + mManagerStatusListeners);
+            pw.println("  mInterfaceInfoCache: " + mInterfaceInfoCache);
+        }
+        pw.println("  mDebugChipsInfo: " + Arrays.toString(getAllChipInfo(false)));
     }
 }
diff --git a/service/java/com/android/server/wifi/HostapdHalAidlImp.java b/service/java/com/android/server/wifi/HostapdHalAidlImp.java
index f8c0791..b4a95d5 100644
--- a/service/java/com/android/server/wifi/HostapdHalAidlImp.java
+++ b/service/java/com/android/server/wifi/HostapdHalAidlImp.java
@@ -50,7 +50,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.WifiNative.HostapdDeathEventHandler;
+import com.android.server.wifi.WifiNative.SoftApHalCallback;
 import com.android.server.wifi.util.ApConfigUtil;
+import com.android.server.wifi.util.HalAidlUtil;
 import com.android.server.wifi.util.NativeUtil;
 import com.android.wifi.resources.R;
 
@@ -87,7 +89,7 @@
     // Hostapd HAL interface objects
     private IHostapd mIHostapd;
     private HashMap<String, Runnable> mSoftApFailureListeners = new HashMap<>();
-    private WifiNative.SoftApHalCallback mSoftApEventCallback;
+    private HashMap<String, SoftApHalCallback> mSoftApHalCallbacks = new HashMap<>();
     private Set<String> mActiveInstances = new HashSet<>();
     private HostapdDeathEventHandler mDeathEventHandler;
     private boolean mServiceDeclared = false;
@@ -191,10 +193,10 @@
     }
 
     /**
-     * Register the provided callback handler for SoftAp events.
+     * Register the provided callback handler for SoftAp events on the specified iface.
      * <p>
-     * Note that only one callback can be registered at a time - any registration overrides previous
-     * registrations.
+     * Note that only one callback can be registered per iface at a time - any registration on the
+     * same iface overrides previous registrations.
      *
      * @param ifaceName Name of the interface.
      * @param listener Callback listener for AP events.
@@ -202,14 +204,14 @@
      */
     @Override
     public boolean registerApCallback(@NonNull String ifaceName,
-            @NonNull WifiNative.SoftApHalCallback callback) {
+            @NonNull SoftApHalCallback callback) {
         // TODO(b/195980798) : Create a hashmap to associate the listener with the ifaceName
         synchronized (mLock) {
             if (callback == null) {
                 Log.e(TAG, "registerApCallback called with a null callback");
                 return false;
             }
-            mSoftApEventCallback = callback;
+            mSoftApHalCallbacks.put(ifaceName, callback);
             Log.i(TAG, "registerApCallback Successful in " + ifaceName);
             return true;
         }
@@ -269,7 +271,7 @@
             }
             try {
                 mSoftApFailureListeners.remove(ifaceName);
-                mSoftApEventCallback = null;
+                mSoftApHalCallbacks.remove(ifaceName);
                 mIHostapd.removeAccessPoint(ifaceName);
                 return true;
             } catch (RemoteException e) {
@@ -377,12 +379,13 @@
         public void onFailure(String ifaceName, String instanceName) {
             Log.w(TAG, "Failure on iface " + ifaceName + ", instance: " + instanceName);
             Runnable onFailureListener = mSoftApFailureListeners.get(ifaceName);
+            SoftApHalCallback callback = mSoftApHalCallbacks.get(ifaceName);
             if (onFailureListener != null) {
                 mActiveInstances.remove(instanceName);
                 if (mActiveInstances.size() == 0) {
                     onFailureListener.run();
-                } else if (mSoftApEventCallback != null) {
-                    mSoftApEventCallback.onInstanceFailure(instanceName);
+                } else if (callback != null) {
+                    callback.onInstanceFailure(instanceName);
                 }
             }
         }
@@ -392,8 +395,9 @@
             Log.v(TAG, "onApInstanceInfoChanged on " + info.ifaceName + " / "
                     + info.apIfaceInstance);
             try {
-                if (mSoftApEventCallback != null) {
-                    mSoftApEventCallback.onInfoChanged(info.apIfaceInstance, info.freqMhz,
+                SoftApHalCallback callback = mSoftApHalCallbacks.get(info.ifaceName);
+                if (callback != null) {
+                    callback.onInfoChanged(info.apIfaceInstance, info.freqMhz,
                             mapHalChannelBandwidthToSoftApInfo(info.channelBandwidth),
                             mapHalGenerationToWifiStandard(info.generation),
                             MacAddress.fromBytes(info.apIfaceInstanceMacAddress));
@@ -411,8 +415,9 @@
                         + " / " + info.apIfaceInstance
                         + " and Mac is " + MacAddress.fromBytes(info.clientAddress).toString()
                         + " isConnected: " + info.isConnected);
-                if (mSoftApEventCallback != null) {
-                    mSoftApEventCallback.onConnectedClientsChanged(info.apIfaceInstance,
+                SoftApHalCallback callback = mSoftApHalCallbacks.get(info.ifaceName);
+                if (callback != null) {
+                    callback.onConnectedClientsChanged(info.apIfaceInstance,
                             MacAddress.fromBytes(info.clientAddress), info.isConnected);
                 }
             } catch (IllegalArgumentException iae) {
@@ -881,6 +886,11 @@
                 || ifaceParams.channelParams == null) {
             return null;
         }
+        if (isServiceVersionAtLeast(2) && SdkLevel.isAtLeastV()
+                && !config.getVendorData().isEmpty()) {
+            ifaceParams.vendorData =
+                    HalAidlUtil.frameworkToHalOuiKeyedDataList(config.getVendorData());
+        }
         return ifaceParams;
     }
 
@@ -902,11 +912,9 @@
                 R.bool.config_wifiSoftapHeMuBeamformerSupported);
         hwModeParams.enableHeTargetWakeTime = mContext.getResources().getBoolean(
                 R.bool.config_wifiSoftapHeTwtSupported);
-        hwModeParams.enable80211BE = ApConfigUtil.isIeee80211beSupported(mContext);
-        //Update 80211be support with the configuration.
-        hwModeParams.enable80211BE &= config.isIeee80211beEnabledInternal();
 
         if (SdkLevel.isAtLeastT()) {
+            hwModeParams.enable80211BE = config.isIeee80211beEnabled();
             hwModeParams.maximumChannelBandwidth =
                     mapSoftApInfoBandwidthToHal(config.getMaxChannelBandwidth());
         } else {
diff --git a/service/java/com/android/server/wifi/HostapdHalHidlImp.java b/service/java/com/android/server/wifi/HostapdHalHidlImp.java
index 825cf1a..48b5131 100644
--- a/service/java/com/android/server/wifi/HostapdHalHidlImp.java
+++ b/service/java/com/android/server/wifi/HostapdHalHidlImp.java
@@ -81,7 +81,7 @@
     private IServiceManager mIServiceManager;
     private IHostapd mIHostapd;
     private HashMap<String, Runnable> mSoftApFailureListeners = new HashMap<>();
-    private SoftApHalCallback mSoftApEventListener;
+    private HashMap<String, SoftApHalCallback> mSoftApHalCallbacks = new HashMap<>();
     private HostapdDeathEventHandler mDeathEventHandler;
     private ServiceManagerDeathRecipient mServiceManagerDeathRecipient;
     private HostapdDeathRecipient mHostapdDeathRecipient;
@@ -395,10 +395,10 @@
     }
 
     /**
-     * Register the provided callback handler for SoftAp events.
+     * Register the provided callback handler for SoftAp events on the specified iface.
      * <p>
-     * Note that only one callback can be registered at a time - any registration overrides previous
-     * registrations.
+     * Note that only one callback can be registered per iface at a time - any registration on the
+     * same iface overrides previous registrations.
      *
      * @param ifaceName Name of the interface.
      * @param listener Callback listener for AP events.
@@ -417,7 +417,7 @@
                 Log.d(TAG, "The current HAL doesn't support event callback.");
                 return false;
             }
-            mSoftApEventListener = listener;
+            mSoftApHalCallbacks.put(ifaceName, listener);
             Log.i(TAG, "registerApCallback Successful in " + ifaceName);
             return true;
         }
@@ -526,7 +526,7 @@
                     return false;
                 }
                 mSoftApFailureListeners.remove(ifaceName);
-                mSoftApEventListener = null;
+                mSoftApHalCallbacks.remove(ifaceName);
                 return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1273,8 +1273,9 @@
                 int frequency, int bandwidth, int generation, byte[] apIfaceInstanceMacAddress) {
             Log.d(TAG, "onApInstanceInfoChanged on " + ifaceName + " / " + apIfaceInstance);
             try {
-                if (mSoftApEventListener != null) {
-                    mSoftApEventListener.onInfoChanged(apIfaceInstance, frequency,
+                SoftApHalCallback callback = mSoftApHalCallbacks.get(ifaceName);
+                if (callback != null) {
+                    callback.onInfoChanged(apIfaceInstance, frequency,
                             mapHalBandwidthToSoftApInfo(bandwidth),
                             mapHalGenerationToWifiStandard(generation),
                             MacAddress.fromBytes(apIfaceInstanceMacAddress));
@@ -1291,8 +1292,9 @@
                 Log.d(TAG, "onConnectedClientsChanged on " + ifaceName + " / " + apIfaceInstance
                         + " and Mac is " + MacAddress.fromBytes(clientAddress).toString()
                         + " isConnected: " + isConnected);
-                if (mSoftApEventListener != null) {
-                    mSoftApEventListener.onConnectedClientsChanged(apIfaceInstance,
+                SoftApHalCallback callback = mSoftApHalCallbacks.get(ifaceName);
+                if (callback != null) {
+                    callback.onConnectedClientsChanged(apIfaceInstance,
                             MacAddress.fromBytes(clientAddress), isConnected);
                 }
             } catch (IllegalArgumentException iae) {
diff --git a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java
index 3139218..38e5d34 100644
--- a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java
+++ b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java
@@ -259,19 +259,23 @@
     /**
      * Stores a received certificate for later use.
      *
-     * @param ssid the target network SSID.
+     * @param networkId networkId of the target network.
      * @param depth the depth of this cert. The Root CA should be 0 or
      *        a positive number, and the server cert is 0.
      * @param certInfo a certificate info object from the server.
      * @return true if the cert is cached; otherwise, false.
      */
-    public boolean addPendingCertificate(@NonNull String ssid, int depth,
+    public boolean addPendingCertificate(int networkId, int depth,
             @NonNull CertificateEventInfo certInfo) {
         String configProfileKey = mCurrentTofuConfig != null
                 ? mCurrentTofuConfig.getProfileKey() : "null";
-        if (TextUtils.isEmpty(ssid)) return false;
+        if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+            return false;
+        }
         if (null == mCurrentTofuConfig) return false;
-        if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) return false;
+        if (mCurrentTofuConfig.networkId != networkId) {
+            return false;
+        }
         if (null == certInfo) return false;
         if (depth < 0) return false;
 
@@ -295,8 +299,9 @@
 
         if (!mServerCertChain.contains(certInfo.getCert())) {
             mServerCertChain.addFirst(certInfo.getCert());
-            Log.d(TAG, "addPendingCertificate: " + "SSID=" + ssid + " depth=" + depth
-                    + " certHash=" + certInfo.getCertHash() + " current config=" + configProfileKey
+            Log.d(TAG, "addPendingCertificate: " + "SSID=" + mCurrentTofuConfig.SSID
+                    + " depth=" + depth + " certHash=" + certInfo.getCertHash()
+                    + " current config=" + configProfileKey
                     + "\ncertificate content:\n" + certInfo.getCert());
         }
 
@@ -471,6 +476,38 @@
     }
 
     /**
+     * Check whether certificate pinning should be used.
+     *
+     * @param verbose whether to print logs during the check.
+     * @return true if certificate pinning should be used, false otherwise.
+     */
+    private boolean useCertificatePinning(boolean verbose) {
+        if (mServerCertChain.size() == 1) {
+            if (verbose) {
+                Log.i(TAG, "Only one certificate provided, use server certificate pinning");
+            }
+            return true;
+        }
+        if (mPendingRootCaCert.getSubjectX500Principal().getName()
+                .equals(mPendingRootCaCert.getIssuerX500Principal().getName())) {
+            if (mPendingRootCaCert.getVersion() >= 2
+                    && mPendingRootCaCert.getBasicConstraints() < 0) {
+                if (verbose) {
+                    Log.i(TAG, "Root CA with no CA bit set in basic constraints, "
+                            + "use server certificate pinning");
+                }
+                return true;
+            }
+        } else {
+            if (verbose) {
+                Log.i(TAG, "Root CA is not self-signed, use server certificate pinning");
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Configure the server validation method based on the incoming server certificate chain.
      * If a valid method is found, the method returns true, and the caller can continue the TOFU
      * process.
@@ -488,20 +525,7 @@
             Log.e(TAG, "No certificate chain provided by the server.");
             return false;
         }
-        if (mServerCertChain.size() == 1) {
-            Log.i(TAG, "Only one certificate provided, use server certificate pinning");
-            return true;
-        }
-        if (mPendingRootCaCert.getSubjectX500Principal().getName()
-                .equals(mPendingRootCaCert.getIssuerX500Principal().getName())) {
-            if (mPendingRootCaCert.getVersion() >= 2
-                    && mPendingRootCaCert.getBasicConstraints() < 0) {
-                Log.i(TAG, "Root CA with no CA bit set in basic constraints, "
-                        + "use server certificate pinning");
-                return true;
-            }
-        } else {
-            Log.i(TAG, "Root CA is not self-signed, use server certificate pinning");
+        if (useCertificatePinning(true)) {
             return true;
         }
 
@@ -596,8 +620,14 @@
                 Log.e(TAG, "Cannot update CA cert to network " + mCurrentTofuConfig.getProfileKey()
                         + ", CA cert = " + mPendingRootCaCert);
             }
+            int postConnectionMethod = useCertificatePinning(false)
+                    ? WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING
+                    : WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA;
+            mWifiConfigManager.setTofuPostConnectionState(
+                    mCurrentTofuConfig.networkId, postConnectionMethod);
         }
         int networkId = mCurrentTofuConfig.networkId;
+        mWifiConfigManager.setTofuDialogApproved(networkId, true);
         mWifiConfigManager.updateNetworkSelectionStatus(networkId,
                 WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE);
         dismissDialogAndNotification();
@@ -611,6 +641,7 @@
         if (!isConnectionValid(ssid)) return;
         boolean disconnectRequired = !useTrustOnFirstUse();
 
+        mWifiConfigManager.setTofuDialogApproved(mCurrentTofuConfig.networkId, false);
         mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId,
                 WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER);
         dismissDialogAndNotification();
diff --git a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
index ea773bf..6a02b17 100644
--- a/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
+++ b/service/java/com/android/server/wifi/NetworkSuggestionNominator.java
@@ -29,7 +29,6 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion;
 import com.android.server.wifi.entitlement.PseudonymInfo;
-import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -57,19 +56,17 @@
 
     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
     private final WifiConfigManager mWifiConfigManager;
-    private final PasspointNetworkNominateHelper mPasspointNetworkNominateHelper;
     private final LocalLog mLocalLog;
     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
     private final WifiPseudonymManager mWifiPseudonymManager;
     private final WifiMetrics mWifiMetrics;
 
     NetworkSuggestionNominator(WifiNetworkSuggestionsManager networkSuggestionsManager,
-            WifiConfigManager wifiConfigManager, PasspointNetworkNominateHelper nominateHelper,
+            WifiConfigManager wifiConfigManager,
             LocalLog localLog, WifiCarrierInfoManager wifiCarrierInfoManager,
             WifiPseudonymManager wifiPseudonymManager, WifiMetrics wifiMetrics) {
         mWifiNetworkSuggestionsManager = networkSuggestionsManager;
         mWifiConfigManager = wifiConfigManager;
-        mPasspointNetworkNominateHelper = nominateHelper;
         mLocalLog = localLog;
         mWifiCarrierInfoManager = wifiCarrierInfoManager;
         mWifiPseudonymManager = wifiPseudonymManager;
@@ -78,25 +75,29 @@
 
     @Override
     public void update(List<ScanDetail> scanDetails) {
-        // Update the matching profiles into WifiConfigManager, help displaying Suggestion and
-        // Passpoint networks in Wifi Picker
+        // Update the matching profiles into WifiConfigManager, help displaying Suggestion
+        // networks in Wifi Picker
         addOrUpdateSuggestionsToWifiConfigManger(scanDetails);
-        mPasspointNetworkNominateHelper.updatePasspointConfig(scanDetails);
     }
 
     @Override
-    public void nominateNetworks(List<ScanDetail> scanDetails,
+    public void nominateNetworks(@NonNull List<ScanDetail> scanDetails,
+            @NonNull List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates,
             boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed,
-            boolean oemPrivateNetworkAllowed, Set<Integer> restrictedNetworkAllowedUids,
+            boolean oemPrivateNetworkAllowed,
+            @NonNull Set<Integer> restrictedNetworkAllowedUids,
             @NonNull OnConnectableListener onConnectableListener) {
         if (scanDetails.isEmpty()) {
             return;
         }
         MatchMetaInfo matchMetaInfo = new MatchMetaInfo();
+        if (passpointCandidates != null) {
+            findMatchedPasspointSuggestionNetworks(
+                    passpointCandidates, matchMetaInfo, untrustedNetworkAllowed,
+                    oemPaidNetworkAllowed,
+                    oemPrivateNetworkAllowed, restrictedNetworkAllowedUids);
+        }
 
-        findMatchedPasspointSuggestionNetworks(
-                scanDetails, matchMetaInfo, untrustedNetworkAllowed, oemPaidNetworkAllowed,
-                oemPrivateNetworkAllowed, restrictedNetworkAllowedUids);
         findMatchedSuggestionNetworks(scanDetails, matchMetaInfo, untrustedNetworkAllowed,
                 oemPaidNetworkAllowed,
                 oemPrivateNetworkAllowed, restrictedNetworkAllowedUids);
@@ -189,14 +190,16 @@
         return false;
     }
 
-    private void findMatchedPasspointSuggestionNetworks(List<ScanDetail> scanDetails,
+    private void findMatchedPasspointSuggestionNetworks(
+            List<Pair<ScanDetail, WifiConfiguration>> candidates,
             MatchMetaInfo matchMetaInfo, boolean untrustedNetworkAllowed,
             boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed,
             Set<Integer> restrictedNetworkAllowedUids) {
-        List<Pair<ScanDetail, WifiConfiguration>> candidates =
-                mPasspointNetworkNominateHelper.getPasspointNetworkCandidates(scanDetails, true);
         for (Pair<ScanDetail, WifiConfiguration> candidate : candidates) {
             WifiConfiguration config = candidate.second;
+            if (!config.fromWifiNetworkSuggestion) {
+                continue;
+            }
             Set<ExtendedWifiNetworkSuggestion> matchingExtNetworkSuggestions =
                     mWifiNetworkSuggestionsManager.getNetworkSuggestionsForFqdn(config.FQDN);
             if (matchingExtNetworkSuggestions.isEmpty()) {
diff --git a/service/java/com/android/server/wifi/RunnerHandler.java b/service/java/com/android/server/wifi/RunnerHandler.java
index 9fae648..2456cf1 100644
--- a/service/java/com/android/server/wifi/RunnerHandler.java
+++ b/service/java/com/android/server/wifi/RunnerHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wifi;
 
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_THREAD_TASK_EXECUTED;
+
 import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Handler;
@@ -27,6 +29,7 @@
 import android.util.LocalLog;
 
 import com.android.modules.utils.HandlerExecutor;
+import com.android.server.wifi.proto.WifiStatsLog;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -44,7 +47,6 @@
     private static final int METRICS_THRESHOLD_MILLIS = 100;
 
     private final int mRunningTimeThresholdInMilliseconds;
-    private final WifiMetrics mWifiMetrics;
     private Set<String> mIgnoredClasses = new HashSet<>();
     private Set<String> mIgnoredMethods = new HashSet<>();
 
@@ -54,15 +56,13 @@
     /**
      * The Runner handler Constructor
      *
-     * @param looper    looper for the handler
+     * @param looper looper for the handler
      * @param threshold the running time threshold in milliseconds
      */
-    public RunnerHandler(Looper looper, int threshold, @NonNull LocalLog localLog,
-            WifiMetrics wifiMetrics) {
+    public RunnerHandler(Looper looper, int threshold, @NonNull LocalLog localLog) {
         super(looper);
         mRunningTimeThresholdInMilliseconds = threshold;
         mLocalLog = localLog;
-        mWifiMetrics = wifiMetrics;
         mIgnoredClasses.add(WifiThreadRunner.class.getName());
         mIgnoredClasses.add(WifiThreadRunner.class.getName() + "$BlockingRunnable");
         mIgnoredClasses.add(RunnerHandler.class.getName());
@@ -132,8 +132,11 @@
             mLocalLog.log(signatureToLog + " schedule latency " + scheduleLatency + " ms");
         }
         if (runTime > METRICS_THRESHOLD_MILLIS || scheduleLatency > METRICS_THRESHOLD_MILLIS) {
-            mWifiMetrics.wifiThreadTaskExecuted(signatureToLog, (int) scheduleLatency,
-                    (int) runTime);
+            WifiStatsLog.write(
+                    WIFI_THREAD_TASK_EXECUTED,
+                    (int) runTime,
+                    (int) scheduleLatency,
+                    signatureToLog);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/RunnerState.java b/service/java/com/android/server/wifi/RunnerState.java
index d219ee9..39f8424 100644
--- a/service/java/com/android/server/wifi/RunnerState.java
+++ b/service/java/com/android/server/wifi/RunnerState.java
@@ -16,12 +16,15 @@
 
 package com.android.server.wifi;
 
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_THREAD_TASK_EXECUTED;
+
 import android.annotation.NonNull;
 import android.os.Message;
 import android.os.Trace;
 import android.util.LocalLog;
 
 import com.android.internal.util.State;
+import com.android.server.wifi.proto.WifiStatsLog;
 
 /**
  * RunnerState class is a wrapper based on State class to monitor and track the State enter/exit
@@ -34,6 +37,7 @@
  */
 public abstract class RunnerState extends State {
     private static final String TAG = "RunnerState";
+    private static final int METRICS_THRESHOLD_MILLIS = 100;
 
     /** Message.what value when entering */
     public static final int STATE_ENTER_CMD = -3;
@@ -56,40 +60,51 @@
 
     @Override
     public boolean processMessage(Message message) {
-        Long startTime = System.currentTimeMillis();
+        long startTime = System.currentTimeMillis();
 
-        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, getMessageLogRec(message.what));
+        String signatureToLog = getMessageLogRec(message.what);
+        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
         boolean ret = processMessageImpl(message);
         Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
-        Long runTime = System.currentTimeMillis() - startTime;
+        long runTime = System.currentTimeMillis() - startTime;
         if (runTime > mRunningTimeThresholdInMilliseconds) {
-            mLocalLog.log(getMessageLogRec(message.what) + " was running for " + runTime + " ms");
+            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
+        }
+        if (runTime > METRICS_THRESHOLD_MILLIS) {
+            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
         }
         return ret;
     }
 
     @Override
     public void enter() {
-        Long startTime = System.currentTimeMillis();
-        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, getMessageLogRec(STATE_ENTER_CMD));
+        long startTime = System.currentTimeMillis();
+        String signatureToLog = getMessageLogRec(STATE_ENTER_CMD);
+        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
         enterImpl();
         Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
-        Long runTime = System.currentTimeMillis() - startTime;
+        long runTime = System.currentTimeMillis() - startTime;
         if (runTime > mRunningTimeThresholdInMilliseconds) {
-            mLocalLog.log(
-                    getMessageLogRec(STATE_ENTER_CMD) + " was running for " + runTime + " ms");
+            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
+        }
+        if (runTime > METRICS_THRESHOLD_MILLIS) {
+            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
         }
     }
 
     @Override
     public void exit() {
-        Long startTime = System.currentTimeMillis();
-        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, getMessageLogRec(STATE_EXIT_CMD));
+        long startTime = System.currentTimeMillis();
+        String signatureToLog = getMessageLogRec(STATE_EXIT_CMD);
+        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
         exitImpl();
         Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
-        Long runTime = System.currentTimeMillis() - startTime;
+        long runTime = System.currentTimeMillis() - startTime;
         if (runTime > mRunningTimeThresholdInMilliseconds) {
-            mLocalLog.log(getMessageLogRec(STATE_EXIT_CMD) + " was running for " + runTime + " ms");
+            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
+        }
+        if (runTime > METRICS_THRESHOLD_MILLIS) {
+            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java
index 3bd9ebf..a454dfe 100644
--- a/service/java/com/android/server/wifi/SarManager.java
+++ b/service/java/com/android/server/wifi/SarManager.java
@@ -31,10 +31,8 @@
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.PowerManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.modules.utils.HandlerExecutor;
@@ -95,19 +93,26 @@
     private final WifiNative mWifiNative;
     private final Handler mHandler;
 
-    /**
-     * Create new instance of SarManager.
-     */
-    SarManager(Context context,
-               TelephonyManager telephonyManager,
-               Looper looper,
-               WifiNative wifiNative) {
+    /** Create new instance of SarManager. */
+    SarManager(
+            Context context,
+            TelephonyManager telephonyManager,
+            Looper looper,
+            WifiNative wifiNative,
+            WifiDeviceStateChangeManager wifiDeviceStateChangeManager) {
         mContext = context;
         mTelephonyManager = telephonyManager;
         mWifiNative = wifiNative;
         mAudioManager = mContext.getSystemService(AudioManager.class);
         mHandler = new Handler(looper);
         mPhoneStateListener = new WifiPhoneStateListener(looper);
+        wifiDeviceStateChangeManager.registerStateChangeCallback(
+                new WifiDeviceStateChangeManager.StateChangeCallback() {
+                    @Override
+                    public void onScreenStateChanged(boolean screenOn) {
+                        handleScreenStateChanged(screenOn);
+                    }
+                });
     }
 
     /**
@@ -232,32 +237,9 @@
             /* Listen for Phone State changes */
             registerPhoneStateListener();
             registerVoiceStreamListener();
-            registerScreenListener();
         }
     }
 
-    private void registerScreenListener() {
-        // Listen to screen changes
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
-                            handleScreenStateChanged(true);
-                        } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
-                            handleScreenStateChanged(false);
-                        }
-                    }
-                }, filter, null, mHandler);
-        PowerManager powerManager = mContext.getSystemService(PowerManager.class);
-        handleScreenStateChanged(powerManager.isInteractive());
-    }
-
     private void registerVoiceStreamListener() {
         Log.i(TAG, "Registering for voice stream status");
 
diff --git a/service/java/com/android/server/wifi/SavedNetworkNominator.java b/service/java/com/android/server/wifi/SavedNetworkNominator.java
index 52d3f71..60cd348 100644
--- a/service/java/com/android/server/wifi/SavedNetworkNominator.java
+++ b/service/java/com/android/server/wifi/SavedNetworkNominator.java
@@ -24,7 +24,6 @@
 import android.util.LocalLog;
 import android.util.Pair;
 
-import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
 import java.util.List;
@@ -41,18 +40,16 @@
     private final LocalLog mLocalLog;
     private final WifiCarrierInfoManager mWifiCarrierInfoManager;
     private final WifiPseudonymManager mWifiPseudonymManager;
-    private final PasspointNetworkNominateHelper mPasspointNetworkNominateHelper;
     private final WifiPermissionsUtil mWifiPermissionsUtil;
     private final WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
 
     SavedNetworkNominator(WifiConfigManager configManager,
-            PasspointNetworkNominateHelper nominateHelper, LocalLog localLog,
+            LocalLog localLog,
             WifiCarrierInfoManager wifiCarrierInfoManager,
             WifiPseudonymManager wifiPseudonymManager,
             WifiPermissionsUtil wifiPermissionsUtil,
             WifiNetworkSuggestionsManager wifiNetworkSuggestionsManager) {
         mWifiConfigManager = configManager;
-        mPasspointNetworkNominateHelper = nominateHelper;
         mLocalLog = localLog;
         mWifiCarrierInfoManager = wifiCarrierInfoManager;
         mWifiPseudonymManager = wifiPseudonymManager;
@@ -85,9 +82,6 @@
      */
     @Override
     public void update(List<ScanDetail> scanDetails) {
-        // Update the matching profiles into WifiConfigManager, help displaying Passpoint networks
-        // in Wifi Picker
-        mPasspointNetworkNominateHelper.updatePasspointConfig(scanDetails);
     }
 
     /**
@@ -95,14 +89,19 @@
      *
      */
     @Override
-    public void nominateNetworks(List<ScanDetail> scanDetails,
+    public void nominateNetworks(@NonNull List<ScanDetail> scanDetails,
+            @NonNull List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates,
             boolean untrustedNetworkAllowed /* unused */,
             boolean oemPaidNetworkAllowed /* unused */,
             boolean oemPrivateNetworkAllowed /* unused */,
             Set<Integer> restrictedNetworkAllowedUids /* unused */,
             @NonNull OnConnectableListener onConnectableListener) {
-        findMatchedSavedNetworks(scanDetails, onConnectableListener);
-        findMatchedPasspointNetworks(scanDetails, onConnectableListener);
+        if (scanDetails != null) {
+            findMatchedSavedNetworks(scanDetails, onConnectableListener);
+        }
+        if (passpointCandidates != null) {
+            findMatchedPasspointNetworks(passpointCandidates, onConnectableListener);
+        }
     }
 
     private void findMatchedSavedNetworks(List<ScanDetail> scanDetails,
@@ -205,12 +204,13 @@
         }
     }
 
-    private void findMatchedPasspointNetworks(List<ScanDetail> scanDetails,
+    private void findMatchedPasspointNetworks(List<Pair<ScanDetail, WifiConfiguration>> candidates,
             OnConnectableListener onConnectableListener) {
-        List<Pair<ScanDetail, WifiConfiguration>> candidates =
-                mPasspointNetworkNominateHelper.getPasspointNetworkCandidates(scanDetails, false);
         for (Pair<ScanDetail, WifiConfiguration> candidate : candidates) {
             WifiConfiguration config = candidate.second;
+            if (config.fromWifiNetworkSuggestion) {
+                return;
+            }
             if (!config.allowAutojoin) {
                 continue;
             }
diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java
index 018aefd..fec34ed 100644
--- a/service/java/com/android/server/wifi/ScanDetail.java
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -25,7 +25,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wifi.hotspot2.NetworkDetail;
-import com.android.server.wifi.hotspot2.Utils;
 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
 import com.android.server.wifi.hotspot2.anqp.Constants;
 import com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement;
@@ -67,8 +66,11 @@
             channelWidth = networkDetail.getChannelWidth();
             centerFreq0 = networkDetail.getCenterfreq0();
             centerFreq1 = networkDetail.getCenterfreq1();
-            isPasspoint = caps.contains("EAP")
-                    && networkDetail.isInterworking() && networkDetail.getHSRelease() != null;
+            isPasspoint =
+                    caps.contains("EAP")
+                            && !caps.contains("SUITE_B_192")
+                            && networkDetail.isInterworking()
+                            && networkDetail.getHSRelease() != null;
             is80211McResponder = networkDetail.is80211McResponderSupport();
         }
         mScanResult = new ScanResult(wifiSsid, bssid, hessid, anqpDomainId, osuProviders, caps,
@@ -182,8 +184,7 @@
         if (networkDetail != null) {
             return networkDetail.toKeyString();
         } else {
-            return "'" + mScanResult.BSSID + "':" + Utils.macToSimpleString(
-                    Utils.parseMac(mScanResult.BSSID));
+            return "'" + mScanResult.SSID + "':" + mScanResult.BSSID;
         }
     }
 
@@ -212,11 +213,6 @@
 
     @Override
     public String toString() {
-        try {
-            return "'" + mScanResult.BSSID + "'/" + Utils.macToSimpleString(
-                    Utils.parseMac(mScanResult.BSSID));
-        } catch (IllegalArgumentException iae) {
-            return "'" + mScanResult.BSSID + "'/----";
-        }
+        return "'" + mScanResult.SSID + "'/" + mScanResult.BSSID;
     }
 }
diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java
index ee9e72f..651f487 100644
--- a/service/java/com/android/server/wifi/ScanRequestProxy.java
+++ b/service/java/com/android/server/wifi/ScanRequestProxy.java
@@ -39,6 +39,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.LruCache;
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
@@ -49,7 +50,6 @@
 import com.android.wifi.resources.R;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -90,6 +90,8 @@
     @VisibleForTesting
     public static final int SCAN_REQUEST_THROTTLE_INTERVAL_BG_APPS_MS = 30 * 60 * 1000;
 
+    public static final int PARTIAL_SCAN_CACHE_SIZE = 200;
+
     private final Context mContext;
     private final Handler mHandler;
     private final AppOpsManager mAppOps;
@@ -118,10 +120,13 @@
     // Values in the map = List of the last few scan request timestamps from the app.
     private final ArrayMap<Pair<Integer, String>, LinkedList<Long>> mLastScanTimestampsForFgApps =
             new ArrayMap();
-    // Scan results cached from the last full single scan request.
+    // Full scan results cached from the last full single scan request.
     // Stored as a map of bssid -> ScanResult to allow other clients to perform ScanResult lookup
     // for bssid more efficiently.
-    private final Map<String, ScanResult> mLastScanResultsMap = new HashMap<>();
+    private final Map<String, ScanResult> mFullScanCache = new HashMap<>();
+    // Partial scan results cached since the last full single scan request.
+    private final LruCache<String, ScanResult> mPartialScanCache =
+            new LruCache<>(PARTIAL_SCAN_CACHE_SIZE);
     // external ScanResultCallback tracker
     private final RemoteCallbackList<IScanResultsCallback> mRegisteredScanResultsCallbacks;
     private class GlobalScanListener implements WifiScanner.ScanListener {
@@ -152,19 +157,36 @@
                 Log.d(TAG, "Received " + scanResults.length + " scan results");
             }
             // Only process full band scan results.
-            if (WifiScanner.isFullBandScan(scanData.getScannedBandsInternal(), false)) {
-                // Store the last scan results & send out the scan completion broadcast.
-                mLastScanResultsMap.clear();
-                Arrays.stream(scanResults).forEach(s -> {
-                    ScanResult scanResult = mLastScanResultsMap.get(s.BSSID);
-                    // If a hidden network is configured, wificond may report two scan results for
-                    // the same BSS, ie. One with the SSID and another one without SSID. So avoid
-                    // overwriting the scan result of the same BSS with Hidden SSID scan result
-                    if (scanResult == null
-                            || TextUtils.isEmpty(scanResult.SSID) || !TextUtils.isEmpty(s.SSID)) {
-                        mLastScanResultsMap.put(s.BSSID, s);
+            boolean isFullBandScan = WifiScanner.isFullBandScan(
+                    scanData.getScannedBandsInternal(), false);
+            if (isFullBandScan) {
+                // If is full scan, clear the cache so only the latest data is available
+                mFullScanCache.clear();
+                mPartialScanCache.evictAll();
+            }
+            for (ScanResult s : scanResults) {
+                ScanResult scanResult = mFullScanCache.get(s.BSSID);
+                if (isFullBandScan && scanResult == null) {
+                    mFullScanCache.put(s.BSSID, s);
+                    continue;
+                }
+                // If a hidden network is configured, wificond may report two scan results for
+                // the same BSS, ie. One with the SSID and another one without SSID. So avoid
+                // overwriting the scan result of the same BSS with Hidden SSID scan result
+                if (scanResult != null) {
+                    if (TextUtils.isEmpty(scanResult.SSID) || !TextUtils.isEmpty(s.SSID)) {
+                        mFullScanCache.put(s.BSSID, s);
                     }
-                });
+                    continue;
+                }
+                scanResult = mPartialScanCache.get(s.BSSID);
+                if (scanResult == null
+                        || TextUtils.isEmpty(scanResult.SSID) || !TextUtils.isEmpty(s.SSID)) {
+                    mPartialScanCache.put(s.BSSID, s);
+                }
+            }
+            if (isFullBandScan) {
+                // Only trigger broadcasts for full scans
                 sendScanResultBroadcast(true);
                 sendScanResultsAvailableToCallbacks();
             }
@@ -548,7 +570,7 @@
      */
     public List<ScanResult> getScanResults() {
         // return a copy to prevent external modification
-        return new ArrayList<>(mLastScanResultsMap.values());
+        return new ArrayList<>(combineScanResultsCache().values());
     }
 
     /**
@@ -558,8 +580,11 @@
      * @return ScanResult for the corresponding bssid if found, null otherwise.
      */
     public @Nullable ScanResult getScanResult(@NonNull String bssid) {
-        ScanResult scanResult = mLastScanResultsMap.get(bssid);
-        if (scanResult == null) return null;
+        ScanResult scanResult = mFullScanCache.get(bssid);
+        if (scanResult == null) {
+            scanResult = mPartialScanCache.get(bssid);
+            if (scanResult == null) return null;
+        }
         // return a copy to prevent external modification
         return new ScanResult(scanResult);
     }
@@ -570,7 +595,8 @@
      */
     private void clearScanResults() {
         synchronized (mThrottleEnabledLock) {
-            mLastScanResultsMap.clear();
+            mFullScanCache.clear();
+            mPartialScanCache.evictAll();
             mLastScanTimestampForBgApps = 0;
             mLastScanTimestampsForFgApps.clear();
         }
@@ -604,6 +630,14 @@
         mRegisteredScanResultsCallbacks.finishBroadcast();
     }
 
+    /** Combine the full and partial scan results */
+    private Map<String, ScanResult> combineScanResultsCache() {
+        Map<String, ScanResult> combinedCache = new HashMap<>();
+        combinedCache.putAll(mFullScanCache);
+        combinedCache.putAll(mPartialScanCache.snapshot());
+        return combinedCache;
+    }
+
     /**
      * Register a callback on scan event
      * @param callback IScanResultListener instance to add.
@@ -649,49 +683,49 @@
 
     /** Indicate whether there are WPA2 personal only networks. */
     public boolean isWpa2PersonalOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForPskOnlyNetwork(r));
     }
 
     /** Indicate whether there are WPA3 only networks. */
     public boolean isWpa3PersonalOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForSaeOnlyNetwork(r));
     }
 
     /** Indicate whether there are WPA2/WPA3 transition mode networks. */
     public boolean isWpa2Wpa3PersonalTransitionNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, ScanResultUtil.createQuotedSsid(r.SSID))
                         && ScanResultUtil.isScanResultForPskSaeTransitionNetwork(r));
     }
 
     /** Indicate whether there are OPEN only networks. */
     public boolean isOpenOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForOpenOnlyNetwork(r));
     }
 
     /** Indicate whether there are OWE only networks. */
     public boolean isOweOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForOweOnlyNetwork(r));
     }
 
     /** Indicate whether there are WPA2 Enterprise only networks. */
     public boolean isWpa2EnterpriseOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(r));
     }
 
     /** Indicate whether there are WPA3 Enterprise only networks. */
     public boolean isWpa3EnterpriseOnlyNetworkInRange(String ssid) {
-        return mLastScanResultsMap.values().stream().anyMatch(r ->
+        return combineScanResultsCache().values().stream().anyMatch(r ->
                 TextUtils.equals(ssid, r.getWifiSsid().toString())
                         && ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(r));
     }
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 51ce4bc..c2e8968 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -18,11 +18,6 @@
 
 import static android.net.wifi.WifiManager.SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED;
 
-import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
-import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
-import static com.android.server.wifi.util.ApConfigUtil.ERROR_UNSUPPORTED_CONFIGURATION;
-import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
@@ -31,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.MacAddress;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApCapability;
 import android.net.wifi.SoftApConfiguration;
@@ -40,6 +36,7 @@
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.os.BatteryManager;
 import android.os.Handler;
@@ -51,6 +48,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IState;
 import com.android.internal.util.Preconditions;
@@ -68,6 +67,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
@@ -90,6 +91,84 @@
     @VisibleForTesting
     public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
             + " Soft AP Send Message Timeout on ";
+
+    // Start result codes. These should reflect the SoftApStopped.StartResult metrics codes.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            START_RESULT_UNKNOWN,
+            START_RESULT_SUCCESS,
+            START_RESULT_FAILURE_GENERAL,
+            START_RESULT_FAILURE_NO_CHANNEL,
+            START_RESULT_FAILURE_UNSUPPORTED_CONFIG,
+            START_RESULT_FAILURE_START_HAL,
+            START_RESULT_FAILURE_START_HOSTAPD,
+            START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED,
+            START_RESULT_FAILURE_INTERFACE_CONFLICT,
+            START_RESULT_FAILURE_CREATE_INTERFACE,
+            START_RESULT_FAILURE_SET_COUNTRY_CODE,
+            START_RESULT_FAILURE_SET_MAC_ADDRESS,
+            START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD,
+            START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND,
+            START_RESULT_FAILURE_ADD_AP_HOSTAPD,
+    })
+    public @interface StartResult {}
+
+    // Unknown start result
+    public static final int START_RESULT_UNKNOWN = 0;
+    // Successful start
+    public static final int START_RESULT_SUCCESS = 1;
+    // General failure
+    public static final int START_RESULT_FAILURE_GENERAL = 2;
+    // Failed due to no channel available
+    public static final int START_RESULT_FAILURE_NO_CHANNEL = 3;
+    // Failed due to config being unsupported
+    public static final int START_RESULT_FAILURE_UNSUPPORTED_CONFIG = 4;
+    // Failed to start the HAL
+    public static final int START_RESULT_FAILURE_START_HAL = 5;
+    // Failed to start hostapd
+    public static final int START_RESULT_FAILURE_START_HOSTAPD = 6;
+    // Failed due to interface conflict with user rejection
+    public static final int START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED = 7;
+    // Failed due to interface conflict
+    public static final int START_RESULT_FAILURE_INTERFACE_CONFLICT = 8;
+    // Failed to create interface in vendor HAL
+    public static final int START_RESULT_FAILURE_CREATE_INTERFACE = 9;
+    // Failed to set country code
+    public static final int START_RESULT_FAILURE_SET_COUNTRY_CODE = 10;
+    // Failed to set mac address
+    public static final int START_RESULT_FAILURE_SET_MAC_ADDRESS = 11;
+    // Failed to register AP callback with hostapd
+    public static final int START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD = 12;
+    // Failed to register AP callback with wificond
+    public static final int START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND = 13;
+    // Failed to add AP to hostapd
+    public static final int START_RESULT_FAILURE_ADD_AP_HOSTAPD = 14;
+
+    // Stop event codes. These should reflect the SoftApStopped.StopEvent metrics codes.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            STOP_EVENT_UNKNOWN,
+            STOP_EVENT_STOPPED,
+            STOP_EVENT_INTERFACE_DOWN,
+            STOP_EVENT_INTERFACE_DESTROYED,
+            STOP_EVENT_HOSTAPD_FAILURE,
+            STOP_EVENT_NO_USAGE_TIMEOUT,
+    })
+    public @interface StopEvent {}
+
+    // Unknown stop event
+    public static final int STOP_EVENT_UNKNOWN = 0;
+    // Stopped by the user
+    public static final int STOP_EVENT_STOPPED = 1;
+    // Stopped due to interface down
+    public static final int STOP_EVENT_INTERFACE_DOWN = 2;
+    // Stopped due to interface destroyed
+    public static final int STOP_EVENT_INTERFACE_DESTROYED = 3;
+    // Stopped due to hostapd failure
+    public static final int STOP_EVENT_HOSTAPD_FAILURE = 4;
+    // Stopped due to no usage timeout
+    public static final int STOP_EVENT_NO_USAGE_TIMEOUT = 5;
+
     private final WifiContext mContext;
     private final FrameworkFacade mFrameworkFacade;
     private final WifiNative mWifiNative;
@@ -134,16 +213,13 @@
     private boolean mVerboseLoggingEnabled = false;
 
     /**
-     * Original configuration, which is the passed configuration when init or
-     * the user-configured {@code WifiApConfigStore#getApConfiguration}tethering}
-     * settings when input is null.
+     * The specified configuration passed in during initialization or during a configuration update
+     * that doesn't require a restart.
      *
-     * Use it when doing configuration update to know if the input configuration was changed.
-     * For others use case, it should use {@code mCurrentSoftApConfiguration}.
+     * Use it when doing configuration update to know if the input configuration was changed. For
+     * others use case, it should use {@code mCurrentSoftApConfiguration}.
      */
-    @NonNull
-    private final SoftApModeConfiguration mOriginalModeConfiguration;
-
+    @NonNull private SoftApModeConfiguration mSpecifiedModeConfiguration;
 
     /**
      * Current Soft AP configuration which is used to start Soft AP.
@@ -199,6 +275,10 @@
 
     private boolean mIsPlugged = false;
 
+    private int mCurrentApState = WifiManager.WIFI_AP_STATE_DISABLED;
+
+    private boolean mIsSoftApStartedEventWritten = false;
+
     /**
      * A map stores shutdown timeouts for each Soft Ap instance.
      * There are three timeout messages now.
@@ -388,8 +468,12 @@
             // may still be null if we fail to load the default config
         }
         // Store mode configuration before update the configuration.
-        mOriginalModeConfiguration = new SoftApModeConfiguration(apConfig.getTargetMode(),
-                mCurrentSoftApConfiguration, mCurrentSoftApCapability, mCountryCode);
+        mSpecifiedModeConfiguration =
+                new SoftApModeConfiguration(
+                        apConfig.getTargetMode(),
+                        mCurrentSoftApConfiguration,
+                        mCurrentSoftApCapability,
+                        mCountryCode);
         if (mCurrentSoftApConfiguration != null) {
             mIsUnsetBssid = mCurrentSoftApConfiguration.getBssid() == null;
             if (mCurrentSoftApCapability.areFeaturesSupported(
@@ -465,6 +549,12 @@
         return timeout > 0 ? timeout : mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis;
     }
 
+    private List<OuiKeyedData> getVendorData() {
+        return (SdkLevel.isAtLeastV() && mCurrentSoftApConfiguration != null)
+                ? mCurrentSoftApConfiguration.getVendorData()
+                : new ArrayList<>();
+    }
+
     private String getHighestFrequencyInstance(Set<String> candidateInstances) {
         int currentHighestFrequencyOnAP = 0;
         String highestFrequencyInstance = null;
@@ -538,8 +628,11 @@
      * Retrieve the {@link SoftApModeConfiguration} instance associated with this mode manager.
      */
     public SoftApModeConfiguration getSoftApModeConfiguration() {
-        return new SoftApModeConfiguration(mOriginalModeConfiguration.getTargetMode(),
-                mCurrentSoftApConfiguration, mCurrentSoftApCapability, mCountryCode);
+        return new SoftApModeConfiguration(
+                mSpecifiedModeConfiguration.getTargetMode(),
+                mSpecifiedModeConfiguration.getSoftApConfiguration(),
+                mCurrentSoftApCapability,
+                mCountryCode);
     }
 
     /**
@@ -569,8 +662,9 @@
         pw.println("mApInterfaceName: " + mApInterfaceName);
         pw.println("mIfaceIsUp: " + mIfaceIsUp);
         pw.println("mSoftApCountryCode: " + mCountryCode);
-        pw.println("mOriginalModeConfiguration.targetMode: "
-                + mOriginalModeConfiguration.getTargetMode());
+        pw.println(
+                "mSpecifiedModeConfiguration.targetMode: "
+                        + mSpecifiedModeConfiguration.getTargetMode());
         pw.println("mCurrentSoftApConfiguration: " + mCurrentSoftApConfiguration);
         pw.println("mCurrentSoftApCapability: " + mCurrentSoftApCapability);
         pw.println("getConnectedClientList().size(): " + getConnectedClientList().size());
@@ -629,6 +723,7 @@
      * @param reason       Failure reason if the new AP state is in failure state
      */
     private void updateApState(int newState, int currentState, int reason) {
+        mCurrentApState = newState;
         mSoftApCallback.onStateChanged(newState, reason);
 
         //send the AP state change broadcast
@@ -642,7 +737,8 @@
         }
 
         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
-        intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mOriginalModeConfiguration.getTargetMode());
+        intent.putExtra(
+                WifiManager.EXTRA_WIFI_AP_MODE, mSpecifiedModeConfiguration.getTargetMode());
         if (SdkLevel.isAtLeastSv2()) {
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                     android.Manifest.permission.ACCESS_WIFI_STATE);
@@ -665,16 +761,16 @@
             if (mWifiNative.isApSetMacAddressSupported(mApInterfaceName)) {
                 if (!mWifiNative.setApMacAddress(mApInterfaceName, mac)) {
                     Log.e(getTag(), "failed to set explicitly requested MAC address");
-                    return ERROR_GENERIC;
+                    return START_RESULT_FAILURE_SET_MAC_ADDRESS;
                 }
             } else if (!mIsUnsetBssid) {
                 // If hardware does not support MAC address setter,
                 // only report the error for non randomization.
-                return ERROR_UNSUPPORTED_CONFIGURATION;
+                return START_RESULT_FAILURE_UNSUPPORTED_CONFIG;
             }
         }
 
-        return SUCCESS;
+        return START_RESULT_SUCCESS;
     }
 
     /**
@@ -693,17 +789,17 @@
         return false;
     }
 
-    private int setCountryCode() {
+    private boolean setCountryCode() {
         int band = mCurrentSoftApConfiguration.getBand();
         if (TextUtils.isEmpty(mCountryCode)) {
             if (band == SoftApConfiguration.BAND_5GHZ || band == SoftApConfiguration.BAND_6GHZ) {
                 // Country code is mandatory for 5GHz/6GHz band.
                 Log.e(getTag(), "Invalid country code, "
                         + "required for setting up soft ap in band:" + band);
-                return ERROR_GENERIC;
+                return false;
             }
             // Absence of country code is not fatal for 2Ghz & Any band options.
-            return SUCCESS;
+            return true;
         }
         if (!mWifiNative.setApCountryCode(
                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
@@ -712,19 +808,19 @@
                 // 5GHz/6GHz band.
                 Log.e(getTag(), "Failed to set country code, "
                         + "required for setting up soft ap in band: " + band);
-                return ERROR_GENERIC;
+                return false;
             }
             // Failure to set country code is not fatal for other band options.
         }
-        return SUCCESS;
+        return true;
     }
 
     /**
      * Start a soft AP instance as configured.
      *
-     * @return integer result code
+     * @return One of {@link StartResult}
      */
-    private int startSoftAp() {
+    private @StartResult int startSoftAp() {
         if (SdkLevel.isAtLeastS()) {
             Log.d(getTag(), "startSoftAp: channels " + mCurrentSoftApConfiguration.getChannels()
                     + " iface " + mApInterfaceName + " country " + mCountryCode);
@@ -732,21 +828,21 @@
             Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.getBand());
         }
 
-        int result = setMacAddress();
-        if (result != SUCCESS) {
-            return result;
+        int startResult = setMacAddress();
+        if (startResult != START_RESULT_SUCCESS) {
+            return startResult;
         }
 
         // Make a copy of configuration for updating AP band and channel.
         SoftApConfiguration.Builder localConfigBuilder =
                 new SoftApConfiguration.Builder(mCurrentSoftApConfiguration);
 
-        result = ApConfigUtil.updateApChannelConfig(
+        startResult = ApConfigUtil.updateApChannelConfig(
                 mWifiNative, mCoexManager, mContext.getResources(), mCountryCode,
                 localConfigBuilder, mCurrentSoftApConfiguration, mCurrentSoftApCapability);
-        if (result != SUCCESS) {
+        if (startResult != START_RESULT_SUCCESS) {
             Log.e(getTag(), "Failed to update AP band and channel");
-            return result;
+            return startResult;
         }
 
         if (mCurrentSoftApConfiguration.isHiddenSsid()) {
@@ -757,40 +853,53 @@
                 mCurrentSoftApConfiguration, mCurrentSoftApCapability)) {
             Log.d(getTag(), "Unsupported Configuration detect! config = "
                     + mCurrentSoftApConfiguration);
-            return ERROR_UNSUPPORTED_CONFIGURATION;
+            return START_RESULT_FAILURE_UNSUPPORTED_CONFIG;
         }
 
-        if (!mWifiNative.startSoftAp(mApInterfaceName,
-                  localConfigBuilder.build(),
-                  mOriginalModeConfiguration.getTargetMode() ==  WifiManager.IFACE_IP_MODE_TETHERED,
-                  mSoftApHalCallback)) {
+        startResult =
+                mWifiNative.startSoftAp(
+                        mApInterfaceName,
+                        localConfigBuilder.build(),
+                        mSpecifiedModeConfiguration.getTargetMode()
+                                == WifiManager.IFACE_IP_MODE_TETHERED,
+                        mSoftApHalCallback);
+        if (startResult != START_RESULT_SUCCESS) {
             Log.e(getTag(), "Soft AP start failed");
-            return ERROR_GENERIC;
+            return startResult;
         }
 
         mWifiDiagnostics.startLogging(mApInterfaceName);
         mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
         Log.d(getTag(), "Soft AP is started ");
 
-        return SUCCESS;
+        return START_RESULT_SUCCESS;
     }
 
-    private void handleStartSoftApFailure(int result) {
-        if (result == SUCCESS) {
+    /**
+     * Handles a start failure and writes the start failure metrics.
+     * @param startResult One of {@link StartResult}.
+     */
+    private void handleStartSoftApFailure(@StartResult int startResult) {
+        if (startResult == START_RESULT_SUCCESS) {
+            Log.wtf(TAG, "handleStartSoftApFailure called with START_RESULT_SUCCESS");
             return;
         }
-        int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
-        if (result == ERROR_NO_CHANNEL) {
-            failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
-        } else if (result == ERROR_UNSUPPORTED_CONFIGURATION) {
-            failureReason = WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;
+
+        int wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_GENERAL;
+        if (startResult == START_RESULT_FAILURE_NO_CHANNEL) {
+            wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+        } else if (startResult == START_RESULT_FAILURE_UNSUPPORTED_CONFIG) {
+            wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION;
+        } else if (startResult == START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED) {
+            wifiManagerFailureReason = WifiManager.SAP_START_FAILURE_USER_REJECTED;
         }
         updateApState(WifiManager.WIFI_AP_STATE_FAILED,
-                WifiManager.WIFI_AP_STATE_ENABLING,
-                failureReason);
+                mCurrentApState,
+                wifiManagerFailureReason);
         stopSoftAp();
-        mWifiMetrics.incrementSoftApStartResult(false, failureReason);
+        mWifiMetrics.incrementSoftApStartResult(false, wifiManagerFailureReason);
         mModeListener.onStartFailure(SoftApManager.this);
+        writeSoftApStartedEvent(startResult);
     }
 
     /**
@@ -1044,22 +1153,28 @@
             public boolean processMessageImpl(Message message) {
                 switch (message.what) {
                     case CMD_STOP:
+                        writeSoftApStoppedEvent(STOP_EVENT_STOPPED);
                         quitNow();
                         break;
                     case CMD_START:
+                        boolean isCountryCodeChanged = false;
                         mRequestorWs = (WorkSource) message.obj;
                         WifiSsid wifiSsid = mCurrentSoftApConfiguration != null
                                 ? mCurrentSoftApConfiguration.getWifiSsid() : null;
                         if (wifiSsid == null || wifiSsid.getBytes().length == 0) {
                             Log.e(getTag(), "Unable to start soft AP without valid configuration");
-                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
-                                    WifiManager.WIFI_AP_STATE_DISABLED,
-                                    WifiManager.SAP_START_FAILURE_GENERAL);
-                            mWifiMetrics.incrementSoftApStartResult(
-                                    false, WifiManager.SAP_START_FAILURE_GENERAL);
-                            mModeListener.onStartFailure(SoftApManager.this);
+                            handleStartSoftApFailure(START_RESULT_FAILURE_GENERAL);
                             break;
                         }
+                        if (!TextUtils.isEmpty(mCountryCode)
+                                && !TextUtils.equals(
+                                mCountryCode, mCurrentSoftApCapability.getCountryCode())) {
+                            isCountryCodeChanged = true;
+                            Log.i(getTag(), "CountryCode changed - "
+                                    + " mCountryCode = " + mCountryCode
+                                    + ", base country in SoftApCapability = "
+                                    + mCurrentSoftApCapability.getCountryCode());
+                        }
                         if (isBridgedMode()) {
                             boolean isFallbackToSingleAp = false;
                             final List<ClientModeManager> cmms =
@@ -1090,27 +1205,32 @@
                                     isFallbackToSingleAp = true;
                                 }
                             }
-                            if (mWifiNative.isSoftApInstanceDiedHandlerSupported()
-                                    && !TextUtils.equals(mCountryCode,
-                                      mCurrentSoftApCapability.getCountryCode())) {
-                                Log.i(getTag(), "CountryCode changed, bypass the supported band"
-                                        + "capability check, mCountryCode = " + mCountryCode
-                                        + ", base country in SoftApCapability = "
-                                        + mCurrentSoftApCapability.getCountryCode());
-                            } else {
+                            if (isCountryCodeChanged && mCountryCode.equalsIgnoreCase(
+                                    mContext.getResources().getString(
+                                            R.string.config_wifiDriverWorldModeCountryCode))) {
+                                Log.i(getTag(), "Country code changed to world mode"
+                                        + " - fallback to single AP");
+                                isFallbackToSingleAp = true;
+                            }
+                            if (!isCountryCodeChanged) {
                                 SoftApConfiguration tempConfig =
                                         ApConfigUtil.removeUnavailableBandsFromConfig(
                                                 mCurrentSoftApConfiguration,
-                                                mCurrentSoftApCapability, mCoexManager, mContext);
+                                                mCurrentSoftApCapability,
+                                                mCoexManager,
+                                                mContext);
                                 if (tempConfig == null) {
-                                    handleStartSoftApFailure(ERROR_UNSUPPORTED_CONFIGURATION);
+                                    handleStartSoftApFailure(
+                                            START_RESULT_FAILURE_UNSUPPORTED_CONFIG);
                                     break;
                                 }
                                 mCurrentSoftApConfiguration = tempConfig;
                                 if (mCurrentSoftApConfiguration.getBands().length == 1) {
                                     isFallbackToSingleAp = true;
-                                    Log.i(getTag(), "Removed unavailable bands"
-                                            + " - fallback to single AP");
+                                    Log.i(
+                                            getTag(),
+                                            "Removed unavailable bands"
+                                                    + " - fallback to single AP");
                                 }
                             }
                             // Fall back to Single AP if it's not possible to create a Bridged AP.
@@ -1144,15 +1264,17 @@
                         // Note: 6GHz only band is already handled by initial validation
                         SoftApConfiguration tempConfig =
                                 ApConfigUtil.remove6gBandForUnsupportedSecurity(
-                                    mCurrentSoftApConfiguration);
+                                        mContext.getResources(),
+                                        mCurrentSoftApConfiguration, isBridgedMode());
                         if (tempConfig == null) {
-                            handleStartSoftApFailure(ERROR_UNSUPPORTED_CONFIGURATION);
+                            handleStartSoftApFailure(START_RESULT_FAILURE_UNSUPPORTED_CONFIG);
                             break;
                         }
                         mCurrentSoftApConfiguration = tempConfig;
                         // Don't show the ICM dialog if this is for tethering.
-                        boolean bypassDialog = mOriginalModeConfiguration.getTargetMode()
-                                == WifiManager.IFACE_IP_MODE_TETHERED;
+                        boolean bypassDialog =
+                                mSpecifiedModeConfiguration.getTargetMode()
+                                        == WifiManager.IFACE_IP_MODE_TETHERED;
                         int icmResult = mInterfaceConflictManager
                                 .manageInterfaceConflictForStateMachine(
                                         TAG, message, mStateMachine, mWaitingForIcmDialogState,
@@ -1162,53 +1284,58 @@
                                         mRequestorWs, bypassDialog);
                         if (icmResult == InterfaceConflictManager.ICM_ABORT_COMMAND) {
                             Log.e(getTag(), "User refused to set up interface");
-                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
-                                    WifiManager.WIFI_AP_STATE_DISABLED,
-                                    WifiManager.SAP_START_FAILURE_USER_REJECTED);
-                            mModeListener.onStartFailure(SoftApManager.this);
+                            handleStartSoftApFailure(
+                                    START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED);
                             break;
                         } else if (icmResult
                                 == InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER) {
                             break;
                         }
 
+                        // Only check if it's possible to create single AP, since a DBS request
+                        // already falls back to single AP if we can't create DBS.
+                        if (!mWifiNative.isItPossibleToCreateApIface(mRequestorWs)) {
+                            handleStartSoftApFailure(START_RESULT_FAILURE_INTERFACE_CONFLICT);
+                            break;
+                        }
+                        if (SdkLevel.isAtLeastT()
+                                && mCurrentSoftApConfiguration.isIeee80211beEnabled()
+                                && !mCurrentSoftApCapability.areFeaturesSupported(
+                                SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE)) {
+                            Log.d(getTag(), "11BE is not supported, removing from configuration");
+                            mCurrentSoftApConfiguration = new SoftApConfiguration
+                                    .Builder(mCurrentSoftApConfiguration)
+                                    .setIeee80211beEnabled(false)
+                                    .build();
+                        }
                         mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
                                 mWifiNativeInterfaceCallback, mRequestorWs,
                                 mCurrentSoftApConfiguration.getBand(), isBridgeRequired(),
-                                SoftApManager.this);
+                                SoftApManager.this, getVendorData());
                         if (TextUtils.isEmpty(mApInterfaceName)) {
                             Log.e(getTag(), "setup failure when creating ap interface.");
-                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
-                                    WifiManager.WIFI_AP_STATE_DISABLED,
-                                    WifiManager.SAP_START_FAILURE_GENERAL);
-                            mWifiMetrics.incrementSoftApStartResult(
-                                    false, WifiManager.SAP_START_FAILURE_GENERAL);
-                            mModeListener.onStartFailure(SoftApManager.this);
+                            handleStartSoftApFailure(START_RESULT_FAILURE_CREATE_INTERFACE);
                             break;
                         }
                         mSoftApNotifier.dismissSoftApShutdownTimeoutExpiredNotification();
                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
-                        int result = setCountryCode();
-                        if (result != SUCCESS) {
-                            handleStartSoftApFailure(result);
+                        if (!setCountryCode()) {
+                            handleStartSoftApFailure(START_RESULT_FAILURE_SET_COUNTRY_CODE);
                             break;
                         }
-                        if (mContext.getResources().getBoolean(
-                                R.bool.config_wifiDriverSupportedNl80211RegChangedEvent)
-                                && !TextUtils.isEmpty(mCountryCode)
-                                && !TextUtils.equals(
-                                        mCountryCode, mCurrentSoftApCapability.getCountryCode())) {
+                        if (isCountryCodeChanged) {
                             Log.i(getTag(), "Need to wait for driver country code update before"
                                     + " starting");
                             transitionTo(mWaitingForDriverCountryCodeChangedState);
                             break;
                         }
-                        result = startSoftAp();
-                        if (result != SUCCESS) {
-                            handleStartSoftApFailure(result);
+                        int startResult = startSoftAp();
+                        if (startResult != START_RESULT_SUCCESS) {
+                            handleStartSoftApFailure(startResult);
                             break;
                         }
+
                         transitionTo(mStartedState);
                         break;
                     case CMD_UPDATE_CAPABILITY:
@@ -1216,13 +1343,20 @@
                         mCurrentSoftApCapability = new SoftApCapability(capability);
                         updateSafeChannelFrequencyList();
                         break;
-                    case CMD_UPDATE_CONFIG:
+                    case CMD_UPDATE_CONFIG: {
                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
+                        mSpecifiedModeConfiguration =
+                                new SoftApModeConfiguration(
+                                        mSpecifiedModeConfiguration.getTargetMode(),
+                                        newConfig,
+                                        mCurrentSoftApCapability,
+                                        mCountryCode);
                         Log.d(getTag(), "Configuration changed to " + newConfig);
                         // Idle mode, update all configurations.
                         mCurrentSoftApConfiguration = newConfig;
                         configureInternalConfiguration();
                         break;
+                    }
                     case CMD_UPDATE_COUNTRY_CODE:
                         String countryCode = (String) message.obj;
                         if (!TextUtils.isEmpty(countryCode)) {
@@ -1274,6 +1408,33 @@
                             ApConfigUtil.updateSoftApCapabilityWithAvailableChannelList(
                                     mCurrentSoftApCapability, mContext, mWifiNative);
                     updateSafeChannelFrequencyList();
+                    if (isBridgedMode()) {
+                        SoftApConfiguration tempConfig =
+                                ApConfigUtil.removeUnavailableBandsFromConfig(
+                                        mCurrentSoftApConfiguration,
+                                        mCurrentSoftApCapability, mCoexManager, mContext);
+                        if (tempConfig == null) {
+                            handleStartSoftApFailure(START_RESULT_FAILURE_UNSUPPORTED_CONFIG);
+                            transitionTo(mIdleState);
+                            return HANDLED;
+                        }
+                        mCurrentSoftApConfiguration = tempConfig;
+                        if (mCurrentSoftApConfiguration.getBands().length == 1) {
+                            Log.i(getTag(), "Moving to single AP after updating the CC and band."
+                                    + " Teardown bridged interface and setup single AP interface");
+                            mWifiNative.teardownInterface(mApInterfaceName);
+                            mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
+                                    mWifiNativeInterfaceCallback, mRequestorWs,
+                                    mCurrentSoftApConfiguration.getBand(), isBridgeRequired(),
+                                    SoftApManager.this, getVendorData());
+                            if (TextUtils.isEmpty(mApInterfaceName)) {
+                                Log.e(getTag(), "setup failure when creating single AP iface");
+                                handleStartSoftApFailure(START_RESULT_FAILURE_GENERAL);
+                                transitionTo(mIdleState);
+                                return HANDLED;
+                            }
+                        }
+                    }
                 } else if (message.what == CMD_DRIVER_COUNTRY_CODE_CHANGE_TIMED_OUT) {
                     Log.i(getTag(), "Timed out waiting for driver country code change, "
                             + "continue starting anyway.");
@@ -1283,9 +1444,9 @@
                     deferMessage(message);
                     return HANDLED;
                 }
-                int result = startSoftAp();
-                if (result != SUCCESS) {
-                    handleStartSoftApFailure(result);
+                int startResult = startSoftAp();
+                if (startResult != START_RESULT_SUCCESS) {
+                    handleStartSoftApFailure(startResult);
                     transitionTo(mIdleState);
                     return HANDLED;
                 }
@@ -1524,7 +1685,7 @@
                 mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
                         getConnectedClientList().size(),
                         mConnectedClientWithApInfoMap.get(apInstanceIdentifier).size(),
-                        mOriginalModeConfiguration.getTargetMode(),
+                        mSpecifiedModeConfiguration.getTargetMode(),
                         mCurrentSoftApInfoMap.get(apInstanceIdentifier));
 
                 rescheduleTimeoutMessages(apInstanceIdentifier);
@@ -1555,12 +1716,14 @@
                                 mConnectedClientWithApInfoMap, isBridgeRequired());
                         if (isClientConnected) {
                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
-                                    getConnectedClientList().size(), 0,
-                                    mOriginalModeConfiguration.getTargetMode(), apInfo);
+                                    getConnectedClientList().size(),
+                                    0,
+                                    mSpecifiedModeConfiguration.getTargetMode(),
+                                    apInfo);
                         }
                         if (isBridgeRequired()) {
                             mWifiMetrics.addSoftApInstanceDownEventInDualMode(
-                                    mOriginalModeConfiguration.getTargetMode(), apInfo);
+                                    mSpecifiedModeConfiguration.getTargetMode(), apInfo);
                         }
                     }
                     return;
@@ -1609,7 +1772,8 @@
                         && apInfo.getBandwidth() != SoftApInfo.CHANNEL_WIDTH_INVALID) {
                     mWifiMetrics.addSoftApChannelSwitchedEvent(
                             new ArrayList<>(mCurrentSoftApInfoMap.values()),
-                            mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
+                            mSpecifiedModeConfiguration.getTargetMode(),
+                            isBridgeRequired());
                     updateUserBandPreferenceViolationMetricsIfNeeded(apInfo);
                 }
             }
@@ -1636,14 +1800,20 @@
                     // the interface was up, but goes down
                     sendMessage(CMD_INTERFACE_DOWN);
                 }
-                mWifiMetrics.addSoftApUpChangedEvent(isUp,
-                        mOriginalModeConfiguration.getTargetMode(),
-                        mDefaultShutdownTimeoutMillis, isBridgeRequired());
+                mWifiMetrics.addSoftApUpChangedEvent(
+                        isUp,
+                        mSpecifiedModeConfiguration.getTargetMode(),
+                        mDefaultShutdownTimeoutMillis,
+                        isBridgeRequired());
                 if (isUp) {
-                    mWifiMetrics.updateSoftApConfiguration(mCurrentSoftApConfiguration,
-                            mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
-                    mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
-                            mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
+                    mWifiMetrics.updateSoftApConfiguration(
+                            mCurrentSoftApConfiguration,
+                            mSpecifiedModeConfiguration.getTargetMode(),
+                            isBridgeRequired());
+                    mWifiMetrics.updateSoftApCapability(
+                            mCurrentSoftApCapability,
+                            mSpecifiedModeConfiguration.getTargetMode(),
+                            isBridgeRequired());
                 }
             }
 
@@ -1667,6 +1837,7 @@
                 mConnectedClientWithApInfoMap.clear();
                 mPendingDisconnectClients.clear();
                 mEverReportMetricsForMaxClient = false;
+                writeSoftApStartedEvent(START_RESULT_SUCCESS);
             }
 
             @Override
@@ -1682,9 +1853,10 @@
                     for (List<WifiClient> it : mConnectedClientWithApInfoMap.values()) {
                         if (it.size() != 0) {
                             mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(
-                                    0, 0, mOriginalModeConfiguration.getTargetMode(),
-                                    mCurrentSoftApInfoMap
-                                            .get(it.get(0).getApInstanceIdentifier()));
+                                    0,
+                                    0,
+                                    mSpecifiedModeConfiguration.getTargetMode(),
+                                    mCurrentSoftApInfoMap.get(it.get(0).getApInstanceIdentifier()));
                         }
                     }
                     mConnectedClientWithApInfoMap.clear();
@@ -1703,9 +1875,11 @@
                 }
                 // Need this here since we are exiting |Started| state and won't handle any
                 // future CMD_INTERFACE_STATUS_CHANGED events after this point
-                mWifiMetrics.addSoftApUpChangedEvent(false,
-                        mOriginalModeConfiguration.getTargetMode(),
-                        mDefaultShutdownTimeoutMillis, isBridgeRequired());
+                mWifiMetrics.addSoftApUpChangedEvent(
+                        false,
+                        mSpecifiedModeConfiguration.getTargetMode(),
+                        mDefaultShutdownTimeoutMillis,
+                        isBridgeRequired());
                 updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
                         WifiManager.WIFI_AP_STATE_DISABLING, 0);
 
@@ -1811,6 +1985,7 @@
                         Log.i(getTag(), "Timeout message received. Stopping soft AP.");
                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
+                        writeSoftApStoppedEvent(STOP_EVENT_NO_USAGE_TIMEOUT);
                         quitNow();
                         break;
                     case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT_ON_ONE_INSTANCE:
@@ -1833,6 +2008,7 @@
                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
                         mIfaceIsDestroyed = true;
+                        writeSoftApStoppedEvent(STOP_EVENT_INTERFACE_DESTROYED);
                         quitNow();
                         break;
                     case CMD_FAILURE:
@@ -1861,6 +2037,7 @@
                             }
                         }
                         Log.w(getTag(), "hostapd failure, stop and report failure");
+                        writeSoftApStoppedEvent(STOP_EVENT_HOSTAPD_FAILURE);
                         /* fall through */
                     case CMD_INTERFACE_DOWN:
                         Log.w(getTag(), "interface error, stop and report failure");
@@ -1869,22 +2046,31 @@
                                 WifiManager.SAP_START_FAILURE_GENERAL);
                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                 WifiManager.WIFI_AP_STATE_FAILED, 0);
+                        writeSoftApStoppedEvent(STOP_EVENT_INTERFACE_DOWN);
                         quitNow();
                         break;
                     case CMD_UPDATE_CAPABILITY:
                         SoftApCapability capability = (SoftApCapability) message.obj;
                         mCurrentSoftApCapability = new SoftApCapability(capability);
-                        mWifiMetrics.updateSoftApCapability(mCurrentSoftApCapability,
-                                mOriginalModeConfiguration.getTargetMode(), isBridgeRequired());
+                        mWifiMetrics.updateSoftApCapability(
+                                mCurrentSoftApCapability,
+                                mSpecifiedModeConfiguration.getTargetMode(),
+                                isBridgeRequired());
                         updateClientConnection();
                         updateSafeChannelFrequencyList();
                         break;
-                    case CMD_UPDATE_CONFIG:
+                    case CMD_UPDATE_CONFIG: {
                         SoftApConfiguration newConfig = (SoftApConfiguration) message.obj;
                         SoftApConfiguration originalConfig =
-                                mOriginalModeConfiguration.getSoftApConfiguration();
+                                mSpecifiedModeConfiguration.getSoftApConfiguration();
                         if (!ApConfigUtil.checkConfigurationChangeNeedToRestart(
                                 originalConfig, newConfig)) {
+                            mSpecifiedModeConfiguration =
+                                    new SoftApModeConfiguration(
+                                            mSpecifiedModeConfiguration.getTargetMode(),
+                                            newConfig,
+                                            mCurrentSoftApCapability,
+                                            mCountryCode);
                             Log.d(getTag(), "Configuration changed to " + newConfig);
                             if (mCurrentSoftApConfiguration.getMaxNumberOfClients()
                                     != newConfig.getMaxNumberOfClients()) {
@@ -1893,11 +2079,11 @@
                             }
                             boolean needRescheduleTimeoutMessage =
                                     mCurrentSoftApConfiguration.getShutdownTimeoutMillis()
-                                    != newConfig.getShutdownTimeoutMillis()
-                                    || mTimeoutEnabled != newConfig.isAutoShutdownEnabled()
-                                    || mBridgedModeOpportunisticsShutdownTimeoutEnabled
-                                    != newConfig
-                                    .isBridgedModeOpportunisticShutdownEnabledInternal();
+                                            != newConfig.getShutdownTimeoutMillis()
+                                            || mTimeoutEnabled != newConfig.isAutoShutdownEnabled()
+                                            || mBridgedModeOpportunisticsShutdownTimeoutEnabled
+                                            != newConfig
+                                            .isBridgedModeOpportunisticShutdownEnabledInternal();
                             updateChangeableConfiguration(newConfig);
                             updateClientConnection();
                             if (needRescheduleTimeoutMessage) {
@@ -1915,13 +2101,14 @@
                             }
                             mWifiMetrics.updateSoftApConfiguration(
                                     mCurrentSoftApConfiguration,
-                                    mOriginalModeConfiguration.getTargetMode(),
+                                    mSpecifiedModeConfiguration.getTargetMode(),
                                     isBridgeRequired());
                         } else {
                             Log.d(getTag(), "Ignore the config: " + newConfig
                                     + " update since it requires restart");
                         }
                         break;
+                    }
                     case CMD_UPDATE_COUNTRY_CODE:
                         String countryCode = (String) message.obj;
                         if (!TextUtils.isEmpty(countryCode)
@@ -2012,4 +2199,84 @@
             }
         }
     }
+
+    // Logging code
+
+    private int getCurrentStaFreqMhz() {
+        int staFreqMhz = WifiInfo.UNKNOWN_FREQUENCY;
+        for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) {
+            WifiInfo wifiConnectedInfo = cmm.getConnectionInfo();
+            if (wifiConnectedInfo != null) {
+                staFreqMhz = wifiConnectedInfo.getFrequency();
+                break;
+            }
+        }
+        return staFreqMhz;
+    }
+
+    /**
+     * Writes the SoftApStarted event to metrics. Only the first call will write the metrics, any
+     * subsequent calls will be ignored.
+     */
+    public void writeSoftApStartedEvent(@StartResult int startResult) {
+        if (mIsSoftApStartedEventWritten) {
+            return;
+        }
+        mIsSoftApStartedEventWritten = true;
+        int band1 = WifiScanner.WIFI_BAND_UNSPECIFIED;
+        int band2 = WifiScanner.WIFI_BAND_UNSPECIFIED;
+        @SoftApConfiguration.SecurityType int securityType = SoftApConfiguration.SECURITY_TYPE_OPEN;
+        if (mCurrentSoftApConfiguration != null) {
+            int[] bands = mCurrentSoftApConfiguration.getBands();
+            if (bands.length >= 1) {
+                band1 = bands[0];
+            }
+            if (bands.length >= 2) {
+                band2 = bands[1];
+            }
+            securityType = mCurrentSoftApConfiguration.getSecurityType();
+        }
+        mWifiMetrics.writeSoftApStartedEvent(startResult,
+                getRole(),
+                band1,
+                band2,
+                ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative),
+                mWifiNative.isStaApConcurrencySupported(),
+                ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative),
+                getCurrentStaFreqMhz(),
+                securityType);
+    }
+
+    private void writeSoftApStoppedEvent(@StopEvent int stopEvent) {
+        @WifiScanner.WifiBand int band = WifiScanner.WIFI_BAND_UNSPECIFIED;
+        @WifiAnnotations.WifiStandard int standard = ScanResult.WIFI_STANDARD_UNKNOWN;
+        for (SoftApInfo info : mCurrentSoftApInfoMap.values()) {
+            band |= ScanResult.toBand(info.getFrequency());
+            if (SdkLevel.isAtLeastS()) {
+                standard = info.getWifiStandard();
+            }
+        }
+        @SoftApConfiguration.SecurityType int securityType = SoftApConfiguration.SECURITY_TYPE_OPEN;
+        if (mCurrentSoftApConfiguration != null) {
+            securityType = mCurrentSoftApConfiguration.getSecurityType();
+        }
+        // TODO(b/245824786): Fill out the rest of the fields
+        mWifiMetrics.writeSoftApStoppedEvent(
+                stopEvent,
+                getRole(),
+                band,
+                isBridgedMode(),
+                mWifiNative.isStaApConcurrencySupported(),
+                ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative),
+                getCurrentStaFreqMhz(),
+                mDefaultShutdownTimeoutMillis > 0,
+                -1,
+                securityType,
+                standard,
+                -1,
+                mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis > 0,
+                -1,
+                -1,
+                null);
+    }
 }
diff --git a/service/java/com/android/server/wifi/SsidTranslator.java b/service/java/com/android/server/wifi/SsidTranslator.java
index 67dc0b0..bb302ef 100644
--- a/service/java/com/android/server/wifi/SsidTranslator.java
+++ b/service/java/com/android/server/wifi/SsidTranslator.java
@@ -465,4 +465,11 @@
         mMockCharsetsPerLocaleLanguage.clear();
         updateCurrentLocaleCharset();
     }
+
+    /**
+     * Indicates whether SSID translation is currently enabled.
+     */
+    public synchronized boolean isSsidTranslationEnabled() {
+        return mCurrentLocaleAlternateCharset != null;
+    }
 }
diff --git a/service/java/com/android/server/wifi/ThroughputPredictor.java b/service/java/com/android/server/wifi/ThroughputPredictor.java
index 0dcc582..8a2063e 100644
--- a/service/java/com/android/server/wifi/ThroughputPredictor.java
+++ b/service/java/com/android/server/wifi/ThroughputPredictor.java
@@ -20,6 +20,7 @@
 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MIN_CHANNEL_UTILIZATION;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiAnnotations.WifiStandard;
@@ -128,7 +129,7 @@
     public int predictMaxTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) {
         return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode,
                 capabilities.channelBandwidth, WifiInfo.MAX_RSSI,
-                capabilities.maxNumberTxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0);
+                capabilities.maxNumberTxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0, null);
     }
 
     /**
@@ -139,7 +140,7 @@
     public int predictMaxRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) {
         return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode,
                 capabilities.channelBandwidth, WifiInfo.MAX_RSSI,
-                capabilities.maxNumberRxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0);
+                capabilities.maxNumberRxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0, null);
     }
 
     /**
@@ -152,7 +153,7 @@
                 INVALID, channelUtilization, false);
         return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode,
                 capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberTxSpatialStreams,
-                channelUtilizationFinal, frequency);
+                channelUtilizationFinal, frequency, null);
     }
 
     /**
@@ -165,7 +166,7 @@
                 INVALID, channelUtilization, false);
         return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode,
                 capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberRxSpatialStreams,
-                channelUtilizationFinal, frequency);
+                channelUtilizationFinal, frequency, null);
     }
 
     /**
@@ -179,13 +180,15 @@
      * @param channelUtilizationBssLoad the channel utilization ratio indicated from BssLoad IE
      * @param channelUtilizationLinkLayerStats the channel utilization ratio detected from scan
      * @param isBluetoothConnected whether the bluetooth adaptor is in connected mode
+     * @param disabledSubchannelBitmap the disabled Subchannel Bitmap (2 bytes) from EHT
+     *                                 Operation IE
      * @return predicted throughput in Mbps
      */
     public int predictThroughput(DeviceWiphyCapabilities deviceCapabilities,
             @WifiStandard int wifiStandardAp,
             int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp,
             int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats,
-            boolean isBluetoothConnected) {
+            boolean isBluetoothConnected, @Nullable byte[] disabledSubchannelBitmap) {
 
         if (deviceCapabilities == null) {
             Log.e(TAG, "Null device capabilities passed to throughput predictor");
@@ -279,12 +282,13 @@
                 isBluetoothConnected);
 
         return predictThroughputInternal(wifiStandard, false/* is11bMode */, channelWidth,
-                rssiDbm, maxNumSpatialStream, channelUtilization, frequency);
+                rssiDbm, maxNumSpatialStream, channelUtilization, frequency,
+                disabledSubchannelBitmap);
     }
 
     private int predictThroughputInternal(@WifiStandard int wifiStandard, boolean is11bMode,
             int channelWidth, int rssiDbm, int maxNumSpatialStream,  int channelUtilization,
-            int frequency) {
+            int frequency, @Nullable byte[] disabledSubchannelBitmap) {
 
         // channel bandwidth in MHz = 20MHz * (2 ^ channelWidthFactor);
         int channelWidthFactor;
@@ -364,6 +368,14 @@
                 numTonePerSym = NUM_TONE_PER_SYM_11BE_320MHZ;
                 channelWidthFactor = 4;
             }
+            int numPunctured20MhzSubChannel = 0;
+            if (disabledSubchannelBitmap != null && disabledSubchannelBitmap.length == 2) {
+                numPunctured20MhzSubChannel = Integer.bitCount(
+                        (disabledSubchannelBitmap[1] << 8) | disabledSubchannelBitmap[0]);
+            }
+            if (numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ < numTonePerSym) {
+                numTonePerSym -= numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ;
+            }
             maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11BE);
             maxBitsPerTone = MAX_BITS_PER_TONE_11BE;
             symDurationNs = SYM_DURATION_11AX_BE_NS;
diff --git a/service/java/com/android/server/wifi/ThroughputScorer.java b/service/java/com/android/server/wifi/ThroughputScorer.java
index ae45afd..9be9d64 100644
--- a/service/java/com/android/server/wifi/ThroughputScorer.java
+++ b/service/java/com/android/server/wifi/ThroughputScorer.java
@@ -110,9 +110,9 @@
                 ? mScoringParams.getBand6GhzBonus() : 0;
         int currentNetworkBoost = (candidate.isCurrentNetwork() && !unExpectedNoInternet)
                 ? currentNetworkBonus : 0;
-        int rssiBoost = (candidate.isCurrentNetwork() && unExpectedNoInternet)
+        int rssiBoost = unExpectedNoInternet && candidate.getNumRebootsSinceLastUse() == 0
                 ? 0 : rssiBaseScore;
-        int throughputBoost = (candidate.isCurrentNetwork() && unExpectedNoInternet)
+        int throughputBoost = unExpectedNoInternet && candidate.getNumRebootsSinceLastUse() == 0
                 ? 0 : throughputBonusScore;
 
         int securityAward = candidate.isOpenNetwork()
@@ -157,6 +157,12 @@
             notOemPrivateAward = 0;
         }
 
+        if (candidate.isIpProvisioningTimedOut()) {
+            savedNetworkAward = 0;
+            unmeteredAward = 0;
+            trustedAward = 0;
+        }
+
         // These scores determine which scoring bucket the candidate falls into. The scoring buckets
         // should not overlap so candidate in a higher bucket should always win against candidate in
         // a lower bucket.
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index add764f..b5546a2 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -41,7 +41,6 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.MacAddressUtils;
 import com.android.server.wifi.util.ApConfigUtil;
-import com.android.server.wifi.util.ArrayUtils;
 import com.android.wifi.resources.R;
 
 import java.nio.charset.CharsetEncoder;
@@ -90,6 +89,7 @@
     private boolean mForceApChannel = false;
     private int mForcedApBand;
     private int mForcedApChannel;
+    private int mForcedApMaximumChannelBandWidth;
     private final boolean mIsAutoAppendLowerBandEnabled;
 
     /**
@@ -165,13 +165,31 @@
         }
 
         if (mForceApChannel) {
-            Log.d(TAG, "getApConfiguration: Band force to " + mForcedApBand
-                    + ", and channel force to " + mForcedApChannel);
-            return mForcedApChannel == 0
-                    ? new SoftApConfiguration.Builder(mPersistentWifiApConfig)
-                            .setBand(mForcedApBand).build()
-                    : new SoftApConfiguration.Builder(mPersistentWifiApConfig)
-                            .setChannel(mForcedApChannel, mForcedApBand).build();
+            Log.d(TAG, "getApConfiguration: Band force to "
+                    + mForcedApBand
+                    + ", and channel force to "
+                    + mForcedApChannel
+                    + ", and maximum channel width limited to "
+                    + mForcedApMaximumChannelBandWidth);
+            if (SdkLevel.isAtLeastT()) {
+                return mForcedApChannel == 0
+                        ? new SoftApConfiguration.Builder(mPersistentWifiApConfig)
+                                .setBand(mForcedApBand)
+                                .setMaxChannelBandwidth(mForcedApMaximumChannelBandWidth)
+                                .build()
+                        : new SoftApConfiguration.Builder(mPersistentWifiApConfig)
+                                .setChannel(mForcedApChannel, mForcedApBand)
+                                .setMaxChannelBandwidth(mForcedApMaximumChannelBandWidth)
+                                .build();
+            } else {
+                return mForcedApChannel == 0
+                        ? new SoftApConfiguration.Builder(mPersistentWifiApConfig)
+                                .setBand(mForcedApBand)
+                                .build()
+                        : new SoftApConfiguration.Builder(mPersistentWifiApConfig)
+                                .setChannel(mForcedApChannel, mForcedApBand)
+                                .build();
+            }
         }
         return mPersistentWifiApConfig;
     }
@@ -370,7 +388,7 @@
             mLastConfiguredPassphrase = config.getPassphrase();
         }
         mHasNewDataToSerialize = true;
-        mWifiConfigManager.saveToStore(true);
+        mHandler.post(() -> mWifiConfigManager.saveToStore(true));
         mBackupManagerProxy.notifyDataChanged();
     }
 
@@ -483,18 +501,16 @@
 
         // Automotive mode can force the LOHS to specific bands
         if (hasAutomotiveFeature(context)) {
+            int desiredBand = SoftApConfiguration.BAND_2GHZ;
             if (context.getResources().getBoolean(R.bool.config_wifiLocalOnlyHotspot6ghz)
-                    && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_6GHZ, mContext)
-                    && !ArrayUtils.isEmpty(capability
-                          .getSupportedChannelList(SoftApConfiguration.BAND_6GHZ))) {
-                configBuilder.setBand(SoftApConfiguration.BAND_6GHZ);
-            } else if (context.getResources().getBoolean(
-                        R.bool.config_wifi_local_only_hotspot_5ghz)
-                    && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_5GHZ, mContext)
-                    && !ArrayUtils.isEmpty(capability
-                          .getSupportedChannelList(SoftApConfiguration.BAND_5GHZ))) {
-                configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
+                    && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_6GHZ, mContext)) {
+                desiredBand |= SoftApConfiguration.BAND_6GHZ;
             }
+            if (context.getResources().getBoolean(R.bool.config_wifi_local_only_hotspot_5ghz)
+                    && ApConfigUtil.isBandSupported(SoftApConfiguration.BAND_5GHZ, mContext)) {
+                desiredBand |= SoftApConfiguration.BAND_5GHZ;
+            }
+            configBuilder.setBand(desiredBand);
         }
         if (customConfig == null || customConfig.getSsid() == null) {
             configBuilder.setSsid(generateLohsSsid(context));
@@ -640,9 +656,12 @@
 
         if (ApConfigUtil.isSecurityTypeRestrictedFor6gBand(authType)) {
             for (int band : apConfig.getBands()) {
-                // Only return failure if requested band is limitted to 6GHz only
-                if (band == SoftApConfiguration.BAND_6GHZ) {
-                    Log.d(TAG, "security type is not allowed for softap in 6GHz band");
+                // Only return failure if requested band is limited to 6GHz only
+                if (band == SoftApConfiguration.BAND_6GHZ
+                        && !ApConfigUtil.canHALConvertRestrictedSecurityTypeFor6GHz(
+                                context.getResources(), authType)) {
+                    Log.d(TAG, "security type: " +  authType
+                            + " is not allowed for softap in 6GHz band");
                     return false;
                 }
             }
@@ -706,12 +725,14 @@
      *
      * @param forcedApBand The forced band.
      * @param forcedApChannel The forced IEEE channel number or 0 when forced AP band only.
+     * @param forcedApMaximumChannelBandWidth The forced maximum channel bandwidth.
      */
-    public synchronized void enableForceSoftApBandOrChannel(@BandType int forcedApBand,
-            int forcedApChannel) {
+    public synchronized void enableForceSoftApBandOrChannel(
+            @BandType int forcedApBand, int forcedApChannel, int forcedApMaximumChannelBandWidth) {
         mForceApChannel = true;
         mForcedApChannel = forcedApChannel;
         mForcedApBand = forcedApBand;
+        mForcedApMaximumChannelBandWidth = forcedApMaximumChannelBandWidth;
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiBlocklistMonitor.java b/service/java/com/android/server/wifi/WifiBlocklistMonitor.java
index 0c362a6..adf313d 100644
--- a/service/java/com/android/server/wifi/WifiBlocklistMonitor.java
+++ b/service/java/com/android/server/wifi/WifiBlocklistMonitor.java
@@ -118,8 +118,6 @@
     private static final int MIN_RSSI_DIFF_TO_UNBLOCK_BSSID = 5;
     @VisibleForTesting
     public static final int NUM_CONSECUTIVE_FAILURES_PER_NETWORK_EXP_BACKOFF = 5;
-    @VisibleForTesting
-    public static final long WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS = TimeUnit.HOURS.toMillis(18);
     private static final String TAG = "WifiBlocklistMonitor";
 
     private final Context mContext;
@@ -135,6 +133,7 @@
     private final Map<Integer, BssidDisableReason> mBssidDisableReasons =
             buildBssidDisableReasons();
     private final SparseArray<DisableReasonInfo> mDisableReasonInfo;
+    private final WifiGlobals mWifiGlobals;
 
     // Map of bssid to BssidStatus
     private Map<String, BssidStatus> mBssidStatusMap = new ArrayMap<>();
@@ -251,7 +250,7 @@
     WifiBlocklistMonitor(Context context, WifiConnectivityHelper connectivityHelper,
             WifiLastResortWatchdog wifiLastResortWatchdog, Clock clock, LocalLog localLog,
             WifiScoreCard wifiScoreCard, ScoringParams scoringParams, WifiMetrics wifiMetrics,
-            WifiPermissionsUtil wifiPermissionsUtil) {
+            WifiPermissionsUtil wifiPermissionsUtil, WifiGlobals wifiGlobals) {
         mContext = context;
         mConnectivityHelper = connectivityHelper;
         mWifiLastResortWatchdog = wifiLastResortWatchdog;
@@ -262,6 +261,7 @@
         mDisableReasonInfo = DISABLE_REASON_INFOS.clone();
         mWifiMetrics = wifiMetrics;
         mWifiPermissionsUtil = wifiPermissionsUtil;
+        mWifiGlobals = wifiGlobals;
         loadCustomConfigsForDisableReasonInfos();
     }
 
@@ -1206,14 +1206,17 @@
         public final int threshold;
         // disable duration in ms. -1 means permanent disable.
         public final int durationMs;
-        public CarrierSpecificEapFailureConfig(int threshold, int durationMs) {
+        public final boolean displayNotification;
+        public CarrierSpecificEapFailureConfig(int threshold, int durationMs,
+                boolean displayNotification) {
             this.threshold = threshold;
             this.durationMs = durationMs;
+            this.displayNotification = displayNotification;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(threshold, durationMs);
+            return Objects.hash(threshold, durationMs, displayNotification);
         }
 
         @Override
@@ -1225,7 +1228,8 @@
                 return false;
             }
             CarrierSpecificEapFailureConfig lhs = (CarrierSpecificEapFailureConfig) obj;
-            return threshold == lhs.threshold && durationMs == lhs.durationMs;
+            return threshold == lhs.threshold && durationMs == lhs.durationMs
+                    && displayNotification == lhs.displayNotification;
         }
 
         @Override
@@ -1233,6 +1237,7 @@
             return new StringBuilder()
                     .append("threshold=").append(threshold)
                     .append(" durationMs=").append(durationMs)
+                    .append(" displayNotification=").append(displayNotification)
                     .toString();
         }
     }
@@ -1381,8 +1386,8 @@
                 - NUM_CONSECUTIVE_FAILURES_PER_NETWORK_EXP_BACKOFF;
         for (int i = 0; i < exponentialBackoffCount; i++) {
             disableDurationMs *= 2;
-            if (disableDurationMs > WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS) {
-                disableDurationMs = WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS;
+            if (disableDurationMs > mWifiGlobals.getWifiConfigMaxDisableDurationMs()) {
+                disableDurationMs = mWifiGlobals.getWifiConfigMaxDisableDurationMs();
                 break;
             }
         }
diff --git a/service/java/com/android/server/wifi/WifiCandidates.java b/service/java/com/android/server/wifi/WifiCandidates.java
index e886606..93e2e71 100644
--- a/service/java/com/android/server/wifi/WifiCandidates.java
+++ b/service/java/com/android/server/wifi/WifiCandidates.java
@@ -25,6 +25,7 @@
 import android.net.wifi.WifiAnnotations;
 import android.net.wifi.WifiConfiguration;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 import com.android.server.wifi.proto.WifiScoreCardProto;
@@ -187,6 +188,11 @@
         MacAddress getApMldMacAddress();
 
         /**
+         * Gets the number of reboots since the WifiConfiguration is last connected or updated.
+         */
+        int getNumRebootsSinceLastUse();
+
+        /**
          * Gets statistics from the scorecard.
          */
         @Nullable WifiScoreCardProto.Signal getEventStatistics(WifiScoreCardProto.Event event);
@@ -202,6 +208,13 @@
          * @return true or false.
          */
         boolean isMultiLinkCapable();
+
+        /**
+         * Returns true if the candidate is Local-Only due to Ip Provisioning Timeout.
+         *
+         * @return true or false.
+         */
+        boolean isIpProvisioningTimedOut();
     }
 
     /**
@@ -231,8 +244,10 @@
         private final boolean mCarrierOrPrivileged;
         private final int mPredictedThroughputMbps;
         private int mPredictedMultiLinkThroughputMbps;
+        private final int mNumRebootsSinceLastUse;
         private final int mEstimatedPercentInternetAvailability;
         private final MacAddress mApMldMacAddress;
+        private final boolean mIpProvisioningTimedOut;
 
         CandidateImpl(Key key, WifiConfiguration config,
                 WifiScoreCard.PerBssid perBssid,
@@ -269,11 +284,13 @@
             this.mOemPrivate = config.oemPrivate;
             this.mCarrierOrPrivileged = isCarrierOrPrivileged;
             this.mPredictedThroughputMbps = predictedThroughputMbps;
+            this.mNumRebootsSinceLastUse = config.numRebootsSinceLastUse;
             this.mEstimatedPercentInternetAvailability = perBssid == null ? 50 :
                     perBssid.estimatePercentInternetAvailability();
             this.mRestricted = config.restricted;
             this.mPredictedMultiLinkThroughputMbps = 0;
             this.mApMldMacAddress = apMldMacAddress;
+            this.mIpProvisioningTimedOut = config.isIpProvisioningTimedOut();
         }
 
         @Override
@@ -402,6 +419,11 @@
         }
 
         @Override
+        public int getNumRebootsSinceLastUse() {
+            return mNumRebootsSinceLastUse;
+        }
+
+        @Override
         public int getEstimatedPercentInternetAvailability() {
             return mEstimatedPercentInternetAvailability;
         }
@@ -411,6 +433,11 @@
             return  mApMldMacAddress;
         }
 
+        @Override
+        public boolean isIpProvisioningTimedOut() {
+            return mIpProvisioningTimedOut;
+        }
+
         /**
          * Accesses statistical information from the score card
          */
@@ -441,6 +468,7 @@
                     + "Mbps = " + getPredictedThroughputMbps() + ", "
                     + "nominator = " + getNominatorId() + ", "
                     + "pInternet = " + getEstimatedPercentInternetAvailability() + ", "
+                    + "numRebootsSinceLastUse = " + getNumRebootsSinceLastUse()  + ", "
                     + lastSelectionWeightString
                     + (isCurrentBssid() ? "connected, " : "")
                     + (isCurrentNetwork() ? "current, " : "")
@@ -454,7 +482,8 @@
                     + (hasNoInternetAccess() ? "noInternet, " : "")
                     + (isNoInternetAccessExpected() ? "noInternetExpected, " : "")
                     + (isPasspoint() ? "passpoint, " : "")
-                    + (isOpenNetwork() ? "open" : "secure") + " }";
+                    + (isOpenNetwork() ? "open" : "secure")
+                    + " }";
         }
     }
 
@@ -632,12 +661,28 @@
      */
     public @Nullable Key keyFromScanDetailAndConfig(ScanDetail scanDetail,
             WifiConfiguration config) {
-        if (!validConfigAndScanDetail(config, scanDetail)) return null;
+        if (!validConfigAndScanDetail(config, scanDetail)) {
+            Log.e(
+                    TAG,
+                    "validConfigAndScanDetail failed! ScanDetail: "
+                            + scanDetail
+                            + " WifiConfig: "
+                            + config);
+            return null;
+        }
 
         ScanResult scanResult = scanDetail.getScanResult();
         SecurityParams params = ScanResultMatchInfo.fromScanResult(scanResult)
                 .matchForNetworkSelection(ScanResultMatchInfo.fromWifiConfiguration(config));
-        if (null == params) return null;
+        if (null == params) {
+            Log.e(
+                    TAG,
+                    "matchForNetworkSelection failed! ScanResult: "
+                            + ScanResultMatchInfo.fromScanResult(scanResult)
+                            + " WifiConfig: "
+                            + ScanResultMatchInfo.fromWifiConfiguration(config));
+            return null;
+        }
         MacAddress bssid = MacAddress.fromString(scanResult.BSSID);
         return new Key(ScanResultMatchInfo.fromScanResult(scanResult), bssid, config.networkId,
                 params.getSecurityType());
diff --git a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java
index 62b39f6..b83896d 100644
--- a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java
+++ b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java
@@ -2302,7 +2302,7 @@
             return false;
         }
         boolean ret = isOobPseudonymFeatureEnabledInResource(carrierId);
-        vlogd("isOobPseudonymFeatureEnabled() = " + ret);
+        vlogd("isOobPseudonymFeatureEnabled(" + carrierId + ") = " + ret);
         return ret;
     }
 
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 373efbe..ec6359f 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -27,6 +27,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +46,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
+import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -52,6 +54,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -65,6 +68,7 @@
 import com.android.server.wifi.util.LruConnectionTracker;
 import com.android.server.wifi.util.MissingCounterTimerLockList;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -116,6 +120,23 @@
     @VisibleForTesting
     public static final String PASSWORD_MASK = "*";
 
+    private final AlarmManager mAlarmManager;
+    private final FeatureFlags mFeatureFlags;
+    private boolean mBufferedWritePending;
+    /** Alarm tag to use for starting alarms for buffering file writes. */
+    @VisibleForTesting public static final String BUFFERED_WRITE_ALARM_TAG = "WriteBufferAlarm";
+    /** Time interval for buffering file writes for non-forced writes */
+    private static final int BUFFERED_WRITE_ALARM_INTERVAL_MS = 10 * 1000;
+    /** Alarm listener for flushing out any buffered writes. */
+    private final AlarmManager.OnAlarmListener mBufferedWriteListener =
+            new AlarmManager.OnAlarmListener() {
+                public void onAlarm() {
+                    if (mBufferedWritePending) {
+                        writeBufferedData(true);
+                    }
+                }
+            };
+
     /**
      * Interface for other modules to listen to the network updated events.
      * Note: Credentials are masked to avoid accidentally sending credentials outside the stack.
@@ -149,9 +170,11 @@
          *
          * @param newConfig Updated WifiConfiguration object.
          * @param oldConfig Prev WifiConfiguration object.
+         * @param hasCredentialChanged true if credential is changed, false otherwise.
          */
         default void onNetworkUpdated(
-                @NonNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig) { };
+                @NonNull WifiConfiguration newConfig, @NonNull WifiConfiguration oldConfig,
+                boolean hasCredentialChanged) { };
 
         /**
          * Invoked when user connect choice is set.
@@ -235,8 +258,7 @@
 
     @VisibleForTesting
     public static final int SCAN_RESULT_MISSING_COUNT_THRESHOLD = 1;
-    @VisibleForTesting
-    protected static final String NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG =
+    public static final String NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG =
             "non_persistent_mac_randomization_force_enabled";
     private static final int NON_CARRIER_MERGED_NETWORKS_SCAN_CACHE_QUERY_DURATION_MS =
             10 * 60 * 1000; // 10 minutes
@@ -326,6 +348,7 @@
 
     private final FrameworkFacade mFrameworkFacade;
     private final DeviceConfigFacade mDeviceConfigFacade;
+    private final Handler mHandler;
 
     /**
      * Verbose logging flag. Toggled by developer options.
@@ -398,9 +421,7 @@
     }
     private final Map<NetworkIdentifier, List<DhcpOption>> mCustomDhcpOptions = new HashMap<>();
 
-    /**
-     * Create new instance of WifiConfigManager.
-     */
+    /** Create new instance of WifiConfigManager. */
     WifiConfigManager(
             Context context,
             WifiKeyStore wifiKeyStore,
@@ -409,8 +430,10 @@
             NetworkListUserStoreData networkListUserStoreData,
             RandomizedMacStoreData randomizedMacStoreData,
             LruConnectionTracker lruConnectionTracker,
-            WifiInjector wifiInjector) {
+            WifiInjector wifiInjector,
+            Handler handler) {
         mContext = context;
+        mHandler = handler;
         mWifiInjector = wifiInjector;
         mClock = wifiInjector.getClock();
         mUserManager = wifiInjector.getUserManager();
@@ -422,6 +445,7 @@
         mWifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil();
         mFrameworkFacade = wifiInjector.getFrameworkFacade();
         mDeviceConfigFacade = wifiInjector.getDeviceConfigFacade();
+        mFeatureFlags = mDeviceConfigFacade.getFeatureFlags();
         mMacAddressUtil = wifiInjector.getMacAddressUtil();
         mBuildProperties = wifiInjector.getBuildProperties();
 
@@ -447,6 +471,7 @@
         mLocalLog = new LocalLog(
                 context.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256);
         mLruConnectionTracker = lruConnectionTracker;
+        mAlarmManager = context.getSystemService(AlarmManager.class);
     }
 
     /**
@@ -1104,6 +1129,20 @@
         }
     }
 
+    private static @WifiEnterpriseConfig.TofuConnectionState int mergeTofuConnectionState(
+            WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
+        // Prioritize the internal config if it has reached a post-connection state.
+        int internalTofuState = internalConfig.enterpriseConfig.getTofuConnectionState();
+        if (internalTofuState == WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA
+                || internalTofuState == WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING) {
+            return internalTofuState;
+        }
+        // Else assign a pre-connection state based on the latest external config.
+        return externalConfig.enterpriseConfig.isTrustOnFirstUseEnabled()
+                ? WifiEnterpriseConfig.TOFU_STATE_ENABLED_PRE_CONNECTION
+                : WifiEnterpriseConfig.TOFU_STATE_NOT_ENABLED;
+    }
+
     /**
      * Copy over public elements from an external WifiConfiguration object to the internal
      * configuration object if element has been set in the provided external WifiConfiguration.
@@ -1133,7 +1172,15 @@
         }
         internalConfig.BSSID = externalConfig.BSSID == null ? null
                 : externalConfig.BSSID.toLowerCase();
-        internalConfig.hiddenSSID = externalConfig.hiddenSSID;
+        if (externalConfig.hiddenSSID) {
+            internalConfig.hiddenSSID = true;
+        } else if (internalConfig.getSecurityParams(
+                externalConfig.getDefaultSecurityParams().getSecurityType()) != null) {
+            // Only set hiddenSSID to false if we're updating an existing config.
+            // This is to prevent users from mistakenly converting an existing hidden config to
+            // unhidden when adding a new config of the same security family.
+            internalConfig.hiddenSSID = false;
+        }
 
         if (externalConfig.preSharedKey != null
                 && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
@@ -1189,10 +1236,18 @@
         }
 
         internalConfig.allowAutojoin = externalConfig.allowAutojoin;
-        // Copy over the |WifiEnterpriseConfig| parameters if set.
+        // Copy over the |WifiEnterpriseConfig| parameters if set. For fields which should
+        // only be set by the framework, cache the internal config's value and restore.
         if (externalConfig.enterpriseConfig != null) {
+            boolean userApproveNoCaCertInternal =
+                    internalConfig.enterpriseConfig.isUserApproveNoCaCert();
+            int tofuDialogStateInternal = internalConfig.enterpriseConfig.getTofuDialogState();
+            int tofuConnectionState = mergeTofuConnectionState(internalConfig, externalConfig);
             internalConfig.enterpriseConfig.copyFromExternal(
                     externalConfig.enterpriseConfig, PASSWORD_MASK);
+            internalConfig.enterpriseConfig.setUserApproveNoCaCert(userApproveNoCaCertInternal);
+            internalConfig.enterpriseConfig.setTofuDialogState(tofuDialogStateInternal);
+            internalConfig.enterpriseConfig.setTofuConnectionState(tofuConnectionState);
         }
 
         // Copy over any metered information.
@@ -1649,7 +1704,8 @@
             } else {
                 listener.onNetworkUpdated(
                         createExternalWifiConfiguration(newConfig, true, Process.WIFI_UID),
-                        createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID));
+                        createExternalWifiConfiguration(existingConfig, true, Process.WIFI_UID),
+                        result.hasCredentialChanged());
             }
         }
         return result;
@@ -1700,6 +1756,43 @@
     }
 
     /**
+     * Filter non app-added networks from the input list.
+     *
+     * Note: Optimized to avoid checking the permissions for each config in the input list,
+     * since {@link WifiPermissionsUtil#isProfileOwner(int, String)} is fairly expensive.
+     *
+     * Many configs will have the same creator, so we can cache the permissions per-creator.
+     *
+     * @param networks List of WifiConfigurations to filter.
+     * @return List of app-added networks.
+     */
+    @VisibleForTesting
+    protected List<WifiConfiguration> filterNonAppAddedNetworks(List<WifiConfiguration> networks) {
+        List<WifiConfiguration> appAddedNetworks = new ArrayList<>();
+        Map<Pair<Integer, String>, Boolean> isAppAddedCache = new ArrayMap<>();
+
+        for (WifiConfiguration network : networks) {
+            Pair<Integer, String> identityPair =
+                    new Pair<>(network.creatorUid, network.creatorName);
+            boolean isAppAdded;
+
+            // Checking the DO/PO/System permissions is expensive - cache the result.
+            if (isAppAddedCache.containsKey(identityPair)) {
+                isAppAdded = isAppAddedCache.get(identityPair);
+            } else {
+                isAppAdded = !isDeviceOwnerProfileOwnerOrSystem(
+                        network.creatorUid, network.creatorName);
+                isAppAddedCache.put(identityPair, isAppAdded);
+            }
+
+            if (isAppAdded) {
+                appAddedNetworks.add(network);
+            }
+        }
+        return appAddedNetworks;
+    }
+
+    /**
      * Removes excess networks in case the number of saved networks exceeds the max limit
      * specified in config_wifiMaxNumWifiConfigurations.
      *
@@ -1737,11 +1830,9 @@
             numExcessNetworks = networkList.size() - maxNumTotalConfigs;
         }
 
-        if (callerIsApp && maxNumAppAddedConfigs >= 0) {
-            List<WifiConfiguration> appAddedNetworks = networkList
-                    .stream()
-                    .filter(n -> !isDeviceOwnerProfileOwnerOrSystem(n.creatorUid, n.creatorName))
-                    .collect(Collectors.toList());
+        if (callerIsApp && maxNumAppAddedConfigs >= 0
+                && networkList.size() > maxNumAppAddedConfigs) {
+            List<WifiConfiguration> appAddedNetworks = filterNonAppAddedNetworks(networkList);
             int numExcessAppAddedNetworks = appAddedNetworks.size() - maxNumAppAddedConfigs;
             if (numExcessAppAddedNetworks > 0) {
                 // Only enforce the limit on app-added networks if it has been exceeded.
@@ -2060,14 +2151,22 @@
     private boolean updateNetworkSelectionStatus(@NonNull WifiConfiguration config, int reason) {
         int prevNetworkSelectionStatus = config.getNetworkSelectionStatus()
                 .getNetworkSelectionStatus();
+        int prevAuthFailureCounter = config.getNetworkSelectionStatus().getDisableReasonCounter(
+                WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE);
         if (!mWifiBlocklistMonitor.updateNetworkSelectionStatus(config, reason)) {
             return false;
         }
         int newNetworkSelectionStatus = config.getNetworkSelectionStatus()
                 .getNetworkSelectionStatus();
+        int newAuthFailureCounter = config.getNetworkSelectionStatus().getDisableReasonCounter(
+                WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE);
         if (prevNetworkSelectionStatus != newNetworkSelectionStatus) {
             sendNetworkSelectionStatusChangedUpdate(config, newNetworkSelectionStatus, reason);
             sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
+        } else if (prevAuthFailureCounter != newAuthFailureCounter) {
+            // Send out configured network changed broadcast in this special case since the UI
+            // may need to update the wrong password text.
+            sendConfiguredNetworkChangedBroadcast(WifiManager.CHANGE_REASON_CONFIG_CHANGE, config);
         }
         saveToStore(false);
         return true;
@@ -2519,6 +2618,7 @@
         config.validatedInternetAccess = validated;
         if (validated) {
             config.numNoInternetAccessReports = 0;
+            config.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(true);
         }
         saveToStore(false);
         return true;
@@ -2541,6 +2641,23 @@
     }
 
     /**
+     * Sets whether the provided network is local only due to ip provisioning timeout
+     *
+     * @param networkId             network ID corresponding to the network.
+     * @param isIpProvisionTimedOut Whether the network is local-only or not.
+     * @return true if the network was found, false otherwise.
+     */
+    public boolean setIpProvisioningTimedOut(int networkId, boolean isIpProvisionTimedOut) {
+        WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+        if (config == null) {
+            return false;
+        }
+        config.setIpProvisioningTimedOut(isIpProvisionTimedOut);
+        return true;
+    }
+
+
+    /**
      * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
      * is done when either the corresponding network is either removed or disabled.
      */
@@ -3226,7 +3343,7 @@
         }
         // Switch out the user store file.
         if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
-            saveToStore(true);
+            writeBufferedData(true);
             mPendingUnlockStoreRead = false;
         }
     }
@@ -3264,7 +3381,7 @@
             return new HashSet<>();
         }
         if (mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
-            saveToStore(true);
+            writeBufferedData(true);
         }
         // Remove any private networks of the old user before switching the userId.
         Set<Integer> removedNetworkIds = clearInternalDataForUser(mCurrentUserId);
@@ -3323,7 +3440,7 @@
         }
         if (userId == mCurrentUserId
                 && mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(mCurrentUserId))) {
-            saveToStore(true);
+            writeBufferedData(true);
             clearInternalDataForUser(mCurrentUserId);
         }
     }
@@ -3632,6 +3749,37 @@
             Log.e(TAG, "Cannot save to store before store is read!");
             return false;
         }
+        if (mFeatureFlags.delaySaveToStore()) {
+            // When feature enabled, always do a delay write
+            startBufferedWriteAlarm();
+            return true;
+        }
+        return writeBufferedData(forceWrite);
+    }
+
+    /** Helper method to start a buffered write alarm if one doesn't already exist. */
+    private void startBufferedWriteAlarm() {
+        if (!mBufferedWritePending) {
+            mAlarmManager.set(
+                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    mClock.getElapsedSinceBootMillis() + BUFFERED_WRITE_ALARM_INTERVAL_MS,
+                    BUFFERED_WRITE_ALARM_TAG,
+                    mBufferedWriteListener,
+                    mHandler);
+            mBufferedWritePending = true;
+        }
+    }
+
+    /** Helper method to stop a buffered write alarm if one exists. */
+    private void stopBufferedWriteAlarm() {
+        if (mBufferedWritePending) {
+            mAlarmManager.cancel(mBufferedWriteListener);
+            mBufferedWritePending = false;
+        }
+    }
+
+    private boolean writeBufferedData(Boolean forceWrite) {
+        stopBufferedWriteAlarm();
         ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
         ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
         // List of network IDs for legacy Passpoint configuration to be removed.
@@ -4309,6 +4457,41 @@
     }
 
     /**
+     * Indicate whether the user approved the TOFU dialog for this network.
+     *
+     * @param networkId networkId corresponding to the network to be updated.
+     * @param approved true if the user approved the dialog, false otherwise.
+     */
+    public void setTofuDialogApproved(int networkId, boolean approved) {
+        WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
+        if (internalConfig == null) return;
+        if (!internalConfig.isEnterprise()) return;
+        if (!internalConfig.enterpriseConfig.isEapMethodServerCertUsed()) return;
+        internalConfig.enterpriseConfig.setTofuDialogApproved(approved);
+    }
+
+    /**
+     * Indicate the post-connection TOFU state for this network.
+     *
+     * @param networkId networkId corresponding to the network to be updated.
+     * @param state one of the post-connection {@link WifiEnterpriseConfig.TofuConnectionState}
+     *              values
+     */
+    public void setTofuPostConnectionState(int networkId,
+            @WifiEnterpriseConfig.TofuConnectionState int state) {
+        if (state != WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA
+                && state != WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING) {
+            Log.e(TAG, "Invalid post-connection TOFU state " + state);
+            return;
+        }
+        WifiConfiguration internalConfig = getInternalConfiguredNetwork(networkId);
+        if (internalConfig == null) return;
+        if (!internalConfig.isEnterprise()) return;
+        if (!internalConfig.enterpriseConfig.isEapMethodServerCertUsed()) return;
+        internalConfig.enterpriseConfig.setTofuConnectionState(state);
+    }
+
+    /**
      * Add custom DHCP options.
      *
      * @param ssid the network SSID.
@@ -4350,4 +4533,15 @@
         }
         return new ArrayList<>(results);
     }
+
+    /**
+     * Handle the device shutdown, should write all cached data to the storage
+     */
+    public void handleShutDown() {
+        if (mPendingStoreRead) {
+            Log.e(TAG, "Cannot save to store before store is read!");
+            return;
+        }
+        writeBufferedData(true);
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index d5de419..a1bfb7a 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -163,6 +163,13 @@
     }
 
     /**
+     * Helper method to check if the provided |config| corresponds to a DPP network or not.
+     */
+    public static boolean isConfigForDppNetwork(WifiConfiguration config) {
+        return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP);
+    }
+
+    /**
      * Helper method to check if the provided |config| corresponds to a WEP network or not.
      */
     public static boolean isConfigForWepNetwork(WifiConfiguration config) {
@@ -336,6 +343,10 @@
                     existingEnterpriseConfig.getDomainSuffixMatch())) {
                 return true;
             }
+            if (newEnterpriseConfig.getMinimumTlsVersion()
+                    != existingEnterpriseConfig.getMinimumTlsVersion()) {
+                return true;
+            }
         } else {
             // One of the configs may have an enterpriseConfig
             if (existingEnterpriseConfig != null || newEnterpriseConfig != null) {
diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java
index 4634c6d..e1c43ec 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityHelper.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java
@@ -22,6 +22,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -164,4 +166,16 @@
         return mWifiInjector.getActiveModeWarden()
                 .getPrimaryClientModeManager().configureRoaming(roamConfig);
     }
+
+    /**
+     * Dump debug information
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Dump of WifiConnectivityHelper");
+        pw.println("WifiConnectivityHelper - Log Begin ----");
+        pw.println("mFirmwareRoamingSupported: " + mFirmwareRoamingSupported);
+        pw.println("mMaxNumBlocklistBssid: " + mMaxNumBlocklistBssid);
+        pw.println("mMaxNumAllowlistSsid: " + mMaxNumAllowlistSsid);
+        pw.println("WifiConnectivityHelper - Log End ----");
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 19131e6..574b0b0 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -30,10 +30,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.IpConfiguration;
 import android.net.MacAddress;
 import android.net.wifi.IPnoScanResultsCallback;
@@ -68,6 +64,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.scanner.WifiScannerInternal;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.wifi.resources.R;
@@ -132,6 +129,7 @@
     // Do not restart PNO scan if network changes happen more than once within this duration.
     private static final long NETWORK_CHANGE_TRIGGER_PNO_THROTTLE_MS = 3000; // 3 seconds
     private static final int POWER_SAVE_SCAN_INTERVAL_MULTIPLIER = 2;
+    private static final int MAX_PRIORITIZED_PASSPOINT_SSIDS_PER_PNO_SCAN = 2;
     // ClientModeManager has a bunch of states. From the
     // WifiConnectivityManager's perspective it only cares
     // if it is in Connected state, Disconnected state or in
@@ -255,6 +253,9 @@
     private long mLatestCandidatesTimestampMs = 0;
     private int[] mCurrentSingleScanScheduleSec;
     private int[] mCurrentSingleScanType;
+    private boolean mPnoScanEnabledByFramework = true;
+    private boolean mEnablePnoScanAfterWifiToggle = true;
+    private Set<String> mPnoScanPasspointSsids;
 
     private int mCurrentSingleScanScheduleIndex;
     // Cached WifiCandidates used in high mobility state to avoid connecting to APs that are
@@ -345,8 +346,11 @@
         /**
          * @param wasCandidateSelected true - if a candidate is selected by WifiNetworkSelector
          *                             false - if no candidate is selected by WifiNetworkSelector
+         * @param candidateIsPasspoint true - if the selected candidate is a Passpoint network
+         *                             false - if no candidate is selected OR the selected
+         *                                     candidate is not a Passpoint network
          */
-        void onHandled(boolean wasCandidateSelected);
+        void onHandled(boolean wasCandidateSelected, boolean candidateIsPasspoint);
     }
 
     /**
@@ -359,16 +363,27 @@
                     mNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks());
         }
         mWifiMetrics.noteFirstNetworkSelectionAfterBoot(false);
-        handleScanResultsListener.onHandled(false);
+        handleScanResultsListener.onHandled(false, false);
     }
 
     /**
      * Helper method to consolidate handling of scan results when a candidate is selected.
      */
     private void handleScanResultsWithCandidate(
-            @NonNull HandleScanResultsListener handleScanResultsListener) {
+            @NonNull HandleScanResultsListener handleScanResultsListener,
+            boolean candidateIsPasspoint) {
         mWifiMetrics.noteFirstNetworkSelectionAfterBoot(true);
-        handleScanResultsListener.onHandled(true);
+        handleScanResultsListener.onHandled(true, candidateIsPasspoint);
+    }
+
+    /**
+     * Utility band filter method for multi-internet use-case.
+     */
+    @VisibleForTesting
+    public boolean filterMultiInternetFrequency(int primaryFreq, int secondaryFreq) {
+        return mWifiGlobals.isSupportMultiInternetDual5G()
+                ? ScanResult.isValidCombinedBandForDual5GHz(primaryFreq, secondaryFreq)
+                : ScanResult.toBand(primaryFreq) != ScanResult.toBand(secondaryFreq);
     }
 
     /**
@@ -408,13 +423,16 @@
                 // A BSSID can only exist in one band, so when evaluating candidates, only those
                 // with a different band from the primary will be considered.
                 secondaryCmmCandidates = candidates.stream()
-                        .filter(c -> ScanResult.toBand(c.getFrequency()) != primaryBand)
+                        .filter(c -> {
+                            return filterMultiInternetFrequency(
+                                    primaryInfo.getFrequency(), c.getFrequency());
+                        })
                         .collect(Collectors.toList());
             }
         } else {
             // Only allow the candidates have the same SSID as the primary.
             secondaryCmmCandidates = candidates.stream().filter(c -> {
-                return ScanResult.toBand(c.getFrequency()) != primaryBand
+                return filterMultiInternetFrequency(primaryInfo.getFrequency(), c.getFrequency())
                         && !primaryCcm.isAffiliatedLinkBssid(c.getKey().bssid) && TextUtils.equals(
                         c.getKey().matchInfo.networkSsid, primaryInfo.getSSID())
                         && c.getKey().networkId == primaryInfo.getNetworkId()
@@ -517,7 +535,8 @@
                     targetNetwork.BSSID = targetBssid2; // specify the BSSID to disable roaming.
                     connectToNetworkUsingCmmWithoutMbb(cm, targetNetwork);
 
-                    handleScanResultsWithCandidate(handleScanResultsListener);
+                    handleScanResultsWithCandidate(handleScanResultsListener,
+                            targetNetwork.isPasspoint());
                 }, secondaryRequestorWs,
                 secondaryCmmCandidate.SSID,
                 bssidToConnect);
@@ -541,6 +560,7 @@
         mWifiCountryCode.updateCountryCodeFromScanResults(scanDetails);
 
         List<WifiNetworkSelector.ClientModeManagerState> cmmStates = new ArrayList<>();
+        WifiNetworkSelector.ClientModeManagerState primaryCmmState = null;
         Set<String> connectedSsids = new HashSet<>();
         boolean hasExistingSecondaryCmm = false;
         for (ClientModeManager clientModeManager :
@@ -555,7 +575,12 @@
             if (clientModeManager.isConnected()) {
                 connectedSsids.add(wifiInfo.getSSID());
             }
-            cmmStates.add(new WifiNetworkSelector.ClientModeManagerState(clientModeManager));
+            WifiNetworkSelector.ClientModeManagerState cmmState =
+                    new WifiNetworkSelector.ClientModeManagerState(clientModeManager);
+            if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
+                primaryCmmState = cmmState;
+            }
+            cmmStates.add(cmmState);
         }
         // We don't have any existing secondary CMM, but are we allowed to create a secondary CMM
         // and do we have a request for OEM_PAID/OEM_PRIVATE request? If yes, we need to perform
@@ -663,7 +688,14 @@
                     listenerName, handleScanResultsListener)) {
                 return;
             }
-            // intentional fallthrough: No multi internet connections, fallback to legacy flow.
+            // No multi-internet connection. Need to re-evaluate if network selection is still
+            // needed on the primary.
+            if (primaryCmmState == null
+                    || !mNetworkSelector.isNetworkSelectionNeededForCmm(primaryCmmState)) {
+                return;
+            }
+            // intentional fallthrough: No multi internet connections, and network selection is
+            // needed on the primary. Fallback to legacy flow.
         }
 
         handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
@@ -758,7 +790,8 @@
                     // flow above)
                     connectToNetworkUsingCmmWithoutMbb(cm, secondaryCmmCandidate);
 
-                    handleScanResultsWithCandidate(handleScanResultsListener);
+                    handleScanResultsWithCandidate(handleScanResultsListener,
+                            secondaryCmmCandidate.isPasspoint());
                 }, secondaryRequestorWs,
                 secondaryCmmCandidate.SSID,
                 mConnectivityHelper.isFirmwareRoamingSupported()
@@ -776,18 +809,8 @@
         WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
         if (candidate != null) {
             localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
-            if (hasMultiInternetConnection()) {
-                // Disconnect secondary cmm first before connecting the primary.
-                final ConcreteClientModeManager secondaryCcm = mActiveModeWarden
-                        .getClientModeManagerInRole(ROLE_CLIENT_SECONDARY_LONG_LIVED);
-                if (secondaryCcm != null && isClientModeManagerConnectedOrConnectingToCandidate(
-                        secondaryCcm, candidate)) {
-                    localLog("Disconnect secondary first.");
-                    secondaryCcm.disconnect();
-                }
-            }
             connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate);
-            handleScanResultsWithCandidate(handleScanResultsListener);
+            handleScanResultsWithCandidate(handleScanResultsListener, candidate.isPasspoint());
         } else {
             localLog(listenerName + ":  No candidate");
             handleScanResultsWithNoCandidate(handleScanResultsListener);
@@ -956,7 +979,7 @@
             }
             handleScanResults(scanDetailList,
                     ALL_SINGLE_SCAN_LISTENER, isFullBandScanResults,
-                    wasCandidateSelected -> {
+                    (wasCandidateSelected, candidateIsPasspoint) -> {
                         // Update metrics to see if a single scan detected a valid network
                         // while PNO scan didn't.
                         // Note: We don't update the background scan metrics any more as it is
@@ -1110,6 +1133,10 @@
         public void onFailure(int reason, String description) {
             localLog("PnoScanListener onFailure:"
                     + " reason: " + reason + " description: " + description);
+            WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                    WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                    0, false, false, false, false, // default values
+                    WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__WIFI_SCANNING_SERVICE_FAILURE);
 
             // reschedule the scan
             if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
@@ -1157,7 +1184,13 @@
             mScanRestartCount = 0;
 
             handleScanResults(scanDetailList, PNO_SCAN_LISTENER, false,
-                    wasCandidateSelected -> {
+                    (wasCandidateSelected, candidateIsPasspoint) -> {
+                        WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                                WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__FOUND_RESULTS,
+                                scanDetailList.size(), !mPnoScanPasspointSsids.isEmpty(),
+                                pnoPasspointResultFound(scanDetailList), wasCandidateSelected,
+                                candidateIsPasspoint,
+                                WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__NO_FAILURE);
                         if (!wasCandidateSelected) {
                             // The scan results were rejected by WifiNetworkSelector due to low
                             // RSSI values
@@ -1177,6 +1210,16 @@
         }
     }
 
+    private boolean pnoPasspointResultFound(List<ScanDetail> results) {
+        if (mPnoScanPasspointSsids.isEmpty()) return false;
+        for (ScanDetail pnoResult : results) {
+            if (mPnoScanPasspointSsids.contains(pnoResult.getSSID())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private final PnoScanListener mPnoScanListener;
     private final WifiScannerInternal.ScanListener mInternalPnoScanListener;
 
@@ -1195,7 +1238,8 @@
             triggerScanOnNetworkChanges();
         }
         @Override
-        public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig) {
+        public void onNetworkUpdated(WifiConfiguration newConfig, WifiConfiguration oldConfig,
+                boolean hasCredentialChanged) {
             triggerScanOnNetworkChanges();
         }
 
@@ -1272,9 +1316,7 @@
         }
     }
 
-    /**
-     * WifiConnectivityManager constructor
-     */
+    /** WifiConnectivityManager constructor */
     WifiConnectivityManager(
             WifiContext context,
             ScoringParams scoringParams,
@@ -1302,7 +1344,8 @@
             WifiPermissionsUtil wifiPermissionsUtil,
             WifiCarrierInfoManager wifiCarrierInfoManager,
             WifiCountryCode wifiCountryCode,
-            @NonNull WifiDialogManager wifiDialogManager) {
+            @NonNull WifiDialogManager wifiDialogManager,
+            WifiDeviceStateChangeManager wifiDeviceStateChangeManager) {
         mContext = context;
         mScoringParams = scoringParams;
         mConfigManager = configManager;
@@ -1334,27 +1377,6 @@
         mWifiCountryCode = wifiCountryCode;
         mWifiDialogManager = wifiDialogManager;
 
-        // Listen for screen state change events.
-        // TODO: We should probably add a shared broadcast receiver in the wifi stack which
-        // can used by various modules to listen to common system events. Creating multiple
-        // broadcast receivers in each class within the wifi stack is *somewhat* expensive.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                            handleScreenStateChanged(true);
-                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                            handleScreenStateChanged(false);
-                        }
-                    }
-                }, filter, null, mEventHandler);
-        handleScreenStateChanged(mPowerManager.isInteractive());
-
         // Listen to WifiConfigManager network update events
         mEventHandler.postToFront(() ->
                 mConfigManager.addOnNetworkUpdateListener(new OnNetworkUpdateListener()));
@@ -1370,6 +1392,14 @@
         mPnoScanListener = new PnoScanListener();
         mInternalPnoScanListener = new WifiScannerInternal.ScanListener(mPnoScanListener,
                 mEventHandler);
+        mPnoScanPasspointSsids = new ArraySet<>();
+        wifiDeviceStateChangeManager.registerStateChangeCallback(
+                new WifiDeviceStateChangeManager.StateChangeCallback() {
+                    @Override
+                    public void onScreenStateChanged(boolean screenOn) {
+                        handleScreenStateChanged(screenOn);
+                    }
+                });
     }
 
     @NonNull
@@ -1737,6 +1767,16 @@
                     + ". Drop it!");
             return;
         }
+        if (hasMultiInternetConnection() && clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
+            // Disconnect secondary cmm first before connecting the primary.
+            final ConcreteClientModeManager secondaryCcm = mActiveModeWarden
+                    .getClientModeManagerInRole(ROLE_CLIENT_SECONDARY_LONG_LIVED);
+            if (secondaryCcm != null && isClientModeManagerConnectedOrConnectingToCandidate(
+                    secondaryCcm, targetNetwork)) {
+                localLog("Disconnect secondary first.");
+                secondaryCcm.disconnect();
+            }
+        }
 
         WifiConfiguration currentNetwork = coalesce(
                 clientModeManager.getConnectedWifiConfiguration(),
@@ -1892,7 +1932,12 @@
                     R.integer.config_wifiInitialPartialScanChannelCacheAgeMins);
             int maxCount = mContext.getResources().getInteger(
                     R.integer.config_wifiInitialPartialScanChannelMaxCount);
-            freqs = fetchChannelSetForPartialScan(maxCount, ageInMillis);
+            int maxCountPerNetwork =
+                    mContext.getResources()
+                            .getInteger(
+                                    R.integer
+                                            .config_wifiInitialPartialScanMaxNewChannelsPerNetwork);
+            freqs = fetchChannelSetForPartialScan(maxCount, maxCountPerNetwork, ageInMillis);
         } else {
             freqs = fetchChannelSetForNetworkForPartialScan(config.networkId);
         }
@@ -1911,24 +1956,44 @@
     }
 
     /**
-     * Add the channels into the channel set with a size limit.
-     * If maxCount equals to 0, will add all available channels into the set.
+     * Add the channels into the channel set with a size limits.
+     *
      * @param channelSet Target set for adding channel to.
      * @param ssid Identifies the network to obtain from WifiScoreCard.
-     * @param maxCount Size limit of the set. If equals to 0, means no limit.
+     * @param maxCount Size limit of the channelSet. If equals to 0, means no limit.
+     * @param maxNewChannelsPerNetwork Max number of new channels to include from the ssid. 0 to
+     *     indicate no such limitation.
      * @param ageInMillis Only consider channel info whose timestamps are younger than this value.
-     * @return True if all available channels for this network are added, otherwise false.
+     * @return True channelSet did not reach max limit after adding channels from the network.
      */
-    private boolean addChannelFromWifiScoreCard(@NonNull Set<Integer> channelSet,
-            @NonNull String ssid, int maxCount, long ageInMillis) {
+    private boolean addChannelFromWifiScoreCardWithLimitPerNetwork(
+            @NonNull Set<Integer> channelSet,
+            @NonNull String ssid,
+            int maxCount,
+            int maxNewChannelsPerNetwork,
+            long ageInMillis) {
+        int allowedChannelsPerNetwork =
+                maxNewChannelsPerNetwork <= 0 ? Integer.MAX_VALUE : maxNewChannelsPerNetwork;
         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(ssid);
         for (Integer channel : network.getFrequencies(ageInMillis)) {
             if (maxCount > 0 && channelSet.size() >= maxCount) {
-                localLog("addChannelFromWifiScoreCard: size limit reached for network:"
-                        + ssid);
+                localLog(
+                        "addChannelFromWifiScoreCardWithLimitPerNetwork: "
+                                + "size limit reached for network:"
+                                + ssid);
                 return false;
             }
-            channelSet.add(channel);
+            if (allowedChannelsPerNetwork <= 0) {
+                // max new channels per network reached, but absolute max count not reached
+                localLog(
+                        "addChannelFromWifiScoreCardWithLimitPerNetwork: "
+                                + "per-network size limit reached for network:"
+                                + ssid);
+                return true;
+            }
+            if (channelSet.add(channel)) {
+                allowedChannelsPerNetwork--;
+            }
         }
         return true;
     }
@@ -1951,16 +2016,19 @@
             channelSet.add(wifiInfo.getFrequency());
         }
         // Then get channels for the network.
-        addChannelFromWifiScoreCard(channelSet, config.SSID, maxNumActiveChannelsForPartialScans,
+        addChannelFromWifiScoreCardWithLimitPerNetwork(
+                channelSet,
+                config.SSID,
+                maxNumActiveChannelsForPartialScans,
+                0,
                 CHANNEL_LIST_AGE_MS);
         return channelSet;
     }
 
-    /**
-     * Fetch channel set for all saved and suggestion non-passpoint network for partial scan.
-     */
+    /** Fetch channel set for all saved and suggestion non-passpoint network for partial scan. */
     @VisibleForTesting
-    public Set<Integer> fetchChannelSetForPartialScan(int maxCount, long ageInMillis) {
+    public Set<Integer> fetchChannelSetForPartialScan(
+            int maxCountTotal, int maxCountPerNetwork, long ageInMillis) {
         List<WifiConfiguration> networks = getAllScanOptimizationNetworks();
         if (networks.isEmpty()) {
             return null;
@@ -1972,7 +2040,8 @@
         Set<Integer> channelSet = new HashSet<>();
 
         for (WifiConfiguration config : networks) {
-            if (!addChannelFromWifiScoreCard(channelSet, config.SSID, maxCount, ageInMillis)) {
+            if (!addChannelFromWifiScoreCardWithLimitPerNetwork(
+                    channelSet, config.SSID, maxCountTotal, maxCountPerNetwork, ageInMillis)) {
                 return channelSet;
             }
         }
@@ -2382,8 +2451,32 @@
         }
     }
 
+    /**
+     * Enable/disable the PNO scan framework feature.
+     */
+    public void setPnoScanEnabledByFramework(boolean enabled,
+            boolean enablePnoScanAfterWifiToggle) {
+        mEnablePnoScanAfterWifiToggle = enablePnoScanAfterWifiToggle;
+        if (mPnoScanEnabledByFramework == enabled) {
+            return;
+        }
+        mPnoScanEnabledByFramework = enabled;
+        if (enabled) {
+            if (!mScreenOn && mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
+                startDisconnectedPnoScan();
+            }
+        } else {
+            stopPnoScan();
+        }
+    }
+
     // Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
     private void startDisconnectedPnoScan() {
+        if (!mPnoScanEnabledByFramework) {
+            localLog("Skipping PNO scan because it's disabled by the framework.");
+            return;
+        }
+
         // Initialize PNO settings
         PnoSettings pnoSettings = new PnoSettings();
         List<PnoSettings.PnoNetwork> pnoNetworkList = retrievePnoNetworkList();
@@ -2416,17 +2509,12 @@
         pnoSettings.isConnected = false;
         mScanner.startPnoScan(scanSettings, pnoSettings, mInternalPnoScanListener);
         mPnoScanStarted = true;
-        mWifiMetrics.logPnoScanStart();
+        WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STARTED, !mPnoScanPasspointSsids.isEmpty());
     }
 
     private @NonNull List<WifiConfiguration> getAllScanOptimizationNetworks() {
         List<WifiConfiguration> networks = mConfigManager.getSavedNetworks(-1);
         networks.addAll(mWifiNetworkSuggestionsManager.getAllScanOptimizationSuggestionNetworks());
-        if (mDeviceConfigFacade.includePasspointSsidsInPnoScans()) {
-            networks.addAll(mPasspointManager.getWifiConfigsForPasspointProfilesWithSsids());
-            networks.addAll(mWifiNetworkSuggestionsManager
-                    .getAllPasspointScanOptimizationSuggestionNetworks());
-        }
         // remove all saved but never connected, auto-join disabled, or network selection disabled
         // networks.
         networks.removeIf(config -> !config.allowAutojoin
@@ -2443,6 +2531,32 @@
     }
 
     /**
+     * Merge Passpoint PNO scan candidates into an existing network list.
+     */
+    private @NonNull List<WifiConfiguration> mergePasspointPnoScanCandidates(
+            List<WifiConfiguration> networks) {
+        List<WifiConfiguration> passpointNetworks =
+                mPasspointManager.getWifiConfigsForPasspointProfiles(true);
+        passpointNetworks.addAll(
+                mWifiNetworkSuggestionsManager.getAllPasspointScanOptimizationSuggestionNetworks(
+                        true));
+        if (passpointNetworks.isEmpty()) return networks;
+
+        // Add up to MAX_PRIORITIZED_PASSPOINT_SSIDS_PER_PNO_SCAN Passpoint networks to
+        // the head of the merged network list.
+        int numPasspointAtHead =
+                Math.min(passpointNetworks.size(), MAX_PRIORITIZED_PASSPOINT_SSIDS_PER_PNO_SCAN);
+        List<WifiConfiguration> mergedNetworks = new ArrayList<>();
+        mergedNetworks.addAll(passpointNetworks.subList(0, numPasspointAtHead));
+        mergedNetworks.addAll(networks);
+
+        // Add any remaining Passpoint networks to the end of the merged network list.
+        mergedNetworks.addAll(
+                passpointNetworks.subList(numPasspointAtHead, passpointNetworks.size()));
+        return mergedNetworks;
+    }
+
+    /**
      * Sets whether global location mode is enabled.
      */
     public void setLocationModeEnabled(boolean enabled) {
@@ -2479,7 +2593,6 @@
         }
     }
 
-
     /**
      * Retrieve the PnoNetworks from Saved and suggestion non-passpoint network.
      */
@@ -2495,11 +2608,15 @@
             return Collections.EMPTY_LIST;
         }
         Collections.sort(networks, mConfigManager.getScanListComparator());
+        if (mDeviceConfigFacade.includePasspointSsidsInPnoScans()) {
+            networks = mergePasspointPnoScanCandidates(networks);
+        }
         boolean pnoFrequencyCullingEnabled = mContext.getResources()
                 .getBoolean(R.bool.config_wifiPnoFrequencyCullingEnabled);
 
         List<PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
         Set<String> pnoSet = new HashSet<>();
+        mPnoScanPasspointSsids.clear();
 
         // Add any externally requested SSIDs to PNO scan list
         for (String ssid : externalRequestedPnoSsids) {
@@ -2513,8 +2630,8 @@
                 continue;
             }
             Set<Integer> channelList = new HashSet<>();
-            addChannelFromWifiScoreCard(channelList, ssid, 0,
-                    MAX_PNO_SCAN_FREQUENCY_AGE_MS);
+            addChannelFromWifiScoreCardWithLimitPerNetwork(
+                    channelList, ssid, 0, 0, MAX_PNO_SCAN_FREQUENCY_AGE_MS);
             channelList.addAll(externalRequestedPnoFrequencies);
             pnoNetwork.frequencies = channelList.stream().mapToInt(Integer::intValue).toArray();
         }
@@ -2529,12 +2646,15 @@
                 pnoNetwork.ssid = originalSsid.toString();
                 pnoList.add(pnoNetwork);
                 pnoSet.add(originalSsid.toString());
+                if (config.isPasspoint()) {
+                    mPnoScanPasspointSsids.add(originalSsid.toString());
+                }
                 if (!pnoFrequencyCullingEnabled) {
                     continue;
                 }
                 Set<Integer> channelList = new HashSet<>();
-                addChannelFromWifiScoreCard(channelList, config.SSID, 0,
-                        MAX_PNO_SCAN_FREQUENCY_AGE_MS);
+                addChannelFromWifiScoreCardWithLimitPerNetwork(
+                        channelList, config.SSID, 0, 0, MAX_PNO_SCAN_FREQUENCY_AGE_MS);
                 pnoNetwork.frequencies = channelList.stream().mapToInt(Integer::intValue).toArray();
             }
         }
@@ -2652,6 +2772,8 @@
                 + " mAutoJoinEnabledExternal=" + mAutoJoinEnabledExternal
                 + " mAutoJoinEnabledExternalSetByDeviceAdmin="
                 + mAutoJoinEnabledExternalSetByDeviceAdmin
+                + " mPnoScanEnabledByFramework=" + mPnoScanEnabledByFramework
+                + " mEnablePnoScanAfterWifiToggle=" + mEnablePnoScanAfterWifiToggle
                 + " mSpecificNetworkRequestInProgress=" + mSpecificNetworkRequestInProgress
                 + " mTrustedConnectionAllowed=" + mTrustedConnectionAllowed
                 + " isSufficiencyCheckEnabled=" + mNetworkSelector.isSufficiencyCheckEnabled()
@@ -2949,9 +3071,7 @@
      */
     public void handleConnectionStateChanged(
             ConcreteClientModeManager clientModeManager, int state) {
-        List<ClientModeManager> internetConnectivityCmms =
-                mActiveModeWarden.getInternetConnectivityClientModeManagers();
-        if (!(internetConnectivityCmms.contains(clientModeManager))) {
+        if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY) {
             Log.w(TAG, "Ignoring call from non primary Mode Manager " + clientModeManager,
                     new Throwable());
             return;
@@ -3134,6 +3254,11 @@
         }
     }
 
+    @VisibleForTesting
+    public int getWifiState() {
+        return mWifiState;
+    }
+
     /**
      * Triggered when {@link RestrictedWifiNetworkFactory} has a new pending restricted network
      * request.
@@ -3314,6 +3439,9 @@
             if (mWifiGlobals.flushAnqpCacheOnWifiToggleOffEvent()) {
                 mPasspointManager.clearAnqpRequestsAndFlushCache();
             }
+            if (mEnablePnoScanAfterWifiToggle) {
+                mPnoScanEnabledByFramework = true;
+            }
         }
         mWifiEnabled = enable;
         updateRunningState();
@@ -3390,11 +3518,14 @@
         pw.println("Dump of WifiConnectivityManager");
         pw.println("WifiConnectivityManager - Log Begin ----");
         pw.println("mIsLocationModeEnabled: " + mIsLocationModeEnabled);
+        pw.println("mPnoScanEnabledByFramework: " + mPnoScanEnabledByFramework);
+        pw.println("mEnablePnoScanAfterWifiToggle: " + mEnablePnoScanAfterWifiToggle);
+        pw.println("mMultiInternetConnectionState " + mMultiInternetConnectionState);
         mLocalLog.dump(fd, pw, args);
         pw.println("WifiConnectivityManager - Log End ----");
-        pw.println(TAG + ": mMultiInternetConnectionState " + mMultiInternetConnectionState);
         mOpenNetworkNotifier.dump(fd, pw, args);
         mWifiBlocklistMonitor.dump(fd, pw, args);
         mExternalPnoScanRequestManager.dump(fd, pw, args);
+        mConnectivityHelper.dump(fd, pw, args);
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index 594a880..cad634a 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -724,9 +724,8 @@
                     // changed case.
                     continue;
                 }
-                // Restart SAP only when 1. overlay enabled 2. CC is not world mode.
-                if (ApConfigUtil.isSoftApRestartRequiredWhenCountryCodeChanged(mContext)
-                        && !mDriverCountryCode.equalsIgnoreCase(mWorldModeCountryCode)) {
+                // Restart SAP if the overlay is enabled.
+                if (ApConfigUtil.isSoftApRestartRequiredWhenCountryCodeChanged(mContext)) {
                     Log.i(TAG, "restart SoftAp required because country code changed to "
                             + country);
                     SoftApModeConfiguration modeConfig = sm.getSoftApModeConfiguration();
diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java
index 2852711..85d4076 100644
--- a/service/java/com/android/server/wifi/WifiDataStall.java
+++ b/service/java/com/android/server/wifi/WifiDataStall.java
@@ -432,7 +432,7 @@
         int maxTimeDeltaMs = mWifiGlobals.getPollRssiIntervalMillis()
                 + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS;
         if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) {
-            mWifiMetrics.incrementConnectionDuration(timeDeltaLastTwoPollsMs,
+            mWifiMetrics.incrementConnectionDuration(ifaceName, timeDeltaLastTwoPollsMs,
                     mIsThroughputSufficient, mIsCellularDataAvailable, wifiInfo.getRssi(),
                     mTxTputKbps, mRxTputKbps);
         }
diff --git a/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java b/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java
new file mode 100644
index 0000000..5fa82fe
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/** A centralized manager to handle all the device state changes */
+public class WifiDeviceStateChangeManager {
+    private final Handler mHandler;
+    private final Context mContext;
+
+    private final PowerManager mPowerManager;
+    private final Set<StateChangeCallback> mChangeCallbackList = new ArraySet<>();
+    private boolean mIsWifiServiceStarted = false;
+
+    /**
+     * Callback to receive the device state change event. Caller should implement the method to
+     * listen to the interested event
+     */
+    public interface StateChangeCallback {
+        /**
+         * Called when the screen state changes
+         *
+         * @param screenOn true for ON, false otherwise
+         */
+        default void onScreenStateChanged(boolean screenOn) {}
+    }
+
+    /** Create the instance of WifiDeviceStateChangeManager. */
+    public WifiDeviceStateChangeManager(Context context, Handler handler) {
+        mHandler = handler;
+        mContext = context;
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+    }
+
+    /** Handle the boot completed event. Start to register the receiver and callback. */
+    public void handleBootCompleted() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        String action = intent.getAction();
+                        if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
+                            handleScreenStateChanged(true);
+                        } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
+                            handleScreenStateChanged(false);
+                        }
+                    }
+                },
+                filter,
+                null,
+                mHandler);
+        handleScreenStateChanged(mPowerManager.isInteractive());
+        mIsWifiServiceStarted = true;
+    }
+
+    /**
+     * Register a state change callback. When the state is changed, caller with receive the callback
+     * event
+     */
+    public void registerStateChangeCallback(StateChangeCallback callback) {
+        mChangeCallbackList.add(callback);
+        if (!mIsWifiServiceStarted) return;
+        callback.onScreenStateChanged(mPowerManager.isInteractive());
+    }
+
+    /**
+     * Unregister a state change callback when caller is not interested the state change anymore.
+     */
+    public void unregisterStateChangeCallback(StateChangeCallback callback) {
+        mChangeCallbackList.remove(callback);
+    }
+
+    private void handleScreenStateChanged(boolean screenOn) {
+        for (StateChangeCallback callback : mChangeCallbackList) {
+            callback.onScreenStateChanged(screenOn);
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/WifiDialogManager.java b/service/java/com/android/server/wifi/WifiDialogManager.java
index 6c35e60..b7a0c40 100644
--- a/service/java/com/android/server/wifi/WifiDialogManager.java
+++ b/service/java/com/android/server/wifi/WifiDialogManager.java
@@ -26,7 +26,6 @@
 import android.net.Uri;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiManager;
-import android.os.PowerManager;
 import android.os.UserHandle;
 import android.provider.Browser;
 import android.text.SpannableString;
@@ -81,48 +80,54 @@
     private final @NonNull WifiThreadRunner mWifiThreadRunner;
     private final @NonNull FrameworkFacade mFrameworkFacade;
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mWifiThreadRunner.post(() -> {
-                String action = intent.getAction();
-                if (mVerboseLoggingEnabled) {
-                    Log.v(TAG, "Received action: " + action);
+    private final BroadcastReceiver mBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mWifiThreadRunner.post(
+                            () -> {
+                                String action = intent.getAction();
+                                if (mVerboseLoggingEnabled) {
+                                    Log.v(TAG, "Received action: " + action);
+                                }
+                                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+                                    // Change all window types to TYPE_APPLICATION_OVERLAY to
+                                    // prevent the dialogs from appearing over the lock screen when
+                                    // the screen turns on again.
+                                    for (LegacySimpleDialogHandle dialogHandle :
+                                            mActiveLegacySimpleDialogs) {
+                                        dialogHandle.changeWindowType(
+                                                WindowManager.LayoutParams
+                                                        .TYPE_APPLICATION_OVERLAY);
+                                    }
+                                } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+                                    // Change all window types to TYPE_KEYGUARD_DIALOG to show the
+                                    // dialogs over the QuickSettings after the screen is unlocked.
+                                    for (LegacySimpleDialogHandle dialogHandle :
+                                            mActiveLegacySimpleDialogs) {
+                                        dialogHandle.changeWindowType(
+                                                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+                                    }
+                                } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+                                    if (intent.getBooleanExtra(
+                                            WifiManager.EXTRA_CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI,
+                                            false)) {
+                                        return;
+                                    }
+                                    if (mVerboseLoggingEnabled) {
+                                        Log.v(
+                                                TAG,
+                                                "ACTION_CLOSE_SYSTEM_DIALOGS received, cancelling"
+                                                        + " all legacy dialogs.");
+                                    }
+                                    for (LegacySimpleDialogHandle dialogHandle :
+                                            mActiveLegacySimpleDialogs) {
+                                        dialogHandle.cancelDialog();
+                                    }
+                                }
+                            });
                 }
-                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                    // Change all window types to TYPE_APPLICATION_OVERLAY to prevent the dialogs
-                    // from appearing over the lock screen when the screen turns on again.
-                    for (LegacySimpleDialogHandle dialogHandle : mActiveLegacySimpleDialogs) {
-                        dialogHandle.changeWindowType(
-                                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
-                    }
-                } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                    // Change all window types to TYPE_KEYGUARD_DIALOG to show the dialogs over the
-                    // QuickSettings after the screen is unlocked.
-                    for (LegacySimpleDialogHandle dialogHandle : mActiveLegacySimpleDialogs) {
-                        dialogHandle.changeWindowType(
-                                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-                    }
-                } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                    if (intent.getBooleanExtra(
-                            WifiManager.EXTRA_CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI, false)) {
-                        return;
-                    }
-                    if (!context.getSystemService(PowerManager.class).isInteractive()) {
-                        // Do not cancel dialogs for ACTION_CLOSE_SYSTEM_DIALOGS due to screen off.
-                        return;
-                    }
-                    if (mVerboseLoggingEnabled) {
-                        Log.v(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received while screen on,"
-                                + " cancelling all legacy dialogs.");
-                    }
-                    for (LegacySimpleDialogHandle dialogHandle : mActiveLegacySimpleDialogs) {
-                        dialogHandle.cancelDialog();
-                    }
-                }
-            });
-        }
-    };
+            };
 
     /**
      * Constructs a WifiDialogManager
diff --git a/service/java/com/android/server/wifi/WifiGlobals.java b/service/java/com/android/server/wifi/WifiGlobals.java
index 641c8dc..b6cc415 100644
--- a/service/java/com/android/server/wifi/WifiGlobals.java
+++ b/service/java/com/android/server/wifi/WifiGlobals.java
@@ -19,14 +19,21 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
 import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.build.SdkLevel;
+import com.android.server.wifi.WifiBlocklistMonitor.CarrierSpecificEapFailureConfig;
 import com.android.wifi.resources.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,11 +46,15 @@
 /** Global wifi service in-memory state that is not persisted. */
 @ThreadSafe
 public class WifiGlobals {
+
+    private static final String TAG = "WifiGlobals";
     private final Context mContext;
 
     private final AtomicInteger mPollRssiIntervalMillis = new AtomicInteger(-1);
     private final AtomicBoolean mIpReachabilityDisconnectEnabled = new AtomicBoolean(true);
     private final AtomicBoolean mIsBluetoothConnected = new AtomicBoolean(false);
+    // Set default to false to check if the value will be overridden by WifiSettingConfigStore.
+    private final AtomicBoolean mIsWepAllowed = new AtomicBoolean(false);
 
     // These are read from the overlay, cache them after boot up.
     private final boolean mIsWpa3SaeUpgradeEnabled;
@@ -63,20 +74,29 @@
     private final int mPollRssiLongIntervalMillis;
     private final int mClientRssiMonitorThresholdDbm;
     private final int mClientRssiMonitorHysteresisDb;
+    private boolean mDisableFirmwareRoamingInIdleMode = false;
+    private final boolean mIsSupportMultiInternetDual5G;
     private final boolean mAdjustPollRssiIntervalEnabled;
     private final boolean mWifiInterfaceAddedSelfRecoveryEnabled;
     private final int mNetworkNotFoundEventThreshold;
     private boolean mIsBackgroundScanSupported;
     private final boolean mIsWepDeprecated;
     private final boolean mIsWpaPersonalDeprecated;
-
+    private final Map<String, List<String>> mCountryCodeToAfcServers;
+    private final long mWifiConfigMaxDisableDurationMs;
     // This is set by WifiManager#setVerboseLoggingEnabled(int).
-    private boolean mIsShowKeyVerboseLoggingModeEnabled = false;
+    private int mVerboseLoggingLevel = WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED;
     private boolean mIsUsingExternalScorer = false;
     private boolean mDisableUnwantedNetworkOnLowRssi = false;
+    private final boolean mIsAfcSupportedOnDevice;
+    private boolean mDisableNudDisconnectsForWapiInSpecificCc = false;
     private Set<String> mMacRandomizationUnsupportedSsidPrefixes = new ArraySet<>();
     private Map<String, BiFunction<String, Boolean, Boolean>> mOverrideMethods = new HashMap<>();
 
+    private SparseArray<SparseArray<CarrierSpecificEapFailureConfig>>
+            mCarrierSpecificEapFailureConfigMapPerCarrierId = new SparseArray<>();
+
+
     public WifiGlobals(Context context) {
         mContext = context;
 
@@ -118,10 +138,16 @@
                 R.integer.config_wifiClientRssiMonitorHysteresisDb);
         mAdjustPollRssiIntervalEnabled = mContext.getResources().getBoolean(
                 R.bool.config_wifiAdjustPollRssiIntervalEnabled);
+        mDisableFirmwareRoamingInIdleMode = mContext.getResources()
+                .getBoolean(R.bool.config_wifiDisableFirmwareRoamingInIdleMode);
+        mIsSupportMultiInternetDual5G = mContext.getResources().getBoolean(
+                R.bool.config_wifiAllowMultiInternetConnectDual5GFrequency);
         mWifiInterfaceAddedSelfRecoveryEnabled = mContext.getResources().getBoolean(
                 R.bool.config_wifiInterfaceAddedSelfRecoveryEnabled);
         mDisableUnwantedNetworkOnLowRssi = mContext.getResources().getBoolean(
                 R.bool.config_wifiDisableUnwantedNetworkOnLowRssi);
+        mDisableNudDisconnectsForWapiInSpecificCc = mContext.getResources().getBoolean(
+                R.bool.config_wifiDisableNudDisconnectsForWapiInSpecificCc);
         mNetworkNotFoundEventThreshold = mContext.getResources().getInteger(
                 R.integer.config_wifiNetworkNotFoundEventThreshold);
         mIsBackgroundScanSupported = mContext.getResources()
@@ -130,8 +156,14 @@
                 .getBoolean(R.bool.config_wifiWepDeprecated);
         mIsWpaPersonalDeprecated = mContext.getResources()
                 .getBoolean(R.bool.config_wifiWpaPersonalDeprecated);
+        mIsAfcSupportedOnDevice = mContext.getResources().getBoolean(R.bool.config_wifiAfcSupported)
+                && mContext.getResources().getBoolean(R.bool.config_wifiSoftap6ghzSupported)
+                && mContext.getResources().getBoolean(R.bool.config_wifi6ghzSupport);
+        mWifiConfigMaxDisableDurationMs = mContext.getResources()
+                .getInteger(R.integer.config_wifiDisableTemporaryMaximumDurationMs);
         Set<String> unsupportedSsidPrefixes = new ArraySet<>(mContext.getResources().getStringArray(
                 R.array.config_wifiForceDisableMacRandomizationSsidPrefixList));
+        mCountryCodeToAfcServers = getCountryCodeToAfcServersMap();
         if (!unsupportedSsidPrefixes.isEmpty()) {
             for (String ssid : unsupportedSsidPrefixes) {
                 String cleanedSsid = ssid.length() > 1 && (ssid.charAt(0) == '"')
@@ -140,6 +172,7 @@
                 mMacRandomizationUnsupportedSsidPrefixes.add(cleanedSsid);
             }
         }
+        loadCarrierSpecificEapFailureConfigMap();
         mOverrideMethods.put("config_wifi_background_scan_support",
                 new BiFunction<String, Boolean, Boolean>() {
                 @Override
@@ -159,6 +192,89 @@
             });
     }
 
+    /**
+     * Gets the CarrierSpecificEapFailureConfig applicable for the carrierId and eapFailureReason.
+     * @param carrierId the carrier ID
+     * @param eapFailureReason EAP failure reason
+     * @return The applicable CarrierSpecificEapFailureConfig, or null if there's no data for this
+     * particular combination of carrierId and eapFailureReason.
+     */
+    public @Nullable CarrierSpecificEapFailureConfig getCarrierSpecificEapFailureConfig(
+            int carrierId, int eapFailureReason) {
+        if (!mCarrierSpecificEapFailureConfigMapPerCarrierId.contains(carrierId)) {
+            return null;
+        }
+        return mCarrierSpecificEapFailureConfigMapPerCarrierId.get(carrierId).get(eapFailureReason);
+    }
+
+    /**
+     * Utility method for unit testing.
+     */
+    public @VisibleForTesting int getCarrierSpecificEapFailureConfigMapSize() {
+        return mCarrierSpecificEapFailureConfigMapPerCarrierId.size();
+    }
+
+    private void loadCarrierSpecificEapFailureConfigMap() {
+        String[] eapFailureOverrides = mContext.getResources().getStringArray(
+                R.array.config_wifiEapFailureConfig);
+        if (eapFailureOverrides == null) {
+            return;
+        }
+        for (String line : eapFailureOverrides) {
+            if (line == null) {
+                continue;
+            }
+            String[] items = line.split(",");
+            if (items.length != 5) {
+                // error case. Should have exactly 5 items.
+                Log.e(TAG, "Failed to parse eapFailureOverrides line=" + line);
+                continue;
+            }
+            try {
+                int carrierId = Integer.parseInt(items[0].trim());
+                int eapFailureCode = Integer.parseInt(items[1].trim());
+                int displayDialogue = Integer.parseInt(items[2].trim());
+                int disableThreshold = Integer.parseInt(items[3].trim());
+                int disableDurationMinutes = Integer.parseInt(items[4].trim());
+                if (!mCarrierSpecificEapFailureConfigMapPerCarrierId.contains(carrierId)) {
+                    mCarrierSpecificEapFailureConfigMapPerCarrierId.put(carrierId,
+                            new SparseArray<>());
+                }
+                SparseArray<CarrierSpecificEapFailureConfig> perEapFailureMap =
+                        mCarrierSpecificEapFailureConfigMapPerCarrierId.get(carrierId);
+                perEapFailureMap.put(eapFailureCode, new CarrierSpecificEapFailureConfig(
+                        disableThreshold, disableDurationMinutes * 60 * 1000, displayDialogue > 0));
+            } catch (Exception e) {
+                // failure to parse. Something is wrong with the config.
+                Log.e(TAG, "Parsing eapFailureOverrides line=" + line
+                        + ". Exception occurred:" + e);
+            }
+        }
+    }
+
+    private Map<String, List<String>> getCountryCodeToAfcServersMap() {
+        Map<String, List<String>> countryCodeToAfcServers = new HashMap<>();
+        String[] countryCodeToAfcServersFromConfig = mContext.getResources().getStringArray(
+                R.array.config_wifiAfcServerUrlsForCountry);
+
+        if (countryCodeToAfcServersFromConfig == null) {
+            return countryCodeToAfcServers;
+        }
+
+        // each entry should be of the form: countryCode,url1,url2...
+        for (String entry : countryCodeToAfcServersFromConfig) {
+            String[] countryAndUrls = entry.split(",");
+
+            // if no servers are specified for a country, then continue to the next entry
+            if (countryAndUrls.length < 2) {
+                continue;
+            }
+            countryCodeToAfcServers.put(countryAndUrls[0], Arrays.asList(Arrays.copyOfRange(
+                    countryAndUrls, 1, countryAndUrls.length)));
+        }
+        return countryCodeToAfcServers;
+    }
+
     public Set<String> getMacRandomizationUnsupportedSsidPrefixes() {
         return mMacRandomizationUnsupportedSsidPrefixes;
     }
@@ -178,6 +294,21 @@
         return mIpReachabilityDisconnectEnabled.get();
     }
 
+    /**
+     * Returns a list of AFC server URLs for a country, or null if AFC is not available in that
+     * country.
+     */
+    public @Nullable List<String> getAfcServerUrlsForCountry(String countryCode) {
+        return mCountryCodeToAfcServers.get(countryCode);
+    }
+
+    /**
+     * Returns whether this device supports AFC.
+     */
+    public boolean isAfcSupportedOnDevice() {
+        return mIsAfcSupportedOnDevice;
+    }
+
     /** Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects. */
     public void setIpReachabilityDisconnectEnabled(boolean enabled) {
         mIpReachabilityDisconnectEnabled.set(enabled);
@@ -220,7 +351,16 @@
      * @return boolean true if WEP networks are deprecated, false otherwise.
      */
     public boolean isWepDeprecated() {
-        return mIsWepDeprecated;
+        return mIsWepDeprecated || !mIsWepAllowed.get();
+    }
+
+    /**
+     * Helper method to check if WEP networks are supported.
+     *
+     * @return boolean true if WEP networks are supported, false otherwise.
+     */
+    public boolean isWepSupported() {
+        return !mIsWepDeprecated;
     }
 
     /**
@@ -233,6 +373,22 @@
     }
 
     /**
+     * Helper method to check whether this device should disable firmware roaming in idle mode.
+     * @return if the device should disable firmware roaming in idle mode.
+     */
+    public boolean isDisableFirmwareRoamingInIdleMode() {
+        return mDisableFirmwareRoamingInIdleMode;
+    }
+
+    /**
+     * Get the configuration for whether Multi-internet are allowed to
+     * connect simultaneously to both 5GHz high and 5GHz low.
+     */
+    public boolean isSupportMultiInternetDual5G() {
+        return mIsSupportMultiInternetDual5G;
+    }
+
+    /**
      * Helper method to check if the device may not connect to the configuration
      * due to deprecated security type
      */
@@ -295,14 +451,21 @@
         return mIsWpa3SaeH2eSupported;
     }
 
-    /** Set if show key verbose logging mode is enabled. */
-    public void setShowKeyVerboseLoggingModeEnabled(boolean enable) {
-        mIsShowKeyVerboseLoggingModeEnabled = enable;
+    /**
+     * Record the verbose logging level
+     */
+    public void setVerboseLoggingLevel(int level) {
+        mVerboseLoggingLevel = level;
+    }
+
+    /** Return the currently set verbose logging level. */
+    public int getVerboseLoggingLevel() {
+        return mVerboseLoggingLevel;
     }
 
     /** Check if show key verbose logging mode is enabled. */
     public boolean getShowKeyVerboseLoggingModeEnabled() {
-        return mIsShowKeyVerboseLoggingModeEnabled;
+        return mVerboseLoggingLevel == WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY;
     }
 
     /** Set whether the external scorer is being used **/
@@ -417,6 +580,13 @@
     }
 
     /**
+     * Get whether to disable NUD disconnects for WAPI configurations in a specific CC.
+     */
+    public boolean disableNudDisconnectsForWapiInSpecificCc() {
+        return mDisableNudDisconnectsForWapiInSpecificCc;
+    }
+
+    /**
      * Get the threshold to use for blocking a network due to NETWORK_NOT_FOUND_EVENT failure.
      */
     public int getNetworkNotFoundEventThreshold() {
@@ -424,6 +594,27 @@
     }
 
     /**
+     * Set whether wep network is allowed by user.
+     */
+    public void setWepAllowed(boolean isAllowed) {
+        mIsWepAllowed.set(isAllowed);
+    }
+
+    /**
+     * Get whether or not wep network is allowed by user.
+     */
+    public boolean isWepAllowed() {
+        return mIsWepAllowed.get();
+    }
+
+    /**
+     * Get the maximum Wifi temporary disable duration.
+     */
+    public long getWifiConfigMaxDisableDurationMs() {
+        return mWifiConfigMaxDisableDurationMs;
+    }
+
+    /**
      * Force Overlay Config Value for background scan.
      */
     public boolean forceOverlayConfigValue(String configString, String value,
@@ -465,5 +656,23 @@
         pw.println("mIsBackgroundScanSupported=" + mIsBackgroundScanSupported);
         pw.println("mIsWepDeprecated=" + mIsWepDeprecated);
         pw.println("mIsWpaPersonalDeprecated=" + mIsWpaPersonalDeprecated);
+        pw.println("mIsWepAllowed=" + mIsWepAllowed.get());
+        pw.println("mDisableFirmwareRoamingInIdleMode=" + mDisableFirmwareRoamingInIdleMode);
+        pw.println("mCarrierSpecificEapFailureConfigMapPerCarrierId mapping below:");
+        pw.println("mWifiConfigMaxDisableDurationMs=" + mWifiConfigMaxDisableDurationMs);
+        for (int i = 0; i < mCarrierSpecificEapFailureConfigMapPerCarrierId.size(); i++) {
+            int carrierId = mCarrierSpecificEapFailureConfigMapPerCarrierId.keyAt(i);
+            SparseArray<CarrierSpecificEapFailureConfig> perFailureMap =
+                    mCarrierSpecificEapFailureConfigMapPerCarrierId.valueAt(i);
+            for (int j = 0; j < perFailureMap.size(); j++) {
+                int eapFailureCode = perFailureMap.keyAt(j);
+                pw.println("carrierId=" + carrierId
+                        + ", eapFailureCode=" + eapFailureCode
+                        + ", displayNotification=" + perFailureMap.valueAt(j).displayNotification
+                        + ", threshold=" + perFailureMap.valueAt(j).threshold
+                        + ", durationMs=" + perFailureMap.valueAt(j).durationMs);
+            }
+        }
+        pw.println("mIsSupportMultiInternetDual5G=" + mIsSupportMultiInternetDual5G);
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index c5ec888..889c935 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -51,6 +51,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.aware.WifiAwareMetrics;
 import com.android.server.wifi.coex.CoexManager;
@@ -71,6 +72,7 @@
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
 import com.android.server.wifi.util.WorkSourceHelper;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import java.io.BufferedReader;
@@ -97,6 +99,10 @@
      */
     private static final int MAX_RECENTLY_CONNECTED_NETWORK = 100;
 
+    private final WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
+    private final PasspointNetworkNominateHelper mNominateHelper;
+    private final AlarmManager mAlarmManager;
+
     private static NetworkCapabilities.Builder makeBaseNetworkCapatibilitiesFilterBuilder() {
         NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
@@ -146,6 +152,7 @@
     private final BatteryStatsManager mBatteryStats;
     private final FrameworkFacade mFrameworkFacade = new FrameworkFacade();
     private final DeviceConfigFacade mDeviceConfigFacade;
+    private final FeatureFlags mFeatureFlags;
     private final UserManager mUserManager;
     private final HandlerThread mWifiHandlerThread;
     private final HandlerThread mWifiP2pServiceHandlerThread;
@@ -185,7 +192,6 @@
     private final WifiConfigManager mWifiConfigManager;
     private final WifiConnectivityHelper mWifiConnectivityHelper;
     private final LocalLog mConnectivityLocalLog;
-    private final LocalLog mWifiAwareLocalLog;
     private final LocalLog mWifiHandlerLocalLog;
     private final ThroughputScorer mThroughputScorer;
     private final WifiNetworkSelector mWifiNetworkSelector;
@@ -253,6 +259,10 @@
     private final WifiNotificationManager mWifiNotificationManager;
     private final LastCallerInfoManager mLastCallerInfoManager;
     private final InterfaceConflictManager mInterfaceConflictManager;
+    private final AfcManager mAfcManager;
+    private final WifiContext mContextWithAttributionTag;
+    private final AfcLocationUtil mAfcLocationUtil;
+    private final AfcClient mAfcClient;
     @NonNull private final WifiDialogManager mWifiDialogManager;
     @NonNull private final SsidTranslator mSsidTranslator;
     @NonNull private final ApplicationQosPolicyRequestHandler mApplicationQosPolicyRequestHandler;
@@ -271,6 +281,7 @@
         sWifiInjector = this;
         mLastCallerInfoManager = new LastCallerInfoManager();
         mContext = context;
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
 
         // Now create and start handler threads
         mWifiHandlerThread = new HandlerThread("WifiHandlerThread");
@@ -283,12 +294,14 @@
         mWifiMonitor = new WifiMonitor();
         mBatteryStats = context.getSystemService(BatteryStatsManager.class);
         mWifiP2pMetrics = new WifiP2pMetrics(mClock, mContext);
-        mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, wifiLooper,
-                awareMetrics, rttMetrics, new WifiPowerMetrics(mBatteryStats), mWifiP2pMetrics,
-                mDppMetrics, mWifiMonitor);
         RunnerHandler wifiHandler = new RunnerHandler(wifiLooper, context.getResources().getInteger(
                 R.integer.config_wifiConfigurationWifiRunnerThresholdInMs),
-                mWifiHandlerLocalLog, mWifiMetrics);
+                mWifiHandlerLocalLog);
+        mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManager(context, wifiHandler);
+        mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, wifiLooper,
+                awareMetrics, rttMetrics, new WifiPowerMetrics(mBatteryStats), mWifiP2pMetrics,
+                mDppMetrics, mWifiMonitor, mWifiDeviceStateChangeManager);
+
         mWifiDiagnosticsHandlerThread = new HandlerThread("WifiDiagnostics");
         mWifiDiagnosticsHandlerThread.start();
 
@@ -317,6 +330,7 @@
                 new HandlerThread("PasspointProvisionerHandlerThread");
         mPasspointProvisionerHandlerThread.start();
         mDeviceConfigFacade = new DeviceConfigFacade(mContext, wifiHandler, mWifiMetrics);
+        mFeatureFlags = mDeviceConfigFacade.getFeatureFlags();
         mAdaptiveConnectivityEnabledSettingObserver =
                 new AdaptiveConnectivityEnabledSettingObserver(wifiHandler, mWifiMetrics,
                         mFrameworkFacade, mContext);
@@ -365,8 +379,13 @@
         // New config store
         mWifiConfigStore = new WifiConfigStore(mContext, wifiHandler, mClock, mWifiMetrics,
                 WifiConfigStore.createSharedFiles(mFrameworkFacade.isNiapModeOn(mContext)));
-        mWifiPseudonymManager = new WifiPseudonymManager(
-                mContext, this, mClock, wifiLooper);
+        mWifiPseudonymManager =
+                new WifiPseudonymManager(
+                        mContext,
+                        this,
+                        mClock,
+                        mAlarmManager,
+                        wifiLooper);
         mWifiCarrierInfoManager = new WifiCarrierInfoManager(makeTelephonyManager(),
                 subscriptionManager, this, mFrameworkFacade, mContext,
                 mWifiConfigStore, wifiHandler, mWifiMetrics, mClock, mWifiPseudonymManager);
@@ -384,26 +403,40 @@
         mConnectivityLocalLog = new LocalLog(
                 mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? maxLinesLowRam
                         : maxLinesHighRam);
-        mWifiAwareLocalLog = new LocalLog(
-                mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? maxLinesLowRam
-                        : maxLinesHighRam);
         mWifiDiagnostics = new WifiDiagnostics(
                 mContext, this, mWifiNative, mBuildProperties,
                 new LastMileLogger(this), mClock, mWifiDiagnosticsHandlerThread.getLooper());
         mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock,
                 mWifiMetrics, mWifiDiagnostics, wifiLooper,
                 mDeviceConfigFacade, mWifiThreadRunner, mWifiMonitor);
-        mWifiBlocklistMonitor = new WifiBlocklistMonitor(mContext, mWifiConnectivityHelper,
-                mWifiLastResortWatchdog, mClock, new LocalLog(
-                mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256),
-                mWifiScoreCard, mScoringParams, mWifiMetrics, mWifiPermissionsUtil);
+        mWifiBlocklistMonitor =
+                new WifiBlocklistMonitor(
+                        mContext,
+                        mWifiConnectivityHelper,
+                        mWifiLastResortWatchdog,
+                        mClock,
+                        new LocalLog(
+                                mContext.getSystemService(ActivityManager.class).isLowRamDevice()
+                                        ? 128
+                                        : 256),
+                        mWifiScoreCard,
+                        mScoringParams,
+                        mWifiMetrics,
+                        mWifiPermissionsUtil,
+                        mWifiGlobals);
         mWifiMetrics.setWifiBlocklistMonitor(mWifiBlocklistMonitor);
         // Config Manager
-        mWifiConfigManager = new WifiConfigManager(mContext, mWifiKeyStore, mWifiConfigStore,
-                new NetworkListSharedStoreData(mContext),
-                new NetworkListUserStoreData(mContext),
-                new RandomizedMacStoreData(),
-                mLruConnectionTracker, this);
+        mWifiConfigManager =
+                new WifiConfigManager(
+                        mContext,
+                        mWifiKeyStore,
+                        mWifiConfigStore,
+                        new NetworkListSharedStoreData(mContext),
+                        new NetworkListUserStoreData(mContext),
+                        new RandomizedMacStoreData(),
+                        mLruConnectionTracker,
+                        this,
+                        wifiHandler);
         mSettingsConfigStore = new WifiSettingsConfigStore(context, wifiHandler,
                 mSettingsMigrationDataHolder, mWifiConfigManager, mWifiConfigStore);
         mSettingsStore = new WifiSettingsStore(mContext, mSettingsConfigStore, mWifiThreadRunner,
@@ -421,7 +454,7 @@
                 mWifiPermissionsUtil, mWifiMetrics, mClock, wifiHandler, mSettingsConfigStore);
         mWifiBlocklistMonitor.setScanRequestProxy(mScanRequestProxy);
         mSarManager = new SarManager(mContext, makeTelephonyManager(), wifiLooper,
-                mWifiNative);
+                mWifiNative, mWifiDeviceStateChangeManager);
         mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiScoreCard, mScoringParams,
                 mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiMetrics, this,
                 mThroughputPredictor, mWifiChannelUtilizationScan, mWifiGlobals,
@@ -443,16 +476,16 @@
                 wifiHandler, mWifiNative, mWifiKeyStore, mClock, new PasspointObjectFactory(),
                 mWifiConfigManager, mWifiConfigStore, mSettingsStore, mWifiMetrics,
                 mWifiCarrierInfoManager, mMacAddressUtil, mWifiPermissionsUtil);
-        PasspointNetworkNominateHelper nominateHelper =
+        mNominateHelper =
                 new PasspointNetworkNominateHelper(mPasspointManager, mWifiConfigManager,
                         mConnectivityLocalLog, mWifiCarrierInfoManager, mContext.getResources(),
                         mClock);
-        mPasspointManager.setPasspointNetworkNominateHelper(nominateHelper);
+        mPasspointManager.setPasspointNetworkNominateHelper(mNominateHelper);
         mSavedNetworkNominator = new SavedNetworkNominator(
-                mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mWifiCarrierInfoManager,
+                mWifiConfigManager, mConnectivityLocalLog, mWifiCarrierInfoManager,
                 mWifiPseudonymManager, mWifiPermissionsUtil, mWifiNetworkSuggestionsManager);
         mNetworkSuggestionNominator = new NetworkSuggestionNominator(mWifiNetworkSuggestionsManager,
-                mWifiConfigManager, nominateHelper, mConnectivityLocalLog, mWifiCarrierInfoManager,
+                mWifiConfigManager, mConnectivityLocalLog, mWifiCarrierInfoManager,
                 mWifiPseudonymManager, mWifiMetrics);
 
         mWifiMetrics.setPasspointManager(mPasspointManager);
@@ -469,7 +502,7 @@
         mActiveModeWarden = new ActiveModeWarden(this, wifiLooper,
                 mWifiNative, mDefaultClientModeManager, mBatteryStats, mWifiDiagnostics,
                 mContext, mSettingsStore, mFrameworkFacade, mWifiPermissionsUtil, mWifiMetrics,
-                mExternalScoreUpdateObserverProxy, mDppManager);
+                mExternalScoreUpdateObserverProxy, mDppManager, mWifiGlobals);
         mWifiMetrics.setActiveModeWarden(mActiveModeWarden);
         mWifiHealthMonitor = new WifiHealthMonitor(mContext, this, mClock, mWifiConfigManager,
             mWifiScoreCard, wifiHandler, mWifiNative, l2KeySeed, mDeviceConfigFacade,
@@ -503,7 +536,8 @@
                 mWifiChannelUtilizationScan, mPasspointManager, mMultiInternetManager,
                 mDeviceConfigFacade, mActiveModeWarden, mFrameworkFacade, mWifiGlobals,
                 mExternalPnoScanRequestManager, mSsidTranslator, mWifiPermissionsUtil,
-                mWifiCarrierInfoManager, mCountryCode, mWifiDialogManager);
+                mWifiCarrierInfoManager, mCountryCode, mWifiDialogManager,
+                mWifiDeviceStateChangeManager);
         mMboOceController = new MboOceController(makeTelephonyManager(), mActiveModeWarden,
                 mWifiThreadRunner);
         mConnectionFailureNotifier = new ConnectionFailureNotifier(
@@ -535,7 +569,7 @@
                 mWifiConnectivityManager);
         mMultiInternetWifiNetworkFactory = new MultiInternetWifiNetworkFactory(
                 wifiLooper, mContext, REGULAR_NETWORK_CAPABILITIES_FILTER,
-                mFrameworkFacade, mContext.getSystemService(AlarmManager.class),
+                mFrameworkFacade, mAlarmManager,
                 mWifiPermissionsUtil, mMultiInternetManager, mWifiConnectivityManager,
                 mConnectivityLocalLog);
         mWifiScanAlwaysAvailableSettingsCompatibility =
@@ -555,7 +589,7 @@
                 this, mFrameworkFacade, mClock, mActiveModeWarden);
         mLockManager = new WifiLockManager(mContext, mBatteryStats, mActiveModeWarden,
                 mFrameworkFacade, wifiHandler, mClock, mWifiMetrics, mDeviceConfigFacade,
-                mWifiPermissionsUtil);
+                mWifiPermissionsUtil, mWifiDeviceStateChangeManager);
         mSelfRecovery = new SelfRecovery(mContext, mActiveModeWarden, mClock, mWifiNative,
                 mWifiGlobals);
         mWifiMulticastLockManager = new WifiMulticastLockManager(mActiveModeWarden, mBatteryStats,
@@ -571,7 +605,15 @@
                 mWifiNotificationManager);
         mWifiPulledAtomLogger = new WifiPulledAtomLogger(
                 mContext.getSystemService(StatsManager.class), wifiHandler,
-                mContext);
+                mContext, this);
+        mAfcLocationUtil = new AfcLocationUtil();
+        mAfcClient = new AfcClient(BackgroundThread.getHandler());
+        mContextWithAttributionTag = new WifiContext(
+                mContext.createAttributionContext("WifiService"));
+        // The context needs to have an Attribution Tag set to get the current location using
+        // {@link LocationManager#getCurrentLocation}, so we need to pass mContextWithAttributionTag
+        // instead of mContext to the AfcManager.
+        mAfcManager = new AfcManager(mContextWithAttributionTag, this);
     }
 
     /**
@@ -658,6 +700,9 @@
     }
 
     public HandlerThread getWifiP2pServiceHandlerThread() {
+        if (mFeatureFlags.singleWifiThread()) {
+            return mWifiHandlerThread;
+        }
         return mWifiP2pServiceHandlerThread;
     }
 
@@ -839,7 +884,7 @@
                 mBatteryStats, supplicantStateTracker, mMboOceController, mWifiCarrierInfoManager,
                 mWifiPseudonymManager,
                 new EapFailureNotifier(mContext, mFrameworkFacade, mWifiCarrierInfoManager,
-                        mWifiNotificationManager),
+                        mWifiNotificationManager, mWifiGlobals),
                 mSimRequiredNotifier,
                 new WifiScoreReport(mScoringParams, mClock, mWifiMetrics, wifiInfo,
                         mWifiNative, mWifiBlocklistMonitor, mWifiThreadRunner, mWifiScoreCard,
@@ -848,7 +893,8 @@
                         mActiveModeWarden, mWifiConnectivityManager, mWifiConfigManager),
                 mWifiP2pConnection, mWifiGlobals, ifaceName, clientModeManager,
                 mCmiMonitor, mBroadcastQueue, mWifiNetworkSelector, makeTelephonyManager(),
-                this, mSettingsConfigStore, verboseLoggingEnabled, mWifiNotificationManager);
+                this, mSettingsConfigStore, verboseLoggingEnabled, mWifiNotificationManager,
+                mWifiConnectivityHelper);
     }
 
     public WifiNetworkAgent makeWifiNetworkAgent(
@@ -959,6 +1005,9 @@
      * TODO: share worker thread with other Wi-Fi handlers (b/27924886)
      */
     public HandlerThread getWifiAwareHandlerThread() {
+        if (mFeatureFlags.singleWifiThread()) {
+            return mWifiHandlerThread;
+        }
         if (mWifiAwareHandlerThread == null) { // lazy initialization
             mWifiAwareHandlerThread = new HandlerThread("wifiAwareService");
             mWifiAwareHandlerThread.start();
@@ -972,6 +1021,9 @@
      * TODO: share worker thread with other Wi-Fi handlers (b/27924886)
      */
     public HandlerThread getRttHandlerThread() {
+        if (mFeatureFlags.singleWifiThread()) {
+            return mWifiHandlerThread;
+        }
         if (mRttHandlerThread == null) { // lazy initialization
             mRttHandlerThread = new HandlerThread("wifiRttService");
             mRttHandlerThread.start();
@@ -1198,10 +1250,26 @@
         return mMultiInternetManager;
     }
 
+    public AfcManager getAfcManager() {
+        return mAfcManager;
+    }
+
     public WifiContext getContext() {
         return mContext;
     }
 
+    public WifiContext getContextWithAttributionTag() {
+        return mContextWithAttributionTag;
+    }
+
+    public AfcLocationUtil getAfcLocationUtil() {
+        return mAfcLocationUtil;
+    }
+
+    public AfcClient getAfcClient() {
+        return mAfcClient;
+    }
+
     /**
      * Creates a BufferedReader for the given filename. Useful for unit tests that depend on IO.
      */
@@ -1211,11 +1279,6 @@
     }
 
     @NonNull
-    public LocalLog getWifiAwareLocalLog() {
-        return mWifiAwareLocalLog;
-    }
-
-    @NonNull
     public LocalLog getWifiHandlerLocalLog() {
         return mWifiHandlerLocalLog;
     }
@@ -1229,4 +1292,19 @@
     public ApplicationQosPolicyRequestHandler getApplicationQosPolicyRequestHandler() {
         return mApplicationQosPolicyRequestHandler;
     }
+
+    @NonNull
+    public WifiDeviceStateChangeManager getWifiDeviceStateChangeManager() {
+        return mWifiDeviceStateChangeManager;
+    }
+
+    @NonNull
+    public PasspointNetworkNominateHelper getPasspointNetworkNominateHelper() {
+        return mNominateHelper;
+    }
+
+    @NonNull
+    public AlarmManager getAlarmManager() {
+        return mAlarmManager;
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java
index 97232d7..8ba4759 100644
--- a/service/java/com/android/server/wifi/WifiLockManager.java
+++ b/service/java/com/android/server/wifi/WifiLockManager.java
@@ -18,22 +18,17 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.wifi.IWifiLowLatencyLockListener;
 import android.net.wifi.WifiManager;
 import android.os.BatteryStatsManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -102,11 +97,24 @@
     private final RemoteCallbackList<IWifiLowLatencyLockListener>
             mWifiLowLatencyLockListeners = new RemoteCallbackList<>();
     private boolean mIsLowLatencyActivated = false;
+    private WorkSource mLowLatencyBlamedWorkSource = new WorkSource();
+    private WorkSource mHighPerfBlamedWorkSource = new WorkSource();
+    private enum BlameReason {
+        WIFI_CONNECTION_STATE_CHANGED,
+        SCREEN_STATE_CHANGED,
+    };
 
-    WifiLockManager(Context context, BatteryStatsManager batteryStats,
-            ActiveModeWarden activeModeWarden, FrameworkFacade frameworkFacade,
-            Handler handler, Clock clock, WifiMetrics wifiMetrics,
-            DeviceConfigFacade deviceConfigFacade, WifiPermissionsUtil wifiPermissionsUtil) {
+    WifiLockManager(
+            Context context,
+            BatteryStatsManager batteryStats,
+            ActiveModeWarden activeModeWarden,
+            FrameworkFacade frameworkFacade,
+            Handler handler,
+            Clock clock,
+            WifiMetrics wifiMetrics,
+            DeviceConfigFacade deviceConfigFacade,
+            WifiPermissionsUtil wifiPermissionsUtil,
+            WifiDeviceStateChangeManager wifiDeviceStateChangeManager) {
         mContext = context;
         mBatteryStats = batteryStats;
         mActiveModeWarden = activeModeWarden;
@@ -118,27 +126,23 @@
         mDeviceConfigFacade = deviceConfigFacade;
         mWifiPermissionsUtil = wifiPermissionsUtil;
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        context.registerReceiver(
-                new BroadcastReceiver() {
+        wifiDeviceStateChangeManager.registerStateChangeCallback(
+                new WifiDeviceStateChangeManager.StateChangeCallback() {
                     @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
-                            handleScreenStateChanged(true);
-                        } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
-                            handleScreenStateChanged(false);
-                        }
+                    public void onScreenStateChanged(boolean screenOn) {
+                        handleScreenStateChanged(screenOn);
                     }
-                }, filter, null, mHandler);
-        handleScreenStateChanged(context.getSystemService(PowerManager.class).isInteractive());
+                });
 
         // Register for UID fg/bg transitions
         registerUidImportanceTransitions();
     }
 
+    private boolean canDisableChipPowerSave() {
+        return mContext.getResources().getBoolean(
+                R.bool.config_wifiLowLatencyLockDisableChipPowerSave);
+    }
+
     // Check for conditions to activate high-perf lock
     private boolean canActivateHighPerfLock(int ignoreMask) {
         boolean check = true;
@@ -200,7 +204,7 @@
             // then UID either share the blame, or removed from sharing
             // whether to start or stop the blame based on UID fg/bg state
             if (canActivateLowLatencyLock(
-                    isAppScreenOnExempted(uid) ? IGNORE_SCREEN_STATE_MASK : 0)) {
+                    uidRec.mIsScreenOnExempted ? IGNORE_SCREEN_STATE_MASK : 0)) {
                 setBlameLowLatencyUid(uid, uidRec.mIsFg);
                 notifyLowLatencyActiveUsersChanged();
             }
@@ -407,7 +411,7 @@
             // Update the running mode
             updateOpMode();
             // Adjust blaming for UIDs in foreground
-            setBlameLowLatencyWatchList(screenOn);
+            setBlameLowLatencyWatchList(BlameReason.SCREEN_STATE_CHANGED, screenOn);
         }
     }
 
@@ -430,8 +434,10 @@
         mWifiConnected = hasAtLeastOneConnection;
 
         // Adjust blaming for UIDs in foreground carrying low latency locks
-        if (canActivateLowLatencyLock(IGNORE_WIFI_STATE_MASK)) {
-            setBlameLowLatencyWatchList(mWifiConnected);
+        if (canActivateLowLatencyLock(countFgLowLatencyUids(/*isScreenOnExempted*/ true) > 0
+                ? IGNORE_SCREEN_STATE_MASK | IGNORE_WIFI_STATE_MASK
+                : IGNORE_WIFI_STATE_MASK)) {
+            setBlameLowLatencyWatchList(BlameReason.WIFI_CONNECTION_STATE_CHANGED, mWifiConnected);
         }
 
         // Adjust blaming for UIDs carrying high perf locks
@@ -468,10 +474,18 @@
         return true;
     }
 
-    private boolean isAppForeground(final int uid, final int importance) {
-        if ((importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)) {
-            return true;
+    private boolean isAnyLowLatencyAppExemptedFromForeground(int[] uids) {
+        if (uids == null) return false;
+        for (int uid : uids) {
+            UidRec uidRec = mLowLatencyUidWatchList.get(uid);
+            if (uidRec != null && uidRec.mIsFgExempted) {
+                return true;
+            }
         }
+        return false;
+    }
+
+    private boolean isAppExemptedFromImportance(int uid, int importance) {
         // Exemption for applications running with CAR Mode permissions.
         if (mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission(uid)
                 && (importance
@@ -483,7 +497,25 @@
         return false;
     }
 
-    private boolean isAppScreenOnExempted(int uid) {
+    private boolean isAppForeground(final int uid, final int importance) {
+        if ((importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)) {
+            return true;
+        }
+        return isAppExemptedFromImportance(uid, importance);
+    }
+
+    private boolean isAnyLowLatencyAppExemptedFromScreenOn(int[] uids) {
+        if (uids == null) return false;
+        for (int uid : uids) {
+            UidRec uidRec = mLowLatencyUidWatchList.get(uid);
+            if (uidRec != null && uidRec.mIsScreenOnExempted) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isAppExemptedFromScreenOn(int uid) {
         // Exemption for applications running with CAR Mode permissions.
         if (mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission(uid)) {
             return true;
@@ -502,13 +534,15 @@
             mLowLatencyUidWatchList.put(uid, uidRec);
             notifyLowLatencyOwnershipChanged();
 
-            // Now check if the uid is running in foreground
-            if (isAppForeground(uid,
-                    mContext.getSystemService(ActivityManager.class).getUidImportance(uid))) {
-                uidRec.mIsFg = true;
-            }
+            uidRec.mIsFg = isAppForeground(uid,
+                    mContext.getSystemService(ActivityManager.class).getUidImportance(uid));
+            // Save the current permission of foreground & 'screen on' exemption.
+            uidRec.mIsFgExempted = isAppExemptedFromImportance(uid,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+            uidRec.mIsScreenOnExempted = isAppExemptedFromScreenOn(uid);
 
-            if (canActivateLowLatencyLock(isAppScreenOnExempted(uid) ? IGNORE_SCREEN_STATE_MASK : 0,
+            if (canActivateLowLatencyLock(
+                    uidRec.mIsScreenOnExempted ? IGNORE_SCREEN_STATE_MASK : 0,
                     uidRec)) {
                 // Share the blame for this uid
                 setBlameLowLatencyUid(uid, true);
@@ -527,16 +561,17 @@
         if (uidRec.mLockCount > 0) {
             uidRec.mLockCount--;
         } else {
-            Log.e(TAG, "Error, uid record conatains no locks");
+            Log.e(TAG, "Error, uid record contains no locks");
         }
         if (uidRec.mLockCount == 0) {
             mLowLatencyUidWatchList.remove(uid);
             notifyLowLatencyOwnershipChanged();
 
-            // Remove blame for this UID if it was alerady set
+            // Remove blame for this UID if it was already set
             // Note that blame needs to be stopped only if it was started before
             // to avoid calling the API unnecessarily, since it is reference counted
-            if (canActivateLowLatencyLock(0, uidRec)) {
+            if (canActivateLowLatencyLock(uidRec.mIsScreenOnExempted ? IGNORE_SCREEN_STATE_MASK : 0,
+                    uidRec)) {
                 setBlameLowLatencyUid(uid, false);
                 notifyLowLatencyActiveUsersChanged();
             }
@@ -634,11 +669,21 @@
             Log.d(TAG, "releaseLock: " + wifiLock);
         }
 
+        WorkSource ws = wifiLock.getWorkSource();
+        Pair<int[], String[]> uidsAndTags = WorkSourceUtil.getUidsAndTagsForWs(ws);
+
         switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
-                ++mFullHighPerfLocksReleased;
                 mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
-                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp());
+                        uidsAndTags.first,
+                        uidsAndTags.second,
+                        mWifiPermissionsUtil.getWifiCallerType(wifiLock.getUid(),
+                                ws.getPackageName(0)),
+                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp(),
+                        canDisableChipPowerSave(),
+                        false,
+                        false);
+                ++mFullHighPerfLocksReleased;
                 // Stop blaming only if blaming was set before (conditions are met).
                 // This is to avoid calling the api unncessarily, since this API is
                 // reference counted in batteryStats and statsd
@@ -647,10 +692,17 @@
                 }
                 break;
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
+                mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                        uidsAndTags.first,
+                        uidsAndTags.second,
+                        mWifiPermissionsUtil.getWifiCallerType(wifiLock.getUid(),
+                                ws.getPackageName(0)),
+                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp(),
+                        canDisableChipPowerSave(),
+                        isAnyLowLatencyAppExemptedFromScreenOn(uidsAndTags.first),
+                        isAnyLowLatencyAppExemptedFromForeground(uidsAndTags.first));
                 removeWsFromLlWatchList(wifiLock.getWorkSource());
                 ++mFullLowLatencyLocksReleased;
-                mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
-                        mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp());
                 break;
             default:
                 // Do nothing
@@ -669,6 +721,7 @@
      * @return true if the operation succeeded, false otherwise
      */
     private boolean resetCurrentMode(@NonNull ClientModeManager clientModeManager) {
+        Pair<int[], String[]> uidsAndTags = null;
         switch (mCurrentOpMode) {
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
                 if (!setPowerSave(clientModeManager, ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
@@ -676,8 +729,15 @@
                     Log.e(TAG, "Failed to reset the OpMode from hi-perf to Normal");
                     return false;
                 }
+                uidsAndTags = WorkSourceUtil.getUidsAndTagsForWs(mHighPerfBlamedWorkSource);
                 mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
-                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs);
+                        uidsAndTags.first,
+                        uidsAndTags.second,
+                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs,
+                        canDisableChipPowerSave(),
+                        false,
+                        false);
+                mHighPerfBlamedWorkSource.clear();
                 break;
 
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
@@ -685,8 +745,15 @@
                     Log.e(TAG, "Failed to reset the OpMode from low-latency to Normal");
                     return false;
                 }
+                uidsAndTags = WorkSourceUtil.getUidsAndTagsForWs(mLowLatencyBlamedWorkSource);
                 mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
-                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs);
+                        uidsAndTags.first,
+                        uidsAndTags.second,
+                        mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs,
+                        canDisableChipPowerSave(),
+                        isAnyLowLatencyAppExemptedFromScreenOn(uidsAndTags.first),
+                        isAnyLowLatencyAppExemptedFromForeground(uidsAndTags.first));
+                mLowLatencyBlamedWorkSource.clear();
                 break;
 
             case WifiManager.WIFI_MODE_NO_LOCKS_HELD:
@@ -707,8 +774,7 @@
     private boolean setPowerSave(@NonNull ClientModeManager clientModeManager,
             @ClientMode.PowerSaveClientType int client, boolean ps) {
         // Check the overlay allows lock to control chip power save.
-        if (mContext.getResources().getBoolean(
-                R.bool.config_wifiLowLatencyLockDisableChipPowerSave)) {
+        if (canDisableChipPowerSave()) {
             return clientModeManager.setPowerSave(client, ps);
         }
         // Otherwise, pretend the call is a success.
@@ -818,9 +884,6 @@
                 clientModeManager.setLowLatencyMode(!enabled);
                 return false;
             }
-            mIsLowLatencyActivated = enabled;
-            notifyLowLatencyActivated();
-            notifyLowLatencyActiveUsersChanged();
         } else if (lowLatencySupport == LOW_LATENCY_NOT_SUPPORTED) {
             // Only set power save mode
             if (!setPowerSave(clientModeManager, ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
@@ -830,6 +893,9 @@
             }
         }
 
+        mIsLowLatencyActivated = enabled;
+        notifyLowLatencyActivated();
+        notifyLowLatencyActiveUsersChanged();
         return true;
     }
 
@@ -955,7 +1021,7 @@
             UidRec uidRec = mLowLatencyUidWatchList.valueAt(idx);
             if (uidRec.mIsFg) {
                 if (isScreenOnExempted) {
-                    if (isAppScreenOnExempted(uidRec.mUid)) uidCount++;
+                    if (uidRec.mIsScreenOnExempted) uidCount++;
                 } else {
                     uidCount++;
                 }
@@ -969,6 +1035,7 @@
         Pair<int[], String[]> uidsAndTags = WorkSourceUtil.getUidsAndTagsForWs(ws);
         try {
             if (shouldBlame) {
+                mHighPerfBlamedWorkSource.add(ws);
                 mBatteryStats.reportFullWifiLockAcquiredFromSource(ws);
                 WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_STATE_CHANGED,
                         uidsAndTags.first, uidsAndTags.second,
@@ -990,6 +1057,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             if (shouldBlame) {
+                mLowLatencyBlamedWorkSource.add(new WorkSource(uid));
                 mBatteryStats.reportFullWifiLockAcquiredFromSource(new WorkSource(uid));
                 WifiStatsLog.write_non_chained(WifiStatsLog.WIFI_LOCK_STATE_CHANGED, uid, null,
                         WifiStatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON,
@@ -1005,10 +1073,15 @@
         }
     }
 
-    private void setBlameLowLatencyWatchList(boolean shouldBlame) {
+    private void setBlameLowLatencyWatchList(BlameReason reason, boolean shouldBlame) {
         boolean notify = false;
         for (int idx = 0; idx < mLowLatencyUidWatchList.size(); idx++) {
             UidRec uidRec = mLowLatencyUidWatchList.valueAt(idx);
+            // The blame state of the UIDs should not be changed if the app is exempted from
+            // screen-on and the reason for blaming is screen state change.
+            if (uidRec.mIsScreenOnExempted && reason == BlameReason.SCREEN_STATE_CHANGED) {
+                continue;
+            }
             // Affect the blame for only UIDs running in foreground
             // UIDs running in the background are already not blamed,
             // and they should remain in that state.
@@ -1101,8 +1174,10 @@
         final int mUid;
         // Count of locks owned or co-owned by this UID
         int mLockCount;
-        // Is this UID running in foreground
+        // Is this UID running in foreground or in exempted state (e.g. foreground-service)
         boolean mIsFg;
+        boolean mIsFgExempted = false;
+        boolean mIsScreenOnExempted = false;
 
         UidRec(int uid) {
             mUid = uid;
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 7a543f8..a6c6d8f 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -20,7 +20,7 @@
 
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONFIG_SAVED;
-import static com.android.server.wifi.proto.WifiStatsLog.WIFI_THREAD_TASK_EXECUTED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED;
 
 import static java.lang.StrictMath.toIntExact;
 
@@ -28,12 +28,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.NetworkCapabilities;
 import android.net.wifi.EAPConstants;
 import android.net.wifi.IOnWifiUsabilityStatsListener;
 import android.net.wifi.MloLink;
@@ -43,12 +42,14 @@
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.SoftApInfo;
 import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiAnnotations;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.DeviceMobilityState;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiUsabilityStatsEntry.ProbeStatus;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.ProvisioningCallback;
@@ -58,7 +59,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -130,6 +131,8 @@
 import com.android.server.wifi.rtt.RttMetrics;
 import com.android.server.wifi.scanner.KnownBandsChannelHelper;
 import com.android.server.wifi.util.InformationElementUtil;
+import com.android.server.wifi.util.InformationElementUtil.ApType6GHz;
+import com.android.server.wifi.util.InformationElementUtil.WifiMode;
 import com.android.server.wifi.util.IntCounter;
 import com.android.server.wifi.util.IntHistogram;
 import com.android.server.wifi.util.MetricsUtils;
@@ -297,6 +300,13 @@
     private IntCounter mRecentFailureAssociationStatus = new IntCounter();
     private boolean mFirstConnectionAfterBoot = true;
     private long mLastTotalBeaconRx = 0;
+    private int mScorerUid = Process.WIFI_UID;
+
+    /**
+     * Wi-Fi usability state per interface as predicted by the network scorer.
+     */
+    public enum WifiUsabilityState {UNKNOWN, USABLE, UNUSABLE};
+    private final Map<String, WifiUsabilityState> mWifiUsabilityStatePerIface = new ArrayMap<>();
 
     /**
      * Metrics are stored within an instance of the WifiLog proto during runtime,
@@ -602,6 +612,36 @@
 
     private final WifiToWifiSwitchStats mWifiToWifiSwitchStats = new WifiToWifiSwitchStats();
 
+    /** Wi-Fi link specific metrics (MLO). */
+    public static class LinkMetrics {
+        private long mTotalBeaconRx = 0;
+        private @android.net.wifi.WifiUsabilityStatsEntry.LinkState int mLinkUsageState =
+                android.net.wifi.WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN;
+
+        /** Get Total beacon received on this link */
+        public long getTotalBeaconRx() {
+            return mTotalBeaconRx;
+        }
+
+        /** Set Total beacon received on this link */
+        public void setTotalBeaconRx(long totalBeaconRx) {
+            this.mTotalBeaconRx = totalBeaconRx;
+        }
+
+        /** Get link usage state */
+        public @android.net.wifi.WifiUsabilityStatsEntry.LinkState int getLinkUsageState() {
+            return mLinkUsageState;
+        }
+
+        /** Set link usage state */
+        public void setLinkUsageState(
+                @android.net.wifi.WifiUsabilityStatsEntry.LinkState int linkUsageState) {
+            this.mLinkUsageState = linkUsageState;
+        }
+    }
+
+    public SparseArray<LinkMetrics> mLastLinkMetrics = new SparseArray<>();
+
     @VisibleForTesting
     static class NetworkSelectionExperimentResults {
         public static final int MAX_CHOICES = 10;
@@ -626,18 +666,51 @@
         private long mSessionEndTimeMillis;
         private int mBand;
         private int mAuthType;
+        private ConnectionEvent mConnectionEvent;
+        private long mLastRoamCompleteMillis;
 
-        SessionData(String ssid, long sessionStartTimeMillis, int band, int authType) {
+        SessionData(ConnectionEvent connectionEvent, String ssid, long sessionStartTimeMillis,
+                int band, int authType) {
+            mConnectionEvent = connectionEvent;
             mSsid = ssid;
             mSessionStartTimeMillis = sessionStartTimeMillis;
             mBand = band;
             mAuthType = authType;
+            mLastRoamCompleteMillis = sessionStartTimeMillis;
+        }
+    }
+
+    /**
+     * Sets the timestamp after roaming is complete.
+     */
+    public void onRoamComplete() {
+        if (mCurrentSession != null) {
+            mCurrentSession.mLastRoamCompleteMillis = mClock.getElapsedSinceBootMillis();
         }
     }
 
     class RouterFingerPrint {
         private final WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto =
                 new WifiMetricsProto.RouterFingerPrint();
+        // Additional parameters which is not captured in WifiMetricsProto.RouterFingerPrint.
+        private boolean mIsFrameworkInitiatedRoaming = false;
+        private @WifiConfiguration.SecurityType int mSecurityMode =
+                WifiConfiguration.SECURITY_TYPE_OPEN;
+        private boolean mIsIncorrectlyConfiguredAsHidden = false;
+        private int mWifiStandard = WifiMode.MODE_UNDEFINED;
+        private boolean mIs11bSupported = false;
+        private boolean mIsMboSupported = false;
+        private boolean mIsOceSupported = false;
+        private boolean mIsFilsSupported = false;
+        private boolean mIsIndividualTwtSupported = false;
+        private boolean mIsBroadcastTwtSupported = false;
+        private boolean mIsRestrictedTwtSupported = false;
+        private boolean mIsTwtRequired = false;
+        private boolean mIs11AzSupported = false;
+        private boolean mIs11McSupported = false;
+        private boolean mIsEcpsPriorityAccessSupported = false;
+        private NetworkDetail.HSRelease mHsRelease = NetworkDetail.HSRelease.Unknown;
+        private ApType6GHz mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_UNKNOWN;
 
         public String toString() {
             StringBuilder sb = new StringBuilder();
@@ -657,6 +730,22 @@
                         .maxSupportedTxLinkSpeedMbps);
                 sb.append(", mMaxSupportedRxLinkSpeedMbps=" + mRouterFingerPrintProto
                         .maxSupportedRxLinkSpeedMbps);
+                sb.append(", mIsFrameworkInitiatedRoaming=" + mIsFrameworkInitiatedRoaming);
+                sb.append(", mIsIncorrectlyConfiguredAsHidden=" + mIsIncorrectlyConfiguredAsHidden);
+                sb.append(", mWifiStandard=" + mWifiStandard);
+                sb.append(", mIs11bSupported=" + mIs11bSupported);
+                sb.append(", mIsMboSupported=" + mIsMboSupported);
+                sb.append(", mIsOceSupported=" + mIsOceSupported);
+                sb.append(", mIsFilsSupported=" + mIsFilsSupported);
+                sb.append(", mIsIndividualTwtSupported=" + mIsIndividualTwtSupported);
+                sb.append(", mIsBroadcastTwtSupported=" + mIsBroadcastTwtSupported);
+                sb.append(", mIsRestrictedTwtSupported=" + mIsRestrictedTwtSupported);
+                sb.append(", mIsTwtRequired=" + mIsTwtRequired);
+                sb.append(", mIs11mcSupported=" + mIs11McSupported);
+                sb.append(", mIs11azSupported=" + mIs11AzSupported);
+                sb.append(", mApType6Ghz=" + mApType6GHz);
+                sb.append(", mIsEcpsPriorityAccessSupported=" + mIsEcpsPriorityAccessSupported);
+                sb.append(", mHsRelease=" + mHsRelease);
             }
             return sb.toString();
         }
@@ -699,7 +788,8 @@
                 return WifiMetricsProto.RouterFingerPrint.TYPE_EAP_UNKNOWN;
         }
     }
-    private int getAuthPhase2MethodProto(int phase2Method) {
+
+    private static int getAuthPhase2MethodProto(int phase2Method) {
         switch (phase2Method) {
             case WifiEnterpriseConfig.Phase2.PAP:
                 return WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_PAP;
@@ -1085,6 +1175,11 @@
         private boolean mIsCarrierWifi;
         private boolean mIsOobPseudonymEnabled;
         private int mRole;
+        private int mCarrierId;
+        private int mEapType;
+        private int mPhase2Method;
+        private int mPasspointRoamingType;
+        private int mTofuConnectionState;
 
         private ConnectionEvent() {
             mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
@@ -1389,6 +1484,7 @@
                         mRouterFingerPrint.mRouterFingerPrintProto.ocspType =
                                 getOcspTypeProto(ocspType);
                     }
+                    mTofuConnectionState = convertTofuConnectionStateToProto(config);
                 }
             }
         }
@@ -1507,10 +1603,18 @@
         }
     }
 
-    public WifiMetrics(Context context, FrameworkFacade facade, Clock clock, Looper looper,
-            WifiAwareMetrics awareMetrics, RttMetrics rttMetrics,
-            WifiPowerMetrics wifiPowerMetrics, WifiP2pMetrics wifiP2pMetrics,
-            DppMetrics dppMetrics, WifiMonitor wifiMonitor) {
+    public WifiMetrics(
+            Context context,
+            FrameworkFacade facade,
+            Clock clock,
+            Looper looper,
+            WifiAwareMetrics awareMetrics,
+            RttMetrics rttMetrics,
+            WifiPowerMetrics wifiPowerMetrics,
+            WifiP2pMetrics wifiP2pMetrics,
+            DppMetrics dppMetrics,
+            WifiMonitor wifiMonitor,
+            WifiDeviceStateChangeManager wifiDeviceStateChangeManager) {
         mContext = context;
         mFacade = facade;
         mClock = clock;
@@ -1538,26 +1642,13 @@
         mCurrentDeviceMobilityStatePnoScanStartMs = -1;
         mOnWifiUsabilityListeners = new RemoteCallbackList<>();
         mScanMetrics = new ScanMetrics(context, clock);
-    }
-
-    /** Begin listening to broadcasts */
-    public void start() {
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
+        wifiDeviceStateChangeManager.registerStateChangeCallback(
+                new WifiDeviceStateChangeManager.StateChangeCallback() {
                     @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                            setScreenState(true);
-                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                            setScreenState(false);
-                        }
+                    public void onScreenStateChanged(boolean screenOn) {
+                        setScreenState(screenOn);
                     }
-                }, filter, null, mHandler);
-        setScreenState(mContext.getSystemService(PowerManager.class).isInteractive());
+                });
     }
 
     /** Sets internal ScoringParams member */
@@ -1850,13 +1941,13 @@
                     endConnectionEvent(ifaceName,
                             ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0, 0);
                 } else {
                     // End Connection Event due to new connection attempt to different network
                     endConnectionEvent(ifaceName,
                             ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
                             WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                            WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0, 0);
                 }
             }
             // If past maximum connection events, start removing the oldest
@@ -1903,6 +1994,13 @@
                         mNetworkIdToNominatorId.get(config.networkId,
                                 WifiMetricsProto.ConnectionEvent.NOMINATOR_UNKNOWN);
                 currentConnectionEvent.mConnectionEvent.isCarrierMerged = config.carrierMerged;
+                currentConnectionEvent.mCarrierId = config.carrierId;
+                if (config.enterpriseConfig != null) {
+                    currentConnectionEvent.mEapType = config.enterpriseConfig.getEapMethod();
+                    currentConnectionEvent.mPhase2Method =
+                            config.enterpriseConfig.getPhase2Method();
+                    currentConnectionEvent.mPasspointRoamingType = Utils.getRoamingType(config);
+                }
 
                 ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
                 if (candidate != null) {
@@ -1919,6 +2017,8 @@
                 currentConnectionEvent.mConnectionEvent.isOsuProvisioned = false;
                 SecurityParams params = config.getNetworkSelectionStatus()
                         .getCandidateSecurityParams();
+                currentConnectionEvent.mRouterFingerPrint.mSecurityMode =
+                        getSecurityMode(config, true);
                 if (config.isPasspoint()) {
                     currentConnectionEvent.mConnectionEvent.networkType =
                             WifiMetricsProto.ConnectionEvent.TYPE_PASSPOINT;
@@ -2039,6 +2139,44 @@
         }
     }
 
+    private int toMetricEapType(int eapType) {
+        switch (eapType) {
+            case WifiEnterpriseConfig.Eap.TLS:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_TLS;
+            case WifiEnterpriseConfig.Eap.TTLS:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_TTLS;
+            case WifiEnterpriseConfig.Eap.SIM:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_SIM;
+            case WifiEnterpriseConfig.Eap.AKA:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_AKA;
+            case WifiEnterpriseConfig.Eap.AKA_PRIME:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_AKA_PRIME;
+            case WifiEnterpriseConfig.Eap.WAPI_CERT:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_WAPI_CERT;
+            case WifiEnterpriseConfig.Eap.UNAUTH_TLS:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_UNAUTH_TLS;
+            case WifiEnterpriseConfig.Eap.PEAP:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_PEAP;
+            case WifiEnterpriseConfig.Eap.PWD:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_PWD;
+            default:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_TYPE__TYPE_EAP_OTHERS;
+        }
+    }
+
+    private int toMetricPhase2Method(int phase2Method) {
+        switch (phase2Method) {
+            case WifiEnterpriseConfig.Phase2.PAP:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_INNER_METHOD__METHOD_PAP;
+            case WifiEnterpriseConfig.Phase2.MSCHAP:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_INNER_METHOD__METHOD_MSCHAP;
+            case WifiEnterpriseConfig.Phase2.MSCHAPV2:
+                return WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_INNER_METHOD__METHOD_MSCHAP_V2;
+            default:
+                return  WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__EAP_INNER_METHOD__METHOD_OTHERS;
+        }
+    }
+
     /**
      * End a Connection event record. Call when wifi connection attempt succeeds or fails.
      * If a Connection event has not been started and is active when .end is called, then this
@@ -2054,7 +2192,8 @@
             int level2FailureCode,
             int connectivityFailureCode,
             int level2FailureReason,
-            int frequency) {
+            int frequency,
+            int statusCode) {
         synchronized (mLock) {
             ConnectionEvent currentConnectionEvent = mCurrentConnectionEventPerIface.get(ifaceName);
             if (currentConnectionEvent != null) {
@@ -2067,7 +2206,8 @@
                                 - currentConnectionEvent.mConnectionEvent.startTimeSinceBootMillis);
 
                 if (connectionSucceeded) {
-                    mCurrentSession = new SessionData(currentConnectionEvent.mConfigSsid,
+                    mCurrentSession = new SessionData(currentConnectionEvent,
+                            currentConnectionEvent.mConfigSsid,
                             mClock.getElapsedSinceBootMillis(),
                             band, currentConnectionEvent.mAuthType);
 
@@ -2101,8 +2241,17 @@
                         timeSinceConnectedSeconds,
                         currentConnectionEvent.mIsCarrierWifi,
                         currentConnectionEvent.mIsOobPseudonymEnabled,
-                        currentConnectionEvent.mRole);
+                        currentConnectionEvent.mRole,
+                        statusCode,
+                        toMetricEapType(currentConnectionEvent.mEapType),
+                        toMetricPhase2Method(currentConnectionEvent.mPhase2Method),
+                        currentConnectionEvent.mPasspointRoamingType,
+                        currentConnectionEvent.mCarrierId,
+                        currentConnectionEvent.mTofuConnectionState);
 
+                if (connectionSucceeded) {
+                    reportRouterCapabilities(currentConnectionEvent.mRouterFingerPrint);
+                }
                 // ConnectionEvent already added to ConnectionEvents List. Safe to remove here.
                 mCurrentConnectionEventPerIface.remove(ifaceName);
                 if (!connectionSucceeded) {
@@ -2113,6 +2262,404 @@
         }
     }
 
+    protected static int convertTofuConnectionStateToProto(WifiConfiguration config) {
+        if (!config.isEnterprise()) {
+            return WifiStatsLog
+                    .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_UNSPECIFIED;
+        }
+
+        switch (config.enterpriseConfig.getTofuConnectionState()) {
+            case WifiEnterpriseConfig.TOFU_STATE_NOT_ENABLED:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_NOT_ENABLED;
+            case WifiEnterpriseConfig.TOFU_STATE_ENABLED_PRE_CONNECTION:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_ENABLED_PRE_CONNECTION;
+            case WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_CONFIGURE_ROOT_CA;
+            case WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_CERT_PINNING;
+            default:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_CONFIGURATION__TOFU_CONFIGURATION_UNSPECIFIED;
+        }
+    }
+
+    protected static int convertTofuDialogStateToProto(WifiConfiguration config) {
+        if (!config.isEnterprise()) {
+            return WifiStatsLog
+                    .WIFI_CONFIGURED_NETWORK_INFO__TOFU_DIALOG_STATE__TOFU_DIALOG_STATE_UNSPECIFIED;
+        }
+
+        switch (config.enterpriseConfig.getTofuDialogState()) {
+            case WifiEnterpriseConfig.TOFU_DIALOG_STATE_REJECTED:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_DIALOG_STATE__TOFU_DIALOG_STATE_REJECTED;
+            case WifiEnterpriseConfig.TOFU_DIALOG_STATE_ACCEPTED:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_DIALOG_STATE__TOFU_DIALOG_STATE_ACCEPTED;
+            default:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__TOFU_DIALOG_STATE__TOFU_DIALOG_STATE_UNSPECIFIED;
+        }
+    }
+
+    protected static int convertMacRandomizationToProto(
+            @WifiConfiguration.MacRandomizationSetting int macRandomizationSetting) {
+        switch (macRandomizationSetting) {
+            case WifiConfiguration.RANDOMIZATION_NONE:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__MAC_RANDOMIZATION__MAC_RANDOMIZATION_NONE;
+            case WifiConfiguration.RANDOMIZATION_PERSISTENT:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__MAC_RANDOMIZATION__MAC_RANDOMIZATION_PERSISTENT;
+            case WifiConfiguration.RANDOMIZATION_NON_PERSISTENT:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__MAC_RANDOMIZATION__MAC_RANDOMIZATION_NON_PERSISTENT;
+            case WifiConfiguration.RANDOMIZATION_AUTO:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__MAC_RANDOMIZATION__MAC_RANDOMIZATION_AUTO;
+            default:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__MAC_RANDOMIZATION__MAC_RANDOMIZATION_UNSPECIFIED;
+        }
+    }
+
+    protected static int convertMeteredOverrideToProto(
+            @WifiConfiguration.MeteredOverride int meteredOverride) {
+        switch (meteredOverride) {
+            case WifiConfiguration.METERED_OVERRIDE_NONE:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__METERED_OVERRIDE__METERED_OVERRIDE_NONE;
+            case WifiConfiguration.METERED_OVERRIDE_METERED:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__METERED_OVERRIDE__METERED_OVERRIDE_METERED;
+            case WifiConfiguration.METERED_OVERRIDE_NOT_METERED:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__METERED_OVERRIDE__METERED_OVERRIDE_NOT_METERED;
+            default:
+                return WifiStatsLog
+                        .WIFI_CONFIGURED_NETWORK_INFO__METERED_OVERRIDE__METERED_OVERRIDE_UNSPECIFIED;
+        }
+    }
+
+    protected static int getSecurityMode(WifiConfiguration config, boolean useCandidateParams) {
+        SecurityParams params =
+                useCandidateParams
+                        ? config.getNetworkSelectionStatus().getCandidateSecurityParams()
+                        : config.getNetworkSelectionStatus().getLastUsedSecurityParams();
+        if (params != null) {
+            return params.getSecurityType();
+        } else if (WifiConfigurationUtil.isConfigForWpa3Enterprise192BitNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT;
+        } else if (WifiConfigurationUtil.isConfigForWpa3EnterpriseNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
+        } else if (WifiConfigurationUtil.isConfigForDppNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_DPP;
+        } else if (WifiConfigurationUtil.isConfigForSaeNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_SAE;
+        } else if (WifiConfigurationUtil.isConfigForWapiPskNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
+        } else if (WifiConfigurationUtil.isConfigForWapiCertNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
+        } else if (WifiConfigurationUtil.isConfigForPskNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_PSK;
+        } else if (WifiConfigurationUtil.isConfigForOweNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_OWE;
+        } else if (WifiConfigurationUtil.isConfigForWepNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_WEP;
+        } else if (WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
+            return WifiConfiguration.SECURITY_TYPE_OPEN;
+        } else {
+            Log.e(TAG, "Unknown security mode for config " + config);
+            return -1;
+        }
+    }
+
+    /**
+     * Check if the provided security type is enabled in the security params list.
+     */
+    private static boolean securityTypeEnabled(List<SecurityParams> securityParamsList,
+            int securityType) {
+        for (SecurityParams params : securityParamsList) {
+            if (params.isSecurityType(securityType) && params.isEnabled()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if any security parameters with the provided type were added by auto-upgrade.
+     */
+    private static boolean securityTypeAddedByAutoUpgrade(List<SecurityParams> securityParamsList,
+            int securityType) {
+        for (SecurityParams params : securityParamsList) {
+            if (params.isSecurityType(securityType) && params.isAddedByAutoUpgrade()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected static int convertSecurityModeToProto(WifiConfiguration config) {
+        if (config == null || config.getDefaultSecurityParams() == null) {
+            return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_UNKNOWN;
+        }
+        SecurityParams defaultParams = config.getDefaultSecurityParams();
+        List<SecurityParams> securityParamsList = config.getSecurityParamsList();
+        switch (defaultParams.getSecurityType()) {
+            case WifiConfiguration.SECURITY_TYPE_OPEN:
+            case WifiConfiguration.SECURITY_TYPE_OWE: {
+                boolean openEnabled = securityTypeEnabled(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_OPEN);
+                boolean oweEnabled = securityTypeEnabled(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_OWE);
+                if (openEnabled && !oweEnabled) {
+                    // OWE params may be disabled or may not exist.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_NONE;
+                } else if (!openEnabled && oweEnabled) {
+                    // Open params may get disabled via TDI.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_OWE;
+                }
+
+                if (securityTypeAddedByAutoUpgrade(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_OWE)) {
+                    // User configured this network using Open, but OWE params were auto-added.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_NONE;
+                }
+                // User manually configured this network with both Open and OWE params.
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_OWE_TRANSITION;
+            }
+            case WifiConfiguration.SECURITY_TYPE_WEP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WEP;
+            case WifiConfiguration.SECURITY_TYPE_PSK: {
+                boolean pskEnabled = securityTypeEnabled(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_PSK);
+                boolean saeEnabled = securityTypeEnabled(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_SAE);
+                if (pskEnabled && !saeEnabled) {
+                    // WPA3 params may be disabled or may not exist.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA2_PERSONAL;
+                } else if (!pskEnabled && saeEnabled) {
+                    // WPA2 params may get disabled via TDI.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_PERSONAL;
+                }
+
+                if (securityTypeAddedByAutoUpgrade(
+                        securityParamsList, WifiConfiguration.SECURITY_TYPE_SAE)) {
+                    // User configured this network using WPA2, but WPA3 params were auto-added.
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA2_PERSONAL;
+                }
+                // User manually configured this network with both WPA2 and WPA3 params.
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_WPA2_PERSONAL_TRANSITION;
+            }
+            case WifiConfiguration.SECURITY_TYPE_SAE:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_PERSONAL;
+            case WifiConfiguration.SECURITY_TYPE_WAPI_PSK:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WAPI_PSK;
+            case WifiConfiguration.SECURITY_TYPE_WAPI_CERT:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WAPI_CERT;
+            case WifiConfiguration.SECURITY_TYPE_DPP:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_DPP;
+            case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_ENTERPRISE_192_BIT;
+            case WifiConfiguration.SECURITY_TYPE_EAP:
+            case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
+            case WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2:
+            case WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3: {
+                if (WifiConfigurationUtil.isConfigForWpa3EnterpriseNetwork(config)) {
+                    return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_ENTERPRISE;
+                }
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA_ENTERPRISE_LEGACY;
+            }
+            default:
+                return WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO__CONNECTED_SECURITY_MODE__SECURITY_MODE_INVALID;
+        }
+    }
+
+    static int convertSecurityModeToProto(@WifiConfiguration.SecurityType int securityMode) {
+        switch (securityMode) {
+            case WifiConfiguration.SECURITY_TYPE_OPEN:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_NONE;
+            case WifiConfiguration.SECURITY_TYPE_WEP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WEP;
+            case WifiConfiguration.SECURITY_TYPE_PSK:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA2_PERSONAL;
+            case WifiConfiguration.SECURITY_TYPE_PASSPOINT_R1_R2:
+                // Passpoint R1 & R2 uses WPA2 Enterprise (Legacy)
+            case WifiConfiguration.SECURITY_TYPE_EAP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA_ENTERPRISE_LEGACY;
+            case WifiConfiguration.SECURITY_TYPE_SAE:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_PERSONAL;
+            case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_ENTERPRISE_192_BIT;
+            case WifiConfiguration.SECURITY_TYPE_OWE:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_OWE;
+            case WifiConfiguration.SECURITY_TYPE_WAPI_PSK:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WAPI_PSK;
+            case WifiConfiguration.SECURITY_TYPE_WAPI_CERT:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WAPI_CERT;
+            case WifiConfiguration.SECURITY_TYPE_PASSPOINT_R3:
+                // Passpoint R3 uses WPA3 Enterprise
+            case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_WPA3_ENTERPRISE;
+            case WifiConfiguration.SECURITY_TYPE_DPP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_DPP;
+            default:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_UNKNOWN;
+        }
+    }
+
+    private int convertHsReleasetoProto(NetworkDetail.HSRelease hsRelease) {
+        if (hsRelease == NetworkDetail.HSRelease.R1) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__PASSPOINT_RELEASE__PASSPOINT_RELEASE_1;
+        } else if (hsRelease == NetworkDetail.HSRelease.R2) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__PASSPOINT_RELEASE__PASSPOINT_RELEASE_2;
+        } else if (hsRelease == NetworkDetail.HSRelease.R3) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__PASSPOINT_RELEASE__PASSPOINT_RELEASE_3;
+        } else {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__PASSPOINT_RELEASE__PASSPOINT_RELEASE_UNKNOWN;
+        }
+    }
+
+    private int convertApType6GhzToProto(ApType6GHz apType6Ghz) {
+        if (apType6Ghz == ApType6GHz.AP_TYPE_6GHZ_INDOOR) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__AP_TYPE_6GHZ__AP_TYPE_6GHZ_INDOOR;
+        } else if (apType6Ghz == ApType6GHz.AP_TYPE_6GHZ_STANDARD_POWER) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__AP_TYPE_6GHZ__AP_TYPE_6GHZ_STANDARD_POWER;
+        } else {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__AP_TYPE_6GHZ__AP_TYPE_6HZ_UNKNOWN;
+        }
+    }
+
+    private int convertWifiStandardToProto(int wifiMode) {
+        switch (wifiMode) {
+            case WifiMode.MODE_11A:
+            case WifiMode.MODE_11B:
+            case WifiMode.MODE_11G:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_LEGACY;
+            case WifiMode.MODE_11N:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_11N;
+            case WifiMode.MODE_11AC:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_11AC;
+            case WifiMode.MODE_11AX:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_11AX;
+            case WifiMode.MODE_11BE:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_11BE;
+            case WifiMode.MODE_UNDEFINED:
+            default:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_UNKNOWN;
+        }
+
+    }
+
+    protected static int convertEapMethodToProto(WifiConfiguration config) {
+        if (config.enterpriseConfig == null) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_UNKNOWN;
+        }
+        return convertEapMethodToProto(config.enterpriseConfig.getEapMethod());
+    }
+
+    private static int convertEapMethodToProto(int eapMethod) {
+        switch (eapMethod) {
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_WAPI_CERT:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_WAPI_CERT;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_TLS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_TLS;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_UNAUTH_TLS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_UNAUTH_TLS;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_PEAP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_PEAP;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_PWD:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_PWD;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_TTLS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_TTLS;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_SIM:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_SIM;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_AKA:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_AKA;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_EAP_AKA_PRIME:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_AKA_PRIME;
+            default:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_UNKNOWN;
+        }
+    }
+
+    protected static int convertEapInnerMethodToProto(WifiConfiguration config) {
+        if (config.enterpriseConfig == null) {
+            return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_UNKNOWN;
+        }
+        int phase2Method = config.enterpriseConfig.getPhase2Method();
+        return convertEapInnerMethodToProto(getAuthPhase2MethodProto(phase2Method));
+    }
+
+    private static int convertEapInnerMethodToProto(int phase2Method) {
+        switch (phase2Method) {
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_PAP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_PAP;
+            case WifiEnterpriseConfig.Phase2.MSCHAP:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_MSCHAP;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_MSCHAPV2:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_MSCHAP_V2;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_GTC:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_GTC;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_SIM:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_SIM;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_AKA:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_AKA;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_PHASE2_AKA_PRIME:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_AKA_PRIME;
+            default:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_UNKNOWN;
+        }
+    }
+
+    private int convertOcspTypeToProto(int ocspType) {
+        switch (ocspType) {
+            case WifiMetricsProto.RouterFingerPrint.TYPE_OCSP_NONE:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_NONE;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_OCSP_REQUEST_CERT_STATUS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_REQUEST_CERT_STATUS;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_OCSP_REQUIRE_CERT_STATUS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_REQUIRE_CERT_STATUS;
+            case WifiMetricsProto.RouterFingerPrint.TYPE_OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS;
+            default:
+                return WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_UNKNOWN;
+        }
+    }
+
+    protected static boolean isFreeOpenRoaming(WifiConfiguration config) {
+        return Utils.getRoamingType(config)
+                == WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE;
+    }
+
+    protected static boolean isSettledOpenRoaming(WifiConfiguration config) {
+        return Utils.getRoamingType(config)
+                == WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED;
+    }
+
+    private void reportRouterCapabilities(RouterFingerPrint r) {
+        WifiStatsLog.write(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED,
+                r.mIsFrameworkInitiatedRoaming, r.mRouterFingerPrintProto.channelInfo,
+                KnownBandsChannelHelper.getBand(r.mRouterFingerPrintProto.channelInfo),
+                r.mRouterFingerPrintProto.dtim, convertSecurityModeToProto(r.mSecurityMode),
+                r.mRouterFingerPrintProto.hidden, r.mIsIncorrectlyConfiguredAsHidden,
+                convertWifiStandardToProto(r.mWifiStandard), r.mIs11bSupported,
+                convertEapMethodToProto(r.mRouterFingerPrintProto.eapMethod),
+                convertEapInnerMethodToProto(r.mRouterFingerPrintProto.authPhase2Method),
+                convertOcspTypeToProto(r.mRouterFingerPrintProto.ocspType),
+                r.mRouterFingerPrintProto.pmkCacheEnabled, r.mIsMboSupported, r.mIsOceSupported,
+                r.mIsFilsSupported, r.mIsTwtRequired, r.mIsIndividualTwtSupported,
+                r.mIsBroadcastTwtSupported, r.mIsRestrictedTwtSupported, r.mIs11McSupported,
+                r.mIs11AzSupported, convertHsReleasetoProto(r.mHsRelease),
+                r.mRouterFingerPrintProto.isPasspointHomeProvider,
+                convertApType6GhzToProto(r.mApType6GHz), r.mIsEcpsPriorityAccessSupported);
+    }
+
     /**
      * Report that an active Wifi network connection was dropped.
      *
@@ -2121,7 +2668,7 @@
      * @param linkSpeed Last seen link speed.
      */
     public void reportNetworkDisconnect(String ifaceName, int disconnectReason, int rssi,
-            int linkSpeed) {
+            int linkSpeed, long lastRssiUpdateMillis) {
         synchronized (mLock) {
             if (!isPrimary(ifaceName)) {
                 return;
@@ -2135,6 +2682,10 @@
                 mCurrentSession.mSessionEndTimeMillis = mClock.getElapsedSinceBootMillis();
                 int durationSeconds = (int) (mCurrentSession.mSessionEndTimeMillis
                         - mCurrentSession.mSessionStartTimeMillis) / 1000;
+                int connectedSinceLastRoamSeconds = (int) (mCurrentSession.mSessionEndTimeMillis
+                        - mCurrentSession.mLastRoamCompleteMillis) / 1000;
+                int timeSinceLastRssiUpdateSeconds = (int) (mClock.getElapsedSinceBootMillis()
+                        - lastRssiUpdateMillis) / 1000;
 
                 WifiStatsLog.write(WifiStatsLog.WIFI_DISCONNECT_REPORTED,
                         durationSeconds,
@@ -2142,7 +2693,14 @@
                         mCurrentSession.mBand,
                         mCurrentSession.mAuthType,
                         rssi,
-                        linkSpeed);
+                        linkSpeed,
+                        timeSinceLastRssiUpdateSeconds,
+                        connectedSinceLastRoamSeconds,
+                        mCurrentSession.mConnectionEvent.mRole,
+                        toMetricEapType(mCurrentSession.mConnectionEvent.mEapType),
+                        toMetricPhase2Method(mCurrentSession.mConnectionEvent.mPhase2Method),
+                        mCurrentSession.mConnectionEvent.mPasspointRoamingType,
+                        mCurrentSession.mConnectionEvent.mCarrierId);
 
                 mPreviousSession = mCurrentSession;
                 mCurrentSession = null;
@@ -2235,6 +2793,12 @@
             currentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
                     dtimInterval;
         }
+
+        if (currentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.hidden
+                && !networkDetail.isHiddenBeaconFrame()) {
+            currentConnectionEvent.mRouterFingerPrint.mIsIncorrectlyConfiguredAsHidden = true;
+        }
+
         final int connectionWifiMode;
         switch (networkDetail.getWifiMode()) {
             case InformationElementUtil.WifiMode.MODE_UNDEFINED:
@@ -2244,6 +2808,7 @@
                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
                 break;
             case InformationElementUtil.WifiMode.MODE_11B:
+                currentConnectionEvent.mRouterFingerPrint.mIs11bSupported = true;
                 connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
                 break;
             case InformationElementUtil.WifiMode.MODE_11G:
@@ -2264,6 +2829,7 @@
         }
         currentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.routerTechnology =
                 connectionWifiMode;
+        currentConnectionEvent.mRouterFingerPrint.mWifiStandard = networkDetail.getWifiMode();
 
         if (networkDetail.isMboSupported()) {
             mWifiLogProto.numConnectToNetworkSupportingMbo++;
@@ -2271,6 +2837,26 @@
                 mWifiLogProto.numConnectToNetworkSupportingOce++;
             }
         }
+
+        currentConnectionEvent.mRouterFingerPrint.mApType6GHz =
+                networkDetail.getApType6GHz();
+        currentConnectionEvent.mRouterFingerPrint.mIsBroadcastTwtSupported =
+                networkDetail.isBroadcastTwtSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIsRestrictedTwtSupported =
+                networkDetail.isRestrictedTwtSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIsIndividualTwtSupported =
+                networkDetail.isIndividualTwtSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIsTwtRequired = networkDetail.isTwtRequired();
+        currentConnectionEvent.mRouterFingerPrint.mIsFilsSupported = networkDetail.isFilsCapable();
+        currentConnectionEvent.mRouterFingerPrint.mIs11AzSupported =
+                networkDetail.is11azSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIs11McSupported =
+                networkDetail.is80211McResponderSupport();
+        currentConnectionEvent.mRouterFingerPrint.mIsMboSupported = networkDetail.isMboSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIsOceSupported = networkDetail.isOceSupported();
+        currentConnectionEvent.mRouterFingerPrint.mIsEcpsPriorityAccessSupported =
+                networkDetail.isEpcsPriorityAccessSupported();
+        currentConnectionEvent.mRouterFingerPrint.mHsRelease = networkDetail.getHSRelease();
     }
 
     /**
@@ -2292,7 +2878,7 @@
                         WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
             } else if (ScanResultUtil.isScanResultForWpa3EnterpriseTransitionNetwork(scanResult)
                     || ScanResultUtil.isScanResultForWpa3EnterpriseOnlyNetwork(scanResult)
-                    || ScanResultUtil.isScanResultForEapNetwork(scanResult)
+                    || ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(scanResult)
                     || ScanResultUtil.isScanResultForEapSuiteBNetwork(scanResult)) {
                 currentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
                         WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
@@ -2978,7 +3564,7 @@
                     wapiPersonalNetworks++;
                 } else if (ScanResultUtil.isScanResultForWapiCertNetwork(scanResult)) {
                     wapiEnterpriseNetworks++;
-                } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
+                } else if (ScanResultUtil.isScanResultForWpa2EnterpriseOnlyNetwork(scanResult)) {
                     enterpriseNetworks++;
                 } else if (ScanResultUtil.isScanResultForSaeNetwork(scanResult)) {
                     wpa3PersonalNetworks++;
@@ -5557,14 +6143,22 @@
     public void logStaEvent(String ifaceName, int type, int frameworkDisconnectReason,
             WifiConfiguration config) {
         switch (type) {
+            case StaEvent.TYPE_CMD_START_ROAM:
+                ConnectionEvent currentConnectionEvent = mCurrentConnectionEventPerIface.get(
+                        ifaceName);
+                if (currentConnectionEvent != null) {
+                    currentConnectionEvent.mRouterFingerPrint.mIsFrameworkInitiatedRoaming = true;
+                }
+                break;
             case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
             case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
             case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
             case StaEvent.TYPE_CMD_START_CONNECT:
-            case StaEvent.TYPE_CMD_START_ROAM:
             case StaEvent.TYPE_CONNECT_NETWORK:
+                break;
             case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
                 mWifiStatusBuilder.setValidated(true);
+                break;
             case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
             case StaEvent.TYPE_SCORE_BREACH:
             case StaEvent.TYPE_MAC_CHANGE:
@@ -6331,6 +6925,40 @@
         if (mWifiIsUnusableList.size() > MAX_UNUSABLE_EVENTS) {
             mWifiIsUnusableList.removeFirst();
         }
+        WifiUsabilityState wifiUsabilityState = mWifiUsabilityStatePerIface.getOrDefault(
+                ifaceName, WifiUsabilityState.UNKNOWN);
+
+        WifiStatsLog.write(WIFI_IS_UNUSABLE_REPORTED,
+                convertWifiUnUsableTypeToProto(triggerType),
+                mScorerUid, wifiUsabilityState == WifiUsabilityState.USABLE,
+                convertWifiUsabilityState(wifiUsabilityState));
+    }
+
+    private int convertWifiUsabilityState(WifiUsabilityState usabilityState) {
+        if (usabilityState == WifiUsabilityState.USABLE) {
+            return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE;
+        } else if (usabilityState == WifiUsabilityState.UNUSABLE) {
+            return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNUSABLE;
+        } else {
+            return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN;
+        }
+    }
+
+    private int convertWifiUnUsableTypeToProto(int triggerType) {
+        switch (triggerType) {
+            case WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_DATA_STALL_BAD_TX;
+            case WifiIsUnusableEvent.TYPE_DATA_STALL_TX_WITHOUT_RX:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_DATA_STALL_TX_WITHOUT_RX;
+            case WifiIsUnusableEvent.TYPE_DATA_STALL_BOTH:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_DATA_STALL_BOTH;
+            case WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_FIRMWARE_ALERT;
+            case WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_IP_REACHABILITY_LOST;
+            default:
+                return WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__TYPE__TYPE_UNKNOWN;
+        }
     }
 
     /**
@@ -6598,7 +7226,7 @@
     }
 
     private android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats[]
-            convertContentionTimeStats(WifiLinkLayerStats stats) {
+            convertContentionTimeStats(WifiLinkLayerStats.LinkSpecificStats stats) {
         android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats[] contentionTimeStatsArray =
                 new android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats[
                         android.net.wifi.WifiUsabilityStatsEntry.NUM_WME_ACCESS_CATEGORIES];
@@ -6652,7 +7280,7 @@
     }
 
     private android.net.wifi.WifiUsabilityStatsEntry.RateStats[] convertRateStats(
-            WifiLinkLayerStats stats) {
+            WifiLinkLayerStats.LinkSpecificStats stats) {
         android.net.wifi.WifiUsabilityStatsEntry.RateStats[] rateStats = null;
         if (stats.peerInfo != null && stats.peerInfo.length > 0
                 && stats.peerInfo[0].rateStats != null) {
@@ -6684,16 +7312,21 @@
         for (MloLink link: info.getAffiliatedMloLinks()) {
             mloLinks.put(link.getLinkId(), link);
         }
+        mLastLinkMetrics.clear();
         // Fill per link stats.
         for (WifiLinkLayerStats.LinkSpecificStats inStat : stats.links) {
             if (inStat == null) break;
+            LinkMetrics linkMetrics = new LinkMetrics();
+            linkMetrics.setTotalBeaconRx(inStat.beacon_rx);
+            linkMetrics.setLinkUsageState(inStat.state);
+            mLastLinkMetrics.put(inStat.link_id, linkMetrics);
             WifiLinkLayerStats.ChannelStats channelStatsMap = stats.channelStatsMap.get(
                     inStat.frequencyMhz);
             // Note: RSSI, Tx & Rx link speed are derived from signal poll stats which is updated in
             // Mlolink or WifiInfo (non-MLO case).
             android.net.wifi.WifiUsabilityStatsEntry.LinkStats outStat =
                     new android.net.wifi.WifiUsabilityStatsEntry.LinkStats(inStat.link_id,
-                            inStat.radio_id, inStat.state,
+                            inStat.state, inStat.radio_id,
                             (mloLinks.size() > 0) ? mloLinks.get(inStat.link_id,
                                     new MloLink()).getRssi() : info.getRssi(),
                             (mloLinks.size() > 0) ? mloLinks.get(inStat.link_id,
@@ -6702,7 +7335,7 @@
                                     new MloLink()).getRxLinkSpeedMbps() : info.getRxLinkSpeedMbps(),
                             inStat.txmpdu_be + inStat.txmpdu_bk + inStat.txmpdu_vi
                                     + inStat.txmpdu_vo,
-                            inStat.retries_be + inStat.retries_be + inStat.retries_vi
+                            inStat.retries_be + inStat.retries_bk + inStat.retries_vi
                                     + inStat.retries_vo,
                             inStat.lostmpdu_be + inStat.lostmpdu_bk + inStat.lostmpdu_vo
                                     + inStat.lostmpdu_vi,
@@ -6711,8 +7344,8 @@
                             inStat.beacon_rx, inStat.timeSliceDutyCycleInPercent,
                             (channelStatsMap != null) ? channelStatsMap.ccaBusyTimeMs : 0 ,
                             (channelStatsMap != null) ? channelStatsMap.radioOnTimeMs : 0,
-                            convertContentionTimeStats(stats),
-                            convertRateStats(stats));
+                            convertContentionTimeStats(inStat),
+                            convertRateStats(inStat));
             linkStats.put(inStat.link_id, outStat);
         }
 
@@ -6824,7 +7457,8 @@
      * Converts bandwidth enum in proto to WifiUsabilityStatsEntry type.
      * @param value
      */
-    private static int convertBandwidthEnumToUsabilityStatsType(int value) {
+    @VisibleForTesting
+    public static int convertBandwidthEnumToUsabilityStatsType(int value) {
         switch (value) {
             case RateStats.WIFI_BANDWIDTH_20_MHZ:
                 return android.net.wifi.WifiUsabilityStatsEntry.WIFI_BANDWIDTH_20_MHZ;
@@ -6848,7 +7482,8 @@
      * Converts spatial streams enum in proto to WifiUsabilityStatsEntry type.
      * @param value
      */
-    private static int convertSpatialStreamEnumToUsabilityStatsType(int value) {
+    @VisibleForTesting
+    public static int convertSpatialStreamEnumToUsabilityStatsType(int value) {
         switch (value) {
             case RateStats.WIFI_SPATIAL_STREAMS_ONE:
                 return android.net.wifi.WifiUsabilityStatsEntry.WIFI_SPATIAL_STREAMS_ONE;
@@ -6866,7 +7501,8 @@
      * Converts preamble type enum in proto to WifiUsabilityStatsEntry type.
      * @param value
      */
-    private static int convertPreambleTypeEnumToUsabilityStatsType(int value) {
+    @VisibleForTesting
+    public static int convertPreambleTypeEnumToUsabilityStatsType(int value) {
         switch (value) {
             case RateStats.WIFI_PREAMBLE_OFDM:
                 return android.net.wifi.WifiUsabilityStatsEntry.WIFI_PREAMBLE_OFDM;
@@ -7609,33 +8245,51 @@
         }
     }
 
-    /** Add a WifiLock acqusition session */
-    public void addWifiLockAcqSession(int lockType, long duration) {
+    /** Add a WifiLock acquisition session */
+    public void addWifiLockAcqSession(int lockType, int[] attrUids, String[] attrTags,
+            int callerType, long duration, boolean isPowersaveDisableAllowed,
+            boolean isAppExemptedFromScreenOn, boolean isAppExemptedFromForeground) {
+        int lockMode;
         switch (lockType) {
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
                 mWifiLockHighPerfAcqDurationSecHistogram.increment((int) (duration / 1000));
+                lockMode = WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_HIGH_PERF;
                 break;
 
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
                 mWifiLockLowLatencyAcqDurationSecHistogram.increment((int) (duration / 1000));
+                lockMode = WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_LOW_LATENCY;
                 break;
-
             default:
                 Log.e(TAG, "addWifiLockAcqSession: Invalid lock type: " + lockType);
-                break;
+                return;
         }
+        WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED,
+                attrUids,
+                attrTags,
+                callerType,
+                lockMode,
+                duration,
+                isPowersaveDisableAllowed,
+                isAppExemptedFromScreenOn,
+                isAppExemptedFromForeground);
     }
 
     /** Add a WifiLock active session */
-    public void addWifiLockActiveSession(int lockType, long duration) {
+    public void addWifiLockActiveSession(int lockType, int[] attrUids, String[] attrTags,
+            long duration, boolean isPowersaveDisableAllowed,
+            boolean isAppExemptedFromScreenOn, boolean isAppExemptedFromForeground) {
+        int lockMode;
         switch (lockType) {
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                lockMode = WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_HIGH_PERF;
                 mWifiLockStats.highPerfActiveTimeMs += duration;
                 mWifiLockHighPerfActiveSessionDurationSecHistogram.increment(
                         (int) (duration / 1000));
                 break;
 
             case WifiManager.WIFI_MODE_FULL_LOW_LATENCY:
+                lockMode = WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_LOW_LATENCY;
                 mWifiLockStats.lowLatencyActiveTimeMs += duration;
                 mWifiLockLowLatencyActiveSessionDurationSecHistogram.increment(
                         (int) (duration / 1000));
@@ -7643,8 +8297,16 @@
 
             default:
                 Log.e(TAG, "addWifiLockActiveSession: Invalid lock type: " + lockType);
-                break;
+                return;
         }
+        WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED,
+                attrUids,
+                attrTags,
+                lockMode,
+                duration,
+                isPowersaveDisableAllowed,
+                isAppExemptedFromScreenOn,
+                isAppExemptedFromForeground);
     }
 
     /** Increments metrics counting number of addOrUpdateNetwork calls. **/
@@ -7890,26 +8552,32 @@
     /**
      * Increment connection duration while link layer stats report are on
      */
-    public void incrementConnectionDuration(int timeDeltaLastTwoPollsMs,
+    public void incrementConnectionDuration(String ifaceName, int timeDeltaLastTwoPollsMs,
             boolean isThroughputSufficient, boolean isCellularDataAvailable, int rssi, int txKbps,
             int rxKbps) {
         synchronized (mLock) {
+            if (!isPrimary(ifaceName)) {
+                return;
+            }
             mConnectionDurationStats.incrementDurationCount(timeDeltaLastTwoPollsMs,
                     isThroughputSufficient, isCellularDataAvailable, mWifiWins);
-
+            WifiUsabilityState wifiUsabilityState = mWifiUsabilityStatePerIface.getOrDefault(
+                    ifaceName, WifiUsabilityState.UNKNOWN);
             int band = KnownBandsChannelHelper.getBand(mLastPollFreq);
             WifiStatsLog.write(WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, timeDeltaLastTwoPollsMs,
-                    isThroughputSufficient || !mWifiWins,  isCellularDataAvailable, band, rssi,
-                    txKbps, rxKbps);
+                    isThroughputSufficient || !mWifiWins, isCellularDataAvailable, band, rssi,
+                    txKbps, rxKbps, mScorerUid, (wifiUsabilityState == WifiUsabilityState.USABLE),
+                    convertWifiUsabilityState(wifiUsabilityState));
         }
     }
 
     /**
      * Sets the status to indicate whether external WiFi connected network scorer is present or not.
      */
-    public void setIsExternalWifiScorerOn(boolean value) {
+    public void setIsExternalWifiScorerOn(boolean value, int callerUid) {
         synchronized (mLock) {
             mWifiLogProto.isExternalWifiScorerOn = value;
+            mScorerUid = callerUid;
         }
     }
 
@@ -8075,6 +8743,20 @@
         return mLastTotalBeaconRx;
     }
 
+    /** Get total beacon receive count for the link */
+    public long getTotalBeaconRxCount(int linkId) {
+        if (!mLastLinkMetrics.contains(linkId)) return 0;
+        return mLastLinkMetrics.get(linkId).getTotalBeaconRx();
+    }
+
+    /** Get link usage state */
+    public @android.net.wifi.WifiUsabilityStatsEntry.LinkState int getLinkUsageState(int linkId) {
+        if (!mLastLinkMetrics.contains(linkId)) {
+            return android.net.wifi.WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN;
+        }
+        return mLastLinkMetrics.get(linkId).getLinkUsageState();
+    }
+
     /** Note whether Wifi was enabled at boot time. */
     public void noteWifiEnabledDuringBoot(boolean isWifiEnabled) {
         synchronized (mLock) {
@@ -8616,12 +9298,315 @@
     }
 
     /**
-     * Logged when the task on the Wifi Thread has been excuted
-     * @param taskName The name of the task
-     * @param delay The dalay time after post the task on the thread
-     * @param runningTime The time usesd for executing the task
+     * Set Wi-Fi usability state per interface as predicted by the scorer
      */
-    public void wifiThreadTaskExecuted(String taskName, int delay, int runningTime) {
-        WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, runningTime, delay, taskName);
+    public void setScorerPredictedWifiUsabilityState(String ifaceName,
+            WifiUsabilityState usabilityState) {
+        mWifiUsabilityStatePerIface.put(ifaceName, usabilityState);
+    }
+
+    private static int getSoftApStartedStartResult(@SoftApManager.StartResult int startResult) {
+        switch (startResult) {
+            case SoftApManager.START_RESULT_UNKNOWN:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_UNKNOWN;
+            case SoftApManager.START_RESULT_SUCCESS:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_SUCCESS;
+            case SoftApManager.START_RESULT_FAILURE_GENERAL:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_GENERAL;
+
+            case SoftApManager.START_RESULT_FAILURE_NO_CHANNEL:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_NO_CHANNEL;
+            case SoftApManager.START_RESULT_FAILURE_UNSUPPORTED_CONFIG:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_UNSUPPORTED_CONFIG;
+            case SoftApManager.START_RESULT_FAILURE_START_HAL:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_START_HAL;
+            case SoftApManager.START_RESULT_FAILURE_START_HOSTAPD:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_START_HOSTAPD;
+            case SoftApManager.START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED;
+            case SoftApManager.START_RESULT_FAILURE_INTERFACE_CONFLICT:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_INTERFACE_CONFLICT;
+            case SoftApManager.START_RESULT_FAILURE_CREATE_INTERFACE:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_CREATE_INTERFACE;
+            case SoftApManager.START_RESULT_FAILURE_SET_COUNTRY_CODE:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_SET_COUNTRY_CODE;
+            case SoftApManager.START_RESULT_FAILURE_SET_MAC_ADDRESS:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_SET_MAC_ADDRESS;
+            case SoftApManager.START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD;
+            case SoftApManager.START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND;
+            case SoftApManager.START_RESULT_FAILURE_ADD_AP_HOSTAPD:
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_FAILURE_ADD_AP_HOSTAPD;
+            default:
+                Log.wtf(TAG, "getSoftApStartedStartResult: unknown StartResult" + startResult);
+                return WifiStatsLog.SOFT_AP_STARTED__RESULT__START_RESULT_UNKNOWN;
+        }
+    }
+
+    private static int getSoftApStartedRole(ActiveModeManager.SoftApRole role) {
+        if (ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY.equals(role)) {
+            return WifiStatsLog.SOFT_AP_STARTED__ROLE__ROLE_LOCAL_ONLY;
+        } else if (ActiveModeManager.ROLE_SOFTAP_TETHERED.equals(role)) {
+            return WifiStatsLog.SOFT_AP_STARTED__ROLE__ROLE_TETHERING;
+        }
+        Log.wtf(TAG, "getSoftApStartedRole: unknown role " + role);
+        return WifiStatsLog.SOFT_AP_STARTED__ROLE__ROLE_UNKNOWN;
+    }
+
+    private static int getSoftApStartedStaApConcurrency(
+            boolean isStaApSupported, boolean isStaDbsSupported) {
+        if (isStaDbsSupported) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_DBS;
+        }
+        if (isStaApSupported) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_SINGLE;
+        }
+        return WifiStatsLog.SOFT_AP_STARTED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_UNSUPPORTED;
+    }
+
+    private static int getSoftApStartedStaStatus(int staFreqMhz) {
+        if (staFreqMhz == WifiInfo.UNKNOWN_FREQUENCY) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_DISCONNECTED;
+        }
+        if (ScanResult.is24GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_CONNECTED_2_GHZ;
+        }
+        if (ScanResult.is5GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_CONNECTED_5_GHZ;
+        }
+        if (ScanResult.is6GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_CONNECTED_6_GHZ;
+        }
+        Log.wtf(TAG, "getSoftApStartedStaStatus: unknown band for freq " + staFreqMhz);
+        return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_UNKNOWN;
+    }
+
+    private static int getSoftApStartedAuthType(
+            @SoftApConfiguration.SecurityType int securityType) {
+        switch (securityType) {
+            case SoftApConfiguration.SECURITY_TYPE_OPEN:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_NONE;
+            case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_SAE_TRANSITION;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_SAE;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_OWE_TRANSITION;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE:
+                return WifiStatsLog.SOFT_AP_STARTED__AUTH_TYPE__AUTH_TYPE_OWE;
+            default:
+                Log.wtf(TAG, "getSoftApStartedAuthType: unknown type " + securityType);
+                return WifiStatsLog.SOFT_AP_STARTED__STA_STATUS__STA_STATUS_UNKNOWN;
+        }
+    }
+
+    /**
+     * Writes the SoftApStarted event to WifiStatsLog.
+     */
+    public void writeSoftApStartedEvent(@SoftApManager.StartResult int startResult,
+            @NonNull ActiveModeManager.SoftApRole role,
+            @WifiScanner.WifiBand int band1,
+            @WifiScanner.WifiBand int band2,
+            boolean isDbsSupported,
+            boolean isStaApSupported,
+            boolean isStaDbsSupported,
+            int staFreqMhz,
+            @SoftApConfiguration.SecurityType int securityType) {
+        WifiStatsLog.write(WifiStatsLog.SOFT_AP_STARTED,
+                getSoftApStartedStartResult(startResult),
+                getSoftApStartedRole(role),
+                band1,
+                band2,
+                isDbsSupported,
+                getSoftApStartedStaApConcurrency(isStaApSupported, isStaDbsSupported),
+                getSoftApStartedStaStatus(staFreqMhz),
+                getSoftApStartedAuthType(securityType));
+    }
+
+    private static int getSoftApStoppedStopEvent(@SoftApManager.StopEvent int stopEvent) {
+        switch (stopEvent) {
+            case SoftApManager.STOP_EVENT_UNKNOWN:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_UNKNOWN;
+            case SoftApManager.STOP_EVENT_STOPPED:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_STOPPED;
+            case SoftApManager.STOP_EVENT_INTERFACE_DOWN:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_INTERFACE_DOWN;
+            case SoftApManager.STOP_EVENT_INTERFACE_DESTROYED:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_INTERFACE_DESTROYED;
+            case SoftApManager.STOP_EVENT_HOSTAPD_FAILURE:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_HOSTAPD_FAILURE;
+            case SoftApManager.STOP_EVENT_NO_USAGE_TIMEOUT:
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_NO_USAGE_TIMEOUT;
+            default:
+                Log.wtf(TAG, "getSoftApStoppedStopEvent: unknown StopEvent " + stopEvent);
+                return WifiStatsLog.SOFT_AP_STOPPED__STOP_EVENT__STOP_EVENT_UNKNOWN;
+        }
+    }
+
+    private static int getSoftApStoppedRole(ActiveModeManager.SoftApRole role) {
+        if (ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY.equals(role)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__ROLE__ROLE_LOCAL_ONLY;
+        } else if (ActiveModeManager.ROLE_SOFTAP_TETHERED.equals(role)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__ROLE__ROLE_TETHERING;
+        }
+        Log.wtf(TAG, "getSoftApStoppedRole: unknown role " + role);
+        return WifiStatsLog.SOFT_AP_STOPPED__ROLE__ROLE_UNKNOWN;
+    }
+
+    private static int getSoftApStoppedStaApConcurrency(
+            boolean isStaApSupported, boolean isStaDbsSupported) {
+        if (isStaDbsSupported) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_DBS;
+        }
+        if (isStaApSupported) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_SINGLE;
+        }
+        return WifiStatsLog.SOFT_AP_STOPPED__STA_AP_CONCURRENCY__STA_AP_CONCURRENCY_UNSUPPORTED;
+    }
+    private static int getSoftApStoppedStaStatus(int staFreqMhz) {
+        if (staFreqMhz == WifiInfo.UNKNOWN_FREQUENCY) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_DISCONNECTED;
+        }
+        if (ScanResult.is24GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_CONNECTED_2_GHZ;
+        }
+        if (ScanResult.is5GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_CONNECTED_5_GHZ;
+        }
+        if (ScanResult.is6GHz(staFreqMhz)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_CONNECTED_6_GHZ;
+        }
+        Log.wtf(TAG, "getSoftApStoppedStaStatus: unknown band for freq " + staFreqMhz);
+        return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_UNKNOWN;
+    }
+
+    private static int getSoftApStoppedAuthType(
+            @SoftApConfiguration.SecurityType int securityType) {
+        switch (securityType) {
+            case SoftApConfiguration.SECURITY_TYPE_OPEN:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_NONE;
+            case SoftApConfiguration.SECURITY_TYPE_WPA2_PSK:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_SAE_TRANSITION;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_SAE:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_SAE;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_OWE_TRANSITION;
+            case SoftApConfiguration.SECURITY_TYPE_WPA3_OWE:
+                return WifiStatsLog.SOFT_AP_STOPPED__AUTH_TYPE__AUTH_TYPE_OWE;
+            default:
+                Log.wtf(TAG, "getSoftApStoppedAuthType: unknown type " + securityType);
+                return WifiStatsLog.SOFT_AP_STOPPED__STA_STATUS__STA_STATUS_UNKNOWN;
+        }
+    }
+
+    private static int getSoftApStoppedStandard(@WifiAnnotations.WifiStandard int standard) {
+        switch (standard) {
+            case ScanResult.WIFI_STANDARD_UNKNOWN:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_UNKNOWN;
+            case ScanResult.WIFI_STANDARD_LEGACY:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_LEGACY;
+            case ScanResult.WIFI_STANDARD_11N:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_11N;
+            case ScanResult.WIFI_STANDARD_11AC:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_11AC;
+            case ScanResult.WIFI_STANDARD_11AX:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_11AX;
+            case ScanResult.WIFI_STANDARD_11AD:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_11AD;
+            case ScanResult.WIFI_STANDARD_11BE:
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_11BE;
+            default:
+                Log.wtf(TAG, "getSoftApStoppedStandard: unknown standard " + standard);
+                return WifiStatsLog.SOFT_AP_STOPPED__STANDARD__WIFI_STANDARD_UNKNOWN;
+        }
+    }
+
+    private static int getSoftApStoppedUpstreamType(@Nullable NetworkCapabilities caps) {
+        if (caps == null) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_UNKNOWN;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+            if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+                if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+                    return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_WIFI_CELLULAR_VPN;
+                }
+                return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_WIFI_VPN;
+            }
+            if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+                return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_CELLULAR_VPN;
+            }
+            if (caps.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
+                return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_BLUETOOTH_VPN;
+            }
+            if (caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
+                return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_ETHERNET_VPN;
+            }
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_WIFI;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_CELLULAR;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_BLUETOOTH;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_ETHERNET;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_WIFI_AWARE;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_LOWPAN;
+        }
+        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_TEST)) {
+            return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_TEST;
+        }
+        Log.wtf(TAG, "getSoftApStoppedStandard: unknown transport types for caps "
+                + Arrays.toString(caps.getTransportTypes()));
+        return WifiStatsLog.SOFT_AP_STOPPED__UPSTREAM_TRANSPORT__TT_UNKNOWN;
+    }
+
+    /**
+     * Writes the SoftApStoppedEvent to WifiStatsLog.
+     */
+    public void writeSoftApStoppedEvent(@SoftApManager.StopEvent int stopEvent,
+            @NonNull ActiveModeManager.SoftApRole role,
+            @WifiScanner.WifiBand int band,
+            boolean isDbs,
+            boolean isStaApSupported,
+            boolean isStaBridgedApSupported,
+            int staFreqMhz,
+            boolean isTimeoutEnabled,
+            int sessionDurationSeconds,
+            @SoftApConfiguration.SecurityType int securityType,
+            @WifiAnnotations.WifiStandard int standard,
+            int maxClients,
+            boolean isDbsTimeoutEnabled,
+            int dbsFailureBand,
+            int dbsTimeoutBand,
+            @Nullable NetworkCapabilities upstreamCaps) {
+        WifiStatsLog.write(WifiStatsLog.SOFT_AP_STOPPED,
+                getSoftApStoppedStopEvent(stopEvent),
+                getSoftApStoppedRole(role),
+                band,
+                isDbs,
+                getSoftApStoppedStaApConcurrency(isStaApSupported, isStaBridgedApSupported),
+                getSoftApStoppedStaStatus(staFreqMhz),
+                isTimeoutEnabled,
+                sessionDurationSeconds,
+                getSoftApStoppedAuthType(securityType),
+                getSoftApStoppedStandard(standard),
+                maxClients,
+                isDbsTimeoutEnabled,
+                dbsFailureBand,
+                dbsTimeoutBand,
+                getSoftApStoppedUpstreamType(upstreamCaps));
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index a835966..69f6180 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -32,6 +32,7 @@
 import android.net.TrafficStats;
 import android.net.apf.ApfCapabilities;
 import android.net.wifi.CoexUnsafeChannel;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.QosPolicyParams;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SecurityParams;
@@ -64,8 +65,10 @@
 import com.android.internal.util.HexDump;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyStatus;
+import com.android.server.wifi.hal.WifiChip;
 import com.android.server.wifi.hotspot2.NetworkDetail;
 import com.android.server.wifi.mockwifi.MockWifiServiceUtil;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.util.FrameParser;
 import com.android.server.wifi.util.InformationElementUtil;
 import com.android.server.wifi.util.NativeUtil;
@@ -503,7 +506,10 @@
         @Override
         public void onScanFailed() {
             Log.d(TAG, "Pno Scan failed event");
-            mWifiMetrics.incrementPnoScanFailedCount();
+            WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                    WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                    0, false, false, false, false, // default values
+                    WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__WIFICOND_SCAN_FAILURE);
         }
     }
 
@@ -840,11 +846,13 @@
     private class SupplicantDeathHandlerInternal implements SupplicantDeathEventHandler {
         @Override
         public void onDeath() {
-            synchronized (mLock) {
-                Log.i(TAG, "wpa_supplicant died. Cleaning up internal state.");
-                onNativeDaemonDeath();
-                mWifiMetrics.incrementNumSupplicantCrashes();
-            }
+            mHandler.post(() -> {
+                synchronized (mLock) {
+                    Log.i(TAG, "wpa_supplicant died. Cleaning up internal state.");
+                    onNativeDaemonDeath();
+                    mWifiMetrics.incrementNumSupplicantCrashes();
+                }
+            });
         }
     }
 
@@ -1097,12 +1105,12 @@
      */
     private String createApIface(@NonNull Iface iface, @NonNull WorkSource requestorWs,
             @SoftApConfiguration.BandType int band, boolean isBridged,
-            @NonNull SoftApManager softApManager) {
+            @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) {
         synchronized (mLock) {
             if (mWifiVendorHal.isVendorHalSupported()) {
                 return mWifiVendorHal.createApIface(
                         new InterfaceDestoyedListenerInternal(iface.id), requestorWs,
-                        band, isBridged, softApManager);
+                        band, isBridged, softApManager, vendorData);
             } else {
                 Log.i(TAG, "Vendor Hal not supported, ignoring createApIface.");
                 return handleIfaceCreationWhenVendorHalNotSupported(iface);
@@ -1354,12 +1362,14 @@
      * @param requestorWs Requestor worksource.
      * @param isBridged Whether or not AP interface is a bridge interface.
      * @param softApManager SoftApManager of the request.
+     * @param vendorData List of {@link OuiKeyedData} containing vendor-provided
+     *                   configuration data. Empty list indicates no vendor data.
      * @return Returns the name of the allocated interface, will be null on failure.
      */
     public String setupInterfaceForSoftApMode(
             @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs,
             @SoftApConfiguration.BandType int band, boolean isBridged,
-            @NonNull SoftApManager softApManager) {
+            @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) {
         synchronized (mLock) {
             String bugTitle = "Wi-Fi BugReport (softAp interface failure)";
             String errorMsg = "";
@@ -1368,6 +1378,7 @@
                 Log.e(TAG, errorMsg);
                 mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
                 takeBugReportInterfaceFailureIfNeeded(bugTitle, errorMsg);
+                softApManager.writeSoftApStartedEvent(SoftApManager.START_RESULT_FAILURE_START_HAL);
                 return null;
             }
             if (!startHostapd()) {
@@ -1375,6 +1386,8 @@
                 Log.e(TAG, errorMsg);
                 mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
                 takeBugReportInterfaceFailureIfNeeded(bugTitle, errorMsg);
+                softApManager.writeSoftApStartedEvent(
+                        SoftApManager.START_RESULT_FAILURE_START_HOSTAPD);
                 return null;
             }
             Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);
@@ -1383,7 +1396,8 @@
                 return null;
             }
             iface.externalListener = interfaceCallback;
-            iface.name = createApIface(iface, requestorWs, band, isBridged, softApManager);
+            iface.name = createApIface(iface, requestorWs, band, isBridged, softApManager,
+                    vendorData);
             if (TextUtils.isEmpty(iface.name)) {
                 errorMsg = "Failed to create softAp iface in vendor HAL";
                 Log.e(TAG, errorMsg);
@@ -1957,8 +1971,11 @@
 
                     @Override
                     public void onPnoRequestFailed() {
-                        mWifiMetrics.incrementPnoScanStartAttemptCount();
-                        mWifiMetrics.incrementPnoScanFailedCount();
+                        WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                                WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                                0, false, false, false, false, // default values
+                                WifiStatsLog
+                                        .PNO_SCAN_STOPPED__FAILURE_CODE__WIFICOND_REQUEST_FAILURE);
                     }
                 });
     }
@@ -2117,18 +2134,18 @@
      * Start Soft AP operation using the provided configuration.
      *
      * @param ifaceName Name of the interface.
-     * @param config Configuration to use for the soft ap created.
+     * @param config    Configuration to use for the soft ap created.
      * @param isMetered Indicates the network is metered or not.
-     * @param callback Callback for AP events.
-     * @return true on success, false otherwise.
+     * @param callback  Callback for AP events.
+     * @return one of {@link SoftApManager.StartResult}
      */
-    public boolean startSoftAp(
+    public @SoftApManager.StartResult int startSoftAp(
             @NonNull String ifaceName, SoftApConfiguration config, boolean isMetered,
             SoftApHalCallback callback) {
         if (mHostapdHal.isApInfoCallbackSupported()) {
             if (!mHostapdHal.registerApCallback(ifaceName, callback)) {
                 Log.e(TAG, "Failed to register ap hal event callback");
-                return false;
+                return SoftApManager.START_RESULT_FAILURE_REGISTER_AP_CALLBACK_HOSTAPD;
             }
         } else {
             SoftApHalCallbackFromWificond softApHalCallbackFromWificond =
@@ -2136,7 +2153,7 @@
             if (!mWifiCondManager.registerApCallback(ifaceName,
                     Runnable::run, softApHalCallbackFromWificond)) {
                 Log.e(TAG, "Failed to register ap hal event callback from wificond");
-                return false;
+                return SoftApManager.START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND;
             }
         }
 
@@ -2146,10 +2163,10 @@
             mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
             takeBugReportInterfaceFailureIfNeeded("Wi-Fi BugReport (softap interface failure)",
                     errorMsg);
-            return false;
+            return SoftApManager.START_RESULT_FAILURE_ADD_AP_HOSTAPD;
         }
 
-        return true;
+        return SoftApManager.START_RESULT_SUCCESS;
     }
 
     /**
@@ -4576,6 +4593,15 @@
             if (iface.phyCapabilities == null) {
                 iface.phyCapabilities = mWifiCondManager.getDeviceWiphyCapabilities(ifaceName);
             }
+            if (iface.phyCapabilities != null
+                    && iface.phyCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)
+                    != mWifiInjector.getSettingsConfigStore()
+                    .get(WifiSettingsConfigStore.WIFI_WIPHY_11BE_SUPPORTED)) {
+                mWifiInjector.getSettingsConfigStore().put(
+                        WifiSettingsConfigStore.WIFI_WIPHY_11BE_SUPPORTED,
+                        iface.phyCapabilities.isWifiStandardSupported(
+                        ScanResult.WIFI_STANDARD_11BE));
+            }
             return iface.phyCapabilities;
         }
     }
@@ -5018,4 +5044,15 @@
     public Set<List<Integer>> getSupportedBandCombinations(@NonNull String ifaceName) {
         return mWifiVendorHal.getSupportedBandCombinations(ifaceName);
     }
+
+    /**
+     * Sends the AFC allowed channels and frequencies to the driver.
+     *
+     * @param afcChannelAllowance the allowed frequencies and channels received from
+     * querying the AFC server.
+     * @return whether the channel allowance was set successfully.
+     */
+    public boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        return mWifiVendorHal.setAfcChannelAllowance(afcChannelAllowance);
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index 993dce9..a03dd40 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -35,10 +35,7 @@
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.companion.CompanionDeviceManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.MacAddress;
@@ -63,7 +60,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PatternMatcher;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -338,6 +334,10 @@
 
         @Override
         public void select(WifiConfiguration wifiConfiguration) {
+            if (wifiConfiguration == null) {
+                Log.wtf(TAG, "User select null config, seems a settings UI issue");
+                return;
+            }
             mHandler.post(() -> {
                 Log.i(TAG, "select configuration " + wifiConfiguration);
                 if (mActiveSpecificNetworkRequest != mNetworkRequest) {
@@ -593,23 +593,6 @@
         mFacade = facade;
         mMultiInternetManager = multiInternetManager;
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        context.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                            handleScreenStateChanged(true);
-                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                            handleScreenStateChanged(false);
-                        }
-                    }
-                }, filter, null, mHandler);
-        handleScreenStateChanged(context.getSystemService(PowerManager.class).isInteractive());
-
         // register the data store for serializing/deserializing data.
         configStore.registerStoreData(
                 wifiInjector.makeNetworkRequestStoreData(new NetworkRequestDataSource()));
@@ -617,6 +600,15 @@
         activeModeWarden.registerModeChangeCallback(new ModeChangeCallback());
 
         setScoreFilter(SCORE_FILTER);
+        mWifiInjector
+                .getWifiDeviceStateChangeManager()
+                .registerStateChangeCallback(
+                        new WifiDeviceStateChangeManager.StateChangeCallback() {
+                            @Override
+                            public void onScreenStateChanged(boolean screenOn) {
+                                handleScreenStateChanged(screenOn);
+                            }
+                        });
     }
 
     private void saveToStore() {
@@ -745,6 +737,11 @@
             Log.e(TAG, "Invalid wifi network specifier: " + wns + ". Rejecting ");
             return false;
         }
+        if (wns.wifiConfiguration.enterpriseConfig != null
+                && wns.wifiConfiguration.enterpriseConfig.isTrustOnFirstUseEnabled()) {
+            Log.e(TAG, "Invalid wifi network specifier with TOFU enabled: " + wns + ". Rejecting ");
+            return false;
+        }
         return true;
     }
 
@@ -1723,8 +1720,7 @@
                         mActiveSpecificNetworkRequest.getRequestorUid()));
         intent.putExtra(UI_START_INTENT_EXTRA_REQUEST_IS_FOR_SINGLE_NETWORK,
                 isActiveRequestForSingleNetwork());
-        mContext.startActivityAsUser(intent, UserHandle.getUserHandleForUid(
-                mActiveSpecificNetworkRequest.getRequestorUid()));
+        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
     }
 
     // Helper method to determine if the specifier does not contain any patterns and matches
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index 9153515..445bf56 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -17,12 +17,9 @@
 package com.android.server.wifi;
 
 import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
-import static android.net.wifi.WifiNetworkSelectionConfig
-        .ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED;
-import static android.net.wifi.WifiNetworkSelectionConfig
-        .ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED;
-import static android.net.wifi.WifiNetworkSelectionConfig
-        .ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE;
+import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_DISABLED;
+import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED;
+import static android.net.wifi.WifiNetworkSelectionConfig.ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -137,6 +134,7 @@
             ASSOCIATED_NETWORK_SELECTION_OVERRIDE_NONE;
     private boolean mScreenOn = false;
     private final WifiNative mWifiNative;
+    private final DevicePolicyManager mDevicePolicyManager;
 
     /**
      * Interface for WiFi Network Nominator
@@ -183,17 +181,22 @@
 
         /**
          * Evaluate all the networks from the scan results.
-         * @param scanDetails              a list of scan details constructed from the scan results
-         * @param untrustedNetworkAllowed  a flag to indicate if untrusted networks are allowed
-         * @param oemPaidNetworkAllowed    a flag to indicate if oem paid networks are allowed
-         * @param oemPrivateNetworkAllowed a flag to indicate if oem private networks are allowed
+         *
+         * @param scanDetails                  a list of scan details constructed from the scan
+         *                                     results
+         * @param untrustedNetworkAllowed      a flag to indicate if untrusted networks are allowed
+         * @param oemPaidNetworkAllowed        a flag to indicate if oem paid networks are allowed
+         * @param oemPrivateNetworkAllowed     a flag to indicate if oem private networks are
+         *                                     allowed
          * @param restrictedNetworkAllowedUids a set of Uids are allowed for restricted network
-         * @param onConnectableListener    callback to record all of the connectable networks
+         * @param onConnectableListener        callback to record all of the connectable networks
          */
-        void nominateNetworks(List<ScanDetail> scanDetails,
+        void nominateNetworks(@NonNull List<ScanDetail> scanDetails,
+                @NonNull List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates,
                 boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed,
-                boolean oemPrivateNetworkAllowed, Set<Integer> restrictedNetworkAllowedUids,
-                OnConnectableListener onConnectableListener);
+                boolean oemPrivateNetworkAllowed,
+                @NonNull Set<Integer> restrictedNetworkAllowedUids,
+                @NonNull OnConnectableListener onConnectableListener);
 
         /**
          * Callback for recording connectable candidates
@@ -351,6 +354,11 @@
             return false;
         }
 
+        if (network.isIpProvisioningTimedOut()) {
+            localLog("Current network has no IPv4 provisioning and therefore insufficient");
+            return false;
+        }
+
         if (!hasSufficientLinkQuality(wifiInfo) && !hasActiveStream(wifiInfo)) {
             localLog("Current network link quality is not sufficient and has low ongoing traffic");
             return false;
@@ -371,7 +379,11 @@
         return mAssociatedNetworkSelectionOverride == ASSOCIATED_NETWORK_SELECTION_OVERRIDE_ENABLED;
     }
 
-    private boolean isNetworkSelectionNeededForCmm(@NonNull ClientModeManagerState cmmState) {
+    /**
+     * Check if network selection is needed on a CMM.
+     * @return True if network selection is needed. False if not needed.
+     */
+    public boolean isNetworkSelectionNeededForCmm(@NonNull ClientModeManagerState cmmState) {
         if (cmmState.connected) {
             // Is roaming allowed?
             if (!isAssociatedNetworkSelectionEnabled()) {
@@ -402,7 +414,7 @@
                 localLog(cmmState.ifaceName + ": Current connected network is not sufficient.");
                 return true;
             }
-        } else if (cmmState.disconnected) {
+        } else if (cmmState.disconnected || cmmState.ipProvisioningTimedOut) {
             return true;
         } else {
             // No network selection if ClientModeImpl is in a state other than
@@ -478,13 +490,10 @@
 
         int numBssidFiltered = 0;
 
-        DevicePolicyManager devicePolicyManager =
-                WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext);
-
-        if (devicePolicyManager != null && SdkLevel.isAtLeastT()) {
+        if (mDevicePolicyManager != null && SdkLevel.isAtLeastT()) {
             adminMinimumSecurityLevel =
-                    devicePolicyManager.getMinimumRequiredWifiSecurityLevel();
-            WifiSsidPolicy policy = devicePolicyManager.getWifiSsidPolicy();
+                    mDevicePolicyManager.getMinimumRequiredWifiSecurityLevel();
+            WifiSsidPolicy policy = mDevicePolicyManager.getWifiSsidPolicy();
             if (policy != null) {
                 adminSsidRestrictionSet = true;
                 if (policy.getPolicyType() == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
@@ -914,6 +923,8 @@
         public final boolean connected;
         /** True if the device is disconnected */
         public final boolean disconnected;
+        /** True if the device is connected in local-only mode due to ip provisioning timeout**/
+        public final boolean ipProvisioningTimedOut;
          /** Currently connected network */
         public final WifiInfo wifiInfo;
 
@@ -921,6 +932,7 @@
             ifaceName = clientModeManager.getInterfaceName();
             connected = clientModeManager.isConnected();
             disconnected = clientModeManager.isDisconnected();
+            ipProvisioningTimedOut = clientModeManager.isIpProvisioningTimedOut();
             wifiInfo = clientModeManager.getConnectionInfo();
         }
 
@@ -929,15 +941,17 @@
             connected = false;
             disconnected = true;
             wifiInfo = new WifiInfo();
+            ipProvisioningTimedOut = false;
         }
 
         @VisibleForTesting
         ClientModeManagerState(@NonNull String ifaceName, boolean connected, boolean disconnected,
-                @NonNull WifiInfo wifiInfo) {
+                @NonNull WifiInfo wifiInfo, boolean ipProvisioningTimedOut) {
             this.ifaceName = ifaceName;
             this.connected = connected;
             this.disconnected = disconnected;
             this.wifiInfo = wifiInfo;
+            this.ipProvisioningTimedOut = ipProvisioningTimedOut;
         }
 
         @Override
@@ -1057,6 +1071,9 @@
         for (NetworkNominator registeredNominator : mNominators) {
             registeredNominator.update(scanDetails);
         }
+        // Update the matching profiles into WifiConfigManager, help displaying Passpoint networks
+        // in Wifi Picker
+        mWifiInjector.getPasspointNetworkNominateHelper().updatePasspointConfig(scanDetails);
 
         // Shall we start network selection at all?
         if (!multiInternetNetworkAllowed && !isNetworkSelectionNeeded(scanDetails, cmmStates)) {
@@ -1112,10 +1129,13 @@
         // Update all configured networks before initiating network selection.
         updateConfiguredNetworks();
 
+        List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates = mWifiInjector
+                .getPasspointNetworkNominateHelper()
+                .getPasspointNetworkCandidates(new ArrayList<>(mFilteredNetworks));
         for (NetworkNominator registeredNominator : mNominators) {
             localLog("About to run " + registeredNominator.getName() + " :");
             registeredNominator.nominateNetworks(
-                    new ArrayList<>(mFilteredNetworks),
+                    new ArrayList<>(mFilteredNetworks), passpointCandidates,
                     untrustedNetworkAllowed, oemPaidNetworkAllowed, oemPrivateNetworkAllowed,
                     restrictedNetworkAllowedUids, (scanDetail, config) -> {
                         WifiCandidates.Key key = wifiCandidates.keyFromScanDetailAndConfig(
@@ -1636,7 +1656,8 @@
                 scanDetail.getNetworkDetail().getMaxNumberSpatialStreams(),
                 scanDetail.getNetworkDetail().getChannelUtilization(),
                 channelUtilizationLinkLayerStats,
-                mWifiGlobals.isBluetoothConnected());
+                mWifiGlobals.isBluetoothConnected(),
+                scanDetail.getNetworkDetail().getDisabledSubchannelBitmap());
     }
 
     /**
@@ -1732,5 +1753,6 @@
         mWifiGlobals = wifiGlobals;
         mScanRequestProxy = scanRequestProxy;
         mWifiNative = wifiNative;
+        mDevicePolicyManager = WifiPermissionsUtil.retrieveDevicePolicyManagerFromContext(mContext);
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
index 41ba214..b437986 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
@@ -1611,9 +1611,16 @@
     }
 
     /**
-     * Get all user-approved Passpoint networks that include an SSID.
+     * Get all user-approved Passpoint networks from suggestion.
+     *
+     * @param requireSsid If true, this method will only return Passpoint suggestions that include
+     *     an SSID. If false, this method will return all Passpoint suggestions, including those
+     *     which do not include an SSID.
+     *     <p>Note: Passpoint SSIDs are recorded upon successful connection to a network. Having an
+     *     SSID indicates that a Passpoint network has connected since the last reboot.
      */
-    public List<WifiConfiguration> getAllPasspointScanOptimizationSuggestionNetworks() {
+    public List<WifiConfiguration> getAllPasspointScanOptimizationSuggestionNetworks(
+            boolean requireSsid) {
         List<WifiConfiguration> networks = new ArrayList<>();
         for (PerAppInfo info : mActiveNetworkSuggestionsPerApp.values()) {
             if (!info.isApproved()) {
@@ -1631,7 +1638,7 @@
                 }
                 network.SSID = mWifiInjector.getPasspointManager()
                         .getMostRecentSsidForProfile(network.getPasspointUniqueId());
-                if (network.SSID == null) {
+                if (requireSsid && network.SSID == null) {
                     continue;
                 }
                 networks.add(network);
diff --git a/service/java/com/android/server/wifi/WifiNotificationManager.java b/service/java/com/android/server/wifi/WifiNotificationManager.java
index da9a933..5d00dfc 100644
--- a/service/java/com/android/server/wifi/WifiNotificationManager.java
+++ b/service/java/com/android/server/wifi/WifiNotificationManager.java
@@ -31,6 +31,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.wifi.resources.R;
 
 import java.util.ArrayList;
@@ -111,7 +112,8 @@
 
     private void cleanAllWifiNotification() {
         for (StatusBarNotification notification : getActiveNotifications()) {
-            if (NOTIFICATION_TAG.equals(notification.getTag())) {
+            if (NOTIFICATION_TAG.equals(notification.getTag())
+                    && notification.getId() != SystemMessage.NOTE_WIFI_APM_NOTIFICATION) {
                 cancel(notification.getId());
             }
         }
diff --git a/service/java/com/android/server/wifi/WifiP2pConnection.java b/service/java/com/android/server/wifi/WifiP2pConnection.java
index 67cbb5a..907ab0d 100644
--- a/service/java/com/android/server/wifi/WifiP2pConnection.java
+++ b/service/java/com/android/server/wifi/WifiP2pConnection.java
@@ -45,6 +45,9 @@
     private AsyncChannel mWifiP2pChannel;
     private boolean mTemporarilyDisconnectWifi = false;
 
+    /** Used to check if P2P state machine is in waitingState */
+    private boolean mWaitingState = false;
+
     public WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden) {
         mContext = context;
         mHandler = new P2pHandler(looper);
@@ -187,4 +190,13 @@
     public boolean shouldTemporarilyDisconnectWifi() {
         return mTemporarilyDisconnectWifi;
     }
+
+    public void setP2pInWaitingState(boolean inWaitingState) {
+        mWaitingState = inWaitingState;
+    }
+
+    /** whether the P2P state machine is in waitingState for user response to create interface */
+    public boolean isP2pInWaitingState() {
+        return mWaitingState;
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiPseudonymManager.java b/service/java/com/android/server/wifi/WifiPseudonymManager.java
index 5465359..e102dd6 100644
--- a/service/java/com/android/server/wifi/WifiPseudonymManager.java
+++ b/service/java/com/android/server/wifi/WifiPseudonymManager.java
@@ -24,6 +24,7 @@
 import static com.android.server.wifi.entitlement.CarrierSpecificServiceEntitlement.REASON_TRANSIENT_FAILURE;
 
 import android.annotation.NonNull;
+import android.app.AlarmManager;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -32,7 +33,6 @@
 import android.net.wifi.WifiStringResourceWrapper;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Process;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
@@ -61,8 +61,11 @@
             "config_wifiOobPseudonymEntitlementServerUrl";
     @VisibleForTesting
     static final long TEN_SECONDS_IN_MILLIS = Duration.ofSeconds(10).toMillis();
+    @VisibleForTesting static final long TEN_MINUTES_IN_MILLIS = Duration.ofMinutes(10).toMillis();
+
     @VisibleForTesting
     private static final long SEVEN_DAYS_IN_MILLIS = Duration.ofDays(7).toMillis();
+
     @VisibleForTesting
     static final long[] RETRY_INTERVALS_FOR_SERVER_ERROR = {
             Duration.ofMinutes(5).toMillis(),
@@ -81,6 +84,7 @@
     private final WifiInjector mWifiInjector;
     private final Clock mClock;
     private final Handler mWifiHandler;
+    private final AlarmManager mAlarmManager;
 
     private boolean mVerboseLogEnabled = false;
 
@@ -89,6 +93,9 @@
      */
     private final SparseArray<PseudonymInfo> mPseudonymInfoArray = new SparseArray<>();
 
+    /** Cached Map of carrier IDs to RetrieveListeners. */
+    private final SparseArray<RetrieveListener> mRetrieveListenerSparseArray = new SparseArray<>();
+
     /*
      * Two cached map of <carrier ID, retry times>.
      */
@@ -130,11 +137,16 @@
     private final Set<PseudonymUpdatingListener> mPseudonymUpdatingListeners =
             new ArraySet<>();
 
-    WifiPseudonymManager(@NonNull WifiContext wifiContext, @NonNull WifiInjector wifiInjector,
-            @NonNull Clock clock, @NonNull Looper wifiLooper) {
+    WifiPseudonymManager(
+            @NonNull WifiContext wifiContext,
+            @NonNull WifiInjector wifiInjector,
+            @NonNull Clock clock,
+            @NonNull AlarmManager alarmManager,
+            @NonNull Looper wifiLooper) {
         mWifiContext = wifiContext;
         mWifiInjector = wifiInjector;
         mClock = clock;
+        mAlarmManager = alarmManager;
         // Create a new handler to have a dedicated message queue.
         mWifiHandler = new Handler(wifiLooper);
     }
@@ -236,7 +248,6 @@
      * Update the input WifiConfiguration's anonymous identity.
      *
      * @param wifiConfiguration WifiConfiguration which will be updated.
-     * @return true if there is a valid pseudonym to update the WifiConfiguration, otherwise false.
      */
     public void updateWifiConfiguration(@NonNull WifiConfiguration wifiConfiguration) {
         if (wifiConfiguration.enterpriseConfig == null
@@ -320,25 +331,23 @@
     @VisibleForTesting
     void setPseudonymAndScheduleRefresh(int carrierId, @NonNull PseudonymInfo pseudonymInfo) {
         mPseudonymInfoArray.put(carrierId, pseudonymInfo);
-        // Cancel all the queued messages and queue another message to refresh the pseudonym
-        mWifiHandler.removeMessages(carrierId);
-        scheduleToRetrieveDelayed(carrierId, pseudonymInfo.getAtrInMillis());
+        scheduleToRetrieveDelayed(carrierId, pseudonymInfo.getLttrInMillis());
     }
 
     /**
      * Retrieves the OOB pseudonym if there is no pseudonym or the existing pseudonym has expired.
-     * This method is called when the CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED is
-     * received or the TTL has elapsed to refresh the OOB pseudonym.
+     * This method is called when the CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED is received
+     * or the TTL has elapsed to refresh the OOB pseudonym.
      *
      * @param carrierId carrier id for target carrier
      */
     public void retrieveOobPseudonymIfNeeded(int carrierId) {
         vlogd("retrieveOobPseudonymIfNeeded(" + carrierId + ")");
         Optional<PseudonymInfo> optionalPseudonymInfo = getValidPseudonymInfo(carrierId);
-        if (optionalPseudonymInfo.isEmpty() || optionalPseudonymInfo.get().shouldBeRefreshed()) {
+        if (optionalPseudonymInfo.isEmpty()) {
             scheduleToRetrieveDelayed(carrierId, 0);
         } else {
-            vlogd("Current pseudonym is still fresh. Exit.");
+            scheduleToRetrieveDelayed(carrierId, optionalPseudonymInfo.get().getLttrInMillis());
         }
     }
 
@@ -379,9 +388,18 @@
     }
 
     private void scheduleToRetrieveDelayed(int carrierId, long delayMillis) {
-        Message msg = Message.obtain(mWifiHandler, new RetrieveRunnable(carrierId));
-        msg.what = carrierId;
-        mWifiHandler.sendMessageDelayed(msg, delayMillis);
+        RetrieveListener listener = mRetrieveListenerSparseArray.get(carrierId);
+        if (listener == null) {
+            listener = new RetrieveListener(carrierId);
+            mRetrieveListenerSparseArray.set(carrierId, listener);
+        }
+        mAlarmManager.setWindow(
+                AlarmManager.RTC_WAKEUP,
+                mClock.getWallClockMillis() + delayMillis,
+                TEN_MINUTES_IN_MILLIS,
+                TAG,
+                listener,
+                mWifiHandler);
         /*
          * Always suppose it fails before the retrieval really starts to prevent multiple messages
          * been queued when there is no data network available to retrieve. After retrieving, this
@@ -414,28 +432,15 @@
     }
 
     @VisibleForTesting
-    class RetrieveRunnable implements Runnable {
-        @VisibleForTesting
-        int mCarrierId;
+    class RetrieveListener implements AlarmManager.OnAlarmListener {
+        @VisibleForTesting int mCarrierId;
 
-        RetrieveRunnable(int carrierId) {
+        RetrieveListener(int carrierId) {
             mCarrierId = carrierId;
         }
 
         @Override
-        public void run() {
-            /*
-             * There may be multiple messages for this mCarrierId in the queue. There is no need to
-             * retrieve them multiple times.
-             *
-             * For example, carrierA's SIM is inserted into the device multiple times right before
-             * carrierA's pseudonym expires, there will be multiple messages in the queue. When the
-             * network becomes available, the pseudonym can't be retrieved because the carrierA's
-             * server reports an error. To prevent connecting with the server several times, we
-             * should cancel all the queued messages.
-             */
-            mWifiHandler.removeMessages(mCarrierId);
-
+        public void onAlarm() {
             if (!mWifiInjector.getWifiCarrierInfoManager()
                     .isOobPseudonymFeatureEnabled(mCarrierId)) {
                 vlogd("do nothing, OOB Pseudonym feature is not enabled for carrier: "
@@ -445,7 +450,7 @@
 
             int subId = mWifiInjector.getWifiCarrierInfoManager().getMatchingSubId(mCarrierId);
             if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                Log.e(TAG, "RetrieveRunnable: " + mCarrierId + ": subId is invalid. Exit.");
+                Log.e(TAG, "RetrieveListener: " + mCarrierId + ": subId is invalid. Exit.");
                 return;
             }
 
diff --git a/service/java/com/android/server/wifi/WifiPulledAtomLogger.java b/service/java/com/android/server/wifi/WifiPulledAtomLogger.java
index 2275bf4..44bc126 100644
--- a/service/java/com/android/server/wifi/WifiPulledAtomLogger.java
+++ b/service/java/com/android/server/wifi/WifiPulledAtomLogger.java
@@ -20,35 +20,44 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkSuggestion;
+import android.net.wifi.WifiSsid;
 import android.os.Handler;
+import android.os.Process;
 import android.util.Log;
 import android.util.StatsEvent;
 
 import com.android.server.wifi.proto.WifiStatsLog;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * This is used to log pulled atoms to StatsD via Callback.
  */
 public class WifiPulledAtomLogger {
-    // Internal version number for tracking.
-    // Digits 0-1 is target build SDK level
-    // Digits 2-3 is the month of the mainline train
-    public static final int WIFI_VERSION_NUMBER = 340899999;
     public static final String WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME = "com.android.wifi";
+    private static final String WIFI_PACKAGE_NAME_SUFFIX = ".android.wifi";
     private int mWifiBuildType = 0;
 
     private static final String TAG = "WifiPulledAtomLogger";
     private final StatsManager mStatsManager;
     private final Handler mWifiHandler;
     private final Context mContext;
+    private final WifiInjector mWifiInjector;
     private StatsManager.StatsPullAtomCallback mStatsPullAtomCallback;
-    public WifiPulledAtomLogger(StatsManager statsManager, Handler handler, Context context) {
+
+    private int mApexVersionNumber = -1;
+
+    public WifiPulledAtomLogger(StatsManager statsManager, Handler handler, Context context,
+            WifiInjector wifiInjector) {
         mStatsManager = statsManager;
         mWifiHandler = handler;
         mStatsPullAtomCallback = new WifiPullAtomCallback();
         mContext = context;
+        mWifiInjector = wifiInjector;
     }
 
     /**
@@ -79,6 +88,12 @@
             switch (atomTag) {
                 case WifiStatsLog.WIFI_MODULE_INFO:
                     return handleWifiVersionPull(atomTag, data);
+                case WifiStatsLog.WIFI_SETTING_INFO:
+                    return handleWifiSettingsPull(atomTag, data);
+                case WifiStatsLog.WIFI_COMPLEX_SETTING_INFO:
+                    return handleWifiComplexSettingsPull(atomTag, data);
+                case WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO:
+                    return handleWifiConfiguredNetworkInfoPull(atomTag, data);
                 default:
                     return StatsManager.PULL_SKIP;
             }
@@ -88,27 +103,165 @@
     private int handleWifiVersionPull(int atomTag, List<StatsEvent> data) {
         if (mWifiBuildType != 0) {
             // build type already cached. No need to get it again.
-            data.add(WifiStatsLog.buildStatsEvent(atomTag, WIFI_VERSION_NUMBER, mWifiBuildType));
+            data.add(WifiStatsLog.buildStatsEvent(atomTag, mApexVersionNumber, mWifiBuildType));
             return StatsManager.PULL_SUCCESS;
         }
-        // Query build type and cache if not already cached.
         PackageManager pm = mContext.getPackageManager();
         if (pm == null) {
             Log.e(TAG, "Failed to get package manager");
             return StatsManager.PULL_SKIP;
         }
+        updateBuildTypeAndVersionCode(pm);
+
+        data.add(WifiStatsLog.buildStatsEvent(atomTag, mApexVersionNumber, mWifiBuildType));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private int handleWifiSettingsPull(int atomTag, List<StatsEvent> data) {
+        WifiSettingsStore settingsStore = mWifiInjector.getWifiSettingsStore();
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCAN_ALWAYS_AVAILABLE,
+                settingsStore.isScanAlwaysAvailable()));
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCAN_THROTTLE,
+                settingsStore.isWifiScanThrottleEnabled()));
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_SCORING,
+                settingsStore.isWifiScoringEnabled()));
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_PASSPOINT,
+                settingsStore.isWifiPasspointEnabled()));
+
+        boolean nonPersistentMacRandEnabled = mWifiInjector.getFrameworkFacade().getIntegerSetting(
+                mContext,
+                WifiConfigManager.NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG, 0)
+                == 1 ? true : false;
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_ENHANCED_MAC_RANDOMIZATION,
+                nonPersistentMacRandEnabled));
+
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_WAKE,
+                mWifiInjector.getWakeupController().isEnabled()));
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__WIFI_NETWORKS_AVAILABLE_NOTIFICATION,
+                mWifiInjector.getOpenNetworkNotifier().isSettingEnabled()));
+        data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__LOCATION_MODE,
+                mWifiInjector.getWifiPermissionsUtil().isLocationModeEnabled()));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private static int frameworkToAtomMultiInternetMode(
+            @WifiManager.WifiMultiInternetMode int mode) {
+        switch (mode) {
+            case WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED:
+                return WifiStatsLog
+                        .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_DISABLED;
+            case WifiManager.WIFI_MULTI_INTERNET_MODE_DBS_AP:
+                return WifiStatsLog
+                        .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_DBS_AP;
+            case WifiManager.WIFI_MULTI_INTERNET_MODE_MULTI_AP:
+                return WifiStatsLog
+                        .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_MULTI_AP;
+            default:
+                Log.i(TAG, "Invalid multi-internet mode: " + mode);
+                return -1;
+        }
+    }
+
+    private int handleWifiComplexSettingsPull(int atomTag, List<StatsEvent> data) {
+        int multiInternetMode = frameworkToAtomMultiInternetMode(
+                mWifiInjector.getWifiSettingsStore().getWifiMultiInternetMode());
+        if (multiInternetMode == -1) {
+            return StatsManager.PULL_SKIP;
+        }
+        data.add(WifiStatsLog.buildStatsEvent(atomTag, multiInternetMode));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private void updateBuildTypeAndVersionCode(PackageManager pm) {
+        // Query build type and cache if not already cached.
         List<PackageInfo> packageInfos = pm.getInstalledPackages(PackageManager.MATCH_APEX);
         boolean found = false;
+        String wifiPackageName = null;
         for (PackageInfo packageInfo : packageInfos) {
-            if (WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME.equals(packageInfo.packageName)) {
-                found = true;
+            if (packageInfo.packageName.endsWith(WIFI_PACKAGE_NAME_SUFFIX)) {
+                mApexVersionNumber = (int) packageInfo.getLongVersionCode();
+                wifiPackageName = packageInfo.packageName;
+                if (packageInfo.packageName.equals(WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME)) {
+                    found = true;
+                }
                 break;
             }
         }
         mWifiBuildType = found
                 ? WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_BUILT_FROM_SOURCE
                 : WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_PREBUILT;
-        data.add(WifiStatsLog.buildStatsEvent(atomTag, WIFI_VERSION_NUMBER, mWifiBuildType));
+        Log.i(TAG, "Wifi Module package name is " + wifiPackageName
+                + ", version is " + mApexVersionNumber);
+    }
+
+    private static boolean configHasUtf8Ssid(WifiConfiguration config) {
+        return WifiSsid.fromString(config.SSID).getUtf8Text() != null;
+    }
+
+    private StatsEvent wifiConfigToStatsEvent(
+            int atomTag, WifiConfiguration config, boolean isSuggestion) {
+        return WifiStatsLog.buildStatsEvent(
+                atomTag,
+                0,  // deprecated network ID field
+                config.isEnterprise(),
+                config.hiddenSSID,
+                config.isPasspoint(),
+                isSuggestion,
+                configHasUtf8Ssid(config),
+                mWifiInjector.getSsidTranslator().isSsidTranslationEnabled(),
+                false, // legacy TOFU field
+                !config.getNetworkSelectionStatus().hasNeverDetectedCaptivePortal(),
+                config.allowAutojoin,
+                WifiMetrics.convertSecurityModeToProto(config),
+                WifiMetrics.convertMacRandomizationToProto(config.getMacRandomizationSetting()),
+                WifiMetrics.convertMeteredOverrideToProto(config.meteredOverride),
+                WifiMetrics.convertEapMethodToProto(config),
+                WifiMetrics.convertEapInnerMethodToProto(config),
+                WifiMetrics.isFreeOpenRoaming(config),
+                WifiMetrics.isSettledOpenRoaming(config),
+                WifiMetrics.convertTofuConnectionStateToProto(config),
+                WifiMetrics.convertTofuDialogStateToProto(config));
+    }
+
+    private int handleWifiConfiguredNetworkInfoPull(int atomTag, List<StatsEvent> data) {
+        List<WifiConfiguration> savedConfigs =
+                mWifiInjector.getWifiConfigManager().getSavedNetworks(Process.WIFI_UID);
+        for (WifiConfiguration config : savedConfigs) {
+            if (!config.isPasspoint()) {
+                data.add(wifiConfigToStatsEvent(atomTag, config, false));
+            }
+        }
+
+        Set<WifiNetworkSuggestion> approvedSuggestions =
+                mWifiInjector.getWifiNetworkSuggestionsManager().getAllApprovedNetworkSuggestions();
+        for (WifiNetworkSuggestion suggestion : approvedSuggestions) {
+            WifiConfiguration config = suggestion.getWifiConfiguration();
+            if (!config.isPasspoint()) {
+                data.add(wifiConfigToStatsEvent(atomTag, config, true));
+            }
+        }
+
+        List<WifiConfiguration> passpointConfigs =
+                mWifiInjector.getPasspointManager().getWifiConfigsForPasspointProfiles(false);
+        for (WifiConfiguration config : passpointConfigs) {
+            data.add(wifiConfigToStatsEvent(atomTag, config, false));
+        }
+
+        List<WifiConfiguration> passpointSuggestions =
+                mWifiInjector.getWifiNetworkSuggestionsManager()
+                        .getAllPasspointScanOptimizationSuggestionNetworks(false);
+        for (WifiConfiguration config : passpointSuggestions) {
+            data.add(wifiConfigToStatsEvent(atomTag, config, true));
+        }
+
         return StatsManager.PULL_SUCCESS;
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index de828e2..43c87a6 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -30,8 +30,11 @@
 import android.net.wifi.WifiConnectedSessionInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiUsabilityStatsEntry;
 import android.os.Build;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -47,6 +50,7 @@
 import java.io.PrintWriter;
 import java.util.Calendar;
 import java.util.LinkedList;
+import java.util.StringJoiner;
 
 /**
  * Class used to calculate scores for connected wifi networks and report it to the associated
@@ -296,6 +300,9 @@
                         : ConnectedScore.WIFI_TRANSITION_SCORE - 1);
             }
             mWifiInfo.setUsable(mIsUsable);
+            mWifiMetrics.setScorerPredictedWifiUsabilityState(mInterfaceName,
+                    mIsUsable ? WifiMetrics.WifiUsabilityState.USABLE
+                            : WifiMetrics.WifiUsabilityState.UNUSABLE);
         }
 
         @Override
@@ -404,6 +411,11 @@
                 return;
             }
         }
+        // Set the usability prediction for the AOSP scorer.
+        mWifiMetrics.setScorerPredictedWifiUsabilityState(mInterfaceName,
+                (mLegacyIntScore < ConnectedScore.WIFI_TRANSITION_SCORE)
+                        ? WifiMetrics.WifiUsabilityState.UNUSABLE
+                        : WifiMetrics.WifiUsabilityState.USABLE);
         // Stay a notch above the transition score if adaptive connectivity is disabled.
         if (!mAdaptiveConnectivityEnabledSettingObserver.get()
                 || !mWifiSettingsStore.isWifiScoringEnabled()) {
@@ -577,6 +589,9 @@
         mActiveModeWarden = activeModeWarden;
         mWifiConnectivityManager = wifiConnectivityManager;
         mWifiConfigManager = wifiConfigManager;
+        mWifiMetrics.setIsExternalWifiScorerOn(false, Process.WIFI_UID);
+        mWifiMetrics.setScorerPredictedWifiUsabilityState(mInterfaceName,
+                WifiMetrics.WifiUsabilityState.UNKNOWN);
     }
 
     /** Returns whether this scores primary network based on the role */
@@ -592,6 +607,8 @@
         mLegacyIntScore = isPrimary() ? ConnectedScore.WIFI_INITIAL_SCORE
                 : ConnectedScore.WIFI_SECONDARY_INITIAL_SCORE;
         mIsUsable = true;
+        mWifiMetrics.setScorerPredictedWifiUsabilityState(mInterfaceName,
+                WifiMetrics.WifiUsabilityState.UNKNOWN);
         mLastKnownNudCheckScore = isPrimary() ? ConnectedScore.WIFI_TRANSITION_SCORE
                 : ConnectedScore.WIFI_SECONDARY_TRANSITION_SCORE;
         mAggressiveConnectedScore.reset();
@@ -850,39 +867,76 @@
             filteredRssi = mVelocityBasedConnectedScore.getFilteredRssi();
             rssiThreshold = mVelocityBasedConnectedScore.getAdjustedRssiThreshold();
         }
-        int freq = mWifiInfo.getFrequency();
-        int txLinkSpeed = mWifiInfo.getLinkSpeed();
-        int rxLinkSpeed = mWifiInfo.getRxLinkSpeedMbps();
         WifiScoreCard.PerNetwork network = mWifiScoreCard.lookupNetwork(mWifiInfo.getSSID());
-        int txThroughputMbps = network.getTxLinkBandwidthKbps() / 1000;
-        int rxThroughputMbps = network.getRxLinkBandwidthKbps() / 1000;
-        double txSuccessRate = mWifiInfo.getSuccessfulTxPacketsPerSecond();
-        double txRetriesRate = mWifiInfo.getRetriedTxPacketsPerSecond();
-        double txBadRate = mWifiInfo.getLostTxPacketsPerSecond();
-        double rxSuccessRate = mWifiInfo.getSuccessfulRxPacketsPerSecond();
-        long totalBeaconRx = mWifiMetrics.getTotalBeaconRxCount();
-        String s;
-        try {
-            Calendar c = Calendar.getInstance();
-            c.setTimeInMillis(now);
-            // Date format: "%tm-%td %tH:%tM:%tS.%tL"
-            String timestamp = StringUtil.calendarToString(c);
-            s = timestamp + "," + mSessionNumber + "," + netId + "," + mWifiInfo.getRssi()
-                    + "," + StringUtil.doubleToString(filteredRssi, 1) + "," + rssiThreshold
-                    + "," + freq + "," + txLinkSpeed
-                    + "," + rxLinkSpeed + "," + txThroughputMbps
-                    + "," + rxThroughputMbps + "," + totalBeaconRx
-                    + "," + StringUtil.doubleToString(txSuccessRate, 2)
-                    + "," + StringUtil.doubleToString(txRetriesRate, 2)
-                    + "," + StringUtil.doubleToString(txBadRate, 2)
-                    + "," + StringUtil.doubleToString(rxSuccessRate, 2)
-                    + "," + mNudYes + "," + mNudCount + "," + s1 + "," + s2 + "," + score;
-        } catch (Exception e) {
-            Log.e(TAG, "format problem", e);
-            return;
+        StringJoiner stats = new StringJoiner(",");
+
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(now);
+        // timestamp format: "%tm-%td %tH:%tM:%tS.%tL"
+        stats.add(StringUtil.calendarToString(c));
+        stats.add(Integer.toString(mSessionNumber));
+        stats.add(Integer.toString(netId));
+        stats.add(Integer.toString(mWifiInfo.getRssi()));
+        stats.add(StringUtil.doubleToString(filteredRssi, 1));
+        stats.add(Double.toString(rssiThreshold));
+        stats.add(Integer.toString(mWifiInfo.getFrequency()));
+        stats.add(Integer.toString(mWifiInfo.getLinkSpeed()));
+        stats.add(Integer.toString(mWifiInfo.getRxLinkSpeedMbps()));
+        stats.add(Integer.toString(network.getTxLinkBandwidthKbps() / 1000));
+        stats.add(Long.toString(network.getRxLinkBandwidthKbps() / 1000));
+        stats.add(Long.toString(mWifiMetrics.getTotalBeaconRxCount()));
+        stats.add(StringUtil.doubleToString(mWifiInfo.getSuccessfulTxPacketsPerSecond(), 2));
+        stats.add(StringUtil.doubleToString(mWifiInfo.getRetriedTxPacketsPerSecond(), 2));
+        stats.add(StringUtil.doubleToString(mWifiInfo.getLostTxPacketsPerSecond(), 2));
+        stats.add(StringUtil.doubleToString(mWifiInfo.getSuccessfulRxPacketsPerSecond(), 2));
+        stats.add(Integer.toString(mNudYes));
+        stats.add(Integer.toString(mNudCount));
+        stats.add(Integer.toString(s1));
+        stats.add(Integer.toString(s2));
+        stats.add(Integer.toString(score));
+        // MLO stats
+        for (MloLink link : mWifiInfo.getAffiliatedMloLinks()) {
+            StringJoiner mloStats = new StringJoiner(",", "{", "}");
+            mloStats.add(Integer.toString(link.getLinkId()));
+            mloStats.add(Integer.toString(link.getRssi()));
+            final int band;
+            switch (link.getBand()) {
+                case WifiScanner.WIFI_BAND_24_GHZ:
+                    band = ScanResult.WIFI_BAND_24_GHZ;
+                    break;
+                case WifiScanner.WIFI_BAND_5_GHZ:
+                    band = ScanResult.WIFI_BAND_5_GHZ;
+                    break;
+                case WifiScanner.WIFI_BAND_6_GHZ:
+                    band = ScanResult.WIFI_BAND_6_GHZ;
+                    break;
+                case WifiScanner.WIFI_BAND_60_GHZ:
+                    band = ScanResult.WIFI_BAND_60_GHZ;
+                    break;
+                default:
+                    band = ScanResult.UNSPECIFIED;
+                    break;
+            }
+            int linkFreq =
+                    ScanResult.convertChannelToFrequencyMhzIfSupported(link.getChannel(), band);
+            mloStats.add(Integer.toString(linkFreq));
+            mloStats.add(Integer.toString(link.getTxLinkSpeedMbps()));
+            mloStats.add(Integer.toString(link.getRxLinkSpeedMbps()));
+            mloStats.add(Long.toString(mWifiMetrics.getTotalBeaconRxCount(link.getLinkId())));
+            mloStats.add(StringUtil.doubleToString(link.getSuccessfulTxPacketsPerSecond(), 2));
+            mloStats.add(StringUtil.doubleToString(link.getRetriedTxPacketsPerSecond(), 2));
+            mloStats.add(StringUtil.doubleToString(link.getLostTxPacketsPerSecond(), 2));
+            mloStats.add(StringUtil.doubleToString(link.getSuccessfulRxPacketsPerSecond(), 2));
+            mloStats.add(MloLink.getStateString(link.getState()));
+            mloStats.add(
+                    WifiUsabilityStatsEntry.getLinkStateString(
+                            mWifiMetrics.getLinkUsageState(link.getLinkId())));
+            stats.add(mloStats.toString());
         }
+
+
         synchronized (mLinkMetricsHistory) {
-            mLinkMetricsHistory.add(s);
+            mLinkMetricsHistory.add(stats.toString());
             while (mLinkMetricsHistory.size() > DUMPSYS_ENTRY_COUNT_LIMIT) {
                 mLinkMetricsHistory.removeFirst();
             }
@@ -905,9 +959,14 @@
         synchronized (mLinkMetricsHistory) {
             history = new LinkedList<>(mLinkMetricsHistory);
         }
-        pw.println("time,session,netid,rssi,filtered_rssi,rssi_threshold,freq,txLinkSpeed,"
-                + "rxLinkSpeed,txTput,rxTput,bcnCnt,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,"
-                + "s1,s2,score");
+        // Note: MLO stats are printed only for multi link connection. It is appended to the
+        // existing print as {link1Id,link1Rssi ... ,link1UsageState}, {link2Id,link2Rssi ... ,
+        // link2UsageState}, ..etc.
+        pw.println(
+                "time,session,netid,rssi,filtered_rssi,rssi_threshold,freq,txLinkSpeed,"
+                    + "rxLinkSpeed,txTput,rxTput,bcnCnt,tx_good,tx_retry,tx_bad,rx_pps,nudrq,nuds,"
+                    + "s1,s2,score,{linkId,linkRssi,linkFreq,txLinkSpeed,rxLinkSpeed,linkBcnCnt,"
+                    + "linkTxGood,linkTxRetry,linkTxBad,linkRxGood,linkMloState,linkUsageState}");
         for (String line : history) {
             pw.println(line);
         }
@@ -918,11 +977,9 @@
 
     /**
      * Set a scorer for Wi-Fi connected network score handling.
-     * @param binder
-     * @param scorer
      */
     public boolean setWifiConnectedNetworkScorer(IBinder binder,
-            IWifiConnectedNetworkScorer scorer) {
+            IWifiConnectedNetworkScorer scorer, int callerUid) {
         if (binder == null || scorer == null) return false;
         // Enforce that only a single scorer can be set successfully.
         if (mWifiConnectedNetworkScorerHolder != null) {
@@ -942,7 +999,7 @@
 
         // Disable AOSP scorer
         mVelocityBasedConnectedScore = null;
-        mWifiMetrics.setIsExternalWifiScorerOn(true);
+        mWifiMetrics.setIsExternalWifiScorerOn(true, callerUid);
         // If there is already a connection, start a new session
         final int netId = getCurrentNetId();
         if (netId > 0 && !mShouldReduceNetworkScore) {
@@ -1129,7 +1186,7 @@
         mWifiConnectedNetworkScorerHolder = null;
         mWifiGlobals.setUsingExternalScorer(false);
         mExternalScoreUpdateObserverProxy.unregisterCallback(mScoreUpdateObserverCallback);
-        mWifiMetrics.setIsExternalWifiScorerOn(false);
+        mWifiMetrics.setIsExternalWifiScorerOn(false, Process.WIFI_UID);
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 814a7fb..bf162ce 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -28,6 +28,7 @@
 import static android.net.wifi.WifiManager.NOT_OVERRIDE_EXISTING_NETWORKS_ON_RESTORE;
 import static android.net.wifi.WifiManager.PnoScanResultsCallback.REGISTER_PNO_CALLBACK_PNO_NOT_SUPPORTED;
 import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
@@ -56,7 +57,10 @@
 import static com.android.server.wifi.SelfRecovery.REASON_API_CALL;
 import static com.android.server.wifi.WifiSettingsConfigStore.SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI;
 import static com.android.server.wifi.WifiSettingsConfigStore.SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_AWARE_VERBOSE_LOGGING_ENABLED;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STA_FACTORY_MAC_ADDRESS;
 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_WEP_ALLOWED;
 
 import android.Manifest;
 import android.annotation.AnyThread;
@@ -256,7 +260,6 @@
     private final WifiContext mContext;
     private final FrameworkFacade mFacade;
     private final Clock mClock;
-
     private final PowerManager mPowerManager;
     private final AppOpsManager mAppOps;
     private final UserManager mUserManager;
@@ -310,6 +313,9 @@
     private final DeviceConfigFacade mDeviceConfigFacade;
     private boolean mIsWifiServiceStarted = false;
     private static final String PACKAGE_NAME_NOT_AVAILABLE = "Not Available";
+    private static final String CERT_INSTALLER_PKG = "com.android.certinstaller";
+
+    private final WifiSettingsConfigStore mSettingsConfigStore;
 
     /**
      * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death.
@@ -374,6 +380,7 @@
 
     private WifiNetworkSelectionConfig mNetworkSelectionConfig;
     private ApplicationQosPolicyRequestHandler mApplicationQosPolicyRequestHandler;
+    private final AfcManager mAfcManager;
 
     /**
      * The wrapper of SoftApCallback is used in WifiService internally.
@@ -496,6 +503,7 @@
         mWifiInjector = wifiInjector;
         mClock = wifiInjector.getClock();
 
+        mSettingsConfigStore = mWifiInjector.getSettingsConfigStore();
         mFacade = mWifiInjector.getFrameworkFacade();
         mWifiMetrics = mWifiInjector.getWifiMetrics();
         mWifiTrafficPoller = mWifiInjector.getWifiTrafficPoller();
@@ -551,6 +559,7 @@
         mDeviceConfigFacade = mWifiInjector.getDeviceConfigFacade();
         mApplicationQosPolicyRequestHandler = mWifiInjector.getApplicationQosPolicyRequestHandler();
         mWifiPulledAtomLogger = mWifiInjector.getWifiPulledAtomLogger();
+        mAfcManager = mWifiInjector.getAfcManager();
     }
 
     /**
@@ -566,7 +575,7 @@
             mWifiConfigManager.incrementNumRebootsSinceLastUse();
             // config store is read, check if verbose logging is enabled.
             enableVerboseLoggingInternal(
-                    mWifiInjector.getSettingsConfigStore().get(WIFI_VERBOSE_LOGGING_ENABLED)
+                    mSettingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED)
                             ? WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED
                             : WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED);
             // Check if wi-fi needs to be enabled
@@ -576,7 +585,8 @@
 
             mWifiInjector.getWifiScanAlwaysAvailableSettingsCompatibility().initialize();
             mWifiInjector.getWifiNotificationManager().createNotificationChannels();
-            mWifiMetrics.start();
+            // Align the value between config stroe (i.e.WifiConfigStore.xml) and WifiGlobals.
+            mWifiGlobals.setWepAllowed(mSettingsConfigStore.get(WIFI_WEP_ALLOWED));
             mContext.registerReceiver(
                     new BroadcastReceiver() {
                         @Override
@@ -695,12 +705,16 @@
             mActiveModeWarden.start();
             registerForCarrierConfigChange();
             mWifiInjector.getAdaptiveConnectivityEnabledSettingObserver().initialize();
+            mWifiInjector.getWifiDeviceStateChangeManager().handleBootCompleted();
             mIsWifiServiceStarted = true;
         });
     }
 
     private void setPulledAtomCallbacks() {
         mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_MODULE_INFO);
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_SETTING_INFO);
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO);
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO);
     }
 
     private void updateLocationMode() {
@@ -708,7 +722,6 @@
         mWifiConnectivityManager.setLocationModeEnabled(mIsLocationModeEnabled);
     }
 
-
     /**
      * Find which user restrictions have changed and take corresponding actions
      */
@@ -960,12 +973,13 @@
             boolean idle = mPowerManager.isDeviceIdleMode();
             if (mInIdleMode != idle) {
                 mInIdleMode = idle;
-                if (!idle) {
+                if (!idle) { // exiting doze mode
                     if (mScanPending) {
                         mScanPending = false;
                         doScan = true;
                     }
                 }
+                mActiveModeWarden.onIdleModeChanged(idle);
             }
         }
         if (doScan) {
@@ -987,6 +1001,7 @@
         // before memory store write triggered by mMemoryStoreImpl.stop().
         mWifiScoreCard.resetAllConnectionStates();
         mMemoryStoreImpl.stop();
+        mWifiConfigManager.handleShutDown();
     }
 
     private boolean checkNetworkSettingsPermission(int pid, int uid) {
@@ -1242,8 +1257,11 @@
             return false;
         }
 
-        // If SoftAp is enabled, only privileged apps are allowed to toggle wifi
-        if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
+        // Pre-S interface priority is solely based on interface type, which allows STA to delete AP
+        // for any requester. To prevent non-privileged apps from deleting a tethering AP by
+        // enabling Wi-Fi, only allow privileged apps to toggle Wi-Fi if tethering AP is up.
+        if (!SdkLevel.isAtLeastS() && !isPrivileged
+                && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
             mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush();
             return false;
         }
@@ -1360,6 +1378,11 @@
                     }
                 };
         Resources res = mContext.getResources();
+        if (mWifiEnableRequestDialogHandles.get(uid) != null) {
+            mLog.info("setWifiEnabled dialog already launched for package=% uid=%").c(packageName)
+                    .c(uid).flush();
+            return;
+        }
         WifiDialogManager.DialogHandle dialogHandle = mWifiDialogManager.createSimpleDialog(
                 res.getString(R.string.wifi_enable_request_dialog_title, appName),
                 res.getString(R.string.wifi_enable_request_dialog_message),
@@ -1620,35 +1643,6 @@
         mWifiThreadRunner.post(() -> mCoexManager.unregisterRemoteCoexCallback(callback));
     }
 
-    private Runnable mRecoverSoftApStateIfNeeded = new Runnable() {
-        @Override
-        public void run() {
-            mTetheredSoftApTracker.setFailedWhileEnabling();
-        }
-    };
-
-    private boolean checkSetEnablingIfAllowed() {
-        Boolean resultSetEnablingIfAllowed = mWifiThreadRunner.call(() -> {
-            if (mWifiThreadRunner.hasCallbacks(mRecoverSoftApStateIfNeeded)) {
-                Log.i(TAG, "An error happened, state is recovering, reject more requests");
-                return false;
-            }
-            return mTetheredSoftApTracker.setEnablingIfAllowed();
-        }, null);
-
-        if (resultSetEnablingIfAllowed == null) {
-            Log.i(TAG, "Timeout happened ! Recover SAP state if needed");
-            mWifiThreadRunner.removeCallbacks(mRecoverSoftApStateIfNeeded);
-            mWifiThreadRunner.post(mRecoverSoftApStateIfNeeded);
-            return false;
-        }
-
-        if (!resultSetEnablingIfAllowed) {
-            mLog.err("Tethering is already active or in recovering.").flush();
-        }
-        return resultSetEnablingIfAllowed;
-    }
-
     /**
      * see {@link android.net.wifi.WifiManager#startSoftAp(WifiConfiguration)}
      * @param wifiConfig SSID, security and channel details as part of WifiConfiguration
@@ -1672,16 +1666,20 @@
             }
         }
 
-        // TODO: b/233363886, handle timeout in general way.
-        if (!checkSetEnablingIfAllowed()) {
+        if (!mTetheredSoftApTracker.setEnablingIfAllowed()) {
+            mLog.err("Tethering is already active or activating.").flush();
             return false;
         }
 
         WorkSource requestorWs = new WorkSource(callingUid, packageName);
-        if (!mWifiThreadRunner.call(
-                () -> mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs), false)) {
-            // Take down LOHS if it is up.
-            mLohsSoftApTracker.stopAll();
+        long id = Binder.clearCallingIdentity();
+        try {
+            if (!mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs)) {
+                // Take down LOHS if it is up.
+                mLohsSoftApTracker.stopAll();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(id);
         }
 
         if (!startSoftApInternal(new SoftApModeConfiguration(
@@ -1718,16 +1716,20 @@
 
         mLog.info("startTetheredHotspot uid=%").c(callingUid).flush();
 
-        // TODO: b/233363886, handle timeout in general way.
-        if (!checkSetEnablingIfAllowed()) {
+        if (!mTetheredSoftApTracker.setEnablingIfAllowed()) {
+            mLog.err("Tethering is already active or activating.").flush();
             return false;
         }
 
         WorkSource requestorWs = new WorkSource(callingUid, packageName);
-        if (!mWifiThreadRunner.call(
-                () -> mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs), false)) {
-            // Take down LOHS if it is up.
-            mLohsSoftApTracker.stopAll();
+        long id = Binder.clearCallingIdentity();
+        try {
+            if (!mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs)) {
+                // Take down LOHS if it is up.
+                mLohsSoftApTracker.stopAll();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(id);
         }
 
         if (!startSoftApInternal(new SoftApModeConfiguration(
@@ -1882,9 +1884,9 @@
                             mLohsSoftApTracker.getSoftApCapability(),
                             WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
                     // Store Soft AP channels for reference after a reboot before the driver is up.
-                    WifiSettingsConfigStore configStore = mWifiInjector.getSettingsConfigStore();
                     Resources res = mContext.getResources();
-                    configStore.put(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE, countryCode);
+                    mSettingsConfigStore.put(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE,
+                            countryCode);
                     List<Integer> freqs = new ArrayList<>();
                     for (int band : SoftApConfiguration.BAND_TYPES) {
                         List<Integer> freqsForBand = ApConfigUtil.getAvailableChannelFreqsForBand(
@@ -1893,7 +1895,8 @@
                             freqs.addAll(freqsForBand);
                         }
                     }
-                    configStore.put(WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ,
+                    mSettingsConfigStore.put(
+                            WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ,
                             new JSONArray(freqs).toString());
                 }
                 if (SdkLevel.isAtLeastT()) {
@@ -1922,6 +1925,7 @@
                     }
                     mRegisteredDriverCountryCodeListeners.finishBroadcast();
                 }
+                mAfcManager.onCountryCodeChange(countryCode);
             });
         }
     }
@@ -2021,6 +2025,8 @@
             synchronized (mLock) {
                 if (mSoftApCapability == null) {
                     mSoftApCapability = ApConfigUtil.updateCapabilityFromResource(mContext);
+                    mSoftApCapability = ApConfigUtil.updateCapabilityFromConfigStore(
+                            mSoftApCapability, mWifiInjector.getSettingsConfigStore());
                     // Default country code
                     mSoftApCapability = updateSoftApCapabilityWithAvailableChannelList(
                             mSoftApCapability, mCountryCode.getCountryCode());
@@ -2407,6 +2413,11 @@
          */
         @GuardedBy("mLocalOnlyHotspotRequests")
         private void sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked() {
+            if (mActiveConfig == null) {
+                Log.e(TAG, "lohs.sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked "
+                        + "mActiveConfig is null");
+                return;
+            }
             for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) {
                 try {
                     requestor.sendHotspotStartedMessage(mActiveConfig.getSoftApConfiguration());
@@ -2628,6 +2639,7 @@
         mLastCallerInfoManager.put(WifiManager.API_START_LOCAL_ONLY_HOTSPOT, Process.myTid(),
                 uid, Binder.getCallingPid(), packageName, true);
 
+        final WorkSource requestorWs = new WorkSource(uid, packageName);
         // the app should be in the foreground
         long ident = Binder.clearCallingIdentity();
         try {
@@ -2637,18 +2649,17 @@
                     && !mFrameworkFacade.isAppForeground(mContext, uid)) {
                 return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
             }
+            // check if we are currently tethering
+            if (!mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs)
+                    && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
+                // Tethering is enabled, cannot start LocalOnlyHotspot
+                mLog.info("Cannot start localOnlyHotspot when WiFi Tethering is active.")
+                        .flush();
+                return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        // check if we are currently tethering
-        final WorkSource requestorWs = new WorkSource(uid, packageName);
-        if (!mActiveModeWarden.canRequestMoreSoftApManagers(requestorWs)
-                && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
-            // Tethering is enabled, cannot start LocalOnlyHotspot
-            mLog.info("Cannot start localOnlyHotspot when WiFi Tethering is active.")
-                    .flush();
-            return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
-        }
 
         // now create the new LOHS request info object
         LocalOnlyHotspotRequestInfo request = new LocalOnlyHotspotRequestInfo(
@@ -3050,35 +3061,35 @@
 
     @Override
     public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) {
+        enforceAccessPermission();
         if (mVerboseLoggingEnabled) {
             mLog.info("getWifiActivityEnergyInfoAsync uid=%")
                     .c(Binder.getCallingUid())
                     .flush();
         }
-        // getWifiActivityEnergyInfo() performs permission checking
-        WifiActivityEnergyInfo info = getWifiActivityEnergyInfo();
-        try {
-            listener.onWifiActivityEnergyInfo(info);
-        } catch (RemoteException e) {
-            Log.e(TAG, "onWifiActivityEnergyInfo: RemoteException -- ", e);
+        if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) {
+            try {
+                listener.onWifiActivityEnergyInfo(null);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onWifiActivityEnergyInfo: RemoteException -- ", e);
+            }
+            return;
         }
+        mWifiThreadRunner.post(() -> {
+            try {
+                listener.onWifiActivityEnergyInfo(getWifiActivityEnergyInfo());
+            } catch (RemoteException e) {
+                Log.e(TAG, "onWifiActivityEnergyInfo: RemoteException -- ", e);
+            }
+        });
     }
 
     private WifiActivityEnergyInfo getWifiActivityEnergyInfo() {
-        enforceAccessPermission();
-        if (mVerboseLoggingEnabled) {
-            mLog.info("getWifiActivityEnergyInfo uid=%").c(Binder.getCallingUid()).flush();
-        }
-        if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) {
-            return null;
-        }
-        WifiLinkLayerStats stats = mWifiThreadRunner.call(
-                () -> mActiveModeWarden.getPrimaryClientModeManager().getWifiLinkLayerStats(),
-                null);
+        WifiLinkLayerStats stats = mActiveModeWarden
+                .getPrimaryClientModeManager().getWifiLinkLayerStats();
         if (stats == null) {
             return null;
         }
-
         final long rxIdleTimeMillis = stats.on_time - stats.tx_time - stats.rx_time;
         if (VDBG || rxIdleTimeMillis < 0 || stats.on_time < 0 || stats.tx_time < 0
                 || stats.rx_time < 0 || stats.on_time_scan < 0) {
@@ -3089,7 +3100,6 @@
                     + " rxIdleTimeMillis=" + rxIdleTimeMillis
                     + " scan_time_millis=" + stats.on_time_scan);
         }
-
         // Convert the LinkLayerStats into WifiActivityEnergyInfo
         return new WifiActivityEnergyInfo(
                 mClock.getElapsedSinceBootMillis(),
@@ -3370,10 +3380,8 @@
                     + "to display when third party apps start wifi");
         }
         mLog.info("uid=% enableWarningDialog=%").c(uid).c(enable).flush();
-        mWifiInjector.getSettingsConfigStore().put(
-                SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI, enable);
-        mWifiInjector.getSettingsConfigStore().put(
-                SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API, true);
+        mSettingsConfigStore.put(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI, enable);
+        mSettingsConfigStore.put(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API, true);
         mLastCallerInfoManager.put(
                 WifiManager.API_SET_THIRD_PARTY_APPS_ENABLING_WIFI_CONFIRMATION_DIALOG,
                 Process.myTid(),
@@ -3381,11 +3389,9 @@
     }
 
     private boolean showDialogWhenThirdPartyAppsEnableWifi() {
-        if (mWifiInjector.getSettingsConfigStore().get(
-                SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)) {
+        if (mSettingsConfigStore.get(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)) {
             // API was called to override the overlay value.
-            return mWifiInjector.getSettingsConfigStore().get(
-                    SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI);
+            return mSettingsConfigStore.get(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI);
         } else {
             return mContext.getResources().getBoolean(
                     R.bool.config_showConfirmationDialogForThirdPartyAppsEnablingWifi);
@@ -4285,12 +4291,24 @@
             mLog.info("getConnectionInfo uid=%").c(uid).flush();
         }
         mWifiPermissionsUtil.checkPackage(uid, callingPackage);
+        if (mActiveModeWarden.getWifiState() != WIFI_STATE_ENABLED) {
+            return new WifiInfo();
+        }
+        WifiInfo wifiInfo;
+        if (isCurrentRequestWsContainsCaller(uid, callingPackage)) {
+            wifiInfo =
+                    mWifiThreadRunner.call(
+                            () ->
+                                    getClientModeManagerIfSecondaryCmmRequestedByCallerPresent(
+                                                    uid, callingPackage)
+                                            .getConnectionInfo(),
+                            new WifiInfo());
+        } else {
+            // If no caller
+            wifiInfo = mActiveModeWarden.getConnectionInfo();
+        }
         long ident = Binder.clearCallingIdentity();
         try {
-            WifiInfo wifiInfo = mWifiThreadRunner.call(
-                    () -> getClientModeManagerIfSecondaryCmmRequestedByCallerPresent(
-                            uid, callingPackage)
-                            .getConnectionInfo(), new WifiInfo());
             long redactions = wifiInfo.getApplicableRedactions();
             if (mWifiPermissionsUtil.checkLocalMacAddressPermission(uid)) {
                 if (mVerboseLoggingEnabled) {
@@ -4326,6 +4344,23 @@
         }
     }
 
+    private boolean isCurrentRequestWsContainsCaller(int uid, String callingPackage) {
+        Set<WorkSource> requestWs = mActiveModeWarden.getSecondaryRequestWs();
+        for (WorkSource ws : requestWs) {
+            WorkSource reqWs = new WorkSource(ws);
+            if (reqWs.size() > 1) {
+                // Remove promoted settings WorkSource if present
+                reqWs.remove(mFrameworkFacade.getSettingsWorkSource(mContext));
+            }
+            WorkSource withCaller = new WorkSource(reqWs);
+            withCaller.add(new WorkSource(uid, callingPackage));
+            if (reqWs.equals(withCaller)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Return the results of the most recent access point scan, in the form of
      * a list of {@link ScanResult} objects.
@@ -4344,6 +4379,9 @@
                     uid, null);
             List<ScanResult> scanResults = mWifiThreadRunner.call(
                     mScanRequestProxy::getScanResults, Collections.emptyList());
+            if (scanResults.size() > 200) {
+                Log.i(TAG, "too many scan results, may break binder transaction");
+            }
             return scanResults;
         } catch (SecurityException e) {
             Log.w(TAG, "Permission violation - getScanResults not allowed for uid="
@@ -4470,8 +4508,21 @@
         }
         mLog.info("addorUpdatePasspointConfiguration uid=%").c(callingUid).flush();
         return mWifiThreadRunner.call(
-                () -> mPasspointManager.addOrUpdateProvider(config, callingUid, packageName,
-                        false, true, false), false);
+                () -> {
+                    boolean success = mPasspointManager.addOrUpdateProvider(config, callingUid,
+                            packageName, false, true, false);
+                    if (success && TextUtils.equals(CERT_INSTALLER_PKG, packageName)) {
+                        int networkId = mActiveModeWarden.getConnectionInfo().getNetworkId();
+                        WifiConfiguration currentConfig =
+                                mWifiConfigManager.getConfiguredNetworkWithPassword(networkId);
+                        if (currentConfig != null
+                                && !currentConfig.getNetworkSelectionStatus()
+                                    .hasNeverDetectedCaptivePortal()) {
+                            mActiveModeWarden.getPrimaryClientModeManager().disconnect();
+                        }
+                    }
+                    return success;
+                }, false);
     }
 
     /**
@@ -5141,6 +5192,11 @@
                         } catch (PackageManager.NameNotFoundException e) {
                             Log.w(TAG, "Couldn't get PackageInfo for package:" + pkgName);
                         }
+                        // If app is updating or replacing, just ignore
+                        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
+                                && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                            return;
+                        }
                         // If package is not removed or disabled, just ignore.
                         if (packageInfo != null
                                 && packageInfo.applicationInfo != null
@@ -5198,13 +5254,11 @@
                 err.getFileDescriptor(), args);
     }
 
+
     private void updateWifiMetrics() {
-        mWifiThreadRunner.run(() -> {
-            mWifiMetrics.updateSavedNetworks(
-                    mWifiConfigManager.getSavedNetworks(WIFI_UID));
-            mActiveModeWarden.updateMetrics();
-            mPasspointManager.updateMetrics();
-        });
+        mWifiMetrics.updateSavedNetworks(mWifiConfigManager.getSavedNetworks(WIFI_UID));
+        mActiveModeWarden.updateMetrics();
+        mPasspointManager.updateMetrics();
         boolean isNonPersistentMacRandEnabled = mFrameworkFacade.getIntegerSetting(mContext,
                 WifiConfigManager.NON_PERSISTENT_MAC_RANDOMIZATION_FEATURE_FORCE_ENABLE_FLAG, 0)
                 == 1 ? true : false;
@@ -5282,7 +5336,7 @@
                 pw.println();
                 pw.println("ScoringParams: " + mWifiInjector.getScoringParams());
                 pw.println();
-                mWifiInjector.getSettingsConfigStore().dump(fd, pw, args);
+                mSettingsConfigStore.dump(fd, pw, args);
                 pw.println();
                 mCountryCode.dump(fd, pw, args);
                 mWifiInjector.getWifiNetworkFactory().dump(fd, pw, args);
@@ -5296,6 +5350,7 @@
                 mWifiConfigManager.dump(fd, pw, args);
                 pw.println();
                 mPasspointManager.dump(pw);
+                mWifiInjector.getPasspointNetworkNominateHelper().dump(pw);
                 pw.println();
                 mWifiInjector.getWifiDiagnostics().captureBugReportData(
                         WifiDiagnostics.REPORT_REASON_USER_ACTION);
@@ -5319,17 +5374,22 @@
     }
 
     @Override
-    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
-        mLog.info("acquireWifiLock uid=% lockMode=%")
+    public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws,
+            @NonNull String packageName, Bundle extras) {
+        mLog.info("acquireWifiLock uid=% lockMode=% packageName=%")
                 .c(Binder.getCallingUid())
-                .c(lockMode).flush();
+                .c(lockMode).c(getPackageName(extras)).flush();
+
+        if (packageName == null) {
+            throw new NullPointerException("Package name should not be null");
+        }
 
         // Check on permission to make this call
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
 
         // If no UID is provided in worksource, use the calling UID
         WorkSource updatedWs = (ws == null || ws.isEmpty())
-                ? new WorkSource(Binder.getCallingUid()) : ws;
+                ? new WorkSource(Binder.getCallingUid(), packageName) : ws;
 
         if (!WifiLockManager.isValidLockMode(lockMode)) {
             throw new IllegalArgumentException("lockMode =" + lockMode);
@@ -5340,8 +5400,11 @@
     }
 
     @Override
-    public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) {
-        mLog.info("updateWifiLockWorkSource uid=%").c(Binder.getCallingUid()).flush();
+    public void updateWifiLockWorkSource(IBinder binder, WorkSource ws, String packageName,
+            Bundle extras) {
+        mLog.info("updateWifiLockWorkSource uid=% package name=%")
+                .c(Binder.getCallingUid())
+                .c(getPackageName(extras)).flush();
 
         // Check on permission to make this call
         mContext.enforceCallingOrSelfPermission(
@@ -5349,7 +5412,7 @@
 
         // If no UID is provided in worksource, use the calling UID
         WorkSource updatedWs = (ws == null || ws.isEmpty())
-                ? new WorkSource(Binder.getCallingUid()) : ws;
+                ? new WorkSource(Binder.getCallingUid(), packageName) : ws;
 
         mWifiThreadRunner.run(() ->
                 mWifiLockManager.updateWifiLockWorkSource(binder, updatedWs));
@@ -5376,14 +5439,14 @@
     @Override
     public void acquireMulticastLock(IBinder binder, String tag) {
         enforceMulticastChangePermission();
-        mLog.info("acquireMulticastLock uid=%").c(Binder.getCallingUid()).flush();
+        mLog.info("acquireMulticastLock uid=% tag=%").c(Binder.getCallingUid()).c(tag).flush();
         mWifiMulticastLockManager.acquireLock(binder, tag);
     }
 
     @Override
     public void releaseMulticastLock(String tag) {
         enforceMulticastChangePermission();
-        mLog.info("releaseMulticastLock uid=%").c(Binder.getCallingUid()).flush();
+        mLog.info("releaseMulticastLock uid=% tag=%").c(Binder.getCallingUid()).c(tag).flush();
         mWifiMulticastLockManager.releaseLock(tag);
     }
 
@@ -5399,40 +5462,54 @@
     @Override
     public void enableVerboseLogging(int verbose) {
         enforceAccessPermission();
-        enforceNetworkSettingsPermission();
+        if (!checkNetworkSettingsPermission(Binder.getCallingPid(), Binder.getCallingUid())
+                && mContext.checkPermission(android.Manifest.permission.DUMP,
+                Binder.getCallingPid(), Binder.getCallingUid()) != PERMISSION_GRANTED) {
+            throw new SecurityException("Caller has neither NETWORK_SETTING nor dump permissions");
+        }
         mLog.info("enableVerboseLogging uid=% verbose=%")
                 .c(Binder.getCallingUid())
                 .c(verbose).flush();
-        boolean enabled = verbose > 0;
-        mWifiInjector.getSettingsConfigStore().put(WIFI_VERBOSE_LOGGING_ENABLED, enabled);
+        boolean enabled = verbose == WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED
+                || verbose == WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY;
+        mSettingsConfigStore.put(WIFI_VERBOSE_LOGGING_ENABLED, enabled);
+        mSettingsConfigStore.put(
+                WIFI_AWARE_VERBOSE_LOGGING_ENABLED,
+                enabled || verbose == VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY);
         onVerboseLoggingStatusChanged(enabled);
         enableVerboseLoggingInternal(verbose);
     }
 
     private void onVerboseLoggingStatusChanged(boolean enabled) {
-        int itemCount = mRegisteredWifiLoggingStatusListeners.beginBroadcast();
-        for (int i = 0; i < itemCount; i++) {
+        synchronized (mRegisteredWifiLoggingStatusListeners) {
+            int itemCount = mRegisteredWifiLoggingStatusListeners.beginBroadcast();
             try {
-                mRegisteredWifiLoggingStatusListeners.getBroadcastItem(i)
-                        .onStatusChanged(enabled);
-            } catch (RemoteException e) {
-                Log.e(TAG, "onVerboseLoggingStatusChanged: RemoteException -- ", e);
+                for (int i = 0; i < itemCount; i++) {
+                    try {
+                        mRegisteredWifiLoggingStatusListeners
+                                .getBroadcastItem(i)
+                                .onStatusChanged(enabled);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "onVerboseLoggingStatusChanged: RemoteException -- ", e);
+                    }
+                }
+            } finally {
+                mRegisteredWifiLoggingStatusListeners.finishBroadcast();
             }
-
         }
-        mRegisteredWifiLoggingStatusListeners.finishBroadcast();
     }
 
     private void updateVerboseLoggingEnabled() {
         final int verboseAlwaysOnLevel = mContext.getResources().getInteger(
                 R.integer.config_wifiVerboseLoggingAlwaysOnLevel);
-        mVerboseLoggingEnabled = mFrameworkFacade.isVerboseLoggingAlwaysOn(verboseAlwaysOnLevel,
-                mBuildProperties)
-                || WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED != mVerboseLoggingLevel;
+        mVerboseLoggingEnabled = WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED == mVerboseLoggingLevel
+                || WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY == mVerboseLoggingLevel
+                || mFrameworkFacade.isVerboseLoggingAlwaysOn(verboseAlwaysOnLevel,
+                mBuildProperties);
     }
 
     private void enableVerboseLoggingInternal(int verboseLoggingLevel) {
-        if (verboseLoggingLevel > WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED
+        if (verboseLoggingLevel == WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY
                 && mBuildProperties.isUserBuild()) {
             throw new SecurityException(TAG + ": Not allowed for the user build.");
         }
@@ -5440,22 +5517,23 @@
 
         // Update wifi globals before sending the verbose logging change.
         mWifiThreadRunner.removeCallbacks(mAutoDisableShowKeyVerboseLoggingModeRunnable);
+        mWifiGlobals.setVerboseLoggingLevel(verboseLoggingLevel);
         if (WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY == mVerboseLoggingLevel) {
-            mWifiGlobals.setShowKeyVerboseLoggingModeEnabled(true);
             mWifiThreadRunner.postDelayed(mAutoDisableShowKeyVerboseLoggingModeRunnable,
                     AUTO_DISABLE_SHOW_KEY_COUNTDOWN_MILLIS);
-        } else {
-            // Ensure the show key mode is disabled.
-            mWifiGlobals.setShowKeyVerboseLoggingModeEnabled(false);
         }
         updateVerboseLoggingEnabled();
         final boolean halVerboseEnabled =
-                WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED != mVerboseLoggingLevel;
+                WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED == mVerboseLoggingLevel
+                        || WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY
+                        == mVerboseLoggingLevel;
+        mAfcManager.enableVerboseLogging(mVerboseLoggingEnabled);
         mActiveModeWarden.enableVerboseLogging(mVerboseLoggingEnabled);
         mWifiLockManager.enableVerboseLogging(mVerboseLoggingEnabled);
         mWifiMulticastLockManager.enableVerboseLogging(mVerboseLoggingEnabled);
         mWifiInjector.enableVerboseLogging(mVerboseLoggingEnabled, halVerboseEnabled);
         mWifiInjector.getSarManager().enableVerboseLogging(mVerboseLoggingEnabled);
+        mWifiThreadRunner.mVerboseLoggingEnabled = mVerboseLoggingEnabled;
         WifiScanner wifiScanner = mWifiInjector.getWifiScanner();
         if (wifiScanner != null) {
             wifiScanner.enableVerboseLogging(mVerboseLoggingEnabled);
@@ -5528,19 +5606,21 @@
         for (PasspointConfiguration config : configs) {
             removePasspointConfigurationInternal(null, config.getUniqueId());
         }
-        mWifiThreadRunner.post(() -> {
-            // Reset SoftApConfiguration to default configuration
-            mWifiApConfigStore.setApConfiguration(null);
-            mPasspointManager.clearAnqpRequestsAndFlushCache();
-            mWifiConfigManager.clearUserTemporarilyDisabledList();
-            mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks();
-            mWifiInjector.getWifiNetworkFactory().clear();
-            mWifiNetworkSuggestionsManager.clear();
-            mWifiInjector.getWifiScoreCard().clear();
-            mWifiHealthMonitor.clear();
-            mWifiCarrierInfoManager.clear();
-            notifyFactoryReset();
-        });
+        mWifiThreadRunner.post(
+                () -> {
+                    // Reset SoftApConfiguration to default configuration
+                    mWifiApConfigStore.setApConfiguration(null);
+                    mPasspointManager.clearAnqpRequestsAndFlushCache();
+                    mWifiConfigManager.clearUserTemporarilyDisabledList();
+                    mWifiConfigManager.removeAllEphemeralOrPasspointConfiguredNetworks();
+                    mWifiInjector.getWifiNetworkFactory().clear();
+                    mWifiNetworkSuggestionsManager.clear();
+                    mWifiInjector.getWifiScoreCard().clear();
+                    mWifiHealthMonitor.clear();
+                    mWifiCarrierInfoManager.clear();
+                    notifyFactoryReset();
+                    mContext.resetResourceCache();
+                });
     }
 
     /**
@@ -5669,10 +5749,18 @@
         }
         int callingUid = Binder.getCallingUid();
         if (configurations.isEmpty()) return;
-        final int batchNum = mContext.getResources().getInteger(
-                    R.integer.config_wifiConfigurationRestoreNetworksBatchNum);
-        mWifiThreadRunner.run(new NetworkUpdater(callingUid, configurations, 0,
-                batchNum > 0 ? batchNum : configurations.size()));
+        final int batchNum =
+                mDeviceConfigFacade.getFeatureFlags().delaySaveToStore()
+                        ? 0
+                        : mContext.getResources()
+                                .getInteger(
+                                        R.integer.config_wifiConfigurationRestoreNetworksBatchNum);
+        mWifiThreadRunner.post(
+                new NetworkUpdater(
+                        callingUid,
+                        configurations,
+                        0,
+                        batchNum > 0 ? batchNum : configurations.size()));
     }
 
     /**
@@ -5726,14 +5814,14 @@
         return softApConfig;
     }
 
-
     /**
-     * Restore state from the older supplicant back up data.
-     * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file.
+     * Restore state from the older supplicant back up data. The old backup data was essentially a
+     * backup of wpa_supplicant.conf & ipconfig.txt file.
      *
      * @param supplicantData Raw byte stream of wpa_supplicant.conf
      * @param ipConfigData Raw byte stream of ipconfig.txt
      */
+    @Override
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         enforceNetworkSettingsPermission();
         mLog.trace("restoreSupplicantBackupData uid=%").c(Binder.getCallingUid()).flush();
@@ -5974,6 +6062,11 @@
             throw new SecurityException("App not allowed to get Wi-Fi factory MAC address "
                     + "(uid = " + uid + ")");
         }
+        // Check the ConfigStore cache first
+        if (mWifiGlobals.isSaveFactoryMacToConfigStoreEnabled()) {
+            String factoryMacAddressStr = mSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS);
+            if (factoryMacAddressStr != null) return new String[] {factoryMacAddressStr};
+        }
         String result = mWifiThreadRunner.call(
                 () -> mActiveModeWarden.getPrimaryClientModeManager().getFactoryMacAddress(),
                 null);
@@ -6187,9 +6280,7 @@
             throw new IllegalArgumentException("Listener must not be null");
         }
         enforceAccessPermission();
-        // Post operation to handler thread
-        mWifiThreadRunner.post(() ->
-                mRegisteredWifiLoggingStatusListeners.register(listener));
+        mRegisteredWifiLoggingStatusListeners.register(listener);
     }
 
     /**
@@ -6208,9 +6299,7 @@
             throw new IllegalArgumentException("Listener must not be null");
         }
         enforceAccessPermission();
-        // Post operation to handler thread
-        mWifiThreadRunner.post(() ->
-                mRegisteredWifiLoggingStatusListeners.unregister(listener));
+        mRegisteredWifiLoggingStatusListeners.unregister(listener);
     }
 
     /**
@@ -6341,109 +6430,130 @@
         mLastCallerInfoManager.put(config != null
                         ? WifiManager.API_CONNECT_CONFIG : WifiManager.API_CONNECT_NETWORK_ID,
                 Process.myTid(), uid, Binder.getCallingPid(), packageName, true);
-        mWifiThreadRunner.post(() -> {
-            ActionListenerWrapper wrapper = new ActionListenerWrapper(callback);
-            final NetworkUpdateResult result;
-            // if connecting using WifiConfiguration, save the network first
-            if (config != null) {
-                if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
-                    mWifiMetrics.logUserActionEvent(
-                            UserActionEvent.EVENT_ADD_OR_UPDATE_NETWORK, config.networkId);
-                }
-                result = mWifiConfigManager.addOrUpdateNetwork(config, uid);
-                if (!result.isSuccess()) {
-                    Log.e(TAG, "connect adding/updating config=" + config + " failed");
-                    wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                    return;
-                }
-                broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
-            } else {
-                if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
-                    mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_MANUAL_CONNECT, netId);
-                }
-                result = new NetworkUpdateResult(netId);
-            }
-            WifiConfiguration configuration = mWifiConfigManager
-                    .getConfiguredNetwork(result.getNetworkId());
-            if (configuration == null) {
-                Log.e(TAG, "connect to Invalid network Id=" + netId);
-                wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                return;
-            }
-            if (mWifiPermissionsUtil.isAdminRestrictedNetwork(configuration)) {
-                Log.e(TAG, "connect to network Id=" + netId + "restricted by admin");
-                wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                return;
-            }
-            if (mWifiGlobals.isDeprecatedSecurityTypeNetwork(configuration)) {
-                Log.e(TAG, "connect to network Id=" + netId + " security type deprecated.");
-                wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                return;
-            }
-            if (configuration.enterpriseConfig != null
-                    && configuration.enterpriseConfig.isAuthenticationSimBased()) {
-                int subId = mWifiCarrierInfoManager.getBestMatchSubscriptionId(configuration);
-                if (!mWifiCarrierInfoManager.isSimReady(subId)) {
-                    Log.e(TAG, "connect to SIM-based config=" + configuration
-                            + "while SIM is absent");
-                    wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                    return;
-                }
-                if (mWifiCarrierInfoManager.requiresImsiEncryption(subId)
-                        && !mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(subId)) {
-                    Log.e(TAG, "Imsi protection required but not available for Network="
-                            + configuration);
-                    wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
-                    return;
-                }
-                if (mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(configuration.carrierId)) {
-                    Optional<PseudonymInfo> pseudonymInfo =
-                            mWifiPseudonymManager.getValidPseudonymInfo(configuration.carrierId);
-                    if (pseudonymInfo.isEmpty()) {
-                        Log.e(TAG, "There isn't any valid pseudonym to update the Network="
-                                + configuration);
-                        mWifiPseudonymManager.retrievePseudonymOnFailureTimeoutExpired(
-                                configuration);
-                        // TODO(b/274148786): new error code and UX for this failure.
+        mWifiThreadRunner.post(
+                () -> {
+                    ActionListenerWrapper wrapper = new ActionListenerWrapper(callback);
+                    final NetworkUpdateResult result;
+                    // if connecting using WifiConfiguration, save the network first
+                    if (config != null) {
+                        if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
+                            mWifiMetrics.logUserActionEvent(
+                                    UserActionEvent.EVENT_ADD_OR_UPDATE_NETWORK, config.networkId);
+                        }
+                        result = mWifiConfigManager.addOrUpdateNetwork(config, uid);
+                        if (!result.isSuccess()) {
+                            Log.e(TAG, "connect adding/updating config=" + config + " failed");
+                            wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                            return;
+                        }
+                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+                    } else {
+                        if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
+                            mWifiMetrics.logUserActionEvent(
+                                    UserActionEvent.EVENT_MANUAL_CONNECT, netId);
+                        }
+                        result = new NetworkUpdateResult(netId);
+                    }
+                    WifiConfiguration configuration =
+                            mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+                    if (configuration == null) {
+                        Log.e(TAG, "connect to Invalid network Id=" + netId);
                         wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
                         return;
-                    } else {
-                        mWifiPseudonymManager.updateWifiConfiguration(
-                                configuration);
                     }
-                }
-            }
+                    if (mWifiPermissionsUtil.isAdminRestrictedNetwork(configuration)) {
+                        Log.e(TAG, "connect to network Id=" + netId + "restricted by admin");
+                        wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                        return;
+                    }
+                    if (mWifiGlobals.isDeprecatedSecurityTypeNetwork(configuration)) {
+                        Log.e(TAG, "connect to network Id=" + netId + " security type deprecated.");
+                        wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                        return;
+                    }
+                    if (configuration.enterpriseConfig != null
+                            && configuration.enterpriseConfig.isAuthenticationSimBased()) {
+                        int subId =
+                                mWifiCarrierInfoManager.getBestMatchSubscriptionId(configuration);
+                        if (!mWifiCarrierInfoManager.isSimReady(subId)) {
+                            Log.e(
+                                    TAG,
+                                    "connect to SIM-based config="
+                                            + configuration
+                                            + "while SIM is absent");
+                            wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                            return;
+                        }
+                        if (mWifiCarrierInfoManager.requiresImsiEncryption(subId)
+                                && !mWifiCarrierInfoManager.isImsiEncryptionInfoAvailable(subId)) {
+                            Log.e(
+                                    TAG,
+                                    "Imsi protection required but not available for Network="
+                                            + configuration);
+                            wrapper.sendFailure(WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                            return;
+                        }
+                        if (mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(
+                                configuration.carrierId)) {
+                            Optional<PseudonymInfo> pseudonymInfo =
+                                    mWifiPseudonymManager.getValidPseudonymInfo(
+                                            configuration.carrierId);
+                            if (pseudonymInfo.isEmpty()) {
+                                Log.e(
+                                        TAG,
+                                        "There isn't any valid pseudonym to update the Network="
+                                                + configuration);
+                                mWifiPseudonymManager.retrievePseudonymOnFailureTimeoutExpired(
+                                        configuration);
+                                // TODO(b/274148786): new error code and UX for this failure.
+                                wrapper.sendFailure(
+                                        WifiManager.ActionListener.FAILURE_INTERNAL_ERROR);
+                                return;
+                            } else {
+                                mWifiPseudonymManager.updateWifiConfiguration(configuration);
+                            }
+                        }
+                    }
 
-            // Tear down secondary CMMs that are already connected to the same network to make
-            // sure the user's manual connection succeeds.
-            ScanResultMatchInfo targetMatchInfo =
-                    ScanResultMatchInfo.fromWifiConfiguration(configuration);
-            for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) {
-                if (!cmm.isConnected()) {
-                    continue;
-                }
-                ActiveModeManager.ClientRole role = cmm.getRole();
-                if (role == ROLE_CLIENT_LOCAL_ONLY || role == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
-                    WifiConfiguration connectedConfig = cmm.getConnectedWifiConfiguration();
-                    if (connectedConfig == null) {
-                        continue;
+                    // Tear down already connected secondary internet CMMs to avoid MCC.
+                    // Also tear down secondary CMMs that are already connected to the same network
+                    // to make sure the user's manual connection succeeds.
+                    ScanResultMatchInfo targetMatchInfo =
+                            ScanResultMatchInfo.fromWifiConfiguration(configuration);
+                    for (ClientModeManager cmm : mActiveModeWarden.getClientModeManagers()) {
+                        if (!cmm.isConnected()) {
+                            continue;
+                        }
+                        ActiveModeManager.ClientRole role = cmm.getRole();
+                        if (role == ROLE_CLIENT_LOCAL_ONLY
+                                || role == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
+                            WifiConfiguration connectedConfig = cmm.getConnectedWifiConfiguration();
+                            if (connectedConfig == null) {
+                                continue;
+                            }
+                            ScanResultMatchInfo connectedMatchInfo =
+                                    ScanResultMatchInfo.fromWifiConfiguration(connectedConfig);
+                            ConcreteClientModeManager concreteCmm = (ConcreteClientModeManager) cmm;
+                            if (concreteCmm.isSecondaryInternet()
+                                    || targetMatchInfo.matchForNetworkSelection(connectedMatchInfo)
+                                    != null) {
+                                if (mVerboseLoggingEnabled) {
+                                    Log.v(
+                                            TAG,
+                                            "Shutting down client mode manager to satisfy user "
+                                                    + "connection: "
+                                                    + cmm);
+                                }
+                                cmm.stop();
+                            }
+                        }
                     }
-                    ScanResultMatchInfo connectedMatchInfo =
-                            ScanResultMatchInfo.fromWifiConfiguration(connectedConfig);
-                    if (targetMatchInfo.matchForNetworkSelection(connectedMatchInfo) == null) {
-                        continue;
-                    }
-                    if (mVerboseLoggingEnabled) {
-                        Log.v(TAG, "Shutting down client mode manager to satisfy user "
-                                + "connection: " + cmm);
-                    }
-                    cmm.stop();
-                }
-            }
 
-            mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(() ->
-                    mConnectHelper.connectToNetwork(result, wrapper, uid, packageName));
-        });
+                    mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(
+                            () ->
+                                    mConnectHelper.connectToNetwork(
+                                            result, wrapper, uid, packageName));
+                });
     }
 
     /**
@@ -6660,6 +6770,39 @@
     }
 
     /**
+     * See {@link WifiManager#setPnoScanState(int)}
+     */
+    @Override
+    public void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle,
+            String packageName) {
+        int callingUid = Binder.getCallingUid();
+        boolean hasPermission = mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)
+                || mWifiPermissionsUtil.checkNetworkSetupWizardPermission(callingUid);
+        if (!hasPermission && SdkLevel.isAtLeastT()) {
+            // MANAGE_WIFI_NETWORK_SELECTION is a new permission added in T.
+            hasPermission = mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(
+                    callingUid);
+        }
+        if (!hasPermission) {
+            throw new SecurityException("Uid " + callingUid
+                    + " is not allowed to set PNO scan state");
+        }
+
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "setPnoScanEnabled " + Binder.getCallingUid() + " enabled=" + enabled
+                    + ", enablePnoScanAfterWifiToggle=" + enablePnoScanAfterWifiToggle);
+        }
+        mLastCallerInfoManager.put(
+                WifiManager.API_SET_PNO_SCAN_ENABLED,
+                Process.myTid(), Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+                enabled);
+        mWifiThreadRunner.post(() -> {
+            mWifiConnectivityManager.setPnoScanEnabledByFramework(enabled,
+                    enablePnoScanAfterWifiToggle);
+        });
+    }
+
+    /**
      * See {@link WifiManager#setExternalPnoScanRequest(List, int[], Executor,
      * WifiManager.PnoScanResultsCallback)}.
      */
@@ -6783,12 +6926,14 @@
         }
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE, "WifiService");
+        int callingUid = Binder.getCallingUid();
         if (mVerboseLoggingEnabled) {
-            mLog.info("setWifiConnectedNetworkScorer uid=%").c(Binder.getCallingUid()).flush();
+            mLog.info("setWifiConnectedNetworkScorer uid=%").c(callingUid).flush();
         }
         // Post operation to handler thread
         return mWifiThreadRunner.call(
-                () -> mActiveModeWarden.setWifiConnectedNetworkScorer(binder, scorer), false);
+                () -> mActiveModeWarden.setWifiConnectedNetworkScorer(binder, scorer, callingUid),
+                false);
     }
 
     /**
@@ -6994,8 +7139,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.NETWORK_SETTINGS, "WifiService");
         // Post operation to handler thread
-        return mWifiThreadRunner.call(
-                () -> mSettingsStore.handleWifiScoringEnabled(enabled), false);
+        return mSettingsStore.handleWifiScoringEnabled(enabled);
     }
 
     @VisibleForTesting
@@ -7019,8 +7163,10 @@
             @WifiScanner.WifiBand int band) {
         List<Integer> freqs = new ArrayList<>();
         try {
-            JSONArray json = new JSONArray(mWifiInjector.getSettingsConfigStore().get(
-                    WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ));
+            JSONArray json =
+                    new JSONArray(
+                            mSettingsConfigStore.get(
+                                    WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ));
             for (int i = 0; i < json.length(); i++) {
                 freqs.add(json.getInt(i));
             }
@@ -7079,9 +7225,9 @@
         if (mWifiNative.isHalSupported() && !mWifiNative.isHalStarted()
                 && mode == WifiAvailableChannel.OP_MODE_SAP
                 && filter == WifiAvailableChannel.FILTER_REGULATORY
-                && TextUtils.equals(mWifiInjector.getSettingsConfigStore().get(
-                        WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE),
-                mCountryCode.getCountryCode())) {
+                && TextUtils.equals(
+                        mSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE),
+                        mCountryCode.getCountryCode())) {
             List<WifiAvailableChannel> storedChannels = getStoredSoftApAvailableChannels(band);
             if (!storedChannels.isEmpty()) {
                 return storedChannels;
@@ -7863,6 +8009,45 @@
     }
 
     /**
+     * See {@link WifiManager#setWepAllowed(boolean)}.
+     */
+    @Override
+    public void setWepAllowed(boolean isAllowed) {
+        int callingUid = Binder.getCallingUid();
+        if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) {
+            throw new SecurityException("Uid " + callingUid
+                    + " is not allowed to set wifi web allowed by user");
+        }
+        mLog.info("setWepAllowed=% uid=%").c(isAllowed).c(callingUid).flush();
+        mWifiThreadRunner.post(() -> {
+            mSettingsConfigStore.put(WIFI_WEP_ALLOWED, isAllowed);
+            mWifiGlobals.setWepAllowed(isAllowed);
+        });
+    }
+
+    /**
+     * See {@link WifiManager#queryWepAllowed(Executor, Consumer)}
+     */
+    @Override
+    public void queryWepAllowed(@NonNull IBooleanListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener should not be null");
+        }
+        int callingUid = Binder.getCallingUid();
+        if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) {
+            throw new SecurityException("Uid " + callingUid
+                    + " is not allowed to get wifi web allowed by user");
+        }
+        mWifiThreadRunner.post(() -> {
+            try {
+                listener.onResult(mSettingsConfigStore.get(WIFI_WEP_ALLOWED));
+            } catch (RemoteException e) {
+                Log.e(TAG, e.getMessage());
+            }
+        });
+    }
+
+    /**
      * Force Overlay Config for testing
      */
     public boolean forceOverlayConfigValue(String configString, String value, boolean isEnabled) {
diff --git a/service/java/com/android/server/wifi/WifiSettingsConfigStore.java b/service/java/com/android/server/wifi/WifiSettingsConfigStore.java
index 2932893..c49ec13 100644
--- a/service/java/com/android/server/wifi/WifiSettingsConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiSettingsConfigStore.java
@@ -71,13 +71,18 @@
             new Key<>("wifi_scan_throttle_enabled", true);
 
     /**
-     * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
-     * will enable it. In the future, additional values may be supported.
+     * Setting to enable verbose logging in Wi-Fi.
      */
     public static final Key<Boolean> WIFI_VERBOSE_LOGGING_ENABLED =
             new Key<>("wifi_verbose_logging_enabled", false);
 
     /**
+     * Setting to enable verbose logging in Wi-Fi Aware.
+     */
+    public static final Key<Boolean> WIFI_AWARE_VERBOSE_LOGGING_ENABLED =
+            new Key<>("wifi_aware_verbose_logging_enabled", false);
+
+    /**
      * The Wi-Fi peer-to-peer device name
      */
     public static final Key<String> WIFI_P2P_DEVICE_NAME =
@@ -177,6 +182,17 @@
     public static final Key<Integer> SUPPLICANT_HAL_AIDL_SERVICE_VERSION =
             new Key<>("supplicant_hal_aidl_service_version", -1);
 
+    /**
+     * Whether the WEP network is allowed or not.
+     */
+    public static final Key<Boolean> WIFI_WEP_ALLOWED = new Key<>("wep_allowed", true);
+
+    /**
+     * Store wiphy capability for 11be support.
+     */
+    public static final Key<Boolean> WIFI_WIPHY_11BE_SUPPORTED =
+            new Key<>("wifi_wiphy_11be_supported", true);
+
     /******** Wifi shared pref keys ***************/
 
     private final Context mContext;
diff --git a/service/java/com/android/server/wifi/WifiSettingsStore.java b/service/java/com/android/server/wifi/WifiSettingsStore.java
index caf1119..bf7870c 100644
--- a/service/java/com/android/server/wifi/WifiSettingsStore.java
+++ b/service/java/com/android/server/wifi/WifiSettingsStore.java
@@ -211,6 +211,10 @@
         return getPersistedWifiPasspointEnabled();
     }
 
+    public synchronized boolean isWifiScanThrottleEnabled() {
+        return getPersistedWifiScanThrottleEnabled();
+    }
+
     public synchronized int getWifiMultiInternetMode() {
         return getPersistedWifiMultiInternetMode();
     }
@@ -482,6 +486,11 @@
                 WifiSettingsConfigStore.WIFI_PASSPOINT_ENABLED);
     }
 
+    private boolean getPersistedWifiScanThrottleEnabled() {
+        return mSettingsConfigStore.get(
+                WifiSettingsConfigStore.WIFI_SCAN_THROTTLE_ENABLED);
+    }
+
     private int getPersistedWifiMultiInternetMode() {
         return mSettingsConfigStore.get(
                 WifiSettingsConfigStore.WIFI_MULTI_INTERNET_MODE);
diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java
index 3afe9f8..1541273 100644
--- a/service/java/com/android/server/wifi/WifiShellCommand.java
+++ b/service/java/com/android/server/wifi/WifiShellCommand.java
@@ -25,6 +25,8 @@
 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT;
 import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_LINGER;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED;
+import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY;
 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
 
@@ -40,6 +42,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.display.DisplayManager;
+import android.location.Location;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.net.MacAddress;
 import android.net.Network;
@@ -101,6 +105,7 @@
 import com.android.server.wifi.ClientMode.LinkProbeCallback;
 import com.android.server.wifi.coex.CoexManager;
 import com.android.server.wifi.coex.CoexUtils;
+import com.android.server.wifi.hal.WifiChip;
 import com.android.server.wifi.hotspot2.NetworkDetail;
 import com.android.server.wifi.util.ApConfigUtil;
 import com.android.server.wifi.util.ArrayUtils;
@@ -115,6 +120,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -206,6 +212,7 @@
     private final SsidTranslator mSsidTranslator;
     private final WifiDiagnostics mWifiDiagnostics;
     private final DeviceConfigFacade mDeviceConfig;
+    private final AfcManager mAfcManager;
     private static final int[] OP_MODE_LIST = {
             WifiAvailableChannel.OP_MODE_STA,
             WifiAvailableChannel.OP_MODE_SAP,
@@ -409,6 +416,7 @@
                     + " targetNetworkId=" + targetNetworkId
                     + " targetBssid=" + targetBssid);
         }
+
         @Override
         public void onNetworkSwitchRejected(
                 int sessionId, int targetNetworkId, String targetBssid) {
@@ -453,6 +461,7 @@
         mSsidTranslator = wifiInjector.getSsidTranslator();
         mWifiDiagnostics = wifiInjector.getWifiDiagnostics();
         mDeviceConfig = wifiInjector.getDeviceConfigFacade();
+        mAfcManager = wifiInjector.getAfcManager();
     }
 
     private String getOpModeName(@WifiAvailableChannel.OpMode int mode) {
@@ -630,13 +639,16 @@
                         String forcedBand = getNextArgRequired();
                         if (forcedBand.equals("2")) {
                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
-                                    SoftApConfiguration.BAND_2GHZ, 0);
+                                    SoftApConfiguration.BAND_2GHZ, 0,
+                                    SoftApInfo.CHANNEL_WIDTH_AUTO);
                         } else if (forcedBand.equals("5")) {
                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
-                                    SoftApConfiguration.BAND_5GHZ, 0);
+                                    SoftApConfiguration.BAND_5GHZ, 0,
+                                    SoftApInfo.CHANNEL_WIDTH_AUTO);
                         } else if (forcedBand.equals("6")) {
                             mWifiApConfigStore.enableForceSoftApBandOrChannel(
-                                    SoftApConfiguration.BAND_6GHZ, 0);
+                                    SoftApConfiguration.BAND_6GHZ, 0,
+                                    SoftApInfo.CHANNEL_WIDTH_AUTO);
                         } else {
                             pw.println("Invalid argument to 'force-softap-band enabled' "
                                     + "- must be a valid band integer (2|5|6)");
@@ -653,8 +665,25 @@
                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
                     if (enabled) {
                         int apChannelMHz;
+                        int apMaxBandWidthMHz = 0;
                         try {
                             apChannelMHz = Integer.parseInt(getNextArgRequired());
+                            String option = getNextOption();
+                            if (option != null && option.equals("-w")) {
+                                if (!SdkLevel.isAtLeastT()) {
+                                    pw.println("Maximum channel bandwidth can be set only on"
+                                            + " SdkLevel T or later.");
+                                    return -1;
+                                }
+                                String bandwidthStr = getNextArgRequired();
+                                try {
+                                    apMaxBandWidthMHz = Integer.parseInt(bandwidthStr);
+                                } catch (NumberFormatException e) {
+                                    pw.println("Invalid maximum channel bandwidth arg: "
+                                            + bandwidthStr);
+                                    return -1;
+                                }
+                            }
                         } catch (NumberFormatException e) {
                             pw.println("Invalid argument to 'force-softap-channel enabled' "
                                     + "- must be a positive integer");
@@ -663,7 +692,32 @@
                         int apChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
                                 apChannelMHz);
                         int band = ApConfigUtil.convertFrequencyToBand(apChannelMHz);
-                        pw.println("channel: " + apChannel + " band: " + band);
+                        int apMaxBandWidth;
+                        switch (apMaxBandWidthMHz) {
+                            case 0:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_AUTO;
+                                break;
+                            case 20:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_20MHZ;
+                                break;
+                            case 40:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_40MHZ;
+                                break;
+                            case 80:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_80MHZ;
+                                break;
+                            case 160:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_160MHZ;
+                                break;
+                            case 320:
+                                apMaxBandWidth = SoftApInfo.CHANNEL_WIDTH_320MHZ;
+                                break;
+                            default:
+                                pw.println("Invalid max channel bandwidth " + apMaxBandWidthMHz);
+                                return -1;
+                        }
+                        pw.println("channel: " + apChannel + " band: " + band
+                                + " maximum channel bandwidth: " + apMaxBandWidthMHz);
                         if (apChannel == -1 || band == -1) {
                             pw.println("Invalid argument to 'force-softap-channel enabled' "
                                     + "- must be a valid WLAN channel");
@@ -691,7 +745,8 @@
                                     + " in a band supported by the device");
                             return -1;
                         }
-                        mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel);
+                        mWifiApConfigStore.enableForceSoftApBandOrChannel(band, apChannel,
+                                apMaxBandWidth);
                         return 0;
                     } else {
                         mWifiApConfigStore.disableForceSoftApBandOrChannel();
@@ -1015,7 +1070,23 @@
                     return 0;
                 case "set-verbose-logging": {
                     boolean enabled = getNextArgRequiredTrueOrFalse("enabled", "disabled");
-                    mWifiService.enableVerboseLogging(enabled ? 1 : 0);
+                    String levelOption = getNextOption();
+                    int level = enabled ? 1 : 0;
+                    if (enabled && levelOption != null && levelOption.equals("-l")) {
+                        String levelStr = getNextArgRequired();
+                        try {
+                            level = Integer.parseInt(levelStr);
+                            if (level < VERBOSE_LOGGING_LEVEL_DISABLED
+                                    || level > VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY) {
+                                pw.println("Not a valid log level: " + level);
+                                return -1;
+                            }
+                        } catch (NumberFormatException e) {
+                            pw.println("Invalid verbose-logging level : " + levelStr);
+                            return -1;
+                        }
+                    }
+                    mWifiService.enableVerboseLogging(level);
                     return 0;
                 }
                 case "is-verbose-logging": {
@@ -1779,35 +1850,263 @@
                     return 0;
                 }
                 case "get-allowed-channel": {
-                    WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
-                    WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class);
-                    List<WifiAvailableChannel> allowedChannels;
-                    List<Integer> availableChannels;
+                    StringBuilder allowedChannel = new StringBuilder();
+                    int band = WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ;
 
-                    for (int opMode : OP_MODE_LIST) {
-                        String allowedChannel = "";
-                        try {
-                            allowedChannels = wifiManager.getAllowedChannels(
-                                    WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ, opMode);
-                            for (WifiAvailableChannel channel : allowedChannels) {
-                                allowedChannel += channel.getFrequencyMhz() + " ";
-                            }
-                            pw.println("Allowed ch in " + getOpModeName(opMode) + " mode:\n"
-                                    + allowedChannel);
-                        } catch (UnsupportedOperationException e) {
-                            availableChannels = wifiScanner.getAvailableChannels(
-                                    WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ);
-                            for (Integer channel : availableChannels) {
-                                allowedChannel += channel + " ";
-                            }
-                            /* In this case, all mode has same allowed channel list. So
-                            immediately return */
-                            pw.println("Allowed ch in all mode:\n" + allowedChannel);
-                            return 0;
+                    String option = getNextOption();
+                    while (option != null) {
+                        if (option.equals("-b")) {
+                            band = Integer.parseInt(getNextArgRequired());
+                        } else {
+                            pw.println("Ignoring unknown option: " + option);
+                            return -1;
                         }
+                        option = getNextOption();
+                    }
+
+                    try {
+                        Bundle extras = new Bundle();
+                        if (SdkLevel.isAtLeastS()) {
+                            extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
+                                    mContext.getAttributionSource());
+                        } else {
+                            throw new UnsupportedOperationException();
+                        }
+                        // The option "-b 2" (getting band 5ghz active channels) is valid for old
+                        // devices, but invalid for new devices. So we first check if device
+                        // supports getUsableChannels() API.
+                        mWifiService.getUsableChannels(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ,
+                                WifiAvailableChannel.OP_MODE_STA,
+                                WifiAvailableChannel.FILTER_REGULATORY, SHELL_PACKAGE_NAME, extras);
+                        try {
+                            for (int opMode : OP_MODE_LIST) {
+                                List<WifiAvailableChannel> usableChannels =
+                                        mWifiService.getUsableChannels(band, opMode,
+                                                WifiAvailableChannel.FILTER_REGULATORY,
+                                                SHELL_PACKAGE_NAME, extras);
+                                allowedChannel = new StringBuilder();
+                                for (WifiAvailableChannel channel : usableChannels) {
+                                    allowedChannel.append(channel.getFrequencyMhz()).append(" ");
+                                }
+                                pw.println("Allowed ch in " + getOpModeName(opMode) + " mode:\n"
+                                        + allowedChannel);
+                            }
+                        } catch (IllegalArgumentException e) {
+                            pw.println("Invalid band: " + band);
+                            return -1;
+                        }
+                    } catch (UnsupportedOperationException e) {
+                        WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class);
+                        try {
+                            List<Integer> availableChannels = wifiScanner.getAvailableChannels(
+                                    band);
+                            for (Integer channel : availableChannels) {
+                                allowedChannel.append(channel).append(" ");
+                            }
+                            pw.println("Allowed ch in all modes:\n" + allowedChannel);
+                        } catch (SecurityException securityException) {
+                            pw.println("Permission is required.");
+                            return -1;
+                        }
+                    } catch (SecurityException e) {
+                        pw.println("Permission is required.");
+                        return -1;
                     }
                     return 0;
                 }
+                case "trigger-afc-location-update":
+                    Double longitude = Double.parseDouble(getNextArgRequired());
+                    Double latitude = Double.parseDouble(getNextArgRequired());
+                    Double height = Double.parseDouble(getNextArgRequired());
+                    Location location = new Location(LocationManager.FUSED_PROVIDER);
+                    location.setLongitude(longitude);
+                    location.setLatitude(latitude);
+                    location.setAltitude(height);
+                    mWifiThreadRunner.post(() -> mAfcManager.onLocationChange(location, true));
+                    pw.println("The updated location with longitude of " + longitude + " degrees, "
+                            + "latitude of " + latitude + " degrees, and height of " + height
+                            + " meters was passed into the Afc Manager onLocationChange method.");
+                    return 0;
+                case "set-afc-channel-allowance": {
+                    WifiChip.AfcChannelAllowance afcChannelAllowance =
+                            new WifiChip.AfcChannelAllowance();
+                    boolean expiryTimeArgumentIncluded = false;
+                    boolean frequencyOrChannelArgumentIncluded = false;
+                    afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
+                    afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
+
+                    String option;
+                    while ((option = getNextOption()) != null) {
+                        switch (option) {
+                            case "-e": {
+                                int secondsUntilExpiry = Integer.parseInt(getNextArgRequired());
+                                // AfcChannelAllowance requires this field to be a UNIX timestamp
+                                // in milliseconds.
+                                afcChannelAllowance.availabilityExpireTimeMs =
+                                        System.currentTimeMillis() + secondsUntilExpiry * 1000;
+                                expiryTimeArgumentIncluded = true;
+
+                                break;
+                            }
+                            case "-f": {
+                                frequencyOrChannelArgumentIncluded = true;
+                                String frequenciesInput = getNextArgRequired();
+
+                                if (frequenciesInput.equals(("none"))) {
+                                    break;
+                                }
+
+                                // parse frequency list, and add it to the AfcChannelAllowance
+                                String[] unparsedFrequencies = frequenciesInput.split(":");
+                                afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
+
+                                for (int i = 0; i < unparsedFrequencies.length; ++i) {
+                                    String[] frequencyPieces = unparsedFrequencies[i].split(",");
+
+                                    if (frequencyPieces.length != 3) {
+                                        throw new IllegalArgumentException("Each frequency in the "
+                                                + "available frequency list should have 3 values, "
+                                                + "but found one with " + frequencyPieces.length);
+                                    }
+
+                                    WifiChip.AvailableAfcFrequencyInfo frequencyInfo = new
+                                            WifiChip.AvailableAfcFrequencyInfo();
+
+                                    frequencyInfo.startFrequencyMhz =
+                                            Integer.parseInt(frequencyPieces[0]);
+                                    frequencyInfo.endFrequencyMhz =
+                                            Integer.parseInt(frequencyPieces[1]);
+                                    frequencyInfo.maxPsdDbmPerMhz =
+                                            Integer.parseInt(frequencyPieces[2]);
+
+                                    afcChannelAllowance.availableAfcFrequencyInfos
+                                                    .add(frequencyInfo);
+                                }
+
+                                break;
+                            }
+                            case "-c": {
+                                frequencyOrChannelArgumentIncluded = true;
+                                String channelsInput = getNextArgRequired();
+
+                                if (channelsInput.equals("none")) {
+                                    break;
+                                }
+
+                                // parse channel list, and add it to the AfcChannelAllowance
+                                String[] unparsedChannels = channelsInput.split(":");
+                                afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
+
+                                for (int i = 0; i < unparsedChannels.length; ++i) {
+                                    String[] channelPieces = unparsedChannels[i].split(",");
+
+                                    if (channelPieces.length != 3) {
+                                        throw new IllegalArgumentException("Each channel in the "
+                                                + "available channel list should have 3 values, "
+                                                + "but found one with " + channelPieces.length);
+                                    }
+
+                                    WifiChip.AvailableAfcChannelInfo channelInfo = new
+                                            WifiChip.AvailableAfcChannelInfo();
+
+                                    channelInfo.globalOperatingClass =
+                                            Integer.parseInt(channelPieces[0]);
+                                    channelInfo.channelCfi = Integer.parseInt(channelPieces[1]);
+                                    channelInfo.maxEirpDbm = Integer.parseInt(channelPieces[2]);
+
+                                    afcChannelAllowance.availableAfcChannelInfos.add(channelInfo);
+                                }
+
+                                break;
+                            }
+                            default: {
+                                pw.println("Unrecognized command line argument.");
+                                return -1;
+                            }
+                        }
+                    }
+                    if (!expiryTimeArgumentIncluded) {
+                        pw.println("Please include the -e flag to set the seconds until the "
+                                + "availability expires.");
+                        return -1;
+                    }
+                    if (!frequencyOrChannelArgumentIncluded) {
+                        pw.println("Please include at least one of the -f or -c flags to set the "
+                                + "frequency or channel availability.");
+                        return -1;
+                    }
+
+                    ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
+                    mWifiThreadRunner.post(() -> {
+                        if (mWifiNative.setAfcChannelAllowance(afcChannelAllowance)) {
+                            queue.offer("Successfully set the allowed AFC channels and "
+                                    + "frequencies.");
+                        } else {
+                            queue.offer("Setting the allowed AFC channels and frequencies "
+                                    + "failed.");
+                        }
+                    });
+
+                    // block until msg is received, or timed out
+                    String msg = queue.poll(3000, TimeUnit.MILLISECONDS);
+                    if (msg == null) {
+                        pw.println("Setting the allowed AFC channels and frequencies timed out.");
+                    } else {
+                        pw.println(msg);
+                    }
+
+                    return 0;
+                }
+                case "configure-afc-server":
+                    final String url = getNextArgRequired();
+
+                    if (!url.startsWith("http")) {
+                        pw.println("The required URL first argument is not a valid server URL for"
+                                + " a HTTP request.");
+                        return -1;
+                    }
+
+                    String secondOption = getNextOption();
+                    Map<String, String> requestProperties = new HashMap<>();
+                    if (secondOption != null && secondOption.equals("-r")) {
+
+                        String key = getNextArg();
+                        while (key != null) {
+
+                            String value = getNextArg();
+                            // Check if there is a next value
+                            if (value != null) {
+                                requestProperties.put(key, value);
+                            } else {
+                                // Fail to proceed as there is no value given for the corresponding
+                                // key
+                                pw.println(
+                                        "No value provided for the corresponding key " + key
+                                                + ". There must be an even number of request"
+                                                + " property arguments provided after the -r "
+                                                + "option.");
+                                return -1;
+                            }
+                            key = getNextArg();
+                        }
+
+                    } else {
+                        pw.println("No -r option was provided as second argument so the HTTP "
+                                + "request will have no request properties.");
+                    }
+
+                    mWifiThreadRunner.post(() -> {
+                        mAfcManager.setServerUrlAndRequestPropertyPairs(url, requestProperties);
+                    });
+
+                    pw.println("The URL is set to " + url);
+                    pw.println("The request properties are set to: ");
+
+                    for (Map.Entry<String, String> requestProperty : requestProperties.entrySet()) {
+                        pw.println("Key: " + requestProperty.getKey() + ", Value: "
+                                + requestProperty.getValue());
+                    }
+                    return 0;
                 case "set-mock-wifimodem-service":
                     String opt = null;
                     String serviceName = null;
@@ -2040,6 +2339,21 @@
                     }
                     configBuilder.setChannel(channels.valueAt(0), channels.keyAt(0));
                 }
+            } else if (option.equals("-w")) {
+                String bandwidth = getNextArgRequired();
+                if (bandwidth.equals("20")) {
+                    configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
+                } else if (bandwidth.equals("40")) {
+                    configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_40MHZ);
+                } else if (bandwidth.equals("80")) {
+                    configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_80MHZ);
+                } else if (bandwidth.equals("160")) {
+                    configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_160MHZ);
+                } else if (bandwidth.equals("320")) {
+                    configBuilder.setMaxChannelBandwidth(SoftApInfo.CHANNEL_WIDTH_320MHZ);
+                } else {
+                    throw new IllegalArgumentException("Invalid bandwidth option " + bandwidth);
+                }
             } else {
                 pw.println("Ignoring unknown option " + option);
             }
@@ -2456,8 +2770,13 @@
         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("  set-verbose-logging enabled|disabled [-l <verbose log level>]");
+        pw.println("    Set the verbose logging enabled or disabled with log level");
+        pw.println("      -l - verbose logging level");
+        pw.println("           0 - verbose logging disabled");
+        pw.println("           1 - verbose logging enabled");
+        pw.println("           2 - verbose logging Show key mode");
+        pw.println("           3 - verbose logging only for Wi-Fi Aware feature");
         pw.println("  is-verbose-logging");
         pw.println("    Check whether verbose logging enabled or disabled");
         pw.println("  start-restricting-auto-join-to-subscription-id subId");
@@ -2530,9 +2849,10 @@
         pw.println("  reset-connected-score");
         pw.println("    Turns on the default connected scorer.");
         pw.println("    Note: Will clear any external scorer set.");
-        pw.println("  start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition) "
-                + "<passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6] [-x]"
-                + " [-f <int> [<int>]]");
+        pw.println(
+                "  start-softap <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)"
+                        + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]"
+                        + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]]");
         pw.println("    Start softap with provided params");
         pw.println("    Note that the shell command doesn't activate internet tethering. In some "
                 + "devices, internet sharing is possible when Wi-Fi STA is also enabled and is"
@@ -2565,6 +2885,7 @@
         pw.println("          Use '-f 2412' to enable single Soft Ap on 2412");
         pw.println("          Use '-f 2412 5745' to enable bridged dual Soft Ap on 2412 and 5745");
         pw.println("    -x - Specifies the SSID as hex digits instead of plain text (T and above)");
+        pw.println("    -w 20|40|80|160|320 - select the maximum channel bandwidth (MHz)");
         pw.println("  stop-softap");
         pw.println("    Stop softap (hotspot)");
         pw.println("  pmksa-flush <networkId>");
@@ -2624,21 +2945,30 @@
         pw.println("  get-ipreach-disconnect");
         pw.println("    Gets setting of CMD_IP_REACHABILITY_LOST events triggering disconnects.");
         pw.println("  take-bugreport");
-        pw.println("    take bugreport through betterBug. "
-                + "If it failed, take bugreport through bugreport manager.");
-        pw.println("  get-allowed-channel");
+        pw.println(
+                "    take bugreport through betterBug. "
+                        + "If it failed, take bugreport through bugreport manager.");
+        pw.println("  get-allowed-channel [-b 1|6|7|8|15|16|31]");
         pw.println(
                 "    get allowed channels in each operation mode from wifiManager if available. "
                         + "Otherwise, it returns from wifiScanner.");
+        pw.println("    -b - set the band in which channels are allowed");
+        pw.println("       '1'  - band 2.4 GHz");
+        pw.println("       '6'  - band 5 GHz with DFS channels");
+        pw.println("       '7'  - band 2.4 and 5 GHz with DFS channels");
+        pw.println("       '8'  - band 6 GHz");
+        pw.println("       '15' - band 2.4, 5, and 6 GHz with DFS channels");
+        pw.println("       '16' - band 60 GHz");
+        pw.println("       '31' - band 2.4, 5, 6 and 60 GHz with DFS channels");
         pw.println("  force-overlay-config-value <overlayName> <configString> enabled|disabled");
         pw.println("    Force overlay to a specified value. See below for supported overlays.");
         pw.println("    <overlayName> - name of the overlay whose value is overridden.");
         pw.println("        - Currently supports:");
-        pw.println("            - config_wifi_background_scan_support: accepts boolean"
+        pw.println("            - config_wifi_background_scan_support: accepts boolean "
                 + "<configString> = true|false.");
-        pw.println("    <configString> - override value of the overlay. See above for accepted"
+        pw.println("    <configString> - override value of the overlay. See above for accepted "
                 + "values per overlay.");
-        pw.println("    enabled|disabled: enable the override or disable it and revert to using"
+        pw.println("    enabled|disabled: enable the override or disable it and revert to using "
                 + "the built-in value.");
     }
 
@@ -2711,8 +3041,11 @@
         pw.println("    Manually triggers a link probe.");
         pw.println("  force-softap-band enabled <int> | disabled");
         pw.println("    Forces soft AP band to 2|5|6");
-        pw.println("  force-softap-channel enabled <int> | disabled");
-        pw.println("    Sets whether soft AP channel is forced to <int> MHz");
+        pw.println("  force-softap-channel enabled <int> | disabled [-w <maxBandwidth>]");
+        pw.println("    Sets whether soft AP channel is forced to <int> MHz [-w <maxBandwidth>]");
+        pw.println("        -w 0|20|40|80|160|320 - select the maximum channel bandwidth (MHz)");
+        pw.println("         Note: If the bandwidth option is not provided or set to 0, framework"
+                + " will set the maximum bandwidth to auto, allowing HAL to select the bandwidth");
         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");
@@ -2821,9 +3154,10 @@
         pw.println("    -h - Enable scanning for hidden networks.");
         pw.println("  set-passpoint-enabled enabled|disabled");
         pw.println("    Sets whether Passpoint should be enabled or disabled");
-        pw.println("  start-lohs <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition) "
-                + "<passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6] [-x]"
-                + " [-f <int> [<int>]])");
+        pw.println(
+                "  start-lohs <ssid> (open|wpa2|wpa3|wpa3_transition|owe|owe_transition)"
+                        + " <passphrase> [-b 2|5|6|any|bridged|bridged_2_5|bridged_2_6|bridged_5_6]"
+                        + " [-x] [-w 20|40|80|160|320] [-f <int> [<int>]])");
         pw.println("    Start local only softap (hotspot) with provided params");
         pw.println("    <ssid> - SSID of the network");
         pw.println("    open|wpa2|wpa3|wpa3_transition|owe|owe_transition - Security type of the "
@@ -2853,6 +3187,7 @@
         pw.println("          Use '-f 2412' to enable single Soft Ap on 2412");
         pw.println("          Use '-f 2412 5745' to enable bridged dual lohs on 2412 and 5745");
         pw.println("    -x - Specifies the SSID as hex digits instead of plain text (T and above)");
+        pw.println("    -w 20|40|80|160|320 - select the maximum bandwidth (MHz)");
         pw.println("  stop-softap");
         pw.println("    Stop softap (hotspot)");
         pw.println("    Note: If the band option is not provided, 2.4GHz is the preferred band.");
@@ -2881,6 +3216,38 @@
         pw.println("    Clears the SSID translation charsets set in set-ssid-charset.");
         pw.println("  get-last-caller-info api_type");
         pw.println("    Get the last caller information for a WifiManager.ApiType");
+        pw.println("  trigger-afc-location-update <longitude> <latitude> <height>");
+        pw.println("    Passes in longitude, latitude, and height values as arguments of type "
+                + "double for a fake location update to trigger framework logic to query the AFC "
+                + "server. The longitude and latitude pair is in decimal degrees and the height"
+                + " is the altitude in meters. The server URL needs to have been previously set "
+                + "with the configure-afc-server shell command.");
+        pw.println("    Example: trigger-afc-location-update 37.425056 -122.984157 3.043");
+        pw.println("  set-afc-channel-allowance -e <secs_until_expiry> [-f <low_freq>,<high_freq>,"
+                + "<psd>:...|none] [-c <operating_class>,<channel_cfi>,<max_eirp>:...|none]");
+        pw.println("    Sets the allowed AFC channels and frequencies.");
+        pw.println("    -e - Seconds until the availability expires.");
+        pw.println("    -f - Colon-separated list of available frequency info.");
+        pw.println("      Note: each frequency should contain 3 comma separated values, where "
+                + "the first is the low frequency (MHz), the second the high frequency (MHz), the "
+                + "third the max PSD (dBm per MHz). To set an empty frequency list, enter \"none\" "
+                + "in place of the list of allowed frequencies.");
+        pw.println("    -c - Colon-separated list of available channel info.");
+        pw.println("      Note: each channel should contain 3 comma separated values, where "
+                + "the first is the global operating class, the second the channel CFI, "
+                + "the third the max EIRP in dBm. To set an empty channel list, enter \"none\" in "
+                + "place of the list of allowed channels.");
+        pw.println("    Example: set-afc-channel-allowance -e 30 -c none -f "
+                + "5925,6020,23:6020,6050,1");
+        pw.println("  configure-afc-server <url> [-r [<request property key> <request property "
+                + "value>] ...]");
+        pw.println("    Sets the server URL and request properties for the HTTP request which the "
+                + "AFC Client will query.");
+        pw.println("    -r - HTTP header fields that come in pairs of key and value which are added"
+                + " to the HTTP request. Must be an even number of arguments. If there is no -r "
+                + "option provided or no arguments provided after the -r option, then set the "
+                + "request properties to none in the request.");
+        pw.println("    Example: configure-afc-server https://testURL -r key1 value1 key2 value2");
     }
 
     @Override
diff --git a/service/java/com/android/server/wifi/WifiThreadRunner.java b/service/java/com/android/server/wifi/WifiThreadRunner.java
index 95ce052..c3210de 100644
--- a/service/java/com/android/server/wifi/WifiThreadRunner.java
+++ b/service/java/com/android/server/wifi/WifiThreadRunner.java
@@ -49,6 +49,8 @@
 
     private final Handler mHandler;
 
+    public boolean mVerboseLoggingEnabled = false;
+
     public WifiThreadRunner(Handler handler) {
         mHandler = handler;
     }
@@ -82,11 +84,13 @@
         if (runWithScissorsSuccess) {
             return result.value;
         } else {
-            Throwable callerThreadThrowable = new Throwable("Caller thread Stack trace:");
-            Throwable wifiThreadThrowable = new Throwable("Wifi thread Stack trace:");
-            wifiThreadThrowable.setStackTrace(mHandler.getLooper().getThread().getStackTrace());
-            Log.e(TAG, "WifiThreadRunner.call() timed out!", callerThreadThrowable);
-            Log.e(TAG, "WifiThreadRunner.call() timed out!", wifiThreadThrowable);
+            if (mVerboseLoggingEnabled) {
+                Throwable callerThreadThrowable = new Throwable("Caller thread Stack trace:");
+                Throwable wifiThreadThrowable = new Throwable("Wifi thread Stack trace:");
+                wifiThreadThrowable.setStackTrace(mHandler.getLooper().getThread().getStackTrace());
+                Log.e(TAG, "WifiThreadRunner.call() timed out!", callerThreadThrowable);
+                Log.e(TAG, "WifiThreadRunner.call() timed out!", wifiThreadThrowable);
+            }
             if (mTimeoutsAreErrors) {
                 throw new RuntimeException("WifiThreadRunner.call() timed out!");
             }
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 9349018..6dadd23 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -26,6 +26,7 @@
 import android.hardware.wifi.WifiStatusCode;
 import android.net.MacAddress;
 import android.net.apf.ApfCapabilities;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiAvailableChannel;
@@ -390,18 +391,20 @@
      * @param band The requesting band for this AP interface.
      * @param isBridged Whether or not AP interface is a bridge interface.
      * @param softApManager SoftApManager of the request.
+     * @param vendorData List of {@link OuiKeyedData} containing vendor-provided
+     *                   configuration data. Empty list indicates no vendor data.
      * @return iface name on success, null otherwise.
      */
     public String createApIface(@Nullable InterfaceDestroyedListener destroyedListener,
             @NonNull WorkSource requestorWs,
             @SoftApConfiguration.BandType int band,
             boolean isBridged,
-            @NonNull SoftApManager softApManager) {
+            @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) {
         synchronized (sLock) {
             WifiApIface iface = mHalDeviceManager.createApIface(
                     getNecessaryCapabilitiesForSoftApMode(band),
                     new ApInterfaceDestroyedListenerInternal(destroyedListener), mHalEventHandler,
-                    requestorWs, isBridged, softApManager);
+                    requestorWs, isBridged, softApManager, vendorData);
             if (iface == null) {
                 mLog.err("Failed to create AP iface").flush();
                 return null;
@@ -1995,4 +1998,12 @@
             return mHalDeviceManager.getSupportedBandCombinations(iface);
         }
     }
+
+    /**
+     * See {@link WifiNative#setAfcChannelAllowance(WifiChip.AfcChannelAllowance)}
+     */
+    public boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        if (mWifiChip == null) return false;
+        return mWifiChip.setAfcChannelAllowance(afcChannelAllowance);
+    }
 }
diff --git a/service/java/com/android/server/wifi/aware/Capabilities.java b/service/java/com/android/server/wifi/aware/Capabilities.java
index b6f1ac2..b47eca2 100644
--- a/service/java/com/android/server/wifi/aware/Capabilities.java
+++ b/service/java/com/android/server/wifi/aware/Capabilities.java
@@ -48,6 +48,8 @@
     public boolean isNanPairingSupported;
     public boolean isSetClusterIdSupported;
     public boolean isSuspensionSupported;
+    public boolean is6gSupported;
+    public boolean isHeSupported;
 
     /**
      * Converts the internal capabilities to a parcelable & potentially app-facing
@@ -56,8 +58,9 @@
     public Characteristics toPublicCharacteristics(DeviceConfigFacade deviceConfigFacade) {
         Bundle bundle = new Bundle();
         bundle.putInt(Characteristics.KEY_MAX_SERVICE_NAME_LENGTH, maxServiceNameLen);
-        bundle.putInt(Characteristics.KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH,
-                maxServiceSpecificInfoLen);
+        bundle.putInt(
+                Characteristics.KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH,
+                Math.max(maxExtendedServiceSpecificInfoLen, maxServiceSpecificInfoLen));
         bundle.putInt(Characteristics.KEY_MAX_MATCH_FILTER_LENGTH, maxMatchFilterLen);
         bundle.putInt(Characteristics.KEY_SUPPORTED_DATA_PATH_CIPHER_SUITES,
                 supportedDataPathCipherSuites);
@@ -99,21 +102,46 @@
 
     @Override
     public String toString() {
-        return "Capabilities [maxConcurrentAwareClusters=" + maxConcurrentAwareClusters
-                + ", maxPublishes=" + maxPublishes + ", maxSubscribes=" + maxSubscribes
-                + ", maxServiceNameLen=" + maxServiceNameLen + ", maxMatchFilterLen="
-                + maxMatchFilterLen + ", maxTotalMatchFilterLen=" + maxTotalMatchFilterLen
-                + ", maxServiceSpecificInfoLen=" + maxServiceSpecificInfoLen
-                + ", maxExtendedServiceSpecificInfoLen=" + maxExtendedServiceSpecificInfoLen
-                + ", maxNdiInterfaces=" + maxNdiInterfaces + ", maxNdpSessions="
-                + maxNdpSessions + ", maxAppInfoLen=" + maxAppInfoLen
-                + ", maxQueuedTransmitMessages=" + maxQueuedTransmitMessages
-                + ", maxSubscribeInterfaceAddresses=" + maxSubscribeInterfaceAddresses
-                + ", supportedCipherSuites=" + supportedDataPathCipherSuites
-                + ", isInstantCommunicationModeSupport=" + isInstantCommunicationModeSupported
-                + ", isNanPairingSupported=" + isNanPairingSupported
-                + ", isSetClusterIdSupported=" + isSetClusterIdSupported
-                + ", isSuspensionSupported=" + isSuspensionSupported
+        return "Capabilities [maxConcurrentAwareClusters="
+                + maxConcurrentAwareClusters
+                + ", maxPublishes="
+                + maxPublishes
+                + ", maxSubscribes="
+                + maxSubscribes
+                + ", maxServiceNameLen="
+                + maxServiceNameLen
+                + ", maxMatchFilterLen="
+                + maxMatchFilterLen
+                + ", maxTotalMatchFilterLen="
+                + maxTotalMatchFilterLen
+                + ", maxServiceSpecificInfoLen="
+                + maxServiceSpecificInfoLen
+                + ", maxExtendedServiceSpecificInfoLen="
+                + maxExtendedServiceSpecificInfoLen
+                + ", maxNdiInterfaces="
+                + maxNdiInterfaces
+                + ", maxNdpSessions="
+                + maxNdpSessions
+                + ", maxAppInfoLen="
+                + maxAppInfoLen
+                + ", maxQueuedTransmitMessages="
+                + maxQueuedTransmitMessages
+                + ", maxSubscribeInterfaceAddresses="
+                + maxSubscribeInterfaceAddresses
+                + ", supportedCipherSuites="
+                + supportedDataPathCipherSuites
+                + ", isInstantCommunicationModeSupport="
+                + isInstantCommunicationModeSupported
+                + ", isNanPairingSupported="
+                + isNanPairingSupported
+                + ", isSetClusterIdSupported="
+                + isSetClusterIdSupported
+                + ", isSuspensionSupported="
+                + isSuspensionSupported
+                + ", is6gSupported="
+                + is6gSupported
+                + ", isHeSupported="
+                + isHeSupported
                 + "]";
     }
 }
diff --git a/service/java/com/android/server/wifi/aware/PairingConfigManager.java b/service/java/com/android/server/wifi/aware/PairingConfigManager.java
index 18028ff..6bc7e35 100644
--- a/service/java/com/android/server/wifi/aware/PairingConfigManager.java
+++ b/service/java/com/android/server/wifi/aware/PairingConfigManager.java
@@ -118,12 +118,14 @@
 
     private boolean checkMatchAlias(String alias, byte[] nonce, byte[] tag, byte[] mac) {
         byte[] nik = mAliasToNikMap.get(alias);
+        byte[] nir = {'N', 'I', 'R'};
         if (nik == null) return false;
         SecretKeySpec spec = new SecretKeySpec(nik, "HmacSHA256");
 
         try {
             Mac hash = Mac.getInstance("HmacSHA256");
             hash.init(spec);
+            hash.update(nir);
             hash.update(mac);
             hash.update(nonce);
             byte[] message = Arrays.copyOf(hash.doFinal(), TAG_SIZE_IN_BYTE);
@@ -155,6 +157,7 @@
         Set<String> pairedDevices = mPerAppPairedAliasMap.get(packageName);
         if (pairedDevices == null) {
             pairedDevices = new HashSet<>();
+            mPerAppPairedAliasMap.put(packageName, pairedDevices);
         }
         pairedDevices.add(alias);
         mAliasToNikMap.put(alias, info.mPeerNik);
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareClientState.java b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
index 09f85f8..63e49d2 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
@@ -28,7 +28,6 @@
 import android.net.wifi.aware.IdentityChangedListener;
 import android.net.wifi.util.HexEncoding;
 import android.os.RemoteException;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -49,13 +48,12 @@
  */
 public class WifiAwareClientState {
     private static final String TAG = "WifiAwareClientState";
-    private static final boolean VDBG = false; // STOPSHIP if true
+    private boolean mVdbg = false; // STOPSHIP if true
     private boolean mDbg = false;
 
     private final Context mContext;
     private final IWifiAwareEventCallback mCallback;
     private final SparseArray<WifiAwareDiscoverySessionState> mSessions = new SparseArray<>();
-    private final LocalLog mLocalLog;
 
     private final int mClientId;
     private ConfigRequest mConfigRequest;
@@ -80,7 +78,7 @@
             String callingPackage, @Nullable String callingFeatureId,
             IWifiAwareEventCallback callback, ConfigRequest configRequest,
             boolean notifyIdentityChange, long creationTime,
-            WifiPermissionsUtil wifiPermissionsUtil, Object attributionSource, LocalLog localLog,
+            WifiPermissionsUtil wifiPermissionsUtil, Object attributionSource,
             boolean awareOffload, int callerType) {
         mContext = context;
         mClientId = clientId;
@@ -97,15 +95,15 @@
         mCreationTime = creationTime;
         mWifiPermissionsUtil = wifiPermissionsUtil;
         mAttributionSource = attributionSource;
-        mLocalLog = localLog;
         mCallerType = callerType;
     }
 
     /**
      * Enable verbose logging.
      */
-    public void enableVerboseLogging(boolean verbose) {
-        mDbg = verbose | VDBG;
+    public void enableVerboseLogging(boolean verbose, boolean vDbg) {
+        mDbg = verbose;
+        mVdbg = vDbg;
     }
 
     /**
@@ -113,7 +111,9 @@
      * the client. Destroys all discovery sessions belonging to this client.
      */
     public void destroy() {
-        mLocalLog.log("onAwareSessionTerminated, ClientId:" + mClientId);
+        if (mDbg) {
+            Log.v(TAG, "onAwareSessionTerminated, ClientId:" + mClientId);
+        }
         for (int i = 0; i < mSessions.size(); ++i) {
             mSessions.valueAt(i).terminate();
         }
@@ -250,17 +250,19 @@
      *            client.
      */
     public void onInterfaceAddressChange(byte[] mac) {
-        mLocalLog.log("onInterfaceAddressChange: mClientId=" + mClientId
-                + ", mNotifyIdentityChange=" + mNotifyIdentityChange
-                + ", mac=" + String.valueOf(HexEncoding.encode(mac))
-                + ", mLastDiscoveryInterfaceMac="
-                + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac)));
+        if (mDbg) {
+            Log.v(TAG, "onInterfaceAddressChange: mClientId=" + mClientId
+                    + ", mNotifyIdentityChange=" + mNotifyIdentityChange
+                    + ", mac=" + String.valueOf(HexEncoding.encode(mac))
+                    + ", mLastDiscoveryInterfaceMac="
+                    + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac)));
+        }
         if (mNotifyIdentityChange && !Arrays.equals(mac, mLastDiscoveryInterfaceMac)) {
             boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission(
                     mCallingPackage, mCallingFeatureId, mUid,
                     /* coarseForTargetSdkLessThanQ */ true, null);
             try {
-                if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
+                if (mVdbg) Log.v(TAG, "hasPermission=" + hasPermission);
                 mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
             } catch (RemoteException e) {
                 Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
@@ -302,7 +304,7 @@
         boolean hasPermission = mWifiPermissionsUtil.checkCallersLocationPermission(
                 mCallingPackage, mCallingFeatureId, mUid,
                 /* coarseForTargetSdkLessThanQ */ true, null);
-        if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
+        if (mVdbg) Log.v(TAG, "hasPermission=" + hasPermission);
         if (!Arrays.equals(currentDiscoveryInterfaceMac, mLastDiscoveryInterfaceMac)) {
             try {
                 mCallback.onIdentityChanged(
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index 1d770ab..0bf8e13 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -46,7 +46,6 @@
 import android.os.Looper;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -91,7 +90,7 @@
  */
 public class WifiAwareDataPathStateManager {
     private static final String TAG = "WifiAwareDataPathStMgr";
-    private static final boolean VDBG = false; // STOPSHIP if true
+    private static boolean sVdbg = false; // STOPSHIP if true
 
     private static final String AWARE_INTERFACE_PREFIX = "aware_data";
     private static final String NETWORK_TAG = "WIFI_AWARE_FACTORY";
@@ -122,17 +121,14 @@
     private Handler mHandler;
     private WifiAwareNetworkFactory mNetworkFactory;
     public NetdWrapper mNetdWrapper;
-    private final LocalLog mLocalLog;
     private final SparseArray<Object> mDelayNetworkValidationMap = new SparseArray<>();
 
     // internal debug flag to override API check
     /* package */ boolean mAllowNdpResponderFromAnyOverride = false;
 
-    public WifiAwareDataPathStateManager(WifiAwareStateManager mgr, Clock clock,
-            LocalLog localLog) {
+    public WifiAwareDataPathStateManager(WifiAwareStateManager mgr, Clock clock) {
         mMgr = mgr;
         mClock = clock;
-        mLocalLog = localLog;
     }
 
     private static NetworkCapabilities makeNetworkCapabilitiesFilter() {
@@ -162,7 +158,7 @@
     public void start(Context context, Looper looper, WifiAwareMetrics awareMetrics,
             WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper,
             NetdWrapper netdWrapper) {
-        if (VDBG) Log.v(TAG, "start");
+        if (sVdbg) Log.v(TAG, "start");
 
         mContext = context;
         mAwareMetrics = awareMetrics;
@@ -182,8 +178,9 @@
      * Enable/Disable verbose logging.
      *
      */
-    public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
+    public void enableVerboseLogging(boolean verboseEnabled, boolean vDbg) {
         mVerboseLoggingEnabled = verboseEnabled;
+        sVdbg = vDbg;
     }
 
     /**
@@ -213,10 +210,10 @@
 
     private Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation>
                 getNetworkRequestByCanonicalDescriptor(CanonicalConnectionInfo cci) {
-        if (VDBG) Log.v(TAG, "getNetworkRequestByCanonicalDescriptor: cci=" + cci);
+        if (sVdbg) Log.v(TAG, "getNetworkRequestByCanonicalDescriptor: cci=" + cci);
         for (Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> entry :
                 mNetworkRequestsCache.entrySet()) {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "getNetworkRequestByCanonicalDescriptor: entry=" + entry.getValue()
                         + " --> cci=" + entry.getValue().getCanonicalDescriptor());
             }
@@ -390,15 +387,17 @@
      */
     public boolean onDataPathRequest(int pubSubId, byte[] mac, int ndpId,
             byte[] message) {
-        mLocalLog.log("onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
-                HexEncoding.encode(mac)) + ", ndpId=" + ndpId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
+                    HexEncoding.encode(mac)) + ", ndpId=" + ndpId);
+        }
 
         // it is also possible that this is an initiator-side data-path request indication (which
         // happens when the Responder responds). In such a case it will be matched by the NDP ID.
         Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
                 getNetworkRequestByNdpId(ndpId);
         if (nnriE != null) {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG,
                         "onDataPathRequest: initiator-side indication for " + nnriE);
             }
@@ -468,7 +467,7 @@
         if (nnri == null) {
             Log.w(TAG, "onDataPathRequest: can't find a request with specified pubSubId=" + pubSubId
                     + ", mac=" + String.valueOf(HexEncoding.encode(mac)));
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "onDataPathRequest: network request cache = " + mNetworkRequestsCache);
             }
             mMgr.respondToDataPathRequest(false, ndpId, "", null, false, null);
@@ -511,14 +510,16 @@
      * @return true if framework start to waiting for the confirm
      */
     public boolean onRespondToDataPathRequest(int ndpId, boolean success, int reasonOnFailure) {
-        mLocalLog.log("onRespondToDataPathRequest: ndpId=" + ndpId + ", success=" + success);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onRespondToDataPathRequest: ndpId=" + ndpId + ", success=" + success);
+        }
         Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
                 getNetworkRequestByNdpId(ndpId);
 
         if (nnriE == null) {
             Log.w(TAG, "onRespondToDataPathRequest: can't find a request with specified ndpId="
                     + ndpId);
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "onRespondToDataPathRequest: network request cache = "
                         + mNetworkRequestsCache);
             }
@@ -579,11 +580,13 @@
      */
     public boolean onDataPathConfirm(int ndpId, byte[] mac, boolean accept,
             int reason, byte[] message, List<WifiAwareChannelInfo> channelInfo) {
-        mLocalLog.log("onDataPathConfirm: ndpId=" + ndpId
-                + ", mac=" + String.valueOf(HexEncoding.encode(mac))
-                + ", accept=" + accept + ", reason=" + reason
-                + ", message.length=" + ((message == null) ? 0 : message.length)
-                + ", channelInfo=" + channelInfo);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onDataPathConfirm: ndpId=" + ndpId
+                    + ", mac=" + String.valueOf(HexEncoding.encode(mac))
+                    + ", accept=" + accept + ", reason=" + reason
+                    + ", message.length=" + ((message == null) ? 0 : message.length)
+                    + ", channelInfo=" + channelInfo);
+        }
 
         Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
                 getNetworkRequestByNdpId(ndpId);
@@ -636,7 +639,7 @@
                     return true;
                 }
             } else {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "onDataPathConfirm: interface already configured: "
                             + nnri.interfaceName);
                 }
@@ -663,7 +666,7 @@
             nnri.startValidationTimestamp = mClock.getElapsedSinceBootMillis();
             handleAddressValidation(nnri, ndpInfo, networkSpecifier.isOutOfBand(), mac);
         } else {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "onDataPathConfirm: data-path for networkSpecifier=" + networkSpecifier
                         + " rejected - reason=" + reason);
             }
@@ -715,7 +718,7 @@
         if (!(ndpInfo.peerIpv6 != null && mNiWrapper.configureAgentProperties(nnri,
                 ncBuilder, linkProperties)
                 && mNiWrapper.isAddressUsable(linkProperties))) {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.d(TAG, "Failed address validation");
             }
             if (!isAddressValidationExpired(nnri, ndpInfo.ndpId)) {
@@ -739,7 +742,9 @@
                     ndpInfo.peerPort, ndpInfo.peerTransportProtocol,
                     ndpInfo.channelInfos);
             ncBuilder.setTransportInfo(ni);
-            mLocalLog.log("onDataPathConfirm: AwareNetworkInfo=" + ni);
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "onDataPathConfirm: AwareNetworkInfo=" + ni);
+            }
             final NetworkAgentConfig naConfig = new NetworkAgentConfig.Builder()
                     .setLegacyType(ConnectivityManager.TYPE_NONE)
                     .setLegacyTypeName(NETWORK_TAG)
@@ -783,14 +788,16 @@
      * @param ndpId The ID of the terminated data-path.
      */
     public void onDataPathEnd(int ndpId) {
-        mLocalLog.log("onDataPathEnd: ndpId=" + ndpId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onDataPathEnd: ndpId=" + ndpId);
+        }
 
         cleanNetworkValidationTask(ndpId);
 
         Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
                 getNetworkRequestByNdpId(ndpId);
         if (nnriE == null) {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "onDataPathEnd: network request not found for ndpId=" + ndpId);
             }
             return;
@@ -872,7 +879,7 @@
         Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
                 getNetworkRequestByNdpId(ndpId);
         if (nnriE == null) {
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG,
                         "handleDataPathTimeout: network request not found for networkSpecifier="
                                 + ndpId);
@@ -904,7 +911,7 @@
 
         public void tickleConnectivityIfWaiting() {
             if (mWaitingForTermination) {
-                if (VDBG) Log.v(TAG, "tickleConnectivityIfWaiting: was waiting!");
+                if (sVdbg) Log.v(TAG, "tickleConnectivityIfWaiting: was waiting!");
                 mWaitingForTermination = false;
                 reevaluateAllRequests();
             }
@@ -912,7 +919,9 @@
 
         @Override
         public boolean acceptRequest(NetworkRequest request) {
-            mLocalLog.log("WifiAwareNetworkFactory.acceptRequest: request=" + request);
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request);
+            }
 
             NetworkSpecifier networkSpecifierBase = request.getNetworkSpecifier();
             if (!(networkSpecifierBase instanceof WifiAwareNetworkSpecifier)) {
@@ -922,7 +931,7 @@
             }
 
             if (!mMgr.isUsageEnabled()) {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
                             + " -- Aware disabled");
                 }
@@ -943,7 +952,7 @@
             // look up specifier - are we being called again?
             AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
             if (nnri != null) {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
                             + " - already in cache with state=" + nnri.state);
                 }
@@ -972,9 +981,11 @@
             Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> primaryRequest =
                     getNetworkRequestByCanonicalDescriptor(nnri.getCanonicalDescriptor());
             if (primaryRequest != null) {
-                mLocalLog.log("WifiAwareNetworkFactory.acceptRequest: request=" + request
-                        + ", already has a primary request=" + primaryRequest.getKey()
-                        + " with state=" + primaryRequest.getValue().state);
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+                            + ", already has a primary request=" + primaryRequest.getKey()
+                            + " with state=" + primaryRequest.getValue().state);
+                }
 
                 if (primaryRequest.getValue().state
                         == AwareNetworkRequestInformation.STATE_TERMINATING) {
@@ -993,7 +1004,9 @@
 
         @Override
         protected void needNetworkFor(NetworkRequest networkRequest) {
-            mLocalLog.log("WifiAwareNetworkFactory.needNetworkFor: networkRequest=");
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest=");
+            }
 
             NetworkSpecifier networkSpecifierObj = networkRequest.getNetworkSpecifier();
             WifiAwareNetworkSpecifier networkSpecifier = null;
@@ -1008,7 +1021,7 @@
             }
 
             if (nnri.state != AwareNetworkRequestInformation.STATE_IDLE) {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
                             + networkRequest + " - already in progress");
                     // TODO: understand how/when can be called again/while in progress (seems
@@ -1050,8 +1063,10 @@
 
         @Override
         protected void releaseNetworkFor(NetworkRequest networkRequest) {
-            mLocalLog.log("WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
-                    + networkRequest);
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+                        + networkRequest);
+            }
 
             NetworkSpecifier networkSpecifierObj = networkRequest.getNetworkSpecifier();
             WifiAwareNetworkSpecifier networkSpecifier = null;
@@ -1067,7 +1082,7 @@
             }
 
             if (nnri.networkAgent != null) {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
                             + networkRequest + ", nnri=" + nnri
                             + ": agent already created - deferring ending data-path to agent"
@@ -1088,7 +1103,7 @@
                             + networkRequest);
                 }
                 if (nnri.ndpInfos.size() != 0) {
-                    if (VDBG) Log.v(TAG, "releaseNetworkFor: in progress NDP being terminated");
+                    if (sVdbg) Log.v(TAG, "releaseNetworkFor: in progress NDP being terminated");
                     for (int index = 0; index < nnri.ndpInfos.size(); index++) {
                         int ndpId = nnri.ndpInfos.keyAt(index);
                         cleanNetworkValidationTask(ndpId);
@@ -1099,7 +1114,7 @@
                     mNetworkRequestsCache.remove(networkSpecifier);
                 }
             } else {
-                if (VDBG) {
+                if (sVdbg) {
                     Log.v(TAG, "releaseNetworkFor: equivalent requests exist - not terminating "
                             + "networkRequest=" + networkRequest);
                 }
@@ -1156,13 +1171,17 @@
     }
 
     private void tearDownInterfaceIfPossible(AwareNetworkRequestInformation nnri) {
-        mLocalLog.log("tearDownInterfaceIfPossible: nnri=" + nnri);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "tearDownInterfaceIfPossible: nnri=" + nnri);
+        }
 
         if (!TextUtils.isEmpty(nnri.interfaceName)) {
             boolean interfaceUsedByAnotherNdp = isInterfaceUpAndUsedByAnotherNdp(nnri);
             if (interfaceUsedByAnotherNdp) {
-                mLocalLog.log("tearDownInterfaceIfPossible: interfaceName=" + nnri.interfaceName
-                        + ", still in use - not turning down");
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "tearDownInterfaceIfPossible: interfaceName=" + nnri.interfaceName
+                            + ", still in use - not turning down");
+                }
             } else {
                 try {
                     mNetdWrapper.setInterfaceDown(nnri.interfaceName);
@@ -1241,7 +1260,7 @@
             unused.remove(nnri.interfaceName);
         }
 
-        if (VDBG) {
+        if (sVdbg) {
             Log.v(TAG, "selectInterfaceForRequest: unUsed=" + unused + ", inuse=" + inuse
                     + ", invalid" + invalid + ", allInterfaces" + mInterfaces);
         }
@@ -1356,7 +1375,7 @@
         public Set<NetworkRequest> equivalentRequests = new HashSet<>();
 
         void updateToSupportNewRequest(NetworkRequest ns) {
-            if (VDBG) Log.v(TAG, "updateToSupportNewRequest: ns=" + ns);
+            if (sVdbg) Log.v(TAG, "updateToSupportNewRequest: ns=" + ns);
             if (equivalentRequests.add(ns) && state == STATE_CONFIRMED) {
                 if (networkAgent == null) {
                     Log.wtf(TAG, "updateToSupportNewRequest: null agent in CONFIRMED state!?");
@@ -1368,7 +1387,7 @@
         }
 
         void removeSupportForRequest(NetworkRequest ns) {
-            if (VDBG) Log.v(TAG, "removeSupportForRequest: ns=" + ns);
+            if (sVdbg) Log.v(TAG, "removeSupportForRequest: ns=" + ns);
             equivalentRequests.remove(ns);
 
             // we will not update the agent:
@@ -1416,7 +1435,7 @@
             String packageName = null;
             byte[] peerMac = ns.peerMac;
 
-            if (VDBG) {
+            if (sVdbg) {
                 Log.v(TAG, "processNetworkSpecifier: networkSpecifier=" + ns);
             }
 
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
index 322b091..06bc0a7 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
@@ -33,7 +33,6 @@
 import android.net.wifi.util.HexEncoding;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -65,7 +64,6 @@
     private long mUpdateTime;
     private boolean mInstantModeEnabled;
     private int mInstantModeBand;
-    private final LocalLog mLocalLog;
     private AwarePairingConfig mPairingConfig;
     private boolean mIsSuspendable;
     private boolean mIsSuspended;
@@ -92,7 +90,7 @@
     public WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId,
             byte pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession,
             boolean isRangingEnabled, long creationTime, boolean instantModeEnabled,
-            int instantModeBand, boolean isSuspendable, LocalLog localLog,
+            int instantModeBand, boolean isSuspendable,
             AwarePairingConfig pairingConfig) {
         mWifiAwareNativeApi = wifiAwareNativeApi;
         mSessionId = sessionId;
@@ -105,7 +103,6 @@
         mInstantModeEnabled = instantModeEnabled;
         mInstantModeBand = instantModeBand;
         mIsSuspendable = isSuspendable;
-        mLocalLog = localLog;
         mPairingConfig = pairingConfig;
     }
 
@@ -709,7 +706,6 @@
         PeerInfo newPeerInfo = new PeerInfo(requestorInstanceId, peerMac);
         mPeerInfoByRequestorInstanceId.put(newPeerId, newPeerInfo);
         Log.d(TAG, "New peer info: peerId=" + newPeerId + ", peerInfo=" + newPeerInfo);
-        mLocalLog.log("New peer info: peerId=" + newPeerId + ", peerInfo=" + newPeerInfo);
 
         return newPeerId;
     }
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
index 38a8900..f739197 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
@@ -44,7 +44,7 @@
  */
 public class WifiAwareNativeApi implements WifiAwareShellCommand.DelegatedShellCommand {
     private static final String TAG = "WifiAwareNativeApi";
-    private static final boolean VDBG = false; // STOPSHIP if true
+    private boolean mVdbg = false; // STOPSHIP if true
     private boolean mVerboseLoggingEnabled = false;
 
     private final WifiAwareNativeManager mHal;
@@ -53,26 +53,27 @@
     public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) {
         mHal = wifiAwareNativeManager;
         onReset();
-        if (VDBG) {
-            mTransactionIds = new SparseIntArray();
-        }
     }
 
     /**
      * Enable/Disable verbose logging.
      *
      */
-    public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
+    public void enableVerboseLogging(boolean verboseEnabled, boolean vDbg) {
         mVerboseLoggingEnabled = verboseEnabled;
+        mVdbg = vDbg;
     }
 
     private void recordTransactionId(int transactionId) {
-        if (!VDBG) return;
+        if (!mVdbg) return;
 
         if (transactionId == 0) {
             return; // tid == 0 is used as a placeholder transaction ID in several commands
         }
 
+        if (mTransactionIds == null) {
+            mTransactionIds = new SparseIntArray();
+        }
         int count = mTransactionIds.get(transactionId);
         if (count != 0) {
             Log.wtf(TAG, "Repeated transaction ID == " + transactionId);
@@ -176,18 +177,18 @@
         final PrintWriter pw = parentShell.getErrPrintWriter();
 
         String subCmd = parentShell.getNextArgRequired();
-        if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
+        if (mVdbg) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'");
         switch (subCmd) {
             case "set": {
                 String name = parentShell.getNextArgRequired();
-                if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+                if (mVdbg) Log.v(TAG, "onCommand: name='" + name + "'");
                 if (!mSettableParameters.containsKey(name)) {
                     pw.println("Unknown parameter name -- '" + name + "'");
                     return -1;
                 }
 
                 String valueStr = parentShell.getNextArgRequired();
-                if (VDBG) Log.v(TAG, "onCommand: valueStr='" + valueStr + "'");
+                if (mVdbg) Log.v(TAG, "onCommand: valueStr='" + valueStr + "'");
                 int value;
                 try {
                     value = Integer.valueOf(valueStr);
@@ -203,7 +204,7 @@
                 String name = parentShell.getNextArgRequired();
                 String valueStr = parentShell.getNextArgRequired();
 
-                if (VDBG) {
+                if (mVdbg) {
                     Log.v(TAG, "onCommand: mode='" + mode + "', name='" + name + "'" + ", value='"
                             + valueStr + "'");
                 }
@@ -229,7 +230,7 @@
             }
             case "get": {
                 String name = parentShell.getNextArgRequired();
-                if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'");
+                if (mVdbg) Log.v(TAG, "onCommand: name='" + name + "'");
                 if (!mSettableParameters.containsKey(name)) {
                     pw.println("Unknown parameter name -- '" + name + "'");
                     return -1;
@@ -241,7 +242,7 @@
             case "get-power": {
                 String mode = parentShell.getNextArgRequired();
                 String name = parentShell.getNextArgRequired();
-                if (VDBG) Log.v(TAG, "onCommand: mode='" + mode + "', name='" + name + "'");
+                if (mVdbg) Log.v(TAG, "onCommand: mode='" + mode + "', name='" + name + "'");
                 if (!mSettablePowerParameters.containsKey(mode)) {
                     pw.println("Unknown mode -- '" + mode + "'");
                     return -1;
@@ -764,16 +765,24 @@
             byte[] pairingIdentityKey, boolean enablePairingCache, int requestType, byte[] pmk,
             String password, int akm, int cipherSuite) {
         if (mVerboseLoggingEnabled) {
-            Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
-                    + accept + ", int pairingId=" + pairingId
-                    + ", enablePairingCache=" + enablePairingCache
-                    + ", requestType" + requestType);
+            Log.v(
+                    TAG,
+                    "respondToPairingRequest: transactionId="
+                            + transactionId
+                            + ", accept="
+                            + accept
+                            + ", int pairingId="
+                            + pairingId
+                            + ", enablePairingCache="
+                            + enablePairingCache
+                            + ", requestType"
+                            + requestType);
         }
         recordTransactionId(transactionId);
 
         WifiNanIface iface = mHal.getWifiNanIface();
         if (iface == null) {
-            Log.e(TAG, "respondToDataPathRequest: null interface");
+            Log.e(TAG, "respondToPairingRequest: null interface");
             return false;
         }
         return iface.respondToPairingRequest(transactionId, pairingId, accept, pairingIdentityKey,
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
index 74eb0b3..0e8e33e 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
@@ -43,7 +43,7 @@
 public class WifiAwareNativeCallback implements WifiNanIface.Callback,
         WifiAwareShellCommand.DelegatedShellCommand {
     private static final String TAG = "WifiAwareNativeCallback";
-    private boolean mVerboseHalLoggingEnabled = false;
+    private boolean mVerboseLoggingEnabled = false;
 
     private final WifiAwareStateManager mWifiAwareStateManager;
 
@@ -55,8 +55,8 @@
      * Enable/Disable verbose logging.
      *
      */
-    public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
-        mVerboseHalLoggingEnabled = halVerboseEnabled;
+    public void enableVerboseLogging(boolean verboseEnabled) {
+        mVerboseLoggingEnabled = verboseEnabled;
     }
 
     /*
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
index acbe274..1cdd58c 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -33,7 +33,7 @@
  */
 public class WifiAwareNativeManager {
     private static final String TAG = "WifiAwareNativeManager";
-    private boolean mVerboseHalLoggingEnabled = false;
+    private boolean mVerboseLoggingEnabled = false;
 
     // to be used for synchronizing access to any of the WifiAwareNative objects
     private final Object mLock = new Object();
@@ -58,9 +58,9 @@
      * Enable/Disable verbose logging.
      */
     public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
-        mVerboseHalLoggingEnabled = halVerboseEnabled;
+        mVerboseLoggingEnabled = verboseEnabled;
         if (mWifiNanIface != null) {
-            mWifiNanIface.enableVerboseLogging(mVerboseHalLoggingEnabled);
+            mWifiNanIface.enableVerboseLogging(halVerboseEnabled);
         }
     }
 
@@ -76,7 +76,7 @@
                 new HalDeviceManager.ManagerStatusListener() {
                     @Override
                     public void onStatusChanged() {
-                        if (mVerboseHalLoggingEnabled) Log.v(TAG, "onStatusChanged");
+                        if (mVerboseLoggingEnabled) Log.v(TAG, "onStatusChanged");
                         // only care about isStarted (Wi-Fi started) not isReady - since if not
                         // ready then Wi-Fi will also be down.
                         if (mHalDeviceManager.isStarted()) {
@@ -107,7 +107,7 @@
      */
     public void tryToGetAware(@NonNull WorkSource requestorWs) {
         synchronized (mLock) {
-            if (mVerboseHalLoggingEnabled) {
+            if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface
                         + ", mReferenceCount=" + mReferenceCount + ", requestorWs=" + requestorWs);
             }
@@ -129,7 +129,7 @@
                 Log.e(TAG, "Was not able to obtain a WifiNanIface (even though enabled!?)");
                 awareIsDown(true);
             } else {
-                if (mVerboseHalLoggingEnabled) Log.v(TAG, "Obtained a WifiNanIface");
+                if (mVerboseLoggingEnabled) Log.v(TAG, "Obtained a WifiNanIface");
                 if (!iface.registerFrameworkCallback(mWifiAwareNativeCallback)) {
                     Log.e(TAG, "Unable to register callback with WifiNanIface");
                     mHalDeviceManager.removeIface(iface);
@@ -138,7 +138,7 @@
                 }
                 mWifiNanIface = iface;
                 mReferenceCount = 1;
-                mWifiNanIface.enableVerboseLogging(mVerboseHalLoggingEnabled);
+                mWifiNanIface.enableVerboseLogging(mVerboseLoggingEnabled);
             }
         }
     }
@@ -147,7 +147,7 @@
      * Release the HAL NAN interface.
      */
     public void releaseAware() {
-        if (mVerboseHalLoggingEnabled) {
+        if (mVerboseLoggingEnabled) {
             Log.d(TAG, "releaseAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount="
                     + mReferenceCount);
         }
@@ -178,7 +178,7 @@
      */
     public boolean replaceRequestorWs(@NonNull WorkSource requestorWs) {
         synchronized (mLock) {
-            if (mVerboseHalLoggingEnabled) {
+            if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "replaceRequestorWs: mWifiNanIface=" + mWifiNanIface
                         + ", mReferenceCount=" + mReferenceCount + ", requestorWs=" + requestorWs);
             }
@@ -198,7 +198,7 @@
 
     private void awareIsDown(boolean markAsAvailable) {
         synchronized (mLock) {
-            if (mVerboseHalLoggingEnabled) {
+            if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface
                         + ", mReferenceCount =" + mReferenceCount);
             }
@@ -214,7 +214,7 @@
 
         @Override
         public void onDestroyed(@NonNull String ifaceName) {
-            if (mVerboseHalLoggingEnabled) {
+            if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "Interface was destroyed: mWifiNanIface=" + mWifiNanIface
                         + ", active=" + active);
             }
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index 2bc8a65..c75b0df 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -19,7 +19,7 @@
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128;
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_256;
 
-import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_AWARE_VERBOSE_LOGGING_ENABLED;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -140,22 +140,23 @@
                     interfaceConflictManager);
 
             settingsConfigStore.registerChangeListener(
-                    WIFI_VERBOSE_LOGGING_ENABLED,
+                    WIFI_AWARE_VERBOSE_LOGGING_ENABLED,
                     (key, newValue) -> enableVerboseLogging(newValue),
                     mHandler);
-            enableVerboseLogging(settingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED));
+            enableVerboseLogging(settingsConfigStore.get(WIFI_AWARE_VERBOSE_LOGGING_ENABLED));
         });
     }
 
     private void enableVerboseLogging(boolean verboseEnabled) {
         mVerboseHalLoggingEnabled = verboseEnabled;
         updateVerboseLoggingEnabled();
-        mStateManager.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseLoggingEnabled);
-        mWifiAwareNativeCallback.enableVerboseLogging(mVerboseLoggingEnabled,
-                mVerboseLoggingEnabled);
+        boolean vDbg = verboseEnabled || mContext.getResources()
+                .getBoolean(R.bool.config_aware_vdbg_enable_on_verbose_logging);
+        mStateManager.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseLoggingEnabled, vDbg);
+        mWifiAwareNativeCallback.enableVerboseLogging(mVerboseLoggingEnabled);
         mWifiAwareNativeManager.enableVerboseLogging(mVerboseLoggingEnabled,
                 mVerboseLoggingEnabled);
-        mWifiAwareNativeApi.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseLoggingEnabled);
+        mWifiAwareNativeApi.enableVerboseLogging(mVerboseLoggingEnabled, vDbg);
     }
 
     /**
@@ -246,6 +247,9 @@
         int uid = getMockableCallingUid();
         mWifiPermissionsUtil.checkPackage(uid, callingPackage);
         enforceChangePermission();
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "resetPairedDevices: callingPackage=" + callingPackage);
+        }
         mStateManager.resetPairedDevices(callingPackage);
     }
 
@@ -254,6 +258,9 @@
         int uid = getMockableCallingUid();
         mWifiPermissionsUtil.checkPackage(uid, callingPackage);
         enforceChangePermission();
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "removePairedDevice: callingPackage=" + callingPackage + ", alias=" + alias);
+        }
         mStateManager.removePairedDevice(callingPackage, alias);
     }
 
@@ -273,6 +280,14 @@
         int uid = getMockableCallingUid();
         mWifiPermissionsUtil.checkPackage(uid, callingPackage);
         enforceChangePermission();
+        if (mVerboseLoggingEnabled) {
+            Log.v(
+                    TAG,
+                    "setOpportunisticModeEnabled: callingPackage="
+                            + callingPackage
+                            + ", enabled="
+                            + enabled);
+        }
         mStateManager.setOpportunisticPackage(callingPackage, enabled);
     }
 
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
index b0bed20..4c099d7 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -19,11 +19,44 @@
 import static android.Manifest.permission.ACCESS_WIFI_STATE;
 import static android.net.wifi.WifiAvailableChannel.OP_MODE_WIFI_AWARE;
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_RESUME_INTERNAL_ERROR;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_RESUME_INVALID_SESSION;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_RESUME_REDUNDANT_REQUEST;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_SUSPEND_CANNOT_SUSPEND;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_SUSPEND_INTERNAL_ERROR;
 import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_SUSPEND_INVALID_SESSION;
 import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST;
 
+import static com.android.server.wifi.aware.WifiAwareMetrics.convertNanStatusCodeToWifiStatsLogEnum;
+import static com.android.server.wifi.hal.WifiNanIface.NanStatusCode.NOT_SUPPORTED;
+import static com.android.server.wifi.hal.WifiNanIface.NanStatusCode.NO_CONNECTION;
+import static com.android.server.wifi.hal.WifiNanIface.NanStatusCode.REDUNDANT_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_CAPABILITIES;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_API_UNKNOWN;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_CONFIG_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_CREATE_DATA_INTERFACE_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_DELETE_DATA_INTERFACE_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_DISABLE_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_ENABLE_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_GET_CAPABILITIES_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_BOOTSTRAPPING_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_DATA_PATH_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_PAIRING_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_BOOTSTRAPPING_INDICATION_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_DATA_PATH_INDICATION_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_PAIRING_INDICATION_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESUME_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_START_PUBLISH_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_START_SUBSCRIBE_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_SUSPEND_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TERMINATE_DATA_PATH_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TERMINATE_PAIRING_REQUEST;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TRANSMIT_FOLLOW_UP_REQUEST;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.StatsManager;
 import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -67,10 +100,10 @@
 import android.os.WorkSource;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.MessageUtils;
@@ -86,6 +119,7 @@
 import com.android.server.wifi.WifiInjector;
 import com.android.server.wifi.aware.PairingConfigManager.PairingSecurityAssociationInfo;
 import com.android.server.wifi.hal.WifiNanIface.NanStatusCode;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.util.NetdWrapper;
 import com.android.server.wifi.util.WaitingState;
 import com.android.server.wifi.util.WifiPermissionsUtil;
@@ -113,7 +147,7 @@
  */
 public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShellCommand {
     private static final String TAG = "WifiAwareStateManager";
-    private static final boolean VDBG = false; // STOPSHIP if true - for detailed state machine
+    private boolean mVdbg = false; // STOPSHIP if true - for detailed state machine
     private boolean mVerboseLoggingEnabled = false;
 
     @VisibleForTesting
@@ -337,7 +371,6 @@
     private WifiManager mWifiManager;
     private Handler mHandler;
     private final WifiInjector mWifiInjector;
-    private final LocalLog mLocalLog;
     private final PairingConfigManager mPairingConfigManager;
 
     private final SparseArray<WifiAwareClientState> mClients = new SparseArray<>();
@@ -363,6 +396,9 @@
     private boolean mAwareIsDisabling = false;
     private final SparseArray<PairingInfo> mPairingRequest = new SparseArray<>();
     private final SparseArray<BootStrppingInfo> mBootstrappingRequest = new SparseArray<>();
+    private WifiAwarePullAtomCallback mWifiAwarePullAtomCallback = null;
+
+    private long mStartTime;
 
     private static class PairingInfo {
         public final int mClientId;
@@ -398,7 +434,6 @@
     public WifiAwareStateManager(WifiInjector wifiInjector,
             PairingConfigManager pairingConfigManager) {
         mWifiInjector = wifiInjector;
-        mLocalLog = wifiInjector.getWifiAwareLocalLog();
         mPairingConfigManager = pairingConfigManager;
         onReset();
     }
@@ -406,10 +441,12 @@
     /**
      * Enable/Disable verbose logging.
      */
-    public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) {
+    public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseLogging,
+            boolean vDbg) {
         mVerboseLoggingEnabled = verboseEnabled;
-        mDataPathMgr.enableVerboseLogging(verboseEnabled, halVerboseEnabled);
-        mSm.setDbg(halVerboseEnabled);
+        mDataPathMgr.enableVerboseLogging(verboseEnabled, vDbg);
+        mSm.setDbg(halVerboseLogging);
+        mVdbg = vDbg;
     }
 
     /**
@@ -633,11 +670,11 @@
         mWifiPermissionsUtil = wifiPermissionsUtil;
         mInterfaceConflictMgr = interfaceConflictMgr;
         mSm = new WifiAwareStateMachine(TAG, looper);
-        mSm.setDbg(VDBG);
+        mSm.setDbg(mVdbg);
         mSm.start();
         mHandler = new Handler(looper);
 
-        mDataPathMgr = new WifiAwareDataPathStateManager(this, clock, mLocalLog);
+        mDataPathMgr = new WifiAwareDataPathStateManager(this, clock);
         mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics,
                 wifiPermissionsUtil, permissionsWrapper, netdWrapper);
 
@@ -648,68 +685,85 @@
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (mVerboseLoggingEnabled) Log.v(TAG, "BroadcastReceiver: action=" + action);
-                if (action.equals(Intent.ACTION_SCREEN_ON)
-                        || action.equals(Intent.ACTION_SCREEN_OFF)) {
-                    reconfigure();
-                }
-
-                if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
-                    if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0) {
-                        if (mPowerManager.isDeviceIdleMode()
-                                && !isAnyCallerIgnoringBatteryOptimizations()) {
-                            disableUsage(false);
-                        } else {
-                            enableUsage();
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        String action = intent.getAction();
+                        if (mVerboseLoggingEnabled) {
+                            Log.v(TAG, "BroadcastReceiver: action=" + action);
                         }
-                    } else {
-                        reconfigure();
+                        if (action.equals(Intent.ACTION_SCREEN_ON)
+                                || action.equals(Intent.ACTION_SCREEN_OFF)) {
+                            reconfigure();
+                        }
+
+                        if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+                            if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0) {
+                                if (mPowerManager.isDeviceIdleMode()
+                                        && !isAnyCallerIgnoringBatteryOptimizations()) {
+                                    disableUsage(false);
+                                } else {
+                                    enableUsage();
+                                }
+                            } else {
+                                reconfigure();
+                            }
+                        }
                     }
-                }
-            }
-        }, intentFilter);
+                },
+                intentFilter,
+                null,
+                mHandler);
 
         intentFilter = new IntentFilter();
         intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (mVerboseLoggingEnabled) {
-                    Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent);
-                }
-                if (wifiPermissionsUtil.isLocationModeEnabled()) {
-                    enableUsage();
-                } else {
-                    if (SdkLevel.isAtLeastT()) {
-                        handleLocationModeDisabled();
-                    } else {
-                        disableUsage(false);
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (mVerboseLoggingEnabled) {
+                            Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent);
+                        }
+                        if (wifiPermissionsUtil.isLocationModeEnabled()) {
+                            enableUsage();
+                        } else {
+                            if (SdkLevel.isAtLeastT()) {
+                                handleLocationModeDisabled();
+                            } else {
+                                disableUsage(false);
+                            }
+                        }
                     }
-                }
-            }
-        }, intentFilter);
+                },
+                intentFilter,
+                null,
+                mHandler);
 
         intentFilter = new IntentFilter();
         intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (mVerboseLoggingEnabled) {
-                    Log.v(TAG, "onReceive: WIFI_STATE_CHANGED_ACTION: intent=" + intent);
-                }
-                boolean isEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-                if (isEnabled) {
-                    enableUsage();
-                } else {
-                    disableUsage(false);
-                }
-            }
-        }, intentFilter);
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (mVerboseLoggingEnabled) {
+                            Log.v(TAG, "onReceive: WIFI_STATE_CHANGED_ACTION: intent=" + intent);
+                        }
+                        boolean isEnabled =
+                                intent.getIntExtra(
+                                                WifiManager.EXTRA_WIFI_STATE,
+                                                WifiManager.WIFI_STATE_UNKNOWN)
+                                        == WifiManager.WIFI_STATE_ENABLED;
+                        if (isEnabled) {
+                            enableUsage();
+                        } else {
+                            disableUsage(false);
+                        }
+                    }
+                },
+                intentFilter,
+                null,
+                mHandler);
     }
 
     private class CountryCodeChangeCallback implements
@@ -834,7 +888,7 @@
     public void requestMacAddresses(int uid, int[] peerIds,
             IWifiAwareMacAddressProvider callback) {
         mSm.getHandler().post(() -> {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "requestMacAddresses: uid=" + uid + ", peerIds="
                         + Arrays.toString(peerIds));
             }
@@ -2162,7 +2216,7 @@
         private class DefaultState extends State {
             @Override
             public boolean processMessage(Message msg) {
-                if (VDBG) {
+                if (mVdbg) {
                     Log.v(TAG, getName() + msg.toString());
                 }
 
@@ -2213,7 +2267,7 @@
         private class WaitState extends State {
             @Override
             public boolean processMessage(Message msg) {
-                if (VDBG) {
+                if (mVdbg) {
                     Log.v(TAG, getName() + msg.toString());
                 }
 
@@ -2250,6 +2304,7 @@
                 mTimeoutMessage = new WakeupMessage(mContext, getHandler(), HAL_COMMAND_TIMEOUT_TAG,
                         MESSAGE_TYPE_RESPONSE_TIMEOUT, mCurrentCommand.arg1, mCurrentTransactionId);
                 mTimeoutMessage.schedule(SystemClock.elapsedRealtime() + AWARE_COMMAND_TIMEOUT);
+                mStartTime = SystemClock.elapsedRealtime();
             }
 
             @Override
@@ -2259,7 +2314,7 @@
 
             @Override
             public boolean processMessage(Message msg) {
-                if (VDBG) {
+                if (mVdbg) {
                     Log.v(TAG, getName() + msg.toString());
                 }
 
@@ -2303,7 +2358,7 @@
         }
 
         private void processNotification(Message msg) {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "processNotification: msg=" + msg);
             }
 
@@ -2385,7 +2440,7 @@
                 case NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: {
                     short transactionId = (short) msg.arg2;
                     Message queuedSendCommand = mFwQueuedSendMessages.get(transactionId);
-                    if (VDBG) {
+                    if (mVdbg) {
                         Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: queuedSendCommand="
                                 + queuedSendCommand);
                     }
@@ -2408,7 +2463,7 @@
                     short transactionId = (short) msg.arg2;
                     int reason = (Integer) msg.obj;
                     Message sentMessage = mFwQueuedSendMessages.get(transactionId);
-                    if (VDBG) {
+                    if (mVdbg) {
                         Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: sentMessage="
                                 + sentMessage);
                     }
@@ -2564,7 +2619,7 @@
          * disconnected/terminate commands failure is not possible.
          */
         private boolean processCommand(Message msg) {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "processCommand: msg=" + msg);
             }
 
@@ -2610,19 +2665,13 @@
                     boolean reEnableAware = msg.getData()
                             .getBoolean(MESSAGE_BUNDLE_KEY_RE_ENABLE_AWARE_FROM_OFFLOAD);
                     int callerType = msg.getData().getInt(MESSAGE_BUNDLE_KEY_CALLER_TYPE);
-                    int proceedWithOperation;
-
-                    if (awareOffload || mOpportunisticSet.contains(callingPackage)) {
-                        // As this is lowest priorty, should always execute, no dialog to ask user
-                        proceedWithOperation = InterfaceConflictManager.ICM_EXECUTE_COMMAND;
-                    } else {
-                        proceedWithOperation =
-                                mInterfaceConflictMgr.manageInterfaceConflictForStateMachine(TAG,
-                                        msg, this, mWaitingState, mWaitState,
-                                        HalDeviceManager.HDM_CREATE_IFACE_NAN,
-                                        new WorkSource(uid, callingPackage),
-                                        false /* bypassDialog */);
-                    }
+                    int proceedWithOperation =
+                            mInterfaceConflictMgr.manageInterfaceConflictForStateMachine(TAG,
+                                    msg, this, mWaitingState, mWaitState,
+                                    HalDeviceManager.HDM_CREATE_IFACE_NAN,
+                                    new WorkSource(uid, callingPackage),
+                                    awareOffload || mOpportunisticSet.contains(callingPackage)
+                                    /* bypassDialog */);
 
                     if (proceedWithOperation == InterfaceConflictManager.ICM_ABORT_COMMAND) {
                         // handling user rejection or possible conflict (pending command)
@@ -2715,7 +2764,7 @@
                     break;
                 }
                 case COMMAND_TYPE_ENQUEUE_SEND_MESSAGE: {
-                    if (VDBG) {
+                    if (mVdbg) {
                         Log.v(TAG, "processCommand: ENQUEUE_SEND_MESSAGE - messageId="
                                 + msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID)
                                 + ", mSendArrivalSequenceCounter=" + mSendArrivalSequenceCounter);
@@ -2803,13 +2852,13 @@
                 }
                 case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE: {
                     if (mSendQueueBlocked || mHostQueuedSendMessages.size() == 0) {
-                        if (VDBG) {
+                        if (mVdbg) {
                             Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - blocked or "
                                     + "empty host queue");
                         }
                         waitForResponse = false;
                     } else {
-                        if (VDBG) {
+                        if (mVdbg) {
                             Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - "
                                     + "sendArrivalSequenceCounter="
                                     + mHostQueuedSendMessages.keyAt(0));
@@ -2844,7 +2893,7 @@
                         waitForResponse = mWifiAwareNativeApi.getCapabilities(
                                 mCurrentTransactionId);
                     } else {
-                        if (VDBG) {
+                        if (mVdbg) {
                             Log.v(TAG, "COMMAND_TYPE_GET_CAPABILITIES: already have capabilities - "
                                     + "skipping");
                         }
@@ -2968,7 +3017,7 @@
         }
 
         private void processResponse(Message msg) {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "processResponse: msg=" + msg);
             }
 
@@ -2977,13 +3026,14 @@
                 mCurrentTransactionId = TRANSACTION_ID_IGNORE;
                 return;
             }
+            int reason = NanStatusCode.SUCCESS;
 
             switch (msg.arg1) {
                 case RESPONSE_TYPE_ON_CONFIG_SUCCESS:
                     onConfigCompletedLocal(mCurrentCommand);
                     break;
                 case RESPONSE_TYPE_ON_CONFIG_FAIL: {
-                    int reason = (Integer) msg.obj;
+                    reason = (Integer) msg.obj;
 
                     onConfigFailedLocal(mCurrentCommand, reason);
                     break;
@@ -2996,7 +3046,7 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL: {
-                    int reason = (Integer) msg.obj;
+                    reason = (int) msg.obj;
                     boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
 
                     onSessionConfigFailLocal(mCurrentCommand, isPublish, reason);
@@ -3013,7 +3063,7 @@
                         transmitNextMessage();
                     }
 
-                    if (VDBG) {
+                    if (mVdbg) {
                         Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_SUCCESS - arrivalSeq="
                                 + sentMessage.getData().getInt(
                                 MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ));
@@ -3021,25 +3071,23 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL: {
-                    if (VDBG) {
+                    if (mVdbg) {
                         Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - blocking!");
                     }
-                    int reason = (Integer) msg.obj;
+                    reason = (int) msg.obj;
+                    Message sentMessage = mCurrentCommand.getData().getParcelable(
+                            MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
                     if (reason == NanStatusCode.FOLLOWUP_TX_QUEUE_FULL) {
-                        Message sentMessage = mCurrentCommand.getData().getParcelable(
-                                MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
                         int arrivalSeq = sentMessage.getData().getInt(
                                 MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ);
                         mHostQueuedSendMessages.put(arrivalSeq, sentMessage);
                         mSendQueueBlocked = true;
 
-                        if (VDBG) {
+                        if (mVdbg) {
                             Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - arrivalSeq="
                                     + arrivalSeq + " -- blocking");
                         }
                     } else {
-                        Message sentMessage = mCurrentCommand.getData().getParcelable(
-                                MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
                         onMessageSendFailLocal(sentMessage, NanStatusCode.INTERNAL_FAILURE);
                         if (!mSendQueueBlocked) {
                             transmitNextMessage();
@@ -3052,14 +3100,16 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_CREATE_INTERFACE:
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     onCreateDataPathInterfaceResponseLocal(mCurrentCommand,
                             msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
-                            msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+                            reason);
                     break;
                 case RESPONSE_TYPE_ON_DELETE_INTERFACE:
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     onDeleteDataPathInterfaceResponseLocal(mCurrentCommand,
                             msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
-                            msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+                            reason);
                     break;
                 case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS: {
                     int ndpId = (int) msg.obj;
@@ -3074,16 +3124,17 @@
                                 SystemClock.elapsedRealtime() + AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT);
                         sendAwareResourcesChangedBroadcast();
                     }
-
                     break;
                 }
                 case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL:
-                    onInitiateDataPathResponseFailLocal(mCurrentCommand, (int) msg.obj);
+                    reason = (int) msg.obj;
+                    onInitiateDataPathResponseFailLocal(mCurrentCommand, reason);
                     break;
                 case RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST: {
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     boolean success = onRespondToDataPathSetupRequestResponseLocal(mCurrentCommand,
                             msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
-                            msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+                            reason);
                     if (success) {
                         int ndpId = mCurrentCommand.arg2;
                         WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
@@ -3096,14 +3147,19 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_END_DATA_PATH:
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     onEndPathEndResponseLocal(mCurrentCommand,
                             msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
-                            msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
+                            reason);
                     break;
                 case RESPONSE_TYPE_ON_END_PAIRING:
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
+                    onPairingEndResponseLocal(mCurrentCommand,
+                            msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG), reason);
                     break;
                 case RESPONSE_TYPE_ON_DISABLE:
-                    onDisableResponseLocal(mCurrentCommand, (Integer) msg.obj);
+                    reason = (int) msg.obj;
+                    onDisableResponseLocal(mCurrentCommand, reason);
                     break;
                 case RESPONSE_TYPE_ON_INITIATE_PAIRING_SUCCESS: {
                     int pairingId = (int) msg.obj;
@@ -3117,11 +3173,10 @@
                         timeout.schedule(SystemClock.elapsedRealtime()
                                 + AWARE_WAIT_FOR_PAIRING_CONFIRM_TIMEOUT);
                     }
-
                     break;
                 }
                 case RESPONSE_TYPE_ON_INITIATE_PAIRING_FAIL: {
-                    int reason = (int) msg.obj;
+                    reason = (int) msg.obj;
                     onInitiatePairingResponseFailLocal(mCurrentCommand, reason);
                     break;
                 }
@@ -3141,7 +3196,7 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_RESPONSE_PAIRING_FAIL: {
-                    int reason = (int) msg.obj;
+                    reason = (int) msg.obj;
                     onRespondToPairingIndicationResponseFail(mCurrentCommand, reason);
                     break;
                 }
@@ -3161,7 +3216,7 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_INITIATE_BOOTSTRAPPING_FAIL: {
-                    int reason = (int) msg.obj;
+                    reason = (int) msg.obj;
                     onInitiateBootStrappingResponseFailLocal(mCurrentCommand, reason);
                     break;
                 }
@@ -3170,20 +3225,20 @@
                     break;
                 }
                 case RESPONSE_TYPE_ON_RESPONSE_BOOTSTRAPPING_FAIL: {
-                    int reason = (int) msg.obj;
+                    reason = (int) msg.obj;
                     Log.e(TAG, "RespondToBootstrappingIndication failed, reason: " + reason);
                     break;
                 }
                 case RESPONSE_TYPE_ON_SUSPEND: {
-                    int statusCode = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     boolean success = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG);
-                    onSuspendResponseLocal(mCurrentCommand, success, statusCode);
+                    onSuspendResponseLocal(mCurrentCommand, success, reason);
                     break;
                 }
                 case RESPONSE_TYPE_ON_RESUME: {
-                    int statusCode = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
+                    reason = msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE);
                     boolean success = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG);
-                    onResumeResponseLocal(mCurrentCommand, success, statusCode);
+                    onResumeResponseLocal(mCurrentCommand, success, reason);
                     break;
                 }
                 default:
@@ -3192,6 +3247,11 @@
                     mCurrentTransactionId = TRANSACTION_ID_IGNORE;
                     return;
             }
+            if (msg.arg1 != RESPONSE_TYPE_ON_CONFIG_SUCCESS
+                    && msg.arg1 != RESPONSE_TYPE_ON_CONFIG_FAIL) {
+                // Config response handle separately to identify it's connect or reconfigure
+                recordHalApiCall(mCurrentCommand.arg1, reason, mStartTime);
+            }
 
             mCurrentCommand = null;
             mCurrentTransactionId = TRANSACTION_ID_IGNORE;
@@ -3332,7 +3392,7 @@
         }
 
         private void updateSendMessageTimeout() {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "updateSendMessageTimeout: mHostQueuedSendMessages.size()="
                         + mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
                         + mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
@@ -3354,10 +3414,12 @@
         }
 
         private void processSendMessageTimeout() {
-            mLocalLog.log("processSendMessageTimeout: mHostQueuedSendMessages.size()="
-                    + mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
-                    + mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
-                    + mSendQueueBlocked);
+            if (mVdbg) {
+                Log.v(TAG, "processSendMessageTimeout: mHostQueuedSendMessages.size()="
+                        + mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
+                        + mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
+                        + mSendQueueBlocked);
+            }
 
             /*
              * Note: using 'first' to always time-out (remove) at least 1 notification (partially)
@@ -3438,7 +3500,7 @@
     }
 
     private void sendAwareStateChangedBroadcast(boolean enabled) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "sendAwareStateChangedBroadcast: enabled=" + enabled);
         }
         final Intent intent = new Intent(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
@@ -3450,7 +3512,7 @@
         if (!SdkLevel.isAtLeastT()) {
             return;
         }
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "sendAwareResourcesChangedBroadcast");
         }
         final Intent intent = new Intent(WifiAwareManager.ACTION_WIFI_AWARE_RESOURCE_CHANGED);
@@ -3468,12 +3530,15 @@
             IWifiAwareEventCallback callback, ConfigRequest configRequest,
             boolean notifyIdentityChange, Object attributionSource, boolean awareOffload,
             boolean reEnableAware, int callerType) {
-        mLocalLog.log("connectLocal(): transactionId=" + transactionId + ", clientId=" + clientId
-                + ", uid=" + uid + ", pid=" + pid + ", callingPackage=" + callingPackage
-                + ", callback=" + callback + ", configRequest=" + configRequest
-                + ", notifyIdentityChange=" + notifyIdentityChange
-                + ", awareOffload" + awareOffload
-                + ", reEnableAware" + reEnableAware);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG,
+                    "connectLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+                            + ", uid=" + uid + ", pid=" + pid + ", callingPackage=" + callingPackage
+                            + ", callback=" + callback + ", configRequest=" + configRequest
+                            + ", notifyIdentityChange=" + notifyIdentityChange
+                            + ", awareOffload" + awareOffload
+                            + ", reEnableAware" + reEnableAware);
+        }
         if (!mUsageEnabled) {
             Log.w(TAG, "connect(): called with mUsageEnabled=false");
             try {
@@ -3490,7 +3555,7 @@
             Log.e(TAG, "connectLocal: entry already exists for clientId=" + clientId);
         }
 
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "mCurrentAwareConfiguration=" + mCurrentAwareConfiguration
                     + ", mCurrentIdentityNotification=" + mCurrentIdentityNotification);
         }
@@ -3507,7 +3572,7 @@
                 Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
             }
             return false;
-        } else if (VDBG) {
+        } else if (mVdbg) {
             Log.v(TAG, "connectLocal: merged=" + merged);
         }
 
@@ -3516,7 +3581,9 @@
                 && !reEnableAware) {
             if (awareOffload && !isAwareOffloading()) {
                 try {
-                    mLocalLog.log("Connect failure for clientId:" + clientId);
+                    if (mVdbg) {
+                        Log.v(TAG, "Connect failure for clientId:" + clientId);
+                    }
                     callback.onConnectFail(clientId);
                 } catch (RemoteException e) {
                     Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
@@ -3526,14 +3593,16 @@
             WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
                     callingPackage, callingFeatureId, callback, configRequest, notifyIdentityChange,
                     SystemClock.elapsedRealtime(), mWifiPermissionsUtil, attributionSource,
-                    mLocalLog, awareOffload, callerType);
-            client.enableVerboseLogging(mVerboseLoggingEnabled);
+                    awareOffload, callerType);
+            client.enableVerboseLogging(mVerboseLoggingEnabled, mVdbg);
             client.onClusterChange(mClusterEventType, mClusterId, mCurrentDiscoveryInterfaceMac);
             mClients.append(clientId, client);
             mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients, callerType,
                     callingFeatureId);
             try {
-                mLocalLog.log("Connect success for clientId:" + clientId);
+                if (mVdbg) {
+                    Log.v(TAG, "Connect success for clientId:" + clientId);
+                }
                 callback.onConnectSuccess(clientId);
             } catch (RemoteException e) {
                 Log.w(TAG, "connectLocal onConnectSuccess(): RemoteException (FYI): " + e);
@@ -3586,8 +3655,10 @@
     }
 
     private boolean disconnectLocal(short transactionId, int clientId) {
-        mLocalLog.log("disconnectLocal(): transactionId=" + transactionId
-                + ", clientId=" + clientId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "disconnectLocal(): transactionId=" + transactionId
+                    + ", clientId=" + clientId);
+        }
 
         WifiAwareClientState client = mClients.get(clientId);
         if (client == null) {
@@ -3646,7 +3717,7 @@
     }
 
     private boolean reconfigureLocal(short transactionId) {
-        if (VDBG) Log.v(TAG, "reconfigureLocal(): transactionId=" + transactionId);
+        if (mVdbg) Log.v(TAG, "reconfigureLocal(): transactionId=" + transactionId);
 
         if (mClients.size() == 0) {
             // no clients - Aware is not enabled, nothing to reconfigure
@@ -3670,7 +3741,10 @@
     }
 
     private void terminateSessionLocal(int clientId, int sessionId) {
-        mLocalLog.log("terminateSessionLocal(): clientId=" + clientId + ", sessionId=" + sessionId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "terminateSessionLocal(): clientId=" + clientId
+                    + ", sessionId=" + sessionId);
+        }
 
         WifiAwareClientState client = mClients.get(clientId);
         if (client == null) {
@@ -3693,8 +3767,10 @@
 
     private boolean publishLocal(short transactionId, int clientId, PublishConfig publishConfig,
             IWifiAwareDiscoverySessionCallback callback) {
-        mLocalLog.log("publishLocal(): transactionId=" + transactionId + ", clientId=" + clientId
-                + ", publishConfig=" + publishConfig + ", callback=" + callback);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "publishLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+                    + ", publishConfig=" + publishConfig + ", callback=" + callback);
+        }
 
         WifiAwareClientState client = mClients.get(clientId);
         if (client == null) {
@@ -3728,7 +3804,7 @@
 
     private boolean updatePublishLocal(short transactionId, int clientId, int sessionId,
             PublishConfig publishConfig) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "updatePublishLocal(): transactionId=" + transactionId + ", clientId="
                     + clientId + ", sessionId=" + sessionId + ", publishConfig=" + publishConfig);
         }
@@ -3763,8 +3839,10 @@
 
     private boolean subscribeLocal(short transactionId, int clientId,
             SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback) {
-        mLocalLog.log("subscribeLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "subscribeLocal(): transactionId=" + transactionId + ", clientId=" + clientId
                     + ", subscribeConfig=" + subscribeConfig + ", callback=" + callback);
+        }
 
         WifiAwareClientState client = mClients.get(clientId);
         if (client == null) {
@@ -3800,7 +3878,7 @@
 
     private boolean updateSubscribeLocal(short transactionId, int clientId, int sessionId,
             SubscribeConfig subscribeConfig) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG,
                     "updateSubscribeLocal(): transactionId=" + transactionId + ", clientId="
                             + clientId + ", sessionId=" + sessionId + ", subscribeConfig="
@@ -3836,7 +3914,7 @@
     private boolean initiateNanPairingRequestLocal(short transactionId, int clientId, int sessionId,
             int peerId, String password, int requestType, int akm,
             byte[] pmk, int cipherSuite) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "initiateNanPairingRequestLocal: transactionId=" + transactionId
                     + ", clientId=" + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId);
         }
@@ -3860,7 +3938,7 @@
     private boolean respondToPairingRequestLocal(short transactionId, int clientId, int sessionId,
             int peerId, int pairingId, boolean accept, int requestType, byte[] pmk,
             String password, int akm, int cipherSuite) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG,
                     "respondToPairingRequestLocal: transactionId=" + transactionId + ", clientId="
                             + clientId + ", sessionId=" + sessionId + ", peerId="
@@ -3887,7 +3965,7 @@
     private boolean initiateBootstrappingRequestLocal(short transactionId, int clientId,
             int sessionId, int peerId, int method, byte[] cookie) {
         String methodString = "initiateBootstrappingRequestLocal";
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, methodString + ": transactionId=" + transactionId
                     + ", clientId=" + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId);
         }
@@ -3902,7 +3980,7 @@
     private boolean respondToBootstrappingRequestLocal(short transactionId, int clientId,
             int sessionId, int peerId, int bootstrappingId, boolean accept, int method) {
         String methodString = "respondToBootstrappingRequestLocal";
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, methodString + ": transactionId=" + transactionId
                     + ", clientId=" + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId);
         }
@@ -3918,9 +3996,11 @@
     private boolean sendFollowonMessageLocal(short transactionId, int clientId, int sessionId,
             int peerId, byte[] message, int messageId) {
         String methodString = "sendFollowonMessageLocal";
-        mLocalLog.log(methodString + "(): transactionId=" + transactionId + ", clientId="
-                + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId
-                + ", messageId=" + messageId);
+        if (mVdbg) {
+            Log.v(TAG, methodString + "(): transactionId=" + transactionId + ", clientId="
+                    + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId
+                    + ", messageId=" + messageId);
+        }
 
         WifiAwareDiscoverySessionState session = getClientSession(clientId, sessionId,
                 methodString);
@@ -3970,14 +4050,16 @@
             boolean isOutOfBand, byte[] appInfo) {
         WifiAwareDataPathSecurityConfig securityConfig = networkSpecifier
                 .getWifiAwareDataPathSecurityConfig();
-        mLocalLog.log("initiateDataPathSetupLocal(): transactionId=" + transactionId
-                + ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
-                + ", channelRequestType=" + channelRequestType + ", channel=" + channel
-                + ", peer="
-                + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName=" + interfaceName
-                + ", securityConfig=" + ((securityConfig == null) ? "" : securityConfig)
-                + ", isOutOfBand="
-                + isOutOfBand + ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "initiateDataPathSetupLocal(): transactionId=" + transactionId
+                    + ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
+                    + ", channelRequestType=" + channelRequestType + ", channel=" + channel
+                    + ", peer="
+                    + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName=" + interfaceName
+                    + ", securityConfig=" + ((securityConfig == null) ? "" : securityConfig)
+                    + ", isOutOfBand="
+                    + isOutOfBand + ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
+        }
         byte pubSubId = 0;
         if (!isOutOfBand) {
             WifiAwareClientState client = mClients.get(networkSpecifier.clientId);
@@ -4011,11 +4093,13 @@
             WifiAwareNetworkSpecifier networkSpecifier) {
         WifiAwareDataPathSecurityConfig securityConfig = accept ? networkSpecifier
                 .getWifiAwareDataPathSecurityConfig() : null;
-        mLocalLog.log("respondToDataPathRequestLocal(): transactionId=" + transactionId
-                + ", accept=" + accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
-                + ", securityConfig=" + securityConfig
-                + ", isOutOfBand=" + isOutOfBand
-                + ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "respondToDataPathRequestLocal(): transactionId=" + transactionId
+                    + ", accept=" + accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
+                    + ", securityConfig=" + securityConfig
+                    + ", isOutOfBand=" + isOutOfBand
+                    + ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
+        }
         byte pubSubId = 0;
         if (!isOutOfBand && accept) {
             WifiAwareClientState client = mClients.get(networkSpecifier.clientId);
@@ -4044,13 +4128,17 @@
     }
 
     private boolean endDataPathLocal(short transactionId, int ndpId) {
-        mLocalLog.log("endDataPathLocal: transactionId=" + transactionId + ", ndpId=" + ndpId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "endDataPathLocal: transactionId=" + transactionId + ", ndpId=" + ndpId);
+        }
         sendAwareResourcesChangedBroadcast();
         return mWifiAwareNativeApi.endDataPath(transactionId, ndpId);
     }
 
     private boolean endPairingLocal(short transactionId, int pairId) {
-        mLocalLog.log("endPairingLocal: transactionId=" + transactionId + ", pairId=" + pairId);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "endPairingLocal: transactionId=" + transactionId + ", pairId=" + pairId);
+        }
         return mWifiAwareNativeApi.endPairing(transactionId, pairId);
     }
 
@@ -4065,6 +4153,9 @@
             if (mCurrentAwareConfiguration == null) { // enabled (as opposed to re-configured)
                 queryCapabilities();
                 mDataPathMgr.createAllInterfaces();
+                recordHalApiCall(COMMAND_TYPE_CONNECT, NanStatusCode.SUCCESS, mStartTime);
+            } else {
+                recordHalApiCall(COMMAND_TYPE_RECONFIGURE, NanStatusCode.SUCCESS, mStartTime);
             }
 
             Bundle data = completedCommand.getData();
@@ -4087,13 +4178,15 @@
             WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
                     callingPackage, callingFeatureId, callback, configRequest, notifyIdentityChange,
                     SystemClock.elapsedRealtime(), mWifiPermissionsUtil,
-                    callbackAndAttributionSource.second, mLocalLog, awareOffload, callerType);
-            client.enableVerboseLogging(mVerboseLoggingEnabled);
+                    callbackAndAttributionSource.second, awareOffload, callerType);
+            client.enableVerboseLogging(mVerboseLoggingEnabled, mVdbg);
             mClients.put(clientId, client);
             mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients, callerType,
                     callingFeatureId);
             try {
-                mLocalLog.log("Connect success for clientId:" + clientId);
+                if (mVdbg) {
+                    Log.v(TAG, "Connect success for clientId:" + clientId);
+                }
                 callback.onConnectSuccess(clientId);
             } catch (RemoteException e) {
                 Log.w(TAG,
@@ -4101,10 +4194,12 @@
             }
             client.onClusterChange(mClusterEventType, mClusterId, mCurrentDiscoveryInterfaceMac);
         } else if (completedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
+            recordHalApiCall(COMMAND_TYPE_RECONFIGURE, NanStatusCode.SUCCESS, mStartTime);
             /*
              * NOP (i.e. updated configuration after disconnecting a client)
              */
         } else if (completedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
+            recordHalApiCall(COMMAND_TYPE_RECONFIGURE, NanStatusCode.SUCCESS, mStartTime);
             /*
              * NOP (i.e. updated configuration at power saving event)
              */
@@ -4133,7 +4228,7 @@
     }
 
     private void onConfigFailedLocal(Message failedCommand, int reason) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG,
                     "onConfigFailedLocal: failedCommand=" + failedCommand + ", reason=" + reason);
         }
@@ -4149,7 +4244,9 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "onConfigFailedLocal onConnectFail(): RemoteException (FYI): " + e);
             }
+            recordHalApiCall(COMMAND_TYPE_CONNECT, reason, mStartTime);
         } else if (failedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
+            recordHalApiCall(COMMAND_TYPE_RECONFIGURE, reason, mStartTime);
             /*
              * NOP (tried updating configuration after disconnecting a client -
              * shouldn't fail but there's nothing to do - the old configuration
@@ -4158,6 +4255,7 @@
              * OR: timed-out getting a response to a disable. Either way a NOP.
              */
         } else if (failedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
+            recordHalApiCall(COMMAND_TYPE_RECONFIGURE, reason, mStartTime);
             /*
              * NOP (configuration change as part of possibly power saving event - should not
              * fail but there's nothing to do).
@@ -4189,7 +4287,7 @@
 
     private void onSessionConfigSuccessLocal(Message completedCommand, byte pubSubId,
             boolean isPublish) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onSessionConfigSuccessLocal: completedCommand=" + completedCommand
                     + ", pubSubId=" + pubSubId + ", isPublish=" + isPublish);
         }
@@ -4241,8 +4339,11 @@
 
             int sessionId = mSm.mNextSessionId++;
             try {
-                mLocalLog.log((isPublish ? "publish" : "subscribe") + " session started, sessionId="
-                        + sessionId);
+                if (mVdbg) {
+                    Log.v(TAG,
+                            (isPublish ? "publish" : "subscribe") + " session started, sessionId="
+                                    + sessionId);
+                }
                 callback.onSessionStarted(sessionId);
             } catch (RemoteException e) {
                 Log.e(TAG, "onSessionConfigSuccessLocal: onSessionStarted() RemoteException=" + e);
@@ -4252,7 +4353,7 @@
             WifiAwareDiscoverySessionState session = new WifiAwareDiscoverySessionState(
                     mWifiAwareNativeApi, sessionId, pubSubId, callback, isPublish, isRangingEnabled,
                     SystemClock.elapsedRealtime(), enableInstantMode, instantModeBand,
-                    isSuspendable, mLocalLog, pairingConfig);
+                    isSuspendable, pairingConfig);
             session.enableVerboseLogging(mVerboseLoggingEnabled);
             client.addSession(session);
 
@@ -4311,7 +4412,7 @@
     }
 
     private void onSessionConfigFailLocal(Message failedCommand, boolean isPublish, int reason) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onSessionConfigFailLocal: failedCommand=" + failedCommand + ", isPublish="
                     + isPublish + ", reason=" + reason);
         }
@@ -4379,8 +4480,10 @@
     }
 
     private void onMessageSendSuccessLocal(Message completedCommand) {
-        String methodString = "onMessageSendFailLocal";
-        mLocalLog.log(methodString + ": completedCommand=" + completedCommand);
+        String methodString = "onMessageSendSuccessLocal";
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": completedCommand=" + completedCommand);
+        }
 
         int clientId = completedCommand.arg2;
         int sessionId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
@@ -4401,7 +4504,7 @@
 
     private void onMessageSendFailLocal(Message failedCommand, int reason) {
         String methodString = "onMessageSendFailLocal";
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, methodString + ": failedCommand=" + failedCommand + ", reason=" + reason);
         }
 
@@ -4423,23 +4526,48 @@
     }
 
     private void onCapabilitiesUpdatedResponseLocal(Capabilities capabilities) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onCapabilitiesUpdatedResponseLocal: capabilites=" + capabilities);
         }
 
         mCapabilities = capabilities;
         mCharacteristics = null;
+        if (mWifiAwarePullAtomCallback != null) {
+            //Should only register this callback once
+            return;
+        }
+        StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        if (statsManager != null) {
+            mWifiAwarePullAtomCallback = new WifiAwarePullAtomCallback();
+            statsManager.setPullAtomCallback(WIFI_AWARE_CAPABILITIES, null,
+                    new HandlerExecutor(mHandler), mWifiAwarePullAtomCallback);
+        }
+    }
+    private class WifiAwarePullAtomCallback implements StatsManager.StatsPullAtomCallback {
+
+        @Override
+        public int onPullAtom(int atomTag, @androidx.annotation.NonNull List<StatsEvent> data) {
+            data.add(WifiStatsLog.buildStatsEvent(atomTag,
+                    mCapabilities.isInstantCommunicationModeSupported,
+                    mCapabilities.isNanPairingSupported,
+                    mCapabilities.isSuspensionSupported,
+                    mCapabilities.supportedDataPathCipherSuites,
+                    mCapabilities.maxNdiInterfaces,
+                    mCapabilities.maxNdpSessions,
+                    mCapabilities.maxPublishes));
+            return StatsManager.PULL_SUCCESS;
+        }
     }
 
     private void onCreateDataPathInterfaceResponseLocal(Message command, boolean success,
             int reasonOnFailure) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: command=" + command + ", success="
                     + success + ", reasonOnFailure=" + reasonOnFailure);
         }
 
         if (success) {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: successfully created interface "
                         + command.obj);
             }
@@ -4454,13 +4582,13 @@
 
     private void onDeleteDataPathInterfaceResponseLocal(Message command, boolean success,
             int reasonOnFailure) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: command=" + command + ", success="
                     + success + ", reasonOnFailure=" + reasonOnFailure);
         }
 
         if (success) {
-            if (VDBG) {
+            if (mVdbg) {
                 Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: successfully deleted interface "
                         + command.obj);
             }
@@ -4475,7 +4603,9 @@
 
     private boolean onRespondToPairingIndicationResponseSuccessLocal(Message command) {
         String methodString = "onRespondToPairingIndicationResponseSuccessLocal";
-        mLocalLog.log(methodString + ": command=" + command);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command);
+        }
         Bundle data = command.getData();
         PairingInfo pairingInfo = new PairingInfo(command.arg2,
                 data.getInt(MESSAGE_BUNDLE_KEY_SESSION_ID),
@@ -4494,8 +4624,9 @@
 
     private void onRespondToPairingIndicationResponseFail(Message command, int reason) {
         String methodString = "onRespondToPairingIndicationResponseFail";
-        mLocalLog.log(methodString + ": command=" + command
-                + " reason=" + reason);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + " reason=" + reason);
+        }
 
         Bundle data = command.getData();
         PairingInfo pairingInfo = new PairingInfo(command.arg2,
@@ -4515,17 +4646,15 @@
     }
 
     private boolean onInitiateDataPathResponseSuccessLocal(Message command, int ndpId) {
-        mLocalLog.log("onInitiateDataPathResponseSuccessLocal: command=" + command + ", ndpId="
-                + ndpId);
-
         return mDataPathMgr
                 .onDataPathInitiateSuccess((WifiAwareNetworkSpecifier) command.obj, ndpId);
     }
 
     private boolean onInitiatePairingResponseSuccessLocal(Message command, int paireId) {
         String methodString = "onInitiatePairingResponseSuccessLocal";
-        mLocalLog.log(methodString + ": command=" + command + ", ndpId="
-                + paireId);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + ", ndpId=" + paireId);
+        }
 
         Bundle data = command.getData();
         PairingInfo pairingInfo = new PairingInfo(command.arg2,
@@ -4545,8 +4674,9 @@
 
     private void onInitiatePairingResponseFailLocal(Message command, int reason) {
         String methodString = "onInitiatePairingResponseFailLocal";
-        mLocalLog.log(methodString + ": command=" + command + ", reason="
-                + reason);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + ", reason=" + reason);
+        }
 
         Bundle data = command.getData();
         PairingInfo pairingInfo = new PairingInfo(command.arg2,
@@ -4567,7 +4697,9 @@
 
     private boolean onInitiateBootstrappingResponseSuccessLocal(Message command, int id) {
         String methodString = "onInitiateBootstrappingResponseSuccessLocal";
-        mLocalLog.log(methodString + ": command=" + command + ", ndpId=" + id);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + ", ndpId=" + id);
+        }
 
         Bundle data = command.getData();
         BootStrppingInfo info = new BootStrppingInfo(command.arg2,
@@ -4587,7 +4719,9 @@
 
     private void onInitiateBootStrappingResponseFailLocal(Message command, int reason) {
         String methodString = "onInitiateBootStrappingResponseFailLocal";
-        mLocalLog.log(methodString + ": command=" + command + ", reason=" + reason);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + ", reason=" + reason);
+        }
 
         Bundle data = command.getData();
         BootStrppingInfo info = new BootStrppingInfo(command.arg2,
@@ -4606,7 +4740,9 @@
 
     private void onRespondToBootStrappingRequestSuccessLocal(Message command) {
         String methodString = "onRespondToBootStrappingRequestSuccessLocal";
-        mLocalLog.log(methodString + ": command=" + command);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command);
+        }
 
         Bundle data = command.getData();
         BootStrppingInfo info = new BootStrppingInfo(command.arg2,
@@ -4625,11 +4761,12 @@
         }
     }
 
-    private void onSuspendResponseLocal(Message command, boolean success,
-            @WifiAwareManager.SessionSuspensionFailedReasonCode int reason) {
+    private void onSuspendResponseLocal(Message command, boolean success, int reason) {
         String methodString = "onSuspendResponseLocal";
-        mLocalLog.log(methodString + ": command=" + command + ", success=" + success
-                + ", reason=" + reason);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command=" + command + ", success=" + success
+                    + ", reason=" + reason);
+        }
 
         int clientId = command.arg2;
         int sessionId = command.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
@@ -4642,15 +4779,31 @@
         if (success) {
             session.onSuspendSuccess();
         } else {
-            session.onSuspendFail(reason);
+            session.onSuspendFail(convertFrameworkStatusToSuspensionFailedReason(reason));
+        }
+    }
+
+    @WifiAwareManager.SessionSuspensionFailedReasonCode
+    private int convertFrameworkStatusToSuspensionFailedReason(int reason) {
+        switch (reason) {
+            case REDUNDANT_REQUEST:
+                return WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST;
+            case NOT_SUPPORTED:
+                return WIFI_AWARE_SUSPEND_INVALID_SESSION;
+            case NO_CONNECTION:
+                return WIFI_AWARE_SUSPEND_CANNOT_SUSPEND;
+            default:
+                return WIFI_AWARE_SUSPEND_INTERNAL_ERROR;
         }
     }
 
     private void onResumeResponseLocal(Message command, boolean success,
             @WifiAwareManager.SessionResumptionFailedReasonCode int reason) {
         String methodString = "onResumeResponseLocal";
-        mLocalLog.log(methodString + ": command="
-                + command + ", success=" + success + ", reason=" + reason);
+        if (mVdbg) {
+            Log.v(TAG, methodString + ": command="
+                    + command + ", success=" + success + ", reason=" + reason);
+        }
 
         int clientId = command.arg2;
         int sessionId = command.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
@@ -4661,22 +4814,35 @@
             return;
         }
         if (!success) {
-            session.onResumeFail(reason);
+            session.onResumeFail(convertFrameworkStatusToResumptionFailedReasonCode(reason));
         }
     }
 
+    @WifiAwareManager.SessionResumptionFailedReasonCode
+    private int convertFrameworkStatusToResumptionFailedReasonCode(int reason) {
+        switch (reason) {
+            case REDUNDANT_REQUEST:
+                return WIFI_AWARE_RESUME_REDUNDANT_REQUEST;
+            case NOT_SUPPORTED:
+                return WIFI_AWARE_RESUME_INVALID_SESSION;
+            default:
+                return WIFI_AWARE_RESUME_INTERNAL_ERROR;
+        }
+    }
+
+
+
     private void onInitiateDataPathResponseFailLocal(Message command, int reason) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onInitiateDataPathResponseFailLocal: command=" + command + ", reason="
                     + reason);
         }
-
         mDataPathMgr.onDataPathInitiateFail((WifiAwareNetworkSpecifier) command.obj, reason);
     }
 
     private boolean onRespondToDataPathSetupRequestResponseLocal(Message command, boolean success,
             int reasonOnFailure) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onRespondToDataPathSetupRequestResponseLocal: command=" + command
                     + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
         }
@@ -4685,18 +4851,26 @@
     }
 
     private void onEndPathEndResponseLocal(Message command, boolean success, int reasonOnFailure) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onEndPathEndResponseLocal: command=" + command
                     + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
         }
-
         // TODO: do something with this
     }
 
+    private void onPairingEndResponseLocal(Message command, boolean success, int reasonOnFailure) {
+        if (mVdbg) {
+            Log.v(TAG, "onPairingEndResponseLocal: command=" + command
+                    + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
+        }
+    }
+
     private boolean suspendSessionLocal(short transactionId, int clientId, int sessionId) {
         String methodString = "suspendSessionLocal";
-        mLocalLog.log(methodString + "(): transactionId=" + transactionId + ", clientId="
-                + clientId + ", sessionId=" + sessionId);
+        if (mVdbg) {
+            Log.v(TAG, methodString + "(): transactionId=" + transactionId + ", clientId="
+                    + clientId + ", sessionId=" + sessionId);
+        }
 
         WifiAwareDiscoverySessionState session = getClientSession(clientId, sessionId,
                 methodString);
@@ -4716,8 +4890,10 @@
     }
 
     private boolean resumeSessionLocal(short transactionId, int clientId, int sessionId) {
-        mLocalLog.log("resumeSessionLocal(): transactionId=" + transactionId + ", clientId="
-                + clientId + ", sessionId=" + sessionId);
+        if (mVdbg) {
+            Log.v(TAG, "resumeSessionLocal(): transactionId=" + transactionId + ", clientId="
+                    + clientId + ", sessionId=" + sessionId);
+        }
 
         String methodString = "resumeSessionLocal";
         WifiAwareDiscoverySessionState session = getClientSession(clientId, sessionId,
@@ -4726,11 +4902,11 @@
             return false;
         }
         if (!session.isSuspendable()) {
-            session.onResumeFail(WIFI_AWARE_SUSPEND_INVALID_SESSION);
+            session.onResumeFail(WIFI_AWARE_RESUME_INVALID_SESSION);
             return false;
         }
         if (!session.isSessionSuspended()) {
-            session.onResumeFail(WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST);
+            session.onResumeFail(WIFI_AWARE_RESUME_REDUNDANT_REQUEST);
             return false;
         }
 
@@ -4758,7 +4934,7 @@
         mClusterId = clusterId;
         mClusterEventType = clusterEventType;
 
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG, "onClusterChange: clusterEventType=" + clusterEventType + ", clusterId="
                     + String.valueOf(HexEncoding.encode(clusterId)));
         }
@@ -4775,12 +4951,14 @@
             byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndication, int rangeMm,
             int cipherSuite, byte[] scid, byte[] nonce, byte[] tag,
             AwarePairingConfig pairingConfig) {
-        mLocalLog.log("onMatch: pubSubId=" + pubSubId
-                + ", requestorInstanceId=" + requestorinstanceid
-                + ", peerDiscoveryMac=" + String.valueOf(HexEncoding.encode(peerMac))
-                + ", serviceSpecificInfo=" + Arrays.toString(serviceSpecificInfo)
-                + ", matchFilter=" + Arrays.toString(matchFilter)
-                + ", rangingIndication=" + rangingIndication + ", rangeMm=" + rangeMm);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onMatch: pubSubId=" + pubSubId
+                    + ", requestorInstanceId=" + requestorinstanceid
+                    + ", peerDiscoveryMac=" + String.valueOf(HexEncoding.encode(peerMac))
+                    + ", serviceSpecificInfo=" + Arrays.toString(serviceSpecificInfo)
+                    + ", matchFilter=" + Arrays.toString(matchFilter)
+                    + ", rangingIndication=" + rangingIndication + ", rangeMm=" + rangeMm);
+        }
 
         Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
                 getClientSessionForPubSubId(pubSubId);
@@ -4811,7 +4989,7 @@
     }
 
     private void onMatchExpiredLocal(int pubSubId, int requestorInstanceId) {
-        if (VDBG) {
+        if (mVdbg) {
             Log.v(TAG,
                     "onMatchExpiredNotification: pubSubId=" + pubSubId
                             + ", requestorInstanceId=" + requestorInstanceId);
@@ -4827,8 +5005,10 @@
     }
 
     private void onSessionTerminatedLocal(int pubSubId, boolean isPublish, int reason) {
-        mLocalLog.log("onSessionTerminatedLocal: pubSubId=" + pubSubId + ", isPublish=" + isPublish
-                + ", reason=" + reason);
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onSessionTerminatedLocal: pubSubId=" + pubSubId + ", isPublish=" + isPublish
+                    + ", reason=" + reason);
+        }
 
         Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
                 getClientSessionForPubSubId(pubSubId);
@@ -4856,9 +5036,11 @@
 
     private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
             byte[] message) {
-        mLocalLog.log("onMessageReceivedLocal: pubSubId=" + pubSubId + ", requestorInstanceId="
-                + requestorInstanceId + ", peerDiscoveryMac="
-                + String.valueOf(HexEncoding.encode(peerMac)));
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "onMessageReceivedLocal: pubSubId=" + pubSubId + ", requestorInstanceId="
+                    + requestorInstanceId + ", peerDiscoveryMac="
+                    + String.valueOf(HexEncoding.encode(peerMac)));
+        }
 
         Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
                 getClientSessionForPubSubId(pubSubId);
@@ -5270,6 +5452,7 @@
         pw.println("  mCapabilities: [" + mCapabilities + "]");
         pw.println("  mCurrentAwareConfiguration: " + mCurrentAwareConfiguration);
         pw.println("  mCurrentIdentityNotification: " + mCurrentIdentityNotification);
+        pw.println("  mOpportunisticSet: " + mOpportunisticSet);
         for (int i = 0; i < mClients.size(); ++i) {
             mClients.valueAt(i).dump(fd, pw, args);
         }
@@ -5279,9 +5462,6 @@
         mWifiAwareNativeApi.dump(fd, pw, args);
         pw.println("mAwareMetrics:");
         mAwareMetrics.dump(fd, pw, args);
-        pw.println("AwareStateManager - Log Begin ----");
-        mLocalLog.dump(fd, pw, args);
-        pw.println("AwareStateManager - Log End ----");
     }
 
     private void handleLocationModeDisabled() {
@@ -5372,4 +5552,59 @@
         }
         return false;
     }
+
+    private int convertFrameworkHalCommandToEnum(int cmd) {
+        switch (cmd) {
+            case COMMAND_TYPE_CONNECT:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_ENABLE_REQUEST;
+            case COMMAND_TYPE_RECONFIGURE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_CONFIG_REQUEST;
+            case COMMAND_TYPE_DISABLE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_DISABLE_REQUEST;
+            case COMMAND_TYPE_PUBLISH:
+            case COMMAND_TYPE_UPDATE_PUBLISH:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_START_PUBLISH_REQUEST;
+            case COMMAND_TYPE_SUBSCRIBE:
+            case COMMAND_TYPE_UPDATE_SUBSCRIBE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_START_SUBSCRIBE_REQUEST;
+            case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TRANSMIT_FOLLOW_UP_REQUEST;
+            case COMMAND_TYPE_INITIATE_PAIRING_REQUEST:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_PAIRING_REQUEST;
+            case COMMAND_TYPE_RESPONSE_PAIRING_REQUEST:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_PAIRING_INDICATION_REQUEST;
+            case COMMAND_TYPE_INITIATE_BOOTSTRAPPING_REQUEST:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_BOOTSTRAPPING_REQUEST;
+            case COMMAND_TYPE_RESPONSE_BOOTSTRAPPING_REQUEST:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_BOOTSTRAPPING_INDICATION_REQUEST;
+            case COMMAND_TYPE_GET_CAPABILITIES:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_GET_CAPABILITIES_REQUEST;
+            case COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_CREATE_DATA_INTERFACE_REQUEST;
+            case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_DELETE_DATA_INTERFACE_REQUEST;
+            case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_INITIATE_DATA_PATH_REQUEST;
+            case COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESPOND_TO_DATA_PATH_INDICATION_REQUEST;
+            case COMMAND_TYPE_END_DATA_PATH:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TERMINATE_DATA_PATH_REQUEST;
+            case COMMAND_TYPE_END_PAIRING:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_TERMINATE_PAIRING_REQUEST;
+            case COMMAND_TYPE_SUSPEND_SESSION:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_SUSPEND_REQUEST;
+            case COMMAND_TYPE_RESUME_SESSION:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_RESUME_REQUEST;
+            default:
+                return WIFI_AWARE_HAL_API_CALLED__COMMAND__AWARE_API_UNKNOWN;
+        }
+    }
+
+    private void recordHalApiCall(int command, int state, long starTime) {
+        WifiStatsLog.write(
+                WIFI_AWARE_HAL_API_CALLED,
+                convertFrameworkHalCommandToEnum(command),
+                convertNanStatusCodeToWifiStatsLogEnum(state),
+                (int) (SystemClock.elapsedRealtime() - starTime));
+    }
 }
diff --git a/service/java/com/android/server/wifi/entitlement/PseudonymInfo.java b/service/java/com/android/server/wifi/entitlement/PseudonymInfo.java
index d1cf79b..d3b141a 100644
--- a/service/java/com/android/server/wifi/entitlement/PseudonymInfo.java
+++ b/service/java/com/android/server/wifi/entitlement/PseudonymInfo.java
@@ -47,18 +47,16 @@
     private final long mTtlInMillis;
 
     /*
-     * Age To Refresh in milliseconds from the pseudonym is received. When a pseudonym is expiring,
-     * we should refresh it ahead of time. For example, we refresh a pseudonym half an hour before
-     * it expires. The TTL is 24 hours. So we should refresh this pseudonym when it is 23.5 hours
-     * old.
+     * Refresh Ahead Time in milliseconds. When a pseudonym is expiring, we should refresh it ahead
+     * of time. For example, we refresh a pseudonym half an hour before it expires. If the TTL is 24
+     * hours, we should refresh this pseudonym when it is 23.5 hours old.
      */
-    private final long mAtrInMillis;
+    private final long mRatInMillis;
 
     /*
      * Minimum Age To Refresh in milliseconds from the pseudonym is received. We should not
      * refresh the pseudonym too frequently.
      */
-
     private final long mMinAtrInMillis;
 
     public PseudonymInfo(@NonNull String pseudonym, @NonNull String imsi) {
@@ -76,8 +74,8 @@
         mImsi = imsi;
         mTimeStamp = timeStamp;
         mTtlInMillis = ttlInMillis;
-        mAtrInMillis = ttlInMillis - Math.min(ttlInMillis / 2, REFRESH_AHEAD_TIME_IN_MILLIS);
-        mMinAtrInMillis = Math.min(mAtrInMillis, MINIMUM_REFRESH_INTERVAL_IN_MILLIS);
+        mRatInMillis = Math.min(ttlInMillis / 2, REFRESH_AHEAD_TIME_IN_MILLIS);
+        mMinAtrInMillis = Math.min(ttlInMillis - mRatInMillis, MINIMUM_REFRESH_INTERVAL_IN_MILLIS);
     }
 
     public String getPseudonym() {
@@ -95,11 +93,12 @@
         return mTtlInMillis;
     }
 
-    /*
-     * Returns the Age To Refresh in milliseconds.
-     */
-    public long getAtrInMillis() {
-        return mAtrInMillis;
+    /** Returns the Left Time To Refresh in milliseconds. */
+    public long getLttrInMillis() {
+        long age = Instant.now().toEpochMilli() - mTimeStamp;
+        long leftTimeToLive = mTtlInMillis - age;
+        long leftTimeToRefresh = leftTimeToLive - mRatInMillis;
+        return leftTimeToRefresh > 0 ? leftTimeToRefresh : 0;
     }
 
     /**
@@ -110,14 +109,6 @@
     }
 
     /**
-     * Returns whether the pseudonym should be refreshed. A pseudonym should be refreshed before
-     * it expires.
-     */
-    public boolean shouldBeRefreshed() {
-        return (Instant.now().toEpochMilli() - mTimeStamp) >= mAtrInMillis;
-    }
-
-    /**
      * Returns whether the pseudonym is old enough to refresh. To prevent DOS attack, the pseudonym
      * should not be refreshed too frequently.
      */
@@ -132,9 +123,13 @@
                 + (mPseudonym.length() >= 7 ? (mPseudonym.substring(0, 7) + "***") : mPseudonym)
                 + " mImsi=***"
                 + (mImsi.length() >= 3 ? mImsi.substring(mImsi.length() - 3) : mImsi)
-                + " mTimeStamp=" + mTimeStamp
-                + " mTtlInMillis=" + mTtlInMillis
-                + " mTtrInMillis=" + mAtrInMillis
-                + " mMinTtrInMillis=" + mMinAtrInMillis;
+                + " mTimeStamp="
+                + mTimeStamp
+                + " mTtlInMillis="
+                + mTtlInMillis
+                + " mRatInMillis="
+                + mRatInMillis
+                + " mMinAtrInMillis="
+                + mMinAtrInMillis;
     }
 }
diff --git a/service/java/com/android/server/wifi/hal/IWifiChip.java b/service/java/com/android/server/wifi/hal/IWifiChip.java
index 852d247..cbc8351 100644
--- a/service/java/com/android/server/wifi/hal/IWifiChip.java
+++ b/service/java/com/android/server/wifi/hal/IWifiChip.java
@@ -16,9 +16,11 @@
 
 package com.android.server.wifi.hal;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.wifi.WifiStatusCode;
 import android.net.wifi.CoexUnsafeChannel;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiAvailableChannel;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
@@ -43,18 +45,22 @@
     /**
      * Create an AP interface on the chip.
      *
+     * @param vendorData List of {@link OuiKeyedData} containing vendor-provided
+     *                   configuration data. Empty list indicates no vendor data.
      * @return {@link WifiApIface} object, or null if a failure occurred.
      */
     @Nullable
-    WifiApIface createApIface();
+    WifiApIface createApIface(@NonNull List<OuiKeyedData> vendorData);
 
     /**
      * Create a bridged AP interface on the chip.
      *
+     * @param vendorData List of {@link OuiKeyedData} containing vendor-provided
+     *                   configuration data. Empty list indicates no vendor data.
      * @return {@link WifiApIface} object, or null if a failure occurred.
      */
     @Nullable
-    WifiApIface createBridgedApIface();
+    WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData);
 
     /**
      * Create a NAN interface on the chip.
@@ -455,4 +461,13 @@
      * @return true if successful, false otherwise.
      */
     boolean enableStaChannelForPeerNetwork(boolean enableIndoorChannel, boolean enableDfsChannel);
+
+    /**
+     * Sends the AFC allowed channels and frequencies to the driver.
+     *
+     * @param afcChannelAllowance the allowed frequencies and channels received from
+     * querying the AFC server.
+     * @return whether the channel allowance was set successfully.
+     */
+    boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance);
 }
diff --git a/service/java/com/android/server/wifi/hal/WifiChip.java b/service/java/com/android/server/wifi/hal/WifiChip.java
index 1daa03d..f8ff890 100644
--- a/service/java/com/android/server/wifi/hal/WifiChip.java
+++ b/service/java/com/android/server/wifi/hal/WifiChip.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.wifi.WifiStatusCode;
 import android.net.wifi.CoexUnsafeChannel;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiAvailableChannel;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
@@ -278,6 +279,43 @@
     }
 
     /**
+     * AFC channel allowance.
+     */
+    public static class AfcChannelAllowance {
+        /**
+         * AFC max permissible information queried from AFC server based on frequency.
+         */
+        public List<AvailableAfcFrequencyInfo> availableAfcFrequencyInfos;
+        /**
+         * AFC max permissible information queried from AFC server based on channel number.
+         */
+        public List<AvailableAfcChannelInfo> availableAfcChannelInfos;
+        /**
+         * The time in UTC at which this information expires, as the difference, measured in
+         * milliseconds, between the expiration time and midnight, January 1, 1970 UTC.
+         */
+        public long availabilityExpireTimeMs;
+    }
+
+    /**
+     * Available AFC frequency info.
+     */
+    public static class AvailableAfcFrequencyInfo {
+        public int startFrequencyMhz = 0;
+        public int endFrequencyMhz = 0;
+        public int maxPsdDbmPerMhz = 0;
+    }
+
+    /**
+     * Available AFC channel info.
+     */
+    public static class AvailableAfcChannelInfo {
+        public int globalOperatingClass = 0;
+        public int channelCfi = 0;
+        public int maxEirpDbm = 0;
+    }
+
+    /**
      * Wifi Chip capabilities.
      */
     public static class WifiChipCapabilities {
@@ -492,21 +530,29 @@
     }
 
     /**
-     * See comments for {@link IWifiChip#createApIface()}
+     * See comments for {@link IWifiChip#createApIface(List)}
      */
     @Nullable
-    public WifiApIface createApIface() {
+    public WifiApIface createApIface(@NonNull List<OuiKeyedData> vendorData) {
+        if (vendorData == null) {
+            Log.e(TAG, "createApIface received null vendorData");
+            return null;
+        }
         return validateAndCall("createApIface", null,
-                () -> mWifiChip.createApIface());
+                () -> mWifiChip.createApIface(vendorData));
     }
 
     /**
-     * See comments for {@link IWifiChip#createBridgedApIface()}
+     * See comments for {@link IWifiChip#createBridgedApIface(List)}
      */
     @Nullable
-    public WifiApIface createBridgedApIface() {
+    public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) {
+        if (vendorData == null) {
+            Log.e(TAG, "createBridgedApIface received null vendorData");
+            return null;
+        }
         return validateAndCall("createBridgedApIface", null,
-                () -> mWifiChip.createBridgedApIface());
+                () -> mWifiChip.createBridgedApIface(vendorData));
     }
 
     /**
@@ -902,4 +948,13 @@
                 () -> mWifiChip.enableStaChannelForPeerNetwork(enableIndoorChannel,
                         enableDfsChannel));
     }
+
+    /**
+     * See comments for {@link IWifiChip#setAfcChannelAllowance(AfcChannelAllowance)}
+     */
+    public boolean setAfcChannelAllowance(AfcChannelAllowance afcChannelAllowance) {
+        if (afcChannelAllowance == null) return false;
+        return validateAndCall("setAfcChannelAllowance", false,
+                () -> mWifiChip.setAfcChannelAllowance(afcChannelAllowance));
+    }
 }
diff --git a/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java
index aa94aeb..78851bd 100644
--- a/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java
@@ -21,6 +21,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.hardware.wifi.AfcChannelAllowance;
+import android.hardware.wifi.AvailableAfcChannelInfo;
+import android.hardware.wifi.AvailableAfcFrequencyInfo;
 import android.hardware.wifi.IWifiApIface;
 import android.hardware.wifi.IWifiChip.ChannelCategoryMask;
 import android.hardware.wifi.IWifiChip.CoexRestriction;
@@ -48,6 +51,7 @@
 import android.hardware.wifi.WifiStatusCode;
 import android.hardware.wifi.WifiUsableChannel;
 import android.net.wifi.CoexUnsafeChannel;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiAvailableChannel;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
@@ -62,6 +66,7 @@
 import com.android.server.wifi.WifiNative;
 import com.android.server.wifi.WlanWakeReasonAndCounts;
 import com.android.server.wifi.util.BitMask;
+import com.android.server.wifi.util.HalAidlUtil;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -107,16 +112,24 @@
     }
 
     /**
-     * See comments for {@link IWifiChip#createApIface()}
+     * See comments for {@link IWifiChip#createApIface(List)}
      */
     @Override
     @Nullable
-    public WifiApIface createApIface() {
+    public WifiApIface createApIface(@NonNull List<OuiKeyedData> vendorData) {
         final String methodStr = "createApIface";
         synchronized (mLock) {
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return null;
-                IWifiApIface iface = mWifiChip.createApIface();
+                IWifiApIface iface;
+                if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) {
+                    android.hardware.wifi.common.OuiKeyedData[] halVendorData =
+                            HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData);
+                    iface = mWifiChip.createApOrBridgedApIface(
+                            IfaceConcurrencyType.AP, halVendorData);
+                } else {
+                    iface = mWifiChip.createApIface();
+                }
                 return new WifiApIface(iface);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -128,16 +141,24 @@
     }
 
     /**
-     * See comments for {@link IWifiChip#createBridgedApIface()}
+     * See comments for {@link IWifiChip#createBridgedApIface(List)}
      */
     @Override
     @Nullable
-    public WifiApIface createBridgedApIface() {
+    public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) {
         final String methodStr = "createBridgedApIface";
         synchronized (mLock) {
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return null;
-                IWifiApIface iface = mWifiChip.createBridgedApIface();
+                IWifiApIface iface;
+                if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) {
+                    android.hardware.wifi.common.OuiKeyedData[] halVendorData =
+                            HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData);
+                    iface = mWifiChip.createApOrBridgedApIface(
+                            IfaceConcurrencyType.AP_BRIDGED, halVendorData);
+                } else {
+                    iface = mWifiChip.createBridgedApIface();
+                }
                 return new WifiApIface(iface);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1616,6 +1637,79 @@
         return frameworkCombos;
     }
 
+    /**
+     * Converts the framework version of an AvailableAfcFrequencyInfo object to its AIDL equivalent.
+     */
+    private static AvailableAfcFrequencyInfo frameworkToHalAvailableAfcFrequencyInfo(
+            WifiChip.AvailableAfcFrequencyInfo availableFrequencyInfo) {
+        if (availableFrequencyInfo == null) {
+            return null;
+        }
+
+        AvailableAfcFrequencyInfo halAvailableAfcFrequencyInfo = new AvailableAfcFrequencyInfo();
+        halAvailableAfcFrequencyInfo.startFrequencyMhz = availableFrequencyInfo.startFrequencyMhz;
+        halAvailableAfcFrequencyInfo.endFrequencyMhz = availableFrequencyInfo.endFrequencyMhz;
+        halAvailableAfcFrequencyInfo.maxPsd = availableFrequencyInfo.maxPsdDbmPerMhz;
+
+        return halAvailableAfcFrequencyInfo;
+    }
+
+    /**
+     * Converts the framework version of an AvailableAfcChannelInfo object to its AIDL equivalent.
+     */
+    private static AvailableAfcChannelInfo frameworkToHalAvailableAfcChannelInfo(
+            WifiChip.AvailableAfcChannelInfo availableChannelInfo) {
+        if (availableChannelInfo == null) {
+            return null;
+        }
+
+        AvailableAfcChannelInfo halAvailableAfcChannelInfo = new AvailableAfcChannelInfo();
+        halAvailableAfcChannelInfo.globalOperatingClass = availableChannelInfo.globalOperatingClass;
+        halAvailableAfcChannelInfo.channelCfi = availableChannelInfo.channelCfi;
+        halAvailableAfcChannelInfo.maxEirpDbm = availableChannelInfo.maxEirpDbm;
+
+        return halAvailableAfcChannelInfo;
+    }
+
+    private static AfcChannelAllowance frameworkToHalAfcChannelAllowance(
+            WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        AfcChannelAllowance halAfcChannelAllowance = new AfcChannelAllowance();
+
+        // convert allowed frequencies and channels to their HAL version
+        if (afcChannelAllowance.availableAfcFrequencyInfos == null) {
+            // this should not be left uninitialized, or it may result in an exception
+            halAfcChannelAllowance.availableAfcFrequencyInfos = new AvailableAfcFrequencyInfo[0];
+        } else {
+            halAfcChannelAllowance.availableAfcFrequencyInfos = new AvailableAfcFrequencyInfo[
+                    afcChannelAllowance.availableAfcFrequencyInfos.size()];
+
+            for (int i = 0; i < afcChannelAllowance.availableAfcFrequencyInfos.size(); ++i) {
+                halAfcChannelAllowance.availableAfcFrequencyInfos[i] =
+                        frameworkToHalAvailableAfcFrequencyInfo(
+                                afcChannelAllowance.availableAfcFrequencyInfos.get(i));
+            }
+        }
+
+        if (afcChannelAllowance.availableAfcChannelInfos == null) {
+            // this should not be left uninitialized, or it may result in an exception
+            halAfcChannelAllowance.availableAfcChannelInfos = new AvailableAfcChannelInfo[0];
+        } else {
+            halAfcChannelAllowance.availableAfcChannelInfos = new AvailableAfcChannelInfo[
+                    afcChannelAllowance.availableAfcChannelInfos.size()];
+
+            for (int i = 0; i < afcChannelAllowance.availableAfcChannelInfos.size(); ++i) {
+                halAfcChannelAllowance.availableAfcChannelInfos[i] =
+                        frameworkToHalAvailableAfcChannelInfo(
+                                afcChannelAllowance.availableAfcChannelInfos.get(i));
+            }
+        }
+
+        halAfcChannelAllowance.availabilityExpireTimeMs =
+                afcChannelAllowance.availabilityExpireTimeMs;
+
+        return halAfcChannelAllowance;
+    }
+
     private static WifiChip.WifiChipCapabilities halToFrameworkWifiChipCapabilities(
             WifiChipCapabilities halCapabilities) {
         return new WifiChip.WifiChipCapabilities(halCapabilities.maxMloAssociationLinkCount,
@@ -1684,6 +1778,26 @@
         }
     }
 
+    /**
+     * See comments for {@link IWifiChip#setAfcChannelAllowance(WifiChip.AfcChannelAllowance)}
+     */
+    @Override
+    public boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        final String methodStr = "setAfcChannelAllowance";
+
+        try {
+            AfcChannelAllowance halAfcChannelAllowance =
+                    frameworkToHalAfcChannelAllowance(afcChannelAllowance);
+            mWifiChip.setAfcChannelAllowance(halAfcChannelAllowance);
+            return true;
+        } catch (RemoteException e) {
+            handleRemoteException(e, methodStr);
+        } catch (ServiceSpecificException e) {
+            handleServiceSpecificException(e, methodStr);
+        }
+        return false;
+    }
+
     private @android.hardware.wifi.IWifiChip.ChipMloMode int frameworkToAidlMloMode(
             @WifiManager.MloMode int mode) {
         switch(mode) {
diff --git a/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java b/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java
index 9b7a38b..430f96a 100644
--- a/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java
@@ -36,6 +36,7 @@
 import android.hardware.wifi.V1_6.WifiRadioCombination;
 import android.hardware.wifi.V1_6.WifiRadioConfiguration;
 import android.net.wifi.CoexUnsafeChannel;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiAvailableChannel;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
@@ -96,22 +97,22 @@
     }
 
     /**
-     * See comments for {@link IWifiChip#createApIface()}
+     * See comments for {@link IWifiChip#createApIface(List)}
      */
     @Override
     @Nullable
-    public WifiApIface createApIface() {
+    public WifiApIface createApIface(@NonNull List<OuiKeyedData> vendorData) {
         String methodStr = "createApIface";
         return validateAndCall(methodStr, null,
                 () -> createApIfaceInternal(methodStr));
     }
 
     /**
-     * See comments for {@link IWifiChip#createBridgedApIface()}
+     * See comments for {@link IWifiChip#createBridgedApIface(List)}
      */
     @Override
     @Nullable
-    public WifiApIface createBridgedApIface() {
+    public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) {
         String methodStr = "createBridgedApIface";
         return validateAndCall(methodStr, null,
                 () -> createBridgedApIfaceInternal(methodStr));
@@ -586,6 +587,15 @@
         return false;
     }
 
+    /**
+     * See comments for {@link IWifiChip#setAfcChannelAllowance(WifiChip.AfcChannelAllowance)}
+     */
+    @Override
+    public boolean setAfcChannelAllowance(WifiChip.AfcChannelAllowance afcChannelAllowance) {
+        Log.d(TAG, "setAfcChannelAllowance() is not implemented in hidl.");
+        return false;
+    }
+
     // Internal Implementations
 
     private boolean configureChipInternal(String methodStr, int modeId) {
diff --git a/service/java/com/android/server/wifi/hal/WifiHalAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiHalAidlImpl.java
index 9edfed2..7207d33 100644
--- a/service/java/com/android/server/wifi/hal/WifiHalAidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiHalAidlImpl.java
@@ -52,6 +52,7 @@
     private DeathRecipient mServiceDeathRecipient;
     private WifiHal.DeathRecipient mFrameworkDeathRecipient;
     private final Object mLock = new Object();
+    private static int sServiceVersion;
 
     public WifiHalAidlImpl(@NonNull Context context, @NonNull SsidTranslator ssidTranslator) {
         Log.i(TAG, "Creating the Wifi HAL using the AIDL implementation");
@@ -252,7 +253,8 @@
                     + android.hardware.wifi.IWifi.VERSION);
 
             try {
-                Log.i(TAG, "Remote Version: " + mWifi.getInterfaceVersion());
+                sServiceVersion = mWifi.getInterfaceVersion();
+                Log.i(TAG, "Remote Version: " + sServiceVersion);
                 IBinder serviceBinder = getServiceBinderMockable();
                 if (serviceBinder == null) {
                     Log.e(TAG, "Unable to obtain the service binder");
@@ -384,6 +386,14 @@
         return mWifi.asBinder();
     }
 
+    /**
+     * Check that the service is running at least the expected version. Method is protected
+     * in order to allow calls from the WifiXxxIface classes.
+     */
+    protected static boolean isServiceVersionAtLeast(int expectedVersion) {
+        return expectedVersion <= sServiceVersion;
+    }
+
     private boolean checkWifiAndLogFailure(String methodStr) {
         if (mWifi == null) {
             Log.e(TAG, "Unable to call " + methodStr + " because IWifi is null.");
diff --git a/service/java/com/android/server/wifi/hal/WifiNanIface.java b/service/java/com/android/server/wifi/hal/WifiNanIface.java
index 6645474..62541c9 100644
--- a/service/java/com/android/server/wifi/hal/WifiNanIface.java
+++ b/service/java/com/android/server/wifi/hal/WifiNanIface.java
@@ -199,7 +199,9 @@
         public static final int UNSUPPORTED_CONCURRENCY_NAN_DISABLED = 12;
         public static final int INVALID_PAIRING_ID = 13;
         public static final int INVALID_BOOTSTRAPPING_ID = 14;
-
+        public static final int REDUNDANT_REQUEST = 15;
+        public static final int NOT_SUPPORTED = 16;
+        public static final int NO_CONNECTION = 17;
 
         /**
          * Convert NanStatusCode from HIDL to framework.
@@ -273,6 +275,12 @@
                     return INVALID_PAIRING_ID;
                 case android.hardware.wifi.NanStatusCode.INVALID_BOOTSTRAPPING_ID:
                     return INVALID_BOOTSTRAPPING_ID;
+                case android.hardware.wifi.NanStatusCode.REDUNDANT_REQUEST:
+                    return REDUNDANT_REQUEST;
+                case android.hardware.wifi.NanStatusCode.NOT_SUPPORTED:
+                    return NOT_SUPPORTED;
+                case android.hardware.wifi.NanStatusCode.NO_CONNECTION:
+                    return NO_CONNECTION;
                 default:
                     Log.e(TAG, "Unknown NanStatusType received from AIDL: " + code);
                     return -1;
diff --git a/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java
index f5298d5..0f10214 100644
--- a/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java
@@ -439,6 +439,7 @@
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.terminateDataPathRequest((char) transactionId, ndpId);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -460,6 +461,7 @@
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.respondToPairingIndicationRequest((char) transactionId, request);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -481,6 +483,7 @@
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.initiatePairingRequest((char) transactionId, nanPairingRequest);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -497,6 +500,7 @@
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.terminatePairingRequest((char) transactionId, pairingId);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -516,6 +520,7 @@
             try {
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.initiateBootstrappingRequest((char) transactionId, request);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -535,6 +540,7 @@
                 if (!checkIfaceAndLogFailure(methodStr)) return false;
                 mWifiNanIface.respondToBootstrappingIndicationRequest((char) transactionId,
                         request);
+                return true;
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
             } catch (ServiceSpecificException e) {
@@ -789,8 +795,15 @@
         req.baseConfigs.discoveryCount = 0;
         req.baseConfigs.serviceName = copyArray(publishConfig.mServiceName);
         req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_NEVER;
-        req.baseConfigs.serviceSpecificInfo = copyArray(publishConfig.mServiceSpecificInfo);
-        req.baseConfigs.extendedServiceSpecificInfo = new byte[0];
+        if (publishConfig.mServiceSpecificInfo != null
+                && publishConfig.mServiceSpecificInfo.length > 255) {
+            req.baseConfigs.extendedServiceSpecificInfo =
+                    copyArray(publishConfig.mServiceSpecificInfo);
+            req.baseConfigs.serviceSpecificInfo = new byte[0];
+        } else {
+            req.baseConfigs.serviceSpecificInfo = copyArray(publishConfig.mServiceSpecificInfo);
+            req.baseConfigs.extendedServiceSpecificInfo = new byte[0];
+        }
         if (publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED) {
             req.baseConfigs.txMatchFilter = copyArray(publishConfig.mMatchFilter);
             req.baseConfigs.rxMatchFilter = new byte[0];
@@ -852,8 +865,15 @@
         req.baseConfigs.discoveryCount = 0;
         req.baseConfigs.serviceName = copyArray(subscribeConfig.mServiceName);
         req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
-        req.baseConfigs.serviceSpecificInfo = copyArray(subscribeConfig.mServiceSpecificInfo);
-        req.baseConfigs.extendedServiceSpecificInfo = new byte[0];
+        if (subscribeConfig.mServiceSpecificInfo != null
+                && subscribeConfig.mServiceSpecificInfo.length > 255) {
+            req.baseConfigs.extendedServiceSpecificInfo =
+                    copyArray(subscribeConfig.mServiceSpecificInfo);
+            req.baseConfigs.serviceSpecificInfo = new byte[0];
+        } else {
+            req.baseConfigs.serviceSpecificInfo = copyArray(subscribeConfig.mServiceSpecificInfo);
+            req.baseConfigs.extendedServiceSpecificInfo = new byte[0];
+        }
         if (subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE) {
             req.baseConfigs.txMatchFilter = copyArray(subscribeConfig.mMatchFilter);
             req.baseConfigs.rxMatchFilter = new byte[0];
@@ -919,8 +939,13 @@
         req.addr = dest.toByteArray();
         req.isHighPriority = false;
         req.shouldUseDiscoveryWindow = true;
-        req.serviceSpecificInfo = copyArray(message);
-        req.extendedServiceSpecificInfo = new byte[0];
+        if (message != null && message.length > 255) {
+            req.extendedServiceSpecificInfo = copyArray(message);
+            req.serviceSpecificInfo = new byte[0];
+        } else {
+            req.serviceSpecificInfo = copyArray(message);
+            req.extendedServiceSpecificInfo = new byte[0];
+        }
         req.disableFollowupResultIndication = false;
         return req;
     }
diff --git a/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java
index d80c868..648b344 100644
--- a/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java
@@ -393,26 +393,54 @@
     @Override
     public void eventMatch(NanMatchInd event) {
         if (!checkFrameworkCallback()) return;
-        if (mVerboseLoggingEnabled) {
-            Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId
-                    + ", peerId=" + event.peerId
-                    + ", addr=" + String.valueOf(HexEncoding.encode(event.addr))
-                    + ", serviceSpecificInfo=" + Arrays.toString(event.serviceSpecificInfo)
-                    + ", ssi.size()=" + (event.serviceSpecificInfo == null
-                            ? 0 : event.serviceSpecificInfo.length)
-                    + ", matchFilter=" + Arrays.toString(event.matchFilter)
-                    + ", mf.size()=" + (event.matchFilter == null ? 0 : event.matchFilter.length)
-                    + ", rangingIndicationType=" + event.rangingIndicationType
-                    + ", rangingMeasurementInMm=" + event.rangingMeasurementInMm + ", "
-                    + "scid=" + Arrays.toString(event.scid));
+        byte[] serviceSpecificInfo = event.serviceSpecificInfo;
+        boolean isExtendedServiceSpecificInfo = false;
+        if (serviceSpecificInfo == null || serviceSpecificInfo.length == 0) {
+            serviceSpecificInfo = event.extendedServiceSpecificInfo;
+            isExtendedServiceSpecificInfo = true;
         }
-        mWifiNanIface.getFrameworkCallback().eventMatch(event.discoverySessionId, event.peerId,
-                event.addr, event.serviceSpecificInfo, event.matchFilter,
-                NanRangingIndication.fromAidl(event.rangingIndicationType),
-                event.rangingMeasurementInMm, event.scid,
-                toPublicDataPathCipherSuites(event.peerCipherType),
-                event.peerNira.nonce, event.peerNira.tag,
-                createPublicPairingConfig(event.peerPairingConfig));
+        if (mVerboseLoggingEnabled) {
+            Log.v(
+                    TAG,
+                    "eventMatch: discoverySessionId="
+                            + event.discoverySessionId
+                            + ", peerId="
+                            + event.peerId
+                            + ", addr="
+                            + String.valueOf(HexEncoding.encode(event.addr))
+                            + ", isExtendedServiceSpecificInfo="
+                            + isExtendedServiceSpecificInfo
+                            + ", serviceSpecificInfo="
+                            + Arrays.toString(serviceSpecificInfo)
+                            + ", ssi.size()="
+                            + (serviceSpecificInfo == null ? 0 : serviceSpecificInfo.length)
+                            + ", matchFilter="
+                            + Arrays.toString(event.matchFilter)
+                            + ", mf.size()="
+                            + (event.matchFilter == null ? 0 : event.matchFilter.length)
+                            + ", rangingIndicationType="
+                            + event.rangingIndicationType
+                            + ", rangingMeasurementInMm="
+                            + event.rangingMeasurementInMm
+                            + ", "
+                            + "scid="
+                            + Arrays.toString(event.scid));
+        }
+        mWifiNanIface
+                .getFrameworkCallback()
+                .eventMatch(
+                        event.discoverySessionId,
+                        event.peerId,
+                        event.addr,
+                        serviceSpecificInfo,
+                        event.matchFilter,
+                        NanRangingIndication.fromAidl(event.rangingIndicationType),
+                        event.rangingMeasurementInMm,
+                        event.scid,
+                        toPublicDataPathCipherSuites(event.peerCipherType),
+                        event.peerNira.nonce,
+                        event.peerNira.tag,
+                        createPublicPairingConfig(event.peerPairingConfig));
     }
 
     private AwarePairingConfig createPublicPairingConfig(NanPairingConfig nativePairingConfig) {
@@ -469,15 +497,33 @@
     @Override
     public void eventFollowupReceived(NanFollowupReceivedInd event) {
         if (!checkFrameworkCallback()) return;
-        if (mVerboseLoggingEnabled) {
-            Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId
-                    + ", peerId=" + event.peerId + ", addr=" + String.valueOf(
-                    HexEncoding.encode(event.addr)) + ", serviceSpecificInfo=" + Arrays.toString(
-                    event.serviceSpecificInfo) + ", ssi.size()="
-                    + (event.serviceSpecificInfo == null ? 0 : event.serviceSpecificInfo.length));
+        byte[] serviceSpecificInfo = event.serviceSpecificInfo;
+        boolean isExtendedServiceSpecificInfo = false;
+        if (serviceSpecificInfo == null || serviceSpecificInfo.length == 0) {
+            serviceSpecificInfo = event.extendedServiceSpecificInfo;
+            isExtendedServiceSpecificInfo = true;
         }
-        mWifiNanIface.getFrameworkCallback().eventFollowupReceived(
-                event.discoverySessionId, event.peerId, event.addr, event.serviceSpecificInfo);
+
+        if (mVerboseLoggingEnabled) {
+            Log.v(
+                    TAG,
+                    "eventFollowupReceived: discoverySessionId="
+                            + event.discoverySessionId
+                            + ", peerId="
+                            + event.peerId
+                            + ", addr="
+                            + String.valueOf(HexEncoding.encode(event.addr))
+                            + ", isExtendedServiceSpecificInfo="
+                            + isExtendedServiceSpecificInfo
+                            + ", serviceSpecificInfo="
+                            + Arrays.toString(event.serviceSpecificInfo)
+                            + ", ssi.size()="
+                            + (serviceSpecificInfo == null ? 0 : serviceSpecificInfo.length));
+        }
+        mWifiNanIface
+                .getFrameworkCallback()
+                .eventFollowupReceived(
+                        event.discoverySessionId, event.peerId, event.addr, serviceSpecificInfo);
     }
 
     @Override
@@ -681,7 +727,8 @@
         frameworkCapabilities.isNanPairingSupported = capabilities.supportsPairing;
         frameworkCapabilities.isSetClusterIdSupported = capabilities.supportsSetClusterId;
         frameworkCapabilities.isSuspensionSupported = capabilities.supportsSuspension;
-
+        frameworkCapabilities.is6gSupported = capabilities.supports6g;
+        frameworkCapabilities.isHeSupported = capabilities.supportsHe;
         return frameworkCapabilities;
     }
 
diff --git a/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java
index 3692545..0950ecf 100644
--- a/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java
+++ b/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java
@@ -38,6 +38,8 @@
 import android.hardware.wifi.StaScanData;
 import android.hardware.wifi.StaScanDataFlagMask;
 import android.hardware.wifi.StaScanResult;
+import android.hardware.wifi.TwtSession;
+import android.hardware.wifi.TwtSessionStats;
 import android.hardware.wifi.WifiBand;
 import android.hardware.wifi.WifiChannelStats;
 import android.hardware.wifi.WifiDebugPacketFateFrameType;
@@ -238,6 +240,9 @@
     public WifiNative.ScanCapabilities getBackgroundScanCapabilities() {
         final String methodStr = "getBackgroundScanCapabilities";
         synchronized (mLock) {
+            if (!checkIfaceAndLogFailure(methodStr)) {
+                return null;
+            }
             try {
                 StaBackgroundScanCapabilities halCaps =
                         mWifiStaIface.getBackgroundScanCapabilities();
@@ -725,6 +730,42 @@
         public int getInterfaceVersion() {
             return IWifiStaIfaceEventCallback.VERSION;
         }
+
+        @Override
+        public void onTwtFailure(int cmdId, byte twtErrorCode) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionCreate(int cmdId, TwtSession twtSession) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionUpdate(int cmdId, TwtSession twtSession) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionResume(int cmdId, int sessionId) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionSuspend(int cmdId, int sessionId) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionTeardown(int cmdId, int twtSessionId, byte twtReasonCode) {
+            //TODO: Implementation
+        }
+
+        @Override
+        public void onTwtSessionStats(int cmdId, int twtSessionId,
+                TwtSessionStats twtSessionStats) {
+            //TODO: Implementation
+        }
     }
 
 
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java b/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java
index 071cbd4..993412e 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java
@@ -16,18 +16,24 @@
 
 package com.android.server.wifi.hotspot2;
 
+import android.app.AlarmManager;
+import android.os.Handler;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wifi.Clock;
+import com.android.server.wifi.WifiInjector;
 import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.wifi.flags.FeatureFlags;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 
 /**
  * Class for managing sending of ANQP requests.  This manager will ignore ANQP requests for a
@@ -35,15 +41,22 @@
  * unanswered or failed.  The hold off time will increase exponentially until the max is reached.
  */
 public class ANQPRequestManager {
+    private static final int ANQP_REQUEST_ALARM_INTERVAL_MS = 2_000;
+    @VisibleForTesting
+    public static final String ANQP_REQUEST_ALARM_TAG = "anqpRequestAlarm";
     private static final String TAG = "ANQPRequestManager";
 
     private final PasspointEventHandler mPasspointHandler;
+    private final AlarmManager mAlarmManager;
     private final Clock mClock;
+    private final FeatureFlags mFeatureFlags;
+    private boolean mAnqpRequestPending;
 
     /**
      * List of pending ANQP request associated with an AP (BSSID).
      */
     private final Map<Long, ANQPNetworkKey> mPendingQueries;
+    private final Queue<AnqpRequest> mPendingRequest = new ArrayDeque<>();
 
     /**
      * List of hold off time information associated with APs specified by their BSSID.
@@ -80,6 +93,7 @@
 
     private static final List<Constants.ANQPElementType> R2_ANQP_BASE_SET = Arrays.asList(
             Constants.ANQPElementType.HSOSUProviders);
+    private final Handler mHandler;
 
     /**
      * Class to keep track of AP status for ANQP requests.
@@ -95,12 +109,38 @@
          */
         public long holdOffExpirationTime;
     }
+    private static class AnqpRequest {
 
-    public ANQPRequestManager(PasspointEventHandler handler, Clock clock) {
-        mPasspointHandler = handler;
+        AnqpRequest(long bssid, boolean rcOIs, NetworkDetail.HSRelease hsRelease,
+                ANQPNetworkKey anqpNetworkKey) {
+            mBssid = bssid;
+            mAnqpNetworkKey = anqpNetworkKey;
+            mRcOIs = rcOIs;
+            mHsRelease = hsRelease;
+        }
+        public final long mBssid;
+        public final boolean mRcOIs;
+        public final NetworkDetail.HSRelease mHsRelease;
+        public final ANQPNetworkKey mAnqpNetworkKey;
+    }
+
+    private final AlarmManager.OnAlarmListener mAnqpRequestListener =
+            new AlarmManager.OnAlarmListener() {
+                public void onAlarm() {
+                    mAnqpRequestPending = false;
+                    processNextRequest();
+                }
+            };
+
+    public ANQPRequestManager(PasspointEventHandler passpointEventHandler, Clock clock,
+            WifiInjector wifiInjector, Handler handler) {
+        mPasspointHandler = passpointEventHandler;
         mClock = clock;
-        mPendingQueries = new HashMap<>();
+        mAlarmManager = wifiInjector.getAlarmManager();
+        mFeatureFlags = wifiInjector.getDeviceConfigFacade().getFeatureFlags();
         mHoldOffInfo = new HashMap<>();
+        mPendingQueries = new HashMap<>();
+        mHandler = handler;
     }
 
     /**
@@ -119,6 +159,12 @@
      */
     public boolean requestANQPElements(long bssid, ANQPNetworkKey anqpNetworkKey, boolean rcOIs,
             NetworkDetail.HSRelease hsReleaseVer) {
+        if (mFeatureFlags.anqpRequestWaitForResponse()) {
+            // Put the new request in the queue, process it if possible(no more pending request)
+            mPendingRequest.offer(new AnqpRequest(bssid, rcOIs, hsReleaseVer, anqpNetworkKey));
+            processNextRequest();
+            return true;
+        }
         // Check if we are allow to send the request now.
         if (!canSendRequestNow(bssid)) {
             return false;
@@ -137,6 +183,39 @@
         return true;
     }
 
+    private void processNextRequest() {
+        if (mAnqpRequestPending) {
+            return;
+        }
+        AnqpRequest request;
+        while ((request = mPendingRequest.poll()) != null) {
+            // Check if we are allow to send the request now.
+            if (!canSendRequestNow(request.mBssid)) {
+                continue;
+            }
+            // No need to hold off future requests and set next alarm for send failures.
+            if (mPasspointHandler.requestANQP(request.mBssid, getRequestElementIDs(request.mRcOIs,
+                    request.mHsRelease))) {
+                break;
+            }
+        }
+        if (request == null) {
+            return;
+        }
+        // Update hold off info on when we are allowed to send the next ANQP request to
+        // the given AP.
+        updateHoldOffInfo(request.mBssid);
+        mPendingQueries.put(request.mBssid, request.mAnqpNetworkKey);
+        // Schedule next request in case of time out waiting for response.
+        mAlarmManager.set(
+                AlarmManager.ELAPSED_REALTIME,
+                mClock.getElapsedSinceBootMillis() + ANQP_REQUEST_ALARM_INTERVAL_MS,
+                ANQP_REQUEST_ALARM_TAG,
+                mAnqpRequestListener,
+                mHandler);
+        mAnqpRequestPending = true;
+    }
+
     /**
      * Request Venue URL ANQP-element from the specified AP post connection.
      *
@@ -149,7 +228,6 @@
             return false;
         }
 
-        mPendingQueries.put(bssid, anqpNetworkKey);
         return true;
     }
 
@@ -165,6 +243,10 @@
             // Query succeeded.  No need to hold off request to the given AP.
             mHoldOffInfo.remove(bssid);
         }
+        // Cancel the schedule, and process next request.
+        mAlarmManager.cancel(mAnqpRequestListener);
+        mAnqpRequestPending = false;
+        processNextRequest();
         return mPendingQueries.remove(bssid);
     }
 
@@ -257,5 +339,8 @@
     public void clear() {
         mPendingQueries.clear();
         mHoldOffInfo.clear();
+        mAlarmManager.cancel(mAnqpRequestListener);
+        mAnqpRequestPending = false;
+        mPendingRequest.clear();
     }
 }
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 0186563..407240d 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -131,10 +131,44 @@
     private final boolean mMboCellularDataAware;
     private final boolean mOceSupported;
 
+    // Target wake time (TWT) allows an AP to manage activity in the BSS in order to minimize
+    // contention between STAs and to reduce the required amount of time that a STA utilizing a
+    // power management mode needs to be awake.
+
+    // The HE AP requests that STAs participate in TWT by setting the TWT Required subfield to 1
+    // in HE Operation elements. STAs that support TWT and receive an HE Operation element with
+    // the TWT Required subfield set to 1 must either negotiate individual TWT agreements or
+    // participate in broadcast TWT operation.
+    private final boolean mTwtRequired;
+    // With Individual TWT operation, a STA negotiate a wake schedule with an access point, allowing
+    // it to wake up only when required.
+    private final boolean mIndividualTwtSupported;
+    // In Broadcast TWT operation, an AP can set up a shared TWT session for a group of stations
+    // and specify the TWT parameters periodically in Beacon frames.
+    private final boolean mBroadcastTwtSupported;
+    // Restricted Target Wake Time (TWT) is a feature that allows an access point to allocate
+    // exclusive access to a medium at specified times.
+    private final boolean mRestrictedTwtSupported;
+
+    // EPCS priority access is a mechanism that provides prioritized access to the wireless
+    // medium for authorized users to increase their probability of successful communication
+    // during periods of network congestion.
+    private final boolean mEpcsPriorityAccessSupported;
+
+    // Fast Initial Link Setup (FILS)
+    private final boolean mFilsCapable;
+
+    // 6 GHz Access Point Type
+    private final InformationElementUtil.ApType6GHz mApType6GHz;
+
+    // IEEE 802.11az
+    private final boolean mIs11azSupported;
+
     // MLO Attributes
     private MacAddress mMldMacAddress = null;
     private int mMloLinkId = MloLink.INVALID_MLO_LINK_ID;
     private List<MloLink> mAffiliatedMloLinks = Collections.emptyList();
+    private byte[] mDisabledSubchannelBitmap;
 
     public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements,
             List<String> anqpLines, int freq) {
@@ -270,7 +304,7 @@
             }
         }
         catch (IllegalArgumentException | BufferUnderflowException | ArrayIndexOutOfBoundsException e) {
-            Log.d(Utils.hs2LogTag(getClass()), "Caught " + e);
+            Log.d(TAG, "Caught " + e);
             if (ssidOctets == null) {
                 throw new IllegalArgumentException("Malformed IE string (no SSID)", e);
             }
@@ -331,12 +365,22 @@
         mANQPElements = null;
         //set up channel info
         mPrimaryFreq = freq;
+        mTwtRequired = heOperation.isTwtRequired();
+        mIndividualTwtSupported = heCapabilities.isTwtResponderSupported();
+        mBroadcastTwtSupported = heCapabilities.isBroadcastTwtSupported();
+        mRestrictedTwtSupported = ehtCapabilities.isRestrictedTwtSupported();
+        mEpcsPriorityAccessSupported = ehtCapabilities.isEpcsPriorityAccessSupported();
+        mFilsCapable = extendedCapabilities.isFilsCapable();
+        mApType6GHz = heOperation.getApType6GHz();
+        mIs11azSupported = extendedCapabilities.isTriggerBasedRangingRespSupported()
+                || extendedCapabilities.isNonTriggerBasedRangingRespSupported();
         int channelWidth = ScanResult.UNSPECIFIED;
         int centerFreq0 = mPrimaryFreq;
         int centerFreq1 = 0;
 
         if (ehtOperation.isPresent()) {
             //TODO: include parsing of EHT_Operation to collect BW and center freq.
+            mDisabledSubchannelBitmap = ehtOperation.getDisabledSubchannelBitmap();
         }
 
         if (ehtOperation.isPresent()) {
@@ -509,6 +553,14 @@
         mMboCellularDataAware = base.mMboCellularDataAware;
         mOceSupported = base.mOceSupported;
         mMboAssociationDisallowedReasonCode = base.mMboAssociationDisallowedReasonCode;
+        mTwtRequired = base.mTwtRequired;
+        mIndividualTwtSupported = base.mIndividualTwtSupported;
+        mBroadcastTwtSupported = base.mBroadcastTwtSupported;
+        mRestrictedTwtSupported = base.mRestrictedTwtSupported;
+        mEpcsPriorityAccessSupported = base.mEpcsPriorityAccessSupported;
+        mFilsCapable = base.mFilsCapable;
+        mApType6GHz = base.mApType6GHz;
+        mIs11azSupported = base.mIs11azSupported;
     }
 
     public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
@@ -652,6 +704,10 @@
         return mAffiliatedMloLinks;
     }
 
+    public byte[] getDisabledSubchannelBitmap() {
+        return mDisabledSubchannelBitmap;
+    }
+
     @Override
     public boolean equals(Object thatObject) {
         if (this == thatObject) {
@@ -674,8 +730,8 @@
     @Override
     public String toString() {
         return "NetworkInfo{SSID='" + mSSID
-                + "', HESSID=" + Utils.macToSimpleString(mHESSID)
-                + ", BSSID=" + Utils.macToSimpleString(mBSSID)
+                + "', HESSID=" + Utils.macToString(mHESSID)
+                + ", BSSID=" + Utils.macToString(mBSSID)
                 + ", StationCount=" + mStationCount
                 + ", ChannelUtilization=" + mChannelUtilization
                 + ", Capacity=" + mCapacity
@@ -687,9 +743,9 @@
 
     public String toKeyString() {
         return mHESSID != 0 ?
-                "'" + mSSID + "':" + Utils.macToSimpleString(mBSSID) + " ("
-                        + Utils.macToSimpleString(mHESSID) + ")"
-                : "'" + mSSID + "':" + Utils.macToSimpleString(mBSSID);
+                "'" + mSSID + "':" + Utils.macToString(mBSSID) + " ("
+                        + Utils.macToString(mHESSID) + ")"
+                : "'" + mSSID + "':" + Utils.macToString(mBSSID);
     }
 
     public String getBSSIDString() {
@@ -733,4 +789,59 @@
     public boolean isOceSupported() {
         return mOceSupported;
     }
+
+    /** Return whether the AP supports IEEE 802.11az **/
+    public boolean is11azSupported() {
+        return mIs11azSupported;
+    }
+    /**
+     * Return whether the AP requires HE stations to participate either in individual TWT
+     * agreements or Broadcast TWT operation.
+     **/
+    public boolean isTwtRequired() {
+        return mTwtRequired;
+    }
+
+    /** Return whether individual TWT is supported. */
+    public boolean isIndividualTwtSupported() {
+        return mIndividualTwtSupported;
+    }
+
+    /** Return whether broadcast TWT is supported */
+    public boolean isBroadcastTwtSupported() {
+        return mBroadcastTwtSupported;
+    }
+
+    /**
+     * Returns whether restricted TWT is supported or not. It enables enhanced medium access
+     * protection and resource reservation mechanisms for delivery of latency sensitive
+     * traffic.
+     */
+    public boolean isRestrictedTwtSupported() {
+        return mRestrictedTwtSupported;
+    }
+
+    /**
+     * Returns whether EPCS priority access supported or not. EPCS priority access is a
+     * mechanism that provides prioritized access to the wireless medium for authorized users to
+     * increase their probability of successful communication during periods of network
+     * congestion.
+     */
+    public boolean isEpcsPriorityAccessSupported() {
+        return mEpcsPriorityAccessSupported;
+    }
+
+    /**
+     * @return true if Fast Initial Link Setup (FILS) capable
+     */
+    public boolean isFilsCapable() {
+        return mFilsCapable;
+    }
+
+    /**
+     * Return 6Ghz AP type as defined in {@link InformationElementUtil.ApType6GHz}
+     **/
+    public InformationElementUtil.ApType6GHz getApType6GHz() {
+        return mApType6GHz;
+    }
 }
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java b/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java
index 0d82947..c8f3871 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java
@@ -34,6 +34,7 @@
  * event notifications.
  */
 public class PasspointEventHandler {
+    private static final String TAG = "PasspointEventHandler";
     private final WifiInjector mWifiInjector;
     private final Callbacks mCallbacks;
 
@@ -82,10 +83,10 @@
         if (bssid == 0 || querySets == null) return false;
         if (!mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager().requestAnqp(
                 Utils.macToString(bssid), querySets.first, querySets.second)) {
-            Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " + Utils.macToString(bssid));
+            Log.d(TAG, "ANQP failed on " + Utils.macToString(bssid));
             return false;
         }
-        Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + Utils.macToString(bssid));
+        Log.d(TAG, "ANQP initiated on " + Utils.macToString(bssid));
         return true;
     }
 
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 1eaac10..c5f2c3c 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -87,7 +87,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * This class provides the APIs to manage Passpoint provider configurations.
@@ -296,13 +296,18 @@
     }
 
     private void onUserConnectChoiceRemove(String choiceKey) {
-        if (mProviders.values().stream()
-                .filter(provider -> TextUtils.equals(provider.getConnectChoice(), choiceKey))
-                .peek(provider -> provider.setUserConnectChoice(null, 0))
-                .count() == 0) {
-            return;
+        AtomicBoolean modified = new AtomicBoolean(false);
+        mProviders.values().forEach(provider -> {
+            if (TextUtils.equals(provider.getConnectChoice(), choiceKey)) {
+                provider.setUserConnectChoice(null, 0);
+                if (!modified.get())  {
+                    modified.set(true);
+                }
+            }
+        });
+        if (modified.get()) {
+            mWifiConfigManager.saveToStore(true);
         }
-        mWifiConfigManager.saveToStore(true);
     }
 
     private void onUserConnectChoiceSet(List<WifiConfiguration> networks, String choiceKey,
@@ -327,19 +332,17 @@
      */
     public void removePasspointProviderWithPackage(@NonNull String packageName) {
         stopTrackingAppOpsChange(packageName);
-        for (Map.Entry<String, PasspointProvider> entry : getPasspointProviderWithPackage(
-                packageName).entrySet()) {
-            String uniqueId = entry.getValue().getConfig().getUniqueId();
-            removeProvider(Process.WIFI_UID /* ignored */, true, uniqueId, null);
+        for (PasspointProvider provider : getPasspointProviderWithPackage(packageName)) {
+            removeProvider(Process.WIFI_UID /* ignored */, true,
+                    provider.getConfig().getUniqueId(), null);
         }
     }
 
-    private Map<String, PasspointProvider> getPasspointProviderWithPackage(
+    private List<PasspointProvider> getPasspointProviderWithPackage(
             @NonNull String packageName) {
-        return mProviders.entrySet().stream().filter(
-                entry -> TextUtils.equals(packageName,
-                        entry.getValue().getPackageName())).collect(
-                Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
+        List<PasspointProvider> providers = new ArrayList<>(mProviders.values());
+        providers.removeIf(provider -> !TextUtils.equals(packageName, provider.getPackageName()));
+        return providers;
     }
 
     private void startTrackingAppOpsChange(@NonNull String packageName, int uid) {
@@ -377,7 +380,8 @@
         mObjectFactory = objectFactory;
         mProviders = new HashMap<>();
         mAnqpCache = objectFactory.makeAnqpCache(clock);
-        mAnqpRequestManager = objectFactory.makeANQPRequestManager(mPasspointEventHandler, clock);
+        mAnqpRequestManager = objectFactory.makeANQPRequestManager(mPasspointEventHandler, clock,
+                wifiInjector, mHandler);
         mWifiConfigManager = wifiConfigManager;
         mWifiMetrics = wifiMetrics;
         mProviderIndex = 0;
@@ -868,9 +872,8 @@
             return Collections.emptyList();
         }
         List<Pair<PasspointProvider, PasspointMatch>> allMatches = getAllMatchedProviders(
-                scanResult, anqpRequestAllowed).stream()
-                .filter(a -> !isExpired(a.first.getConfig()))
-                .collect(Collectors.toList());
+                scanResult, anqpRequestAllowed);
+        allMatches.removeIf(a -> isExpired(a.first.getConfig()));
         if (allMatches.isEmpty()) {
             if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "No service provider found for " + scanResult.SSID);
@@ -1197,16 +1200,24 @@
             return Collections.emptyList();
         }
         List<WifiConfiguration> configs = new ArrayList<>();
-        Set<String> uniqueIdSet = new HashSet<>();
-        uniqueIdSet.addAll(idList);
+        Set<String> uniqueIdSet = new HashSet<>(idList);
+        boolean refreshed = false;
         for (String uniqueId : uniqueIdSet) {
             PasspointProvider provider = mProviders.get(uniqueId);
             if (provider == null) {
                 continue;
             }
-            WifiConfiguration config = provider.getWifiConfig();
-            config = mWifiConfigManager.getConfiguredNetwork(config.getProfileKey());
+            String profileKey = provider.getWifiConfig().getProfileKey();
+            WifiConfiguration config = mWifiConfigManager
+                    .getConfiguredNetwork(profileKey);
+            if (config == null && !refreshed) {
+                // Refresh the WifiConfigManager, this may caused by new ANQP response
+                mPasspointNetworkNominateHelper.refreshWifiConfigsForProviders();
+                refreshed = true;
+                config = mWifiConfigManager.getConfiguredNetwork(profileKey);
+            }
             if (config == null) {
+                Log.e(TAG, "After refresh, still not in the WifiConfig, ignore");
                 continue;
             }
             // If the Passpoint configuration is from a suggestion, check if the app shares this
@@ -1232,21 +1243,32 @@
     }
 
     /**
-     * Returns the corresponding wifi configurations for all non-suggestion Passpoint profiles
-     * that include a recent SSID.
+     * Returns the corresponding Wifi configurations for all non-suggestion Passpoint profiles.
      *
+     * @param requireSsid If true, this method will only return Passpoint configs that include an
+     *     SSID. If false, this method will return all Passpoint configs, including those which do
+     *     not include an SSID.
+     *     <p>Note: Passpoint SSIDs are recorded upon successful connection to a network. Having an
+     *     SSID indicates that a Passpoint network has connected since the last reboot.
      * @return List of {@link WifiConfiguration} converted from {@link PasspointProvider}.
      */
-    public List<WifiConfiguration> getWifiConfigsForPasspointProfilesWithSsids() {
+    public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(boolean requireSsid) {
+        if (mProviders.isEmpty()) return Collections.emptyList();
+        List<PasspointProvider> sortedProviders = new ArrayList<>(mProviders.values());
+        Collections.sort(sortedProviders, new PasspointProvider.ConnectionTimeComparator());
+
         List<WifiConfiguration> configs = new ArrayList<>();
-        for (PasspointProvider provider : mProviders.values()) {
-            if (provider == null || provider.getMostRecentSsid() == null
-                    || provider.isFromSuggestion()) {
+        for (PasspointProvider provider : sortedProviders) {
+            if (provider == null
+                    || provider.isFromSuggestion()
+                    || (requireSsid && provider.getMostRecentSsid() == null)) {
                 continue;
             }
             WifiConfiguration config = provider.getWifiConfig();
             config.SSID = provider.getMostRecentSsid();
-            config.getNetworkSelectionStatus().setHasEverConnected(true);
+            if (config.SSID != null) {
+                config.getNetworkSelectionStatus().setHasEverConnected(true);
+            }
             configs.add(config);
         }
         return configs;
@@ -1282,6 +1304,7 @@
             provider.setHasEverConnected(true);
         }
         provider.setMostRecentSsid(ssid);
+        provider.updateMostRecentConnectionTime();
     }
 
     /**
@@ -1445,7 +1468,7 @@
     public void clearAnqpRequestsAndFlushCache() {
         mAnqpRequestManager.clear();
         mAnqpCache.flush();
-        mProviders.values().stream().forEach(p -> p.clearProviderBlock());
+        mProviders.values().forEach(PasspointProvider::clearProviderBlock);
     }
 
     private PKIXParameters mInjectedPKIXParameters;
@@ -1629,7 +1652,7 @@
      * Resets all sim networks state.
      */
     public void resetSimPasspointNetwork() {
-        mProviders.values().stream().forEach(p -> p.setAnonymousIdentity(null));
+        mProviders.values().forEach(p -> p.setAnonymousIdentity(null));
         mWifiConfigManager.saveToStore(true);
     }
 
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelper.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelper.java
index 1048102..44db1f3 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelper.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelper.java
@@ -36,8 +36,10 @@
 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
 import com.android.server.wifi.hotspot2.anqp.Constants;
 import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
+import com.android.server.wifi.util.InformationElementUtil;
 import com.android.wifi.resources.R;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -101,13 +103,13 @@
 
     /**
      * Get best matched available Passpoint network candidates for scanDetails.
+     *
      * @param scanDetails List of ScanDetail.
-     * @param isFromSuggestion True to indicate profile from suggestion, false for user saved.
      * @return List of pair of scanDetail and WifiConfig from matched available provider.
      */
     public List<Pair<ScanDetail, WifiConfiguration>> getPasspointNetworkCandidates(
-            List<ScanDetail> scanDetails, boolean isFromSuggestion) {
-        return findBestMatchScanDetailForProviders(isFromSuggestion,
+            List<ScanDetail> scanDetails) {
+        return findBestMatchScanDetailForProviders(
                 filterAndUpdateScanDetails(scanDetails));
     }
 
@@ -173,7 +175,7 @@
             // WAN Metrics ANQP element is not initialized in this network. Ignore it.
             return false;
         }
-        return wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isAtCapacity();
+        return wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP;
     }
 
     /**
@@ -209,12 +211,12 @@
     /**
      * Match available providers for each scan detail and add their configs to WifiConfigManager.
      * Then for each available provider, find the best scan detail for it.
-     * @param isFromSuggestion True to indicate profile from suggestion, false for user saved.
+     *
      * @param scanDetailList Scan details to choose from.
      * @return List of pair of scanDetail and WifiConfig from matched available provider.
      */
     private @NonNull List<Pair<ScanDetail, WifiConfiguration>> findBestMatchScanDetailForProviders(
-            boolean isFromSuggestion, List<ScanDetail> scanDetailList) {
+            List<ScanDetail> scanDetailList) {
         if (mResources.getBoolean(
                 R.bool.config_wifiPasspointUseApWanLinkStatusAnqpElement)) {
             scanDetailList = scanDetailList.stream()
@@ -232,9 +234,6 @@
         // candidate pair.
         for (Map.Entry<PasspointProvider, List<PasspointNetworkCandidate>> candidates :
                 candidatesPerProvider.entrySet()) {
-            if (candidates.getKey().isFromSuggestion() != isFromSuggestion) {
-                continue;
-            }
             List<PasspointNetworkCandidate> bestCandidates =
                     findHomeNetworksIfPossible(candidates.getValue());
             for (PasspointNetworkCandidate candidate : bestCandidates) {
@@ -368,4 +367,17 @@
         }
         return homeProviderCandidates;
     }
+
+    /**
+     * Dump the current state of PasspointNetworkNominateHelper to the provided output stream.
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("Dump of PasspointNetworkNominateHelper");
+        for (Map.Entry<String, ScanDetail> entry : mCachedScanDetails.entrySet()) {
+            pw.println(entry.getKey());
+            pw.println(InformationElementUtil.getRoamingConsortiumIE(
+                    entry.getValue().getScanResult().informationElements));
+        }
+        pw.println("PasspointNetworkNominateHelper --- end ---");
+    }
 }
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
index 7ad464d..983a1ae 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.os.Handler;
 
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.WifiCarrierInfoManager;
@@ -109,12 +110,13 @@
     /**
      * Create an instance of {@link ANQPRequestManager}.
      *
-     * @param handler Instance of {@link PasspointEventHandler}
-     * @param clock Instance of {@link Clock}
+     * @param passpointEventHandler Instance of {@link PasspointEventHandler}
+     * @param clock                 Instance of {@link Clock}
      * @return {@link ANQPRequestManager}
      */
-    public ANQPRequestManager makeANQPRequestManager(PasspointEventHandler handler, Clock clock) {
-        return new ANQPRequestManager(handler, clock);
+    public ANQPRequestManager makeANQPRequestManager(PasspointEventHandler passpointEventHandler,
+            Clock clock, WifiInjector wifiInjector, Handler handler) {
+        return new ANQPRequestManager(passpointEventHandler, clock, wifiInjector, handler);
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
index 2176121..de0be19 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -64,6 +64,7 @@
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -125,11 +126,23 @@
     private String mConnectChoice = null;
     private int mConnectChoiceRssi = 0;
     private String mMostRecentSsid = null;
+    private long mMostRecentConnectionTime;
 
     // A map that maps SSIDs (String) to a pair of RCOI and a timestamp (both are Long) to be
     // used later when connecting to an RCOI-based Passpoint network.
     private final Map<String, Pair<Long, Long>> mRcoiMatchForNetwork = new HashMap<>();
 
+    /**
+     * Comparator to sort PasspointProviders in descending order by their most recent connection
+     * time.
+     */
+    public static class ConnectionTimeComparator implements Comparator<PasspointProvider> {
+        public int compare(PasspointProvider a, PasspointProvider b) {
+            long diff = a.getMostRecentConnectionTime() - b.getMostRecentConnectionTime();
+            return (diff < 0) ? -1 : 1;
+        }
+    }
+
     public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
             WifiCarrierInfoManager wifiCarrierInfoManager, long providerId, int creatorUid,
             String packageName, boolean isFromSuggestion, Clock clock) {
@@ -1236,6 +1249,15 @@
         return mMostRecentSsid;
     }
 
+    /** Indicate that the most recent connection timestamp should be updated. */
+    public void updateMostRecentConnectionTime() {
+        mMostRecentConnectionTime = mClock.getWallClockMillis();
+    }
+
+    public long getMostRecentConnectionTime() {
+        return mMostRecentConnectionTime;
+    }
+
     /**
      * Add a potential RCOI match of the Passpoint provider to a network in the environment
      * @param scanResult Scan result
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
index 4cf7d8e..646349f 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
@@ -1023,10 +1023,8 @@
             osuProvider.setOsuSsid(null);
 
             // Filter non-Passpoint AP out and sort it by descending order of signal strength.
-            scanResults = scanResults.stream()
-                    .filter((scanResult) -> scanResult.isPasspointNetwork())
-                    .sorted((sr1, sr2) -> sr2.level - sr1.level)
-                    .collect(Collectors.toList());
+            scanResults.removeIf((scanResult) -> !scanResult.isPasspointNetwork());
+            scanResults.sort((sr1, sr2) -> sr2.level - sr1.level);
 
             for (ScanResult scanResult : scanResults) {
                 // Lookup OSU Providers ANQP element by ANQPNetworkKey.
diff --git a/service/java/com/android/server/wifi/hotspot2/Utils.java b/service/java/com/android/server/wifi/hotspot2/Utils.java
index 3e68448..17ea031 100644
--- a/service/java/com/android/server/wifi/hotspot2/Utils.java
+++ b/service/java/com/android/server/wifi/hotspot2/Utils.java
@@ -1,8 +1,15 @@
 package com.android.server.wifi.hotspot2;
 
-import static com.android.server.wifi.hotspot2.anqp.Constants.BYTES_IN_EUI48;
 import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK;
 import static com.android.server.wifi.hotspot2.anqp.Constants.NIBBLE_MASK;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS;
+
+import android.net.wifi.WifiConfiguration;
 
 import com.android.server.wifi.hotspot2.anqp.Constants;
 
@@ -23,9 +30,15 @@
     private static final long EUI48Mask = 0xffffffffffffL;
     private static final String[] PLMNText = {"org", "3gppnetwork", "mcc*", "mnc*", "wlan" };
 
-    public static String hs2LogTag(Class c) {
-        return "HS20";
-    }
+    /*
+     * OpenRoaming defines the use of multiple RCOIs to facilitate the implementation of policies
+     * across the federation. The currently defined RCOIs are:
+     * OpenRoaming-Settled: BA-A2-D0-xx-xx
+     * OpenRoaming-Settlement-Free: 5A-03-BA -xx-xx
+     * Refer to "OpenRoaming Framework & Standard Arch v3.0.0".
+     */
+    private static final long RCOI_OPEN_ROAMING_SETTLED_PREFIX = 0xBAA2D0;
+    private static final long RCOI_OPEN_ROAMING_FREE_PREFIX = 0x5A03BA;
 
     public static List<String> splitDomain(String domain) {
 
@@ -64,20 +77,6 @@
     }
 
     /**
-     * Convert from mac address as long to simple string in hex code, same as "%012x".
-     * @param mac The Mac address as long value.
-     * @return String value of mac address.
-     */
-    public static String macToSimpleString(long mac) {
-        StringBuilder sb = new StringBuilder();
-        for (int n = BYTES_IN_EUI48 - 1; n >= 0; n--) {
-            long b = (mac >>> (n * Byte.SIZE)) & BYTE_MASK;
-            sb.append(b > 0xf ? Long.toHexString(b) : "0" + Long.toHexString(b));
-        }
-        return sb.toString();
-    }
-
-    /**
      * Convert from mac address as long to string in hex code, separated with colon.
      * @param mac The Mac address as long value.
      * @return String value of mac address.
@@ -307,4 +306,87 @@
             return s;
         }
     }
+
+    /*
+     * Gets the first three octets from the RCOI which has 3 or 5 octets.
+     */
+    private static long getRcoiPrefix(long rcoi) {
+        rcoi &= 0xff_ffff_ffffL;
+        if ((rcoi & 0xff_ff00_0000L) != 0) {
+            // This is a 5-octet RCOI, pick the first 3 octets.
+            rcoi >>= 16;
+        }
+        return rcoi;
+    }
+
+    /**
+     * Checks whether the given RCOI is a free Openroaming RCOI.
+     */
+    public static boolean isFreeOpenRoaming(long rcoi) {
+        // There are more than 5 octets in the rcoi
+        if (Long.numberOfLeadingZeros(rcoi) < 24) return false;
+        return getRcoiPrefix(rcoi) == RCOI_OPEN_ROAMING_FREE_PREFIX;
+    }
+
+    /**
+     * Checks whether there is a free OpenRoaming RCOI.
+     */
+    public static boolean containsFreeOpenRoaming(long[] rcois) {
+        for (int i = 0; i < rcois.length; i++) {
+            if (isFreeOpenRoaming(rcois[i])) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether the given RCOI is a settled OpenRoaming RCOI.
+     */
+    public static boolean isSettledOpenRoaming(long rcoi) {
+        // There are more than 5 octets in the rcoi
+        if (Long.numberOfLeadingZeros(rcoi) < 24) return false;
+        return getRcoiPrefix(rcoi) == RCOI_OPEN_ROAMING_SETTLED_PREFIX;
+    }
+
+    /**
+     * Checks whether there is a settled OpenRoaming RCOI.
+     */
+    public static boolean containsSettledOpenRoaming(long[] rcois) {
+        for (int i = 0; i < rcois.length; i++) {
+            if (isSettledOpenRoaming(rcois[i])) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Gets the roaming type of the given WifiConfiguration.
+     *
+     * @param connectConfig the passpoint WifiConfuration which has been connected or is being
+     *                      connecting.
+     * @return ROAMING_NOT_PASSPOINT if the input WifiConfiguration is not for passpoint.
+     *         ROAMING_NONE if it is a home network.
+     *         ROAMING_UNKNOWN if there is no selected RCOI which is set during connection.
+     *         ROAMING_OPENROAMING_FREE if it is a free OpenRoaming network.
+     *         ROAMING_OPENROAMING_SETTLED if it is a settled OpenRoaming network.
+     */
+    public static int getRoamingType(WifiConfiguration connectConfig) {
+        if (!connectConfig.isPasspoint()) {
+            return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT;
+        }
+        if (connectConfig.isHomeProviderNetwork) {
+            return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE;
+        }
+
+        long selectedRcoi = connectConfig.enterpriseConfig.getSelectedRcoi();
+        if (isFreeOpenRoaming(selectedRcoi)) {
+            return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE;
+        }
+        if (isSettledOpenRoaming(selectedRcoi)) {
+            return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED;
+        }
+        if (selectedRcoi != 0L) {
+            return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS;
+        }
+
+        return WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI;
+    }
 }
diff --git a/service/java/com/android/server/wifi/mockwifi/MockWifiServiceUtil.java b/service/java/com/android/server/wifi/mockwifi/MockWifiServiceUtil.java
index 05948aa..a36efab 100644
--- a/service/java/com/android/server/wifi/mockwifi/MockWifiServiceUtil.java
+++ b/service/java/com/android/server/wifi/mockwifi/MockWifiServiceUtil.java
@@ -121,7 +121,8 @@
         intent.setComponent(new ComponentName(mPackageName, mServiceName));
         intent.setAction(actionName);
 
-        status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+        status = mContext.createContextAsUser(CURRENT, 0)
+                .bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
         return status;
     }
 
diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java
index 246ee38..d674755 100644
--- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java
+++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java
@@ -19,10 +19,15 @@
 import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTL;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback;
 import android.hardware.wifi.supplicant.P2pClientEapolIpAddressInfo;
+import android.hardware.wifi.supplicant.P2pDeviceFoundEventParams;
 import android.hardware.wifi.supplicant.P2pGroupStartedEventParams;
+import android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams;
+import android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams;
 import android.hardware.wifi.supplicant.P2pProvDiscStatusCode;
+import android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams;
 import android.hardware.wifi.supplicant.P2pStatusCode;
 import android.hardware.wifi.supplicant.WpsConfigMethods;
 import android.hardware.wifi.supplicant.WpsDevPasswordId;
@@ -41,6 +46,7 @@
 import com.android.server.wifi.util.NativeUtil;
 
 import java.io.ByteArrayInputStream;
+import java.net.Inet4Address;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -96,42 +102,9 @@
     public void onDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType,
             String deviceName, int configMethods, byte deviceCapabilities, int groupCapabilities,
             byte[] wfdDeviceInfo) {
-        if (deviceName == null) {
-            Log.e(TAG, "Missing device name.");
-            return;
-        }
-        WifiP2pDevice device = new WifiP2pDevice();
-        device.deviceName = deviceName;
-        try {
-            device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not decode device address.", e);
-            return;
-        }
-
-        try {
-            device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType);
-        } catch (Exception e) {
-            Log.e(TAG, "Could not encode device primary type.", e);
-            return;
-        }
-
-        device.deviceCapability = deviceCapabilities;
-        device.groupCapability = groupCapabilities;
-        device.wpsConfigMethodsSupported = configMethods;
-        device.status = WifiP2pDevice.AVAILABLE;
-
-        if (wfdDeviceInfo != null && wfdDeviceInfo.length >= 6) {
-            device.wfdInfo = new WifiP2pWfdInfo(
-                    // Use & 0xFF to cast signed byte to unsigned int, otherwise
-                    // sign extension will affect the bitwise operations
-                    ((wfdDeviceInfo[0] & 0xFF) << 8) + (wfdDeviceInfo[1] & 0xFF),
-                    ((wfdDeviceInfo[2] & 0xFF) << 8) + (wfdDeviceInfo[3] & 0xFF),
-                    ((wfdDeviceInfo[4] & 0xFF) << 8) + (wfdDeviceInfo[5] & 0xFF));
-        }
-
-        logd("Device discovered on " + mInterface + ": " + device);
-        mMonitor.broadcastP2pDeviceFound(mInterface, device);
+        handleDeviceFound(srcAddress, p2pDeviceAddress, primaryDeviceType, deviceName,
+                configMethods, deviceCapabilities, groupCapabilities, wfdDeviceInfo,
+                null, null);
     }
 
     /**
@@ -443,9 +416,44 @@
     @Override
     public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest,
             byte status, int configMethods, String generatedPin) {
-        logd("Provision discovery " + (isRequest ? "request" : "response")
-                + " for WPS Config method: " + configMethods
-                + " status: " + status);
+        handleProvisionDiscoveryCompletedEvent(
+                p2pDeviceAddress, isRequest, status, configMethods, generatedPin, null);
+    }
+
+    /**
+     * Used to indicate the completion of a P2P provision discovery request.
+     *
+     * @param provisionDiscoveryCompletedEventParams Parameters associated with P2P provision
+     *     discovery frame notification.
+     */
+    @Override
+    public void onProvisionDiscoveryCompletedEvent(
+            P2pProvisionDiscoveryCompletedEventParams provisionDiscoveryCompletedEventParams) {
+        handleProvisionDiscoveryCompletedEvent(
+                provisionDiscoveryCompletedEventParams.p2pDeviceAddress,
+                provisionDiscoveryCompletedEventParams.isRequest,
+                provisionDiscoveryCompletedEventParams.status,
+                provisionDiscoveryCompletedEventParams.configMethods,
+                provisionDiscoveryCompletedEventParams.generatedPin,
+                provisionDiscoveryCompletedEventParams.groupInterfaceName);
+    }
+
+    private void handleProvisionDiscoveryCompletedEvent(
+            byte[] p2pDeviceAddress,
+            boolean isRequest,
+            byte status,
+            int configMethods,
+            String generatedPin,
+            String groupIfName) {
+        logd(
+                "Provision discovery "
+                        + (isRequest ? "request" : "response")
+                        + " for WPS Config method: "
+                        + configMethods
+                        + " status: "
+                        + status
+                        + " groupIfName: "
+                        + (TextUtils.isEmpty(groupIfName) ? "null" : groupIfName));
 
         WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent();
         event.device = new WifiP2pDevice();
@@ -480,6 +488,7 @@
             mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
         } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
             event.event = WifiP2pProvDiscEvent.ENTER_PIN;
+            event.pin = generatedPin;
             mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
         } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
             event.event = WifiP2pProvDiscEvent.SHOW_PIN;
@@ -545,7 +554,30 @@
      */
     @Override
     public void onStaAuthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
-        logd("STA authorized on " + mInterface);
+        onP2pApStaConnected(null, srcAddress, p2pDeviceAddress, 0);
+    }
+
+    /**
+     * Used to indicate that a P2P client has joined this device group owner.
+     *
+     * @param clientJoinedEventParams Parameters associated with peer client joined event.
+     */
+    @Override
+    public void onPeerClientJoined(P2pPeerClientJoinedEventParams clientJoinedEventParams) {
+        onP2pApStaConnected(
+                clientJoinedEventParams.groupInterfaceName,
+                clientJoinedEventParams.clientInterfaceAddress,
+                clientJoinedEventParams.clientDeviceAddress,
+                clientJoinedEventParams.clientIpAddress);
+    }
+
+    private void onP2pApStaConnected(
+            String groupIfName, byte[] srcAddress, byte[] p2pDeviceAddress, int ipAddress) {
+        logd("STA authorized on " + (TextUtils.isEmpty(groupIfName) ? mInterface : groupIfName));
+        if (ipAddress != 0) {
+            Inet4Address ipAddressClient = intToInet4AddressHTL(ipAddress);
+            logd("IP Address of Client: " + ipAddressClient.getHostAddress());
+        }
         WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
         if (device == null) {
             return;
@@ -561,7 +593,27 @@
      */
     @Override
     public void onStaDeauthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
-        logd("STA deauthorized on " + mInterface);
+        onP2pApStaDisconnected(null, srcAddress, p2pDeviceAddress);
+    }
+
+    /**
+     * Used to indicate that a P2P client has disconnected from this device group owner.
+     *
+     * @param clientDisconnectedEventParams Parameters associated with peer client disconnected
+     *     event.
+     */
+    @Override
+    public void onPeerClientDisconnected(
+            P2pPeerClientDisconnectedEventParams clientDisconnectedEventParams) {
+        onP2pApStaDisconnected(
+                clientDisconnectedEventParams.groupInterfaceName,
+                clientDisconnectedEventParams.clientInterfaceAddress,
+                clientDisconnectedEventParams.clientDeviceAddress);
+    }
+
+    private void onP2pApStaDisconnected(
+            String groupIfName, byte[] srcAddress, byte[] p2pDeviceAddress) {
+        logd("STA deauthorized on " + (TextUtils.isEmpty(groupIfName) ? mInterface : groupIfName));
         WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
         if (device == null) {
             return;
@@ -679,6 +731,35 @@
             byte[] primaryDeviceType, String deviceName, int configMethods,
             byte deviceCapabilities, int groupCapabilities, byte[] wfdDeviceInfo,
             byte[] wfdR2DeviceInfo, byte[] vendorElemBytes) {
+        handleDeviceFound(srcAddress, p2pDeviceAddress, primaryDeviceType, deviceName,
+                configMethods, deviceCapabilities, groupCapabilities, wfdDeviceInfo,
+                wfdR2DeviceInfo, vendorElemBytes);
+    }
+
+    /**
+     * Used to indicate that a P2P device has been found.
+     *
+     * @param deviceFoundEventParams Parameters associated with the device found event.
+     */
+    @Override
+    public void onDeviceFoundWithParams(P2pDeviceFoundEventParams deviceFoundEventParams) {
+        handleDeviceFound(
+                deviceFoundEventParams.srcAddress,
+                deviceFoundEventParams.p2pDeviceAddress,
+                deviceFoundEventParams.primaryDeviceType,
+                deviceFoundEventParams.deviceName,
+                deviceFoundEventParams.configMethods,
+                deviceFoundEventParams.deviceCapabilities,
+                deviceFoundEventParams.groupCapabilities,
+                deviceFoundEventParams.wfdDeviceInfo,
+                deviceFoundEventParams.wfdR2DeviceInfo,
+                deviceFoundEventParams.vendorElemBytes);
+    }
+
+    private void handleDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress,
+            byte[] primaryDeviceType, String deviceName, int configMethods,
+            byte deviceCapabilities, int groupCapabilities, byte[] wfdDeviceInfo,
+            @Nullable byte[] wfdR2DeviceInfo, @Nullable byte[] vendorElemBytes) {
         WifiP2pDevice device = new WifiP2pDevice();
         device.deviceName = deviceName;
         if (deviceName == null) {
diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java
index dcc4cf7..4bdbe44 100644
--- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java
+++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java
@@ -449,6 +449,7 @@
             mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
         } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
             event.event = WifiP2pProvDiscEvent.ENTER_PIN;
+            event.pin = generatedPin;
             mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
         } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
             event.event = WifiP2pProvDiscEvent.SHOW_PIN;
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index 7e5d38c..f16e1e2 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -155,6 +155,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -251,6 +252,10 @@
             android.Manifest.permission.ACCESS_WIFI_STATE
     };
 
+    private static final String[] RECEIVER_PERMISSIONS_FOR_TETHERING = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+    };
+
     // Maximum number of bytes allowed for a network name, i.e. SSID.
     private static final int MAX_NETWORK_NAME_BYTES = 32;
     // Minimum number of bytes for a network name, i.e. DIRECT-xy.
@@ -263,6 +268,9 @@
     private static final int DISABLE_P2P_WAIT_TIME_MS = 5 * 1000;
     private static int sDisableP2pTimeoutIndex = 0;
 
+    private static final int P2P_REJECTION_WAIT_TIME_MS = 100;
+    private static int sP2pRejectionResumeAfterDelayIndex = 0;
+
     // Set a two minute discover timeout to avoid STA scans from being blocked
     private static final int DISCOVER_TIMEOUT_S = 120;
 
@@ -275,7 +283,7 @@
     public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
 
     // User accepted a peer request
-    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
+    @VisibleForTesting static final int PEER_CONNECTION_USER_ACCEPT = BASE + 2;
     // User rejected a peer request
     @VisibleForTesting
     static final int PEER_CONNECTION_USER_REJECT            =   BASE + 3;
@@ -327,6 +335,8 @@
     static final int TETHER_INTERFACE_STATE_CHANGED         =   BASE + 35;
 
     private static final int UPDATE_P2P_DISALLOWED_CHANNELS =   BASE + 36;
+    // Delayed message to timeout group creation
+    public static final int P2P_REJECTION_RESUME_AFTER_DELAY = BASE + 37;
 
     public static final int ENABLED                         = 1;
     public static final int DISABLED                        = 0;
@@ -988,6 +998,7 @@
 
         DeathHandlerData dhd;
         synchronized (mLock) {
+            Log.d(TAG, "close binder:" + binder + " from mDeathDataByBinder:" + mDeathDataByBinder);
             dhd = mDeathDataByBinder.get(binder);
             if (dhd == null) {
                 Log.w(TAG, "close(): no death recipient for binder");
@@ -1137,6 +1148,7 @@
                 new UserAuthorizingJoinState();
         private final OngoingGroupRemovalState mOngoingGroupRemovalState =
                 new OngoingGroupRemovalState();
+        private final P2pRejectWaitState mP2pRejectWaitState = new P2pRejectWaitState();
 
         private final WifiP2pMonitor mWifiMonitor = mWifiInjector.getWifiP2pMonitor();
         private final WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
@@ -1207,6 +1219,7 @@
                         addState(mProvisionDiscoveryState, mGroupCreatingState);
                         addState(mGroupNegotiationState, mGroupCreatingState);
                         addState(mFrequencyConflictState, mGroupCreatingState);
+                        addState(mP2pRejectWaitState, mGroupCreatingState);
                     addState(mGroupCreatedState, mP2pEnabledState);
                         addState(mUserAuthorizingJoinState, mGroupCreatedState);
                         addState(mOngoingGroupRemovalState, mGroupCreatedState);
@@ -1523,6 +1536,8 @@
                     return "WpsInfo.KEYPAD";
                 case WifiP2pManager.SET_VENDOR_ELEMENTS:
                     return "WifiP2pManager.SET_VENDOR_ELEMENTS";
+                case P2P_REJECTION_RESUME_AFTER_DELAY:
+                    return "P2P_REJECTION_RESUME_AFTER_DELAY";
                 default:
                     return "what:" + what;
             }
@@ -2540,12 +2555,14 @@
                             Log.i(TAG, "No valid package name, ignore ENABLE_P2P");
                             break;
                         }
+                        mWifiInjector.getWifiP2pConnection().setP2pInWaitingState(true);
                         int proceedWithOperation =
                                 mInterfaceConflictManager.manageInterfaceConflictForStateMachine(
                                         TAG, message, mP2pStateMachine, mWaitingState,
                                         mP2pDisabledState, HalDeviceManager.HDM_CREATE_IFACE_P2P,
                                         createRequestorWs(message.sendingUid, packageName),
                                         false /* bypassDialog */);
+                        mWifiInjector.getWifiP2pConnection().setP2pInWaitingState(false);
                         if (proceedWithOperation == InterfaceConflictManager.ICM_ABORT_COMMAND) {
                             Log.e(TAG, "User refused to set up P2P");
                             updateThisDevice(WifiP2pDevice.UNAVAILABLE);
@@ -2614,12 +2631,14 @@
                             Log.i(TAG, "No valid package name, do not set up the P2P interface");
                             return NOT_HANDLED;
                         }
+                        mWifiInjector.getWifiP2pConnection().setP2pInWaitingState(true);
                         int proceedWithOperation =
                                 mInterfaceConflictManager.manageInterfaceConflictForStateMachine(
                                         TAG, message, mP2pStateMachine, mWaitingState,
                                         mP2pDisabledState, HalDeviceManager.HDM_CREATE_IFACE_P2P,
                                         createRequestorWs(message.sendingUid, packageName),
                                         false /* bypassDialog */);
+                        mWifiInjector.getWifiP2pConnection().setP2pInWaitingState(false);
                         if (proceedWithOperation == InterfaceConflictManager.ICM_ABORT_COMMAND) {
                             Log.e(TAG, "User refused to set up P2P");
                             updateThisDevice(WifiP2pDevice.UNAVAILABLE);
@@ -2778,7 +2797,6 @@
                                 true);
                         // do not send service discovery request while normal find operation.
                         clearSupplicantServiceRequest();
-                        Log.e(TAG, "-------discover_peers before p2pFind");
                         if (p2pFind(scanType, freq, DISCOVER_TIMEOUT_S)) {
                             mWifiP2pMetrics.incrementPeerScans();
                             replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
@@ -3528,6 +3546,45 @@
             }
         }
 
+        class P2pRejectWaitState extends State {
+            @Override
+            public void enter() {
+                if (mVerboseLoggingEnabled) logd(getName());
+            }
+
+            @Override
+            public boolean processMessage(Message message) {
+                boolean ret = HANDLED;
+                switch (message.what) {
+                    case P2P_REJECTION_RESUME_AFTER_DELAY:
+                        if (sP2pRejectionResumeAfterDelayIndex == message.arg1) {
+                            logd(
+                                    "P2p rejection resume after delay - originated from "
+                                            + getWhatToString(message.what));
+                            if (message.arg2 == WifiP2pManager.CANCEL_CONNECT) {
+                                handleGroupCreationFailure();
+                                if (message.obj != null) {
+                                    replyToMessage(
+                                            (Message) message.obj,
+                                            WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
+                                }
+                            }
+                            transitionTo(mInactiveState);
+                        } else {
+                            loge(
+                                    "Stale P2p rejection resume after delay - cached index: "
+                                            + sP2pRejectionResumeAfterDelayIndex
+                                            + " index from msg: "
+                                            + message.arg1);
+                        }
+                        break;
+                    default:
+                        ret = NOT_HANDLED;
+                }
+                return ret;
+            }
+        }
+
         class GroupCreatingState extends State {
             @Override
             public void enter() {
@@ -3617,13 +3674,19 @@
                         mWifiP2pMetrics.endConnectionEvent(
                                 P2pConnectionEvent.CLF_CANCEL);
                         // Notify the peer about the rejection.
+                        int delay = 0;
                         if (mSavedPeerConfig != null) {
                             mWifiNative.p2pStopFind();
-                            sendP2pRejection();
+                            delay = sendP2pRejection();
                         }
-                        handleGroupCreationFailure();
-                        smTransition(this, mInactiveState);
-                        replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
+                        transitionTo(mP2pRejectWaitState);
+                        sendMessageDelayed(
+                                obtainMessage(
+                                        P2P_REJECTION_RESUME_AFTER_DELAY,
+                                        ++sP2pRejectionResumeAfterDelayIndex,
+                                        WifiP2pManager.CANCEL_CONNECT,
+                                        message),
+                                delay);
                         break;
                     case WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
                         // We hit this scenario when NFC handover is invoked.
@@ -3678,15 +3741,24 @@
                             if (join) {
                                 mWifiNative.p2pCancelConnect();
                                 mWifiNative.p2pStopFind();
-                                sendP2pConnectionChangedBroadcast();
                             }
-                            sendP2pRejection();
+
+                            int delay = sendP2pRejection();
+                            mDetailedState = NetworkInfo.DetailedState.DISCONNECTED;
+                            sendP2pConnectionChangedBroadcast();
                             mSavedPeerConfig.invalidate();
+                            transitionTo(mP2pRejectWaitState);
+                            sendMessageDelayed(
+                                    obtainMessage(
+                                            P2P_REJECTION_RESUME_AFTER_DELAY,
+                                            ++sP2pRejectionResumeAfterDelayIndex,
+                                            PEER_CONNECTION_USER_REJECT),
+                                    delay);
                         } else {
                             mWifiNative.p2pCancelConnect();
                             handleGroupCreationFailure();
+                            smTransition(this, mInactiveState);
                         }
-                        smTransition(this, mInactiveState);
                         break;
                     case PEER_CONNECTION_USER_CONFIRM:
                         mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
@@ -3846,6 +3918,11 @@
                             if (mVerboseLoggingEnabled) {
                                 logd("Found a match " + mSavedPeerConfig);
                             }
+                            if (TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
+                                // Some implementations get the PIN OOB and deliver it from
+                                // Supplicant. This is to avoid connecting with the dialog box
+                                mSavedPeerConfig.wps.pin = provDisc.pin;
+                            }
                             // we already have the pin
                             if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
                                 p2pConnectWithPinDisplay(mSavedPeerConfig,
@@ -3927,6 +4004,10 @@
                         }
                         mGroup = (WifiP2pGroup) message.obj;
                         if (mVerboseLoggingEnabled) logd(getName() + " group started");
+                        if (mWifiNative.p2pExtListen(false, 0, 0)) {
+                            sendP2pListenChangedBroadcast(false);
+                        }
+                        mWifiNative.p2pStopFind();
                         if (mGroup.isGroupOwner()
                                 && EMPTY_DEVICE_ADDRESS.equals(mGroup.getOwner().deviceAddress)) {
                             // wpa_supplicant doesn't set own device address to go_dev_addr.
@@ -4070,6 +4151,17 @@
                                         .setFallbackToNegotiationOnInviteStatusInfoUnavailable();
                                 p2pConnectWithPinDisplay(mSavedPeerConfig,
                                         P2P_CONNECT_TRIGGER_OTHER);
+                            } else {
+                                mWifiNative.p2pStopFind();
+                                if (mWifiNative.p2pExtListen(true,
+                                        mContext.getResources().getInteger(
+                                                R.integer.config_wifiP2pExtListenPeriodMs),
+                                        mContext.getResources().getInteger(
+                                                R.integer.config_wifiP2pExtListenIntervalMs))) {
+                                    logd(" started listen to receive the invitation Request"
+                                            + " frame from Peer device.");
+                                    sendP2pListenChangedBroadcast(true);
+                                }
                             }
                         } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
                             smTransition(this, mFrequencyConflictState);
@@ -4080,6 +4172,13 @@
                             smTransition(this, mInactiveState);
                         }
                         break;
+                    case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT:
+                        loge("Peer rejected the connection request - status: " + message.arg1);
+                        mWifiP2pMetrics.endConnectionEvent(
+                                P2pConnectionEvent.CLF_GROUP_REMOVED);
+                        handleGroupCreationFailure();
+                        smTransition(this, mInactiveState);
+                        break;
                     case WifiP2pMonitor.AP_STA_CONNECTED_EVENT:
                     case WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT:
                         // Group owner needs to wait for tethering completion before
@@ -4100,6 +4199,9 @@
                         replyToMessage(message,
                                 WifiP2pManager.SET_CONNECTION_REQUEST_RESULT_SUCCEEDED);
                         break;
+                    case DISABLE_P2P:
+                        mWifiP2pMetrics.endConnectionEvent(P2pConnectionEvent.CLF_GROUP_REMOVED);
+                        //remaining p2p disabling works will be handled in its parent states
                     default:
                         return NOT_HANDLED;
                 }
@@ -4694,8 +4796,10 @@
                 resetWifiP2pInfo();
                 mDetailedState = NetworkInfo.DetailedState.DISCONNECTED;
                 sendP2pConnectionChangedBroadcast();
-                // Ensure tethering service to stop tethering.
-                sendP2pTetherRequestBroadcast();
+                if (!SdkLevel.isAtLeastU()) {
+                    // Ensure tethering service to stop tethering.
+                    sendP2pTetherRequestBroadcast();
+                }
             }
         }
 
@@ -4858,31 +4962,51 @@
             intent.putExtra(WifiP2pManager.EXTRA_LISTEN_STATE, started
                     ? WifiP2pManager.WIFI_P2P_LISTEN_STARTED :
                     WifiP2pManager.WIFI_P2P_LISTEN_STOPPED);
-            sendBroadcastMultiplePermissions(intent);
+            sendBroadcastWithExcludedPermissions(intent, null);
         }
 
         // TODO(b/193460475): Remove when tooling supports SystemApi to public API.
+        /**
+         * Use the function to send broadcast to apps that hold included permissions and don't
+         * hold excluded permissions.
+         * @param intent The Intent to broadcast
+         * @param excludedPermissions A list of Strings of permissions the receiver must not have.
+         * SdkLevel < T:  Does not support excludedPermissions and sets the value always null.
+         * SdkLevel >= T: Combines all excludedPermissions
+         */
         @SuppressLint("NewApi")
-        private void sendBroadcastMultiplePermissions(Intent intent) {
+        private void sendBroadcastWithExcludedPermissions(Intent intent,
+                @Nullable String[] excludedPermissions) {
             Context context = mContext.createContextAsUser(UserHandle.ALL, 0);
-            String[] permissions = RECEIVER_PERMISSIONS_FOR_BROADCAST;
             boolean isLocationModeEnabled = mWifiPermissionsUtil.isLocationModeEnabled();
-            if (!isLocationModeEnabled) {
-                permissions = RECEIVER_PERMISSIONS_FOR_BROADCAST_LOCATION_OFF;
+            String[] permissions = isLocationModeEnabled ? RECEIVER_PERMISSIONS_FOR_BROADCAST
+                    : RECEIVER_PERMISSIONS_FOR_BROADCAST_LOCATION_OFF;
+            if (SdkLevel.isAtLeastU()) {
+                BroadcastOptions broadcastOptions = mWifiInjector.makeBroadcastOptions();
+                broadcastOptions.setRequireAllOfPermissions(permissions);
+                broadcastOptions.setRequireNoneOfPermissions(excludedPermissions);
+                context.sendBroadcast(intent, null, broadcastOptions.toBundle());
+            } else {
+                context.sendBroadcastWithMultiplePermissions(intent, permissions);
             }
-            context.sendBroadcastWithMultiplePermissions(
-                    intent, permissions);
             if (SdkLevel.isAtLeastT()) {
                 // on Android T or later, also send broadcasts to apps that have NEARBY_WIFI_DEVICES
-                String[] requiredPermissions = new String[] {
+                String[] requiredPermissions = new String[]{
                         android.Manifest.permission.NEARBY_WIFI_DEVICES,
                         android.Manifest.permission.ACCESS_WIFI_STATE
                 };
                 BroadcastOptions broadcastOptions = mWifiInjector.makeBroadcastOptions();
                 broadcastOptions.setRequireAllOfPermissions(requiredPermissions);
+                ArrayList<String> excludedPermissionsList = new ArrayList<>();
                 if (isLocationModeEnabled) {
-                    broadcastOptions.setRequireNoneOfPermissions(
-                            new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION});
+                    excludedPermissionsList.add(android.Manifest.permission.ACCESS_FINE_LOCATION);
+                }
+                if (excludedPermissions != null) {
+                    Collections.addAll(excludedPermissionsList, excludedPermissions);
+                }
+                if (excludedPermissionsList.size() > 0) {
+                    broadcastOptions.setRequireNoneOfPermissions(excludedPermissionsList.toArray(
+                            new String[0]));
                 }
                 context.sendBroadcast(intent, null, broadcastOptions.toBundle());
             }
@@ -4893,29 +5017,36 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE,
                     eraseOwnDeviceAddress(mThisDevice));
-            sendBroadcastMultiplePermissions(intent);
+            sendBroadcastWithExcludedPermissions(intent, null);
         }
 
         private void sendPeersChangedBroadcast() {
             final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
             intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            sendBroadcastMultiplePermissions(intent);
+            sendBroadcastWithExcludedPermissions(intent, null);
+        }
+
+        private Intent getP2pConnectionChangedIntent() {
+            Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
+            intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, makeNetworkInfo());
+            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, eraseOwnDeviceAddress(mGroup));
+            return intent;
         }
 
         private void sendP2pConnectionChangedBroadcast() {
             if (mVerboseLoggingEnabled) logd("sending p2p connection changed broadcast");
-            Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            // P2pConnectionEvent.CONNECTION_FAST connection type is only triggered as a result
-            // of user interaction, so mark the broadcast as foreground.
-            if (mWifiP2pMetrics.isP2pFastConnectionType()) {
-                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            Intent intent = getP2pConnectionChangedIntent();
+            if (SdkLevel.isAtLeastU()) {
+                // First send direct foreground broadcast to Tethering package
+                sendP2pTetherRequestBroadcast();
+                // Then send the same broadcast to remaining apps excluding Tethering package
+                sendBroadcastWithExcludedPermissions(intent, RECEIVER_PERMISSIONS_FOR_TETHERING);
+            } else {
+                sendBroadcastWithExcludedPermissions(intent, null);
             }
-            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
-            intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, makeNetworkInfo());
-            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, eraseOwnDeviceAddress(mGroup));
-            sendBroadcastMultiplePermissions(intent);
             if (mWifiChannel != null) {
                 mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
                         makeNetworkInfo());
@@ -4985,7 +5116,6 @@
 
             for (String pkgName: possiblePackageNames) {
                 if (isPackageExisted(pkgName)) {
-                    Log.d(TAG, "Tethering service package: " + pkgName);
                     return pkgName;
                 }
             }
@@ -4999,20 +5129,19 @@
             Log.i(TAG, "sending p2p tether request broadcast to "
                     + tetheringServicePackage);
 
-            final String[] receiverPermissionsForTetheringRequest = {
+            String[] receiverPermissionsForTetheringRequest = {
                     android.Manifest.permission.TETHER_PRIVILEGED
             };
-            Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+            if (SdkLevel.isAtLeastU()) {
+                receiverPermissionsForTetheringRequest = RECEIVER_PERMISSIONS_FOR_TETHERING;
+            }
+            Intent intent = getP2pConnectionChangedIntent();
             intent.setPackage(tetheringServicePackage);
-            // P2pConnectionEvent.CONNECTION_FAST connection type is only triggered as a result
-            // of user interaction, so mark the broadcast as foreground.
-            if (mWifiP2pMetrics.isP2pFastConnectionType()) {
+            if (SdkLevel.isAtLeastU()) {
+                // Adding the flag to allow recipient to run at foreground priority with a shorter
+                // timeout interval.
                 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
             }
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
-            intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, makeNetworkInfo());
-            intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, eraseOwnDeviceAddress(mGroup));
 
             Context context = mContext.createContextAsUser(UserHandle.ALL, 0);
             context.sendBroadcastWithMultiplePermissions(
@@ -5623,12 +5752,11 @@
                 join = false;
             } else if (join) {
                 int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
-                if (isInvited && netId < 0) {
-                    netId = mGroups.getNetworkId(dev.deviceAddress);
-                }
                 if (netId >= 0) {
                     // Skip WPS and start 4way handshake immediately.
                     return mWifiNative.p2pGroupAdd(netId);
+                } else {
+                    loge("The Network: " + ssid + " is not found in the persistent group list");
                 }
             }
 
@@ -6863,13 +6991,16 @@
             return (getSupportedFeatures() & feature) == feature;
         }
 
-        private void sendP2pRejection() {
-            if (TextUtils.isEmpty(mSavedPeerConfig.deviceAddress)) return;
-
+        private int sendP2pRejection() {
+            if (TextUtils.isEmpty(mSavedPeerConfig.deviceAddress)) {
+                return 0;
+            }
             mWifiNative.p2pReject(mSavedPeerConfig.deviceAddress);
             // p2pReject() only updates the peer state, but not sends this
             // to the peer, trigger provision discovery to notify the peer.
+            // Adding the delay to send pb request with failed status attr.
             mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
+            return P2P_REJECTION_WAIT_TIME_MS;
         }
 
         private boolean isPeerAuthorizing(String deviceAddress) {
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pShellCommand.java b/service/java/com/android/server/wifi/p2p/WifiP2pShellCommand.java
index addea85..a139b26 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pShellCommand.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pShellCommand.java
@@ -367,8 +367,11 @@
                 countDownLatch.await(1000, TimeUnit.MILLISECONDS);
                 return 0;
             case "connect": {
-                WifiP2pConfig config = new WifiP2pConfig();
-                config.deviceAddress = getNextArgRequired();
+                WifiP2pConfig config = buildWifiP2pConfig(pw);
+                if (null == config) {
+                    pw.println("Invalid argument to 'connect'");
+                    return -1;
+                }
                 mWifiP2pManager.connect(sWifiP2pChannel, config, actionListener);
                 countDownLatch.await(3000, TimeUnit.MILLISECONDS);
                 return 0;
@@ -460,6 +463,65 @@
         return "UNKNOWN";
     }
 
+    private WifiP2pConfig buildWifiP2pConfig(PrintWriter pw) {
+        String deviceAddress = getNextArgRequired();
+        int goIntent = WifiP2pConfig.GROUP_OWNER_INTENT_AUTO;
+        int ipProvisioningMode = WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP;
+        String option = getNextOption();
+        while (option != null) {
+            if (option.equals("-i")) {
+                try {
+                    goIntent = Integer.parseInt(getNextArgRequired());
+                } catch (NumberFormatException e) {
+                    pw.println("Invalid argument for group owner intent "
+                            + "- must be an integer");
+                    return null;
+                }
+                if (goIntent < WifiP2pConfig.GROUP_OWNER_INTENT_MIN
+                        || goIntent > WifiP2pConfig.GROUP_OWNER_INTENT_MAX) {
+                    pw.println("Invalid argument for group owner intent "
+                            + "- must be from "
+                            + WifiP2pConfig.GROUP_OWNER_INTENT_MIN + " to "
+                            + WifiP2pConfig.GROUP_OWNER_INTENT_MAX);
+                    return null;
+                }
+            } else if (option.equals("-m")) {
+                if (!SdkLevel.isAtLeastT()) {
+                    pw.println("Invalid argument for group client IP provisioning mode "
+                            + "- IP provisioning mode is supported only on SdkLevel T or later");
+                    return null;
+                }
+                try {
+                    ipProvisioningMode = Integer.parseInt(getNextArgRequired());
+                } catch (NumberFormatException e) {
+                    pw.println("Invalid argument for group client IP provisioning mode "
+                            + "- must be an integer");
+                    return null;
+                }
+                if (ipProvisioningMode != WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP
+                        && ipProvisioningMode
+                        != WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL) {
+                    pw.println("Invalid argument for group client IP provisioning mode "
+                            + "- must be "
+                            + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP + " or "
+                            + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL);
+                    return null;
+                }
+            } else {
+                pw.println("Ignoring unknown option " + option);
+            }
+            option = getNextOption();
+        }
+        WifiP2pConfig.Builder configBuilder = new WifiP2pConfig.Builder();
+        configBuilder.setDeviceAddress(MacAddress.fromString(deviceAddress));
+        if (SdkLevel.isAtLeastT()) {
+            configBuilder.setGroupClientIpProvisioningMode(ipProvisioningMode);
+        }
+        WifiP2pConfig config = configBuilder.build();
+        config.groupOwnerIntent = goIntent;
+        return config;
+    }
+
     private WifiP2pConfig prepareWifiP2pConfig(PrintWriter pw) {
         String networkName = getNextArgRequired();
         String passphrase = getNextArgRequired();
@@ -571,8 +633,20 @@
         pw.println("    Accept an incoming connection request.");
         pw.println("  reject-connection");
         pw.println("    Reject an incoming connection request.");
-        pw.println("  connect <device address>");
-        pw.println("    Connect to a device.");
+        pw.println("  connect <device address> [-i <groupOwnerIntent>] "
+                        + "[-m <groupClientIpProvisioningMode>]");
+        pw.println("    <device address> - the peer's MAC address.");
+        pw.println("    <groupOwnerIntent> - Set group owner intent value. The value range is "
+                + WifiP2pConfig.GROUP_OWNER_INTENT_MIN + " to "
+                + WifiP2pConfig.GROUP_OWNER_INTENT_MAX);
+        pw.println("    <groupClientIpProvisioningMode> - Set group client IP provisioning "
+                        + "mode (supported on SdkLevel T or later)");
+        pw.println("        - Use '" + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP
+                + "' to select IPv4 DHCP which is system default behavior");
+        pw.println("        - Use '"
+                + WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL
+                + "' to select IPv6 link-local.");
+        pw.println("    Connect to a device with provided params.");
         pw.println("  connect-with-config <network name> <passphrase>"
                         + " <bandOrFreq> <persistent>");
         pw.println("    <bandOrFreq> - select the preferred band or frequency.");
diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
index 50c97b0..99899c3 100644
--- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
+++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -1175,6 +1175,7 @@
                     newRequestBuilder.addResponder(rttPeer);
                 }
             }
+            newRequestBuilder.setRttBurstSize(request.request.getRttBurstSize());
             request.request = newRequestBuilder.build();
 
             // run request again
@@ -1279,8 +1280,20 @@
                                         null, null, 0, false));
                     } else {
                         finalResults.add(
-                                new RangingResult(errorCode, peer.peerHandle, 0, 0, 0, 0, 0, null,
-                                        null, null, 0));
+                                new RangingResult(
+                                        errorCode,
+                                        peer.peerHandle,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        0,
+                                        null,
+                                        null,
+                                        null,
+                                        0,
+                                        RangingResult.UNSPECIFIED,
+                                        RangingResult.UNSPECIFIED));
                     }
                 } else {
                     int status = RangingResult.STATUS_SUCCESS;
@@ -1314,18 +1327,21 @@
                                 resultForRequest.mFrequencyMHz,
                                 resultForRequest.mPacketBw));
                     } else {
-                        finalResults.add(new RangingResult(
-                                status,
-                                peer.peerHandle,
-                                resultForRequest.mDistanceMm,
-                                resultForRequest.mDistanceStdDevMm,
-                                resultForRequest.mRssi,
-                                resultForRequest.mNumAttemptedMeasurements,
-                                resultForRequest.mNumSuccessfulMeasurements,
-                                lci,
-                                lcr,
-                                responderLocation,
-                                resultForRequest.mTimestamp));
+                        finalResults.add(
+                                new RangingResult(
+                                        status,
+                                        peer.peerHandle,
+                                        resultForRequest.mDistanceMm,
+                                        resultForRequest.mDistanceStdDevMm,
+                                        resultForRequest.mRssi,
+                                        resultForRequest.mNumAttemptedMeasurements,
+                                        resultForRequest.mNumSuccessfulMeasurements,
+                                        lci,
+                                        lcr,
+                                        responderLocation,
+                                        resultForRequest.mTimestamp,
+                                        resultForRequest.mFrequencyMHz,
+                                        resultForRequest.mPacketBw));
                     }
                 }
             }
diff --git a/service/java/com/android/server/wifi/scanner/ChannelHelper.java b/service/java/com/android/server/wifi/scanner/ChannelHelper.java
index 04d11e7..12c75f1 100644
--- a/service/java/com/android/server/wifi/scanner/ChannelHelper.java
+++ b/service/java/com/android/server/wifi/scanner/ChannelHelper.java
@@ -22,6 +22,8 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.WifiNative;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Set;
 import java.util.StringJoiner;
 
@@ -78,6 +80,8 @@
      */
     public abstract int estimateScanDuration(WifiScanner.ScanSettings settings);
 
+    protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
     /**
      * Update the channel information that this object has. The source of the update is
      * implementation dependent and may result in no change. Warning the behavior of a
diff --git a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
index 936b914..0108b7e 100644
--- a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
+++ b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
@@ -39,6 +39,8 @@
 import com.android.server.wifi.WifiNative;
 import com.android.server.wifi.proto.WifiStatsLog;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -277,6 +279,35 @@
         return band;
     }
 
+    private String getWifiBandIndexName(@WifiBandIndex int wifiBandIndex) {
+        switch (wifiBandIndex) {
+            case WIFI_BAND_INDEX_24_GHZ:
+                return "band 2.4 GHz";
+            case WIFI_BAND_INDEX_5_GHZ:
+                return "band   5 GHz";
+            case WIFI_BAND_INDEX_5_GHZ_DFS_ONLY:
+                return "band   5 GHz (DFS only)";
+            case WIFI_BAND_INDEX_6_GHZ:
+                return "band   6 GHz";
+            case WIFI_BAND_INDEX_60_GHZ:
+                return "band  60 GHz";
+            default:
+                return "unknown band";
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Available channels:");
+        for (int i = 0; i < WIFI_BAND_COUNT; i++) {
+            StringBuilder availableChannel = new StringBuilder();
+            for (WifiScanner.ChannelSpec channelSpec : mBandsToChannels[i]) {
+                availableChannel.append(channelSpec.frequency).append(" ");
+            }
+            pw.println("  " + getWifiBandIndexName(i) + ": " + availableChannel);
+        }
+    }
+
     /**
      * ChannelCollection that merges channels so that the optimal schedule will be generated.
      * When the max channels value is satisfied this implementation will always create a channel
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index cff57ce..5a32c57 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -87,6 +87,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
@@ -156,6 +157,9 @@
             mChannelHelper.updateChannels();
             return mChannelHelper.getAvailableScanChannels(band);
         }, new ChannelSpec[0][0]);
+        if (channelSpecs == null) {
+            channelSpecs = new ChannelSpec[0][0];
+        }
 
         ArrayList<Integer> list = new ArrayList<>();
         for (int i = 0; i < channelSpecs.length; i++) {
@@ -227,19 +231,20 @@
                     false, false);
         } catch (SecurityException e) {
             localLog("registerScanListener: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid  + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
         mWifiThreadRunner.post(() -> {
             if (mClients.get(listener) != null) {
-                logw("duplicate client connection: " + uid + ", listener=" + listener);
+                logw("duplicate client connection: " + uid + ", listener=" + listener
+                        + " AttributionTag " + featureId);
                 return;
             }
             final ExternalClientInfo client = new ExternalClientInfo(uid, packageName,
                     listener);
             client.register();
-            localLog("register scan listener: " + client);
+            localLog("register scan listener: " + client + " AttributionTag " + featureId);
             logScanRequest("registerScanListener", client, null, null, null);
             mSingleScanListeners.addRequest(client, null, null);
             client.replySucceeded();
@@ -256,13 +261,14 @@
                     true, false);
         } catch (SecurityException e) {
             localLog("unregisterScanListener: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
         ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener);
         if (client == null) {
-            logw("no client registered: " + uid + ", listener=" + listener);
+            logw("no client registered: " + uid + ", listener=" + listener
+                    + " AttributionTag " + featureId);
             return;
         }
         mWifiThreadRunner.post(() -> {
@@ -338,10 +344,10 @@
                     false, false);
         } catch (SecurityException e) {
             localLog("getScanResults: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             return false;
         }
-        localLog("get scan result: " + packageName);
+        localLog("get scan result: " + packageName + " AttributionTag " + featureId);
         mBackgroundScanStateMachine.sendMessage(WifiScanner.CMD_GET_SCAN_RESULTS);
         return true;
     }
@@ -382,7 +388,7 @@
                     shouldHideFromAppsForSingleScan(settings));
         } catch (SecurityException e) {
             localLog("startScan: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
@@ -394,7 +400,8 @@
                 client = new ExternalClientInfo(uid, packageName, listener);
                 client.register();
             }
-            localLog("start scan: " + client + " package " + packageName);
+            localLog("start scan: " + client + " package " + packageName + " AttributionTag "
+                    + featureId);
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_START_SINGLE_SCAN;
             msg.obj = new ScanParams(listener, settings, workSource);
@@ -412,7 +419,7 @@
                     true, false);
         } catch (SecurityException e) {
             localLog("stopScan: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
@@ -422,7 +429,7 @@
                 Log.e(TAG, "listener not found " + listener);
                 return;
             }
-            localLog("stop scan: " + client);
+            localLog("stop scan: " + client + " AttributionTag " + featureId);
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_STOP_SINGLE_SCAN;
             msg.obj = new ScanParams(listener, null, null);
@@ -433,7 +440,8 @@
 
     @Override
     public List<ScanResult> getSingleScanResults(String packageName, String featureId) {
-        localLog("get single scan result: package " + packageName);
+        localLog("get single scan result: package " + packageName
+                + " AttributionTag " + featureId);
         final int uid = Binder.getCallingUid();
         try {
             enforcePermission(uid, packageName, featureId,
@@ -441,7 +449,7 @@
                     false, false);
         } catch (SecurityException e) {
             localLog("getSingleScanResults: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             return new ArrayList<>();
         }
         return mWifiThreadRunner.call(() -> mSingleScanStateMachine.filterCachedScanResultsByAge(),
@@ -452,26 +460,27 @@
     public void startPnoScan(IWifiScannerListener listener, WifiScanner.ScanSettings scanSettings,
             WifiScanner.PnoSettings pnoSettings, String packageName, String featureId) {
         final int uid = Binder.getCallingUid();
+        if (listener == null) {
+            Log.e(TAG, "listener is null");
+            return;
+        }
         try {
             enforcePermission(uid, packageName, featureId,
                     isPrivilegedMessage(WifiScanner.CMD_START_PNO_SCAN),
                     false, false);
         } catch (SecurityException e) {
             localLog("startPnoScan: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
         mWifiThreadRunner.post(() -> {
-            ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener);
-            if (client == null) {
-                client = new ExternalClientInfo(uid, packageName, listener);
-                client.register();
-            }
-            localLog("start pno scan: " + client);
+            String clientInfoLog = "ClientInfo[uid=" + uid + ", package=" + packageName + ", "
+                    + listener + "]";
+            localLog("start pno scan: " + clientInfoLog + " AttributionTag " + featureId);
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_START_PNO_SCAN;
-            msg.obj = new ScanParams(listener, scanSettings, pnoSettings, null, null, null);
+            msg.obj = new ScanParams(listener, scanSettings, pnoSettings, null, packageName, null);
             msg.sendingUid = uid;
             mPnoScanStateMachine.sendMessage(msg);
         });
@@ -480,13 +489,17 @@
     @Override
     public void stopPnoScan(IWifiScannerListener listener, String packageName, String featureId) {
         final int uid = Binder.getCallingUid();
+        if (listener == null) {
+            Log.e(TAG, "listener is null");
+            return;
+        }
         try {
             enforcePermission(uid, packageName, featureId,
                     isPrivilegedMessage(WifiScanner.CMD_STOP_PNO_SCAN),
                     true, false);
         } catch (SecurityException e) {
             localLog("stopPnoScan: failed to authorize app: " + packageName + " uid "
-                    + uid);
+                    + uid + " AttributionTag " + featureId);
             notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized");
             return;
         }
@@ -496,7 +509,7 @@
                 Log.e(TAG, "listener not found " + listener);
                 return;
             }
-            localLog("stop pno scan: " + client);
+            localLog("stop pno scan: " + client + " AttributionTag " + featureId);
             Message msg = Message.obtain();
             msg.what = WifiScanner.CMD_STOP_PNO_SCAN;
             msg.obj = new ScanParams(listener, null, null);
@@ -1098,6 +1111,7 @@
                 if (getCurrentState() == mDefaultState && !scanSettings.ignoreLocationSettings) {
                     // Reject regular scan requests if scanning is disabled.
                     ci.replyFailed(WifiScanner.REASON_UNSPECIFIED, "not available");
+                    ci.cleanup();
                     return;
                 }
                 mWifiMetrics.incrementOneshotScanCount();
@@ -1139,6 +1153,7 @@
             } else {
                 logCallback("singleScanInvalidRequest", ci, "bad request");
                 ci.replyFailed(WifiScanner.REASON_INVALID_REQUEST, "bad request");
+                ci.cleanup();
                 mWifiMetrics.incrementScanReturnEntry(
                         WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION, 1);
             }
@@ -1547,8 +1562,9 @@
                 try {
                     entry.clientInfo.mListener.onFailure(reason, description);
                 } catch (RemoteException e) {
-                    loge("Failed to call onFullResult: " + entry.clientInfo);
+                    loge("Failed to call onFailure: " + entry.clientInfo);
                 }
+                entry.clientInfo.unregister();
             }
             clientHandlers.clear();
         }
@@ -2334,8 +2350,17 @@
                     case WifiScanner.CMD_DISABLE:
                         transitionTo(mDefaultState);
                         break;
-                    case WifiScanner.CMD_START_PNO_SCAN:
-                    case WifiScanner.CMD_STOP_PNO_SCAN:
+                    case WifiScanner.CMD_START_PNO_SCAN: {
+                        ScanParams scanParams = (ScanParams) msg.obj;
+                        try {
+                            scanParams.listener.onFailure(WifiScanner.REASON_UNSPECIFIED,
+                                    "not available");
+                        } catch (RemoteException e) {
+                            // not much we can do if message can't be sent.
+                        }
+                        break;
+                    }
+                    case WifiScanner.CMD_STOP_PNO_SCAN: {
                         ScanParams scanParams = (ScanParams) msg.obj;
                         ClientInfo ci = mClients.get(scanParams.listener);
                         if (ci == null) {
@@ -2344,6 +2369,7 @@
                         }
                         ci.replyFailed(WifiScanner.REASON_UNSPECIFIED, "not available");
                         break;
+                    }
                     case CMD_PNO_NETWORK_FOUND:
                     case CMD_PNO_SCAN_FAILED:
                     case WifiScanner.CMD_SCAN_RESULT:
@@ -2383,8 +2409,9 @@
                         }
                         ClientInfo ci = mClients.get(scanParams.listener);
                         if (ci == null) {
-                            localLog("CMD_START_PNO_SCAN ClientInfo is null in StartedState");
-                            break;
+                            ci = new ExternalClientInfo(msg.sendingUid, scanParams.packageName,
+                                    scanParams.listener);
+                            ci.register();
                         }
                         if (scanParams.pnoSettings == null || scanParams.settings == null) {
                             Log.e(TAG, "Failed to get parcelable params");
@@ -2443,8 +2470,9 @@
                         }
                         ClientInfo ci = mClients.get(scanParams.listener);
                         if (ci == null) {
-                            localLog("CMD_START_PNO_SCAN ClientInfo is null in HwPnoScanState");
-                            break;
+                            ci = new ExternalClientInfo(msg.sendingUid, scanParams.packageName,
+                                    scanParams.listener);
+                            ci.register();
                         }
                         if (scanParams.pnoSettings == null || scanParams.settings == null) {
                             Log.e(TAG, "Failed to get parcelable params");
@@ -2756,9 +2784,9 @@
 
                         ClientInfo clientInfo = mClients.get(scanParams.listener);
                         if (clientInfo == null) {
-                            Log.wtf(TAG, "Received Start PNO request without ClientInfo");
-                            transitionTo(mStartedState);
-                            return HANDLED;
+                            clientInfo = new ExternalClientInfo(msg.sendingUid,
+                                    scanParams.packageName, scanParams.listener);
+                            clientInfo.register();
                         }
 
                         if (!mActivePnoScans.isEmpty()) {
@@ -3013,6 +3041,18 @@
         private final WorkSource mWorkSource;
         private boolean mScanWorkReported = false;
         protected final IWifiScannerListener mListener;
+        protected DeathRecipient mDeathRecipient = new DeathRecipient() {
+            @Override
+            public void binderDied() {
+                mWifiThreadRunner.post(() -> {
+                    if (DBG) localLog("binder died: client listener: " + mListener);
+                    if (isVerboseLoggingEnabled()) {
+                        Log.i(TAG, "binder died: client listener: " + mListener);
+                    }
+                    cleanup();
+                });
+            }
+        };
 
         ClientInfo(int uid, String packageName, IWifiScannerListener listener) {
             mUid = uid;
@@ -3040,6 +3080,12 @@
                 Log.i(TAG, "Unregistering listener= " + mListener + " uid=" + mUid
                         + " packageName=" + mPackageName + " workSource=" + mWorkSource);
             }
+            try {
+                mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+            } catch (NoSuchElementException e) {
+                Log.e(TAG, "Failed to unregister death recipient! " + mListener);
+            }
+
             mClients.remove(mListener);
         }
 
@@ -3153,18 +3199,7 @@
             super(uid, packageName, listener);
             if (DBG) localLog("New client, listener: " + listener);
             try {
-                listener.asBinder().linkToDeath(new DeathRecipient() {
-                    @Override
-                    public void binderDied() {
-                        mWifiThreadRunner.post(() -> {
-                            if (DBG) localLog("binder died: client listener: " + listener);
-                            if (isVerboseLoggingEnabled()) {
-                                Log.i(TAG, "binder died: client listener: " + listener);
-                            }
-                            cleanup();
-                        });
-                    }
-                }, 0);
+                listener.asBinder().linkToDeath(mDeathRecipient, 0);
             } catch (RemoteException e) {
                 Log.e(TAG, "can't register death recipient! " + listener);
             }
@@ -3351,6 +3386,11 @@
         }
         pw.println();
 
+        if (mChannelHelper != null) {
+            mChannelHelper.dump(fd, pw, args);
+            pw.println();
+        }
+
         if (mSingleScanStateMachine != null) {
             mSingleScanStateMachine.dump(fd, pw, args);
             pw.println();
diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java
index 8ad6f6b..96c2984 100644
--- a/service/java/com/android/server/wifi/util/ApConfigUtil.java
+++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java
@@ -53,7 +53,9 @@
 import android.util.SparseIntArray;
 
 import com.android.modules.utils.build.SdkLevel;
+import com.android.server.wifi.SoftApManager;
 import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.WifiSettingsConfigStore;
 import com.android.server.wifi.coex.CoexManager;
 import com.android.wifi.resources.R;
 
@@ -82,12 +84,6 @@
     public static final int DEFAULT_AP_CHANNEL = 6;
     public static final int HIGHEST_2G_AP_CHANNEL = 14;
 
-    /* Return code for updateConfiguration. */
-    public static final int SUCCESS = 0;
-    public static final int ERROR_NO_CHANNEL = 1;
-    public static final int ERROR_GENERIC = 2;
-    public static final int ERROR_UNSUPPORTED_CONFIGURATION = 3;
-
     /* Random number generator used for AP channel selection. */
     private static final Random sRandom = new Random();
     private static boolean sVerboseLoggingEnabled = false;
@@ -740,33 +736,58 @@
     }
 
     /**
+     * Checks whether HAL support converting the restricted security type to an allowed one in 6GHz
+     * band configuration.
+     * @param resources the resources to get the OEM configuration for HAL support.
+     * @param type security type.
+     * @return true if HAL support to map WPA3 transition mode to WPA3 in 6GHz band,
+     * false otherwise.
+     */
+    public static boolean canHALConvertRestrictedSecurityTypeFor6GHz(@NonNull Resources resources,
+            @SoftApConfiguration.SecurityType int type) {
+        return type == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
+                && resources.getBoolean(R.bool
+                        .config_wifiSofapHalMapWpa3TransitionModeToWpa3OnlyIn6GHzBand);
+    }
+
+    /**
      * Remove {@link SoftApConfiguration#BAND_6GHZ} if multiple bands are configured
      * as a mask when security type is restricted to operate in this band.
      *
+     * @param resources the resources to get the OEM configuration for HAL support.
      * @param config The current {@link SoftApConfiguration}.
+     * @param isBridgedMode true if bridged mode is enabled, false otherwise.
      *
      * @return the updated SoftApConfiguration.
      */
     public static SoftApConfiguration remove6gBandForUnsupportedSecurity(
-            SoftApConfiguration config) {
+            @NonNull Resources resources,
+            SoftApConfiguration config, boolean isBridgedMode) {
         SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(config);
 
         try {
+            int securityType = config.getSecurityType();
             if (config.getBands().length == 1) {
                 int configuredBand = config.getBand();
                 if ((configuredBand & SoftApConfiguration.BAND_6GHZ) != 0
                         && isSecurityTypeRestrictedFor6gBand(config.getSecurityType())) {
                     Log.i(TAG, "remove BAND_6G if multiple bands are configured "
-                            + "as a mask since security type is restricted");
+                            + "as a mask when security type is restricted");
                     builder.setBand(configuredBand & ~SoftApConfiguration.BAND_6GHZ);
                 }
             } else if (SdkLevel.isAtLeastS()) {
                 SparseIntArray channels = config.getChannels();
                 SparseIntArray newChannels = new SparseIntArray(channels.size());
-                if (isSecurityTypeRestrictedFor6gBand(config.getSecurityType())) {
+                if (isSecurityTypeRestrictedFor6gBand(securityType)) {
                     for (int i = 0; i < channels.size(); i++) {
                         int band = channels.keyAt(i);
-                        if ((band & SoftApConfiguration.BAND_6GHZ) != 0) {
+                        if ((band & SoftApConfiguration.BAND_6GHZ) != 0
+                                && canHALConvertRestrictedSecurityTypeFor6GHz(resources,
+                                securityType) && isBridgedMode) {
+                            Log.i(TAG, "Do not remove BAND_6G in bridged mode for"
+                                    + " security type: " + securityType
+                                    + " as HAL can convert the security type");
+                        } else {
                             Log.i(TAG, "remove BAND_6G if multiple bands are configured "
                                     + "as a mask when security type is restricted");
                             band &= ~SoftApConfiguration.BAND_6GHZ;
@@ -794,9 +815,9 @@
      * @param countryCode country code
      * @param config configuration to update
      * @param capability soft ap capability
-     * @return an integer result code
+     * @return the corresponding {@link SoftApManager.StartResult} result code.
      */
-    public static int updateApChannelConfig(WifiNative wifiNative,
+    public static @SoftApManager.StartResult int updateApChannelConfig(WifiNative wifiNative,
             @NonNull CoexManager coexManager,
             Resources resources,
             String countryCode,
@@ -806,14 +827,14 @@
         /* Use default band and channel for device without HAL. */
         if (!wifiNative.isHalStarted()) {
             configBuilder.setChannel(DEFAULT_AP_CHANNEL, DEFAULT_AP_BAND);
-            return SUCCESS;
+            return SoftApManager.START_RESULT_SUCCESS;
         }
 
         /* Country code is mandatory for 5GHz band. */
         if (config.getBand() == SoftApConfiguration.BAND_5GHZ
                 && countryCode == null) {
             Log.e(TAG, "5GHz band is not allowed without country code");
-            return ERROR_GENERIC;
+            return SoftApManager.START_RESULT_FAILURE_GENERAL;
         }
         if (!capability.areFeaturesSupported(SOFTAP_FEATURE_ACS_OFFLOAD)) {
             /* Select a channel if it is not specified and ACS is not enabled */
@@ -823,7 +844,7 @@
                 if (freq == -1) {
                     /* We're not able to get channel from wificond. */
                     Log.e(TAG, "Failed to get available channel.");
-                    return ERROR_NO_CHANNEL;
+                    return SoftApManager.START_RESULT_FAILURE_NO_CHANNEL;
                 }
                 configBuilder.setChannel(
                         ScanResult.convertFrequencyMhzToChannelIfSupported(freq),
@@ -844,7 +865,7 @@
             }
         }
 
-        return SUCCESS;
+        return SoftApManager.START_RESULT_SUCCESS;
     }
 
     /**
@@ -984,6 +1005,27 @@
     }
 
     /**
+     * Helper function to update SoftApCapability instance based on config store.
+     *
+     * @param capability the original softApCapability
+     * @param configStore where we stored the Capability after first time fetch from driver.
+     * @return SoftApCapability which updated from the config store.
+     */
+    @NonNull
+    public static SoftApCapability updateCapabilityFromConfigStore(
+            SoftApCapability capability,
+            WifiSettingsConfigStore configStore) {
+        if (capability == null) {
+            return null;
+        }
+        if (capability.areFeaturesSupported(SOFTAP_FEATURE_IEEE80211_BE)) {
+            capability.setSupportedFeatures(isIeee80211beEnabledInConfig(configStore),
+                    SOFTAP_FEATURE_IEEE80211_BE);
+        }
+        return capability;
+    }
+
+    /**
      * Helper function to get device support 802.11 AX on Soft AP or not
      *
      * @param context the caller context used to get value from resource file.
@@ -1006,6 +1048,18 @@
     }
 
     /**
+     * Helper function to check Config supports 802.11 BE on Soft AP or not
+     *
+     * @param configStore to check the support from WifiSettingsConfigStore
+     * @return true if supported, false otherwise.
+     */
+    public static boolean isIeee80211beEnabledInConfig(
+            WifiSettingsConfigStore configStore) {
+        return configStore.get(
+                    WifiSettingsConfigStore.WIFI_WIPHY_11BE_SUPPORTED);
+    }
+
+    /**
      * Helper function to get device support AP MAC randomization or not.
      *
      * @param context the caller context used to get value from resource file.
diff --git a/service/java/com/android/server/wifi/util/HalAidlUtil.java b/service/java/com/android/server/wifi/util/HalAidlUtil.java
index 8afb229..e845446 100644
--- a/service/java/com/android/server/wifi/util/HalAidlUtil.java
+++ b/service/java/com/android/server/wifi/util/HalAidlUtil.java
@@ -16,15 +16,21 @@
 
 package com.android.server.wifi.util;
 
+import android.hardware.wifi.common.OuiKeyedData;
 import android.hardware.wifi.supplicant.KeyMgmtMask;
 import android.net.wifi.WifiConfiguration;
+import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.List;
 
 /**
  * Provide utility functions for HAL AIDL implementation.
  */
 public class HalAidlUtil {
+    private static final String TAG = "HalAidlUtil";
+
     private static int supplicantMaskValueToWifiConfigurationBitSet(int supplicantMask,
             int supplicantValue, BitSet bitset, int bitSetPosition) {
         bitset.set(bitSetPosition, (supplicantMask & supplicantValue) == supplicantValue);
@@ -92,4 +98,24 @@
         }
         return bitset;
     }
+
+    /**
+     * Convert a list of {@link android.net.wifi.OuiKeyedData} to its HAL equivalent.
+     * Note: Invalid instances in the input list will be skipped.
+     */
+    public static OuiKeyedData[] frameworkToHalOuiKeyedDataList(
+            List<android.net.wifi.OuiKeyedData> frameworkList) {
+        List<OuiKeyedData> halList = new ArrayList<>();
+        for (android.net.wifi.OuiKeyedData frameworkData : frameworkList) {
+            if (frameworkData == null || !frameworkData.validate()) {
+                Log.e(TAG, "Invalid framework OuiKeyedData: " + frameworkData);
+                continue;
+            }
+            OuiKeyedData halData = new OuiKeyedData();
+            halData.oui = frameworkData.getOui();
+            halData.vendorData = frameworkData.getData();
+            halList.add(halData);
+        }
+        return halList.toArray(new OuiKeyedData[halList.size()]);
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index b81bd6a..b6e991c 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -33,6 +33,8 @@
 import com.android.server.wifi.hotspot2.NetworkDetail;
 import com.android.server.wifi.hotspot2.anqp.Constants;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -46,6 +48,81 @@
     private static final String TAG = "InformationElementUtil";
     private static final boolean DBG = false;
 
+    /**
+     * 6 GHz Access Point type as encoded in the Regulatory Info subfield in the Control field of
+     * the 6 GHz Operation Information field of the HE Operation element.
+     * Reference E.2.7 6 GHz band (IEEE Std 802.11ax-2021).
+     */
+    public enum ApType6GHz {
+        AP_TYPE_6GHZ_UNKNOWN,
+        AP_TYPE_6GHZ_INDOOR,
+        AP_TYPE_6GHZ_STANDARD_POWER,
+    }
+
+    /**
+     * Defragment Element class
+     *
+     * IEEE Std 802.11™‐2020, Section: 10.28.11 Element fragmentation describes a fragmented sub
+     * element as,
+     *    | SubEID | Len | Data | FragId | Len | Data | FragId | Len| Data ...
+     * Octets: 1     1     255     1        1     255     1       1     m
+     * Values: eid   255         fid       255           fid      m
+     *
+     */
+    private static class DefragmentElement {
+        /** Defagmented element bytes */
+        public byte[] bytes;
+        /** Bytes read to defragment the fragmented element */
+        public int bytesRead = 0;
+
+        public static final int FRAG_MAX_LEN = 255;
+        public static final int FRAGMENT_ELEMENT_EID = 242;
+
+        DefragmentElement(byte[] bytes, int start, int eid, int fid) {
+            if (bytes == null) return;
+            ByteArrayOutputStream defrag = new ByteArrayOutputStream();
+            ByteBuffer element = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+            element.position(start);
+            try {
+                if ((element.get() & Constants.BYTE_MASK) != eid) return;
+                // Add EID, 255 as the parser expects the element header.
+                defrag.write(eid);
+                defrag.write(255);
+                int fragLen, fragId;
+                do {
+                    fragLen = element.get() & Constants.BYTE_MASK;
+                    byte[] b = new byte[fragLen];
+                    element.get(b);
+                    defrag.write(b);
+                    // Mark the position to undo the extra read.
+                    element.mark();
+                    if (element.remaining() <= 0) break;
+                    fragId = element.get() & Constants.BYTE_MASK;
+                } while (fragLen == FRAG_MAX_LEN && fragId == fid);
+                // Reset the extra get.
+                element.reset();
+            } catch (IOException e) {
+                if (DBG) {
+                    Log.w(TAG, "Failed to defragment sub element: " + e.getMessage());
+                }
+                return;
+            } catch (IndexOutOfBoundsException e) {
+                if (DBG) {
+                    Log.e(TAG, "Failed to defragment sub element: " + e.getMessage());
+                }
+                return;
+            } catch (BufferUnderflowException e) {
+                if (DBG) {
+                    Log.w(TAG, "Failed to defragment sub element: " + e.getMessage());
+                }
+                return;
+            }
+
+            this.bytes = defrag.toByteArray();
+            bytesRead = element.position() - start;
+        }
+    }
+
     /** Converts InformationElement to hex string */
     public static String toHexString(InformationElement e) {
         StringBuilder sb = new StringBuilder();
@@ -66,6 +143,12 @@
         return parseInformationElements(HexEncoding.decode(data));
     }
 
+    private static boolean isFragmentable(int eid, int eidExt) {
+        // Refer IEE802.11BE D2.3, Section 9.4.2 Elements
+        return ((eid == InformationElement.EID_EXTENSION_PRESENT)
+                && (eidExt == InformationElement.EID_EXT_MULTI_LINK));
+    }
+
     public static InformationElement[] parseInformationElements(byte[] bytes) {
         if (bytes == null) {
             return new InformationElement[0];
@@ -75,9 +158,12 @@
         ArrayList<InformationElement> infoElements = new ArrayList<>();
         boolean found_ssid = false;
         while (data.remaining() > 1) {
+            // Mark the start of the data
+            data.mark();
             int eid = data.get() & Constants.BYTE_MASK;
             int eidExt = 0;
             int elementLength = data.get() & Constants.BYTE_MASK;
+            DefragmentElement defrag = null;
 
             if (elementLength > data.remaining() || (eid == InformationElement.EID_SSID
                     && found_ssid)) {
@@ -94,14 +180,36 @@
                     break;
                 }
                 eidExt = data.get() & Constants.BYTE_MASK;
+                if (isFragmentable(eid, eidExt)
+                        && elementLength == DefragmentElement.FRAG_MAX_LEN) {
+                    // Fragmented IE. Reset the position to head to defragment.
+                    data.reset();
+                    defrag =
+                            new DefragmentElement(
+                                    bytes,
+                                    data.position(),
+                                    eid,
+                                    DefragmentElement.FRAGMENT_ELEMENT_EID);
+                }
                 elementLength--;
             }
 
             InformationElement ie = new InformationElement();
             ie.id = eid;
             ie.idExt = eidExt;
-            ie.bytes = new byte[elementLength];
-            data.get(ie.bytes);
+            if (defrag != null) {
+                if (defrag.bytesRead == 0) {
+                    // Malformed IE skipping
+                    break;
+                }
+                // Skip first three bytes: eid, len, eidExt as it is already processed.
+                ie.bytes = Arrays.copyOfRange(defrag.bytes, 3, defrag.bytes.length);
+                int newPosition = data.position() + defrag.bytesRead;
+                data.position(newPosition);
+            } else {
+                ie.bytes = new byte[elementLength];
+                data.get(ie.bytes);
+            }
             infoElements.add(ie);
         }
         return infoElements.toArray(new InformationElement[infoElements.size()]);
@@ -526,17 +634,22 @@
     public static class HeOperation {
 
         private static final int HE_OPERATION_BASIC_LENGTH = 6;
+        private static final int TWT_REQUIRED_MASK = 0x08;
         private static final int VHT_OPERATION_INFO_PRESENT_MASK = 0x40;
         private static final int HE_6GHZ_INFO_PRESENT_MASK = 0x02;
         private static final int HE_6GHZ_CH_WIDTH_MASK = 0x03;
+        private static final int HE_6GHZ_REG_INFO_MASK = 0x38;
+        private static final int HE_6GHZ_REG_INFO_SHIFT = 3;
         private static final int CO_HOSTED_BSS_PRESENT_MASK = 0x80;
         private static final int VHT_OPERATION_INFO_START_INDEX = 6;
         private static final int HE_BW_80_80_160 = 3;
 
         private boolean mPresent = false;
+        private boolean mTwtRequired = false;
         private boolean mVhtInfoPresent = false;
         private boolean m6GhzInfoPresent = false;
         private int mChannelWidth;
+        private ApType6GHz mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_UNKNOWN;
         private int mPrimaryChannel;
         private int mCenterFreqSeg0;
         private int mCenterFreqSeg1;
@@ -550,6 +663,21 @@
         }
 
         /**
+         * Return whether the AP requires HE stations to participate either in individual TWT
+         * agreements or Broadcast TWT operation. Reference 9.4.2.249 HE Operation element (IEEE
+         * Std 802.11ax™-2021).
+         **/
+        public boolean isTwtRequired() {
+            return mTwtRequired;
+        }
+
+        /**
+         * Return 6Ghz AP type as defined in {@link ApType6GHz}
+         **/
+        public ApType6GHz getApType6GHz() {
+            return mApType6GHz;
+        }
+        /**
          * Returns whether VHT Information field is present.
          */
         public boolean isVhtInfoPresent() {
@@ -650,6 +778,7 @@
                 return;
             }
 
+            mTwtRequired = (ie.bytes[0] & TWT_REQUIRED_MASK) != 0;
             mVhtInfoPresent = (ie.bytes[1] & VHT_OPERATION_INFO_PRESENT_MASK) != 0;
             m6GhzInfoPresent = (ie.bytes[2] & HE_6GHZ_INFO_PRESENT_MASK) != 0;
             boolean coHostedBssPresent = (ie.bytes[1] & CO_HOSTED_BSS_PRESENT_MASK) != 0;
@@ -680,6 +809,13 @@
                         + (coHostedBssPresent ? 1 : 0);
 
                 mChannelWidth = ie.bytes[startIndx + 1] & HE_6GHZ_CH_WIDTH_MASK;
+                int regInfo = (ie.bytes[startIndx + 1] & HE_6GHZ_REG_INFO_MASK)
+                        >> HE_6GHZ_REG_INFO_SHIFT;
+                if (regInfo == 0) {
+                    mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_INDOOR;
+                } else if (regInfo == 1) {
+                    mApType6GHz = ApType6GHz.AP_TYPE_6GHZ_STANDARD_POWER;
+                }
                 mPrimaryChannel = ie.bytes[startIndx] & Constants.BYTE_MASK;
                 mCenterFreqSeg0 = ie.bytes[startIndx + 2] & Constants.BYTE_MASK;
                 mCenterFreqSeg1 = ie.bytes[startIndx + 3] & Constants.BYTE_MASK;
@@ -691,7 +827,16 @@
      * EhtOperation: represents the EHT Operation IE
      */
     public static class EhtOperation {
+        private static final int EHT_OPERATION_BASIC_LENGTH = 5;
+        private static final int EHT_OPERATION_INFO_PRESENT_MASK = 0x01;
+        private static final int DISABLED_SUBCHANNEL_BITMAP_PRESENT_MASK = 0x02;
+        private static final int EHT_OPERATION_INFO_START_INDEX = EHT_OPERATION_BASIC_LENGTH;
+        private static final int DISABLED_SUBCHANNEL_BITMAP_START_INDEX =
+                EHT_OPERATION_INFO_START_INDEX + 3;
         private boolean mPresent = false;
+        private boolean mEhtOperationInfoPresent = false;
+        private boolean mDisabledSubchannelBitmapPresent = false;
+        private byte[] mDisabledSubchannelBitmap;
 
         /**
          * Returns whether the EHT Information Element is present.
@@ -700,15 +845,66 @@
             return mPresent;
         }
 
-        /** Parse EHT Operation IE */
+        /**
+         * Returns whether EHT Operation Information field is present.
+         * Reference 9.4.2.311 EHT Operation element (IEEEStd 802.11be™ Draft2.0).
+         */
+        public boolean isEhtOperationInfoPresent() {
+            return mEhtOperationInfoPresent;
+        }
+
+        /**
+         * Returns whether the Disabled Subchannel Bitmap field is present.
+         */
+        public boolean isDisabledSubchannelBitmapPresent() {
+            return mDisabledSubchannelBitmapPresent;
+        }
+
+        /**
+         * Returns the Disabled Subchannel Bitmap field if it exists. Otherwise, returns null.
+         */
+        public byte[] getDisabledSubchannelBitmap() {
+            return mDisabledSubchannelBitmap;
+        }
+
+        /**
+         * Parse EHT Operation IE
+         */
         public void from(InformationElement ie) {
             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
                     || ie.idExt != InformationElement.EID_EXT_EHT_OPERATION) {
                 throw new IllegalArgumentException("Element id is not EHT_OPERATION");
             }
+            // Make sure the byte array length is at least the fixed size
+            if (ie.bytes.length < EHT_OPERATION_BASIC_LENGTH) {
+                if (DBG) {
+                    Log.w(TAG, "Invalid EHT_OPERATION IE len: " + ie.bytes.length);
+                }
+                // Skipping parsing of the IE
+                return;
+            }
 
+            mEhtOperationInfoPresent = (ie.bytes[0] & EHT_OPERATION_INFO_PRESENT_MASK) != 0;
+            mDisabledSubchannelBitmapPresent =
+                    (ie.bytes[0] & DISABLED_SUBCHANNEL_BITMAP_PRESENT_MASK) != 0;
+            int expectedLen = EHT_OPERATION_BASIC_LENGTH + (mEhtOperationInfoPresent ? (
+                    mDisabledSubchannelBitmapPresent ? 5 : 3) : 0);
+            // Make sure the byte array length is at least fitting the known parameters
+            if (ie.bytes.length < expectedLen) {
+                if (DBG) {
+                    Log.w(TAG, "Invalid EHT_OPERATION info len: " + ie.bytes.length);
+                }
+                // Skipping parsing of the IE
+                return;
+            }
             mPresent = true;
 
+            if (mDisabledSubchannelBitmapPresent) {
+                mDisabledSubchannelBitmap = new byte[2];
+                System.arraycopy(ie.bytes, DISABLED_SUBCHANNEL_BITMAP_START_INDEX,
+                        mDisabledSubchannelBitmap, 0, 2);
+            }
+
             //TODO put more functionality for parsing the IE
         }
     }
@@ -798,8 +994,52 @@
      * HeCapabilities: represents the HE Capabilities IE
      */
     public static class HeCapabilities {
+
+        /**
+         * Represents HE MAC Capabilities Information field. Reference 9.4.2.248.2 HE MAC
+         * Capabilities Information field (IEEE Std 802.11ax-2021).
+         */
+        private static class HeMacCapabilitiesInformation {
+            public static int startOffset = 0;
+            public static int endOffset = 5;
+            public static final int TWT_REQUESTER_SUPPORT_BIT = 1;
+            public static final int TWT_RESPONDER_SUPPORT_BIT = 2;
+            public static final int BROADCAST_TWT_SUPPORT_BIT = 20;
+            public boolean isTwtRequesterSupported = false;
+            public boolean isTwtResponderSupported = false;
+            public boolean isBroadcastTwtSupported = false;
+            private BitSet mBitSet = new BitSet();
+
+            /** Parse HE MAC capabilities Information from the byte array. */
+            public void from(byte[] bytes) {
+                mBitSet = BitSet.valueOf(bytes);
+                isTwtRequesterSupported = mBitSet.get(TWT_REQUESTER_SUPPORT_BIT);
+                isTwtResponderSupported = mBitSet.get(TWT_RESPONDER_SUPPORT_BIT);
+                isBroadcastTwtSupported = mBitSet.get(BROADCAST_TWT_SUPPORT_BIT);
+            }
+        }
+
+        private HeMacCapabilitiesInformation mHeMacCapabilitiesInformation =
+                new HeMacCapabilitiesInformation();
         private int mMaxNumberSpatialStreams = 1;
         private boolean mPresent = false;
+
+        /**
+         * Return whether TWT requester is supported. Set by HE Stations to indicate TWT support
+         */
+        public boolean isTwtRequesterSupported() {
+            return mHeMacCapabilitiesInformation.isTwtRequesterSupported;
+        }
+        /** Return whether TWT responder is supported. Set by HE AP to indicate TWT support. */
+        public boolean isTwtResponderSupported() {
+            return mHeMacCapabilitiesInformation.isTwtResponderSupported;
+        }
+
+        /** Return whether broadcast TWT is supported */
+        public boolean isBroadcastTwtSupported() {
+            return mHeMacCapabilitiesInformation.isBroadcastTwtSupported;
+        }
+
         /** Returns whether HE Capabilities IE is present */
         public boolean isPresent() {
             return mPresent;
@@ -827,19 +1067,65 @@
                     + (ie.bytes[17] & Constants.BYTE_MASK);
             mMaxNumberSpatialStreams = parseMaxNumberSpatialStreamsFromMcsMap(mcsMap);
             mPresent = true;
+            mHeMacCapabilitiesInformation.from(Arrays.copyOfRange(ie.bytes,
+                    mHeMacCapabilitiesInformation.startOffset,
+                    mHeMacCapabilitiesInformation.endOffset));
         }
     }
 
     /**
-     * EhtCapabilities: represents the EHT Capabilities IE
+     * EhtCapabilities: represents the EHT Capabilities IE. Reference 9.4.2.313 EHT Capabilities
+     * element (IEEE P802.11be/D3.1).
      */
     public static class EhtCapabilities {
+        /**
+         * EhtMacCapabilitiesInformation: represents the EHT MAC Capabilities Information element.
+         * Reference 9.4.2.313.2 EHT MAC Capabilities Information field (IEEE P802.11be/D3.1).
+         */
+        public static class EhtMacCapabilitiesInformation {
+            public static int startOffset = 0;
+            public static int endOffset = 1;
+            public static final int EPCS_PRIORITY_ACCESS_SUPPORT_BIT = 0;
+            public static final int RESTRICTED_TWT_SUPPORT_BIT = 4;
+            public boolean isEpcsPriorityAccessSupported = false;
+            public boolean isRestrictedTwtSupported = false;
+            private BitSet mBitSet = new BitSet();
+
+            /** Parse EHT MAC Capabilities Information from the bytes. **/
+            public void from(byte[] bytes) {
+                mBitSet = BitSet.valueOf(bytes);
+                isEpcsPriorityAccessSupported = mBitSet.get(EPCS_PRIORITY_ACCESS_SUPPORT_BIT);
+                isRestrictedTwtSupported = mBitSet.get(RESTRICTED_TWT_SUPPORT_BIT);
+            }
+        }
         private boolean mPresent = false;
-        /** Returns whether HE Capabilities IE is present */
+
+        private EhtMacCapabilitiesInformation mEhtMacCapabilitiesInformation =
+                new EhtMacCapabilitiesInformation();
+        /** Returns whether HE Capabilities IE is present.  */
         public boolean isPresent() {
             return mPresent;
         }
 
+        /**
+         * Returns whether restricted TWT is supported or not. It enables enhanced medium access
+         * protection and resource reservation mechanisms for delivery of latency sensitive
+         * traffic.
+         */
+        public boolean isRestrictedTwtSupported() {
+            return mEhtMacCapabilitiesInformation.isRestrictedTwtSupported;
+        }
+
+        /**
+         * Returns whether EPCS priority access supported or not. EPCS priority access is a
+         * mechanism that provides prioritized access to the wireless medium for authorized users to
+         * increase their probability of successful communication during periods of network
+         * congestion.
+         */
+        public boolean isEpcsPriorityAccessSupported() {
+            return mEhtMacCapabilitiesInformation.isEpcsPriorityAccessSupported;
+        }
+
         /** Parse EHT Capabilities IE */
         public void from(InformationElement ie) {
             if (ie.id != InformationElement.EID_EXTENSION_PRESENT
@@ -847,8 +1133,9 @@
                 throw new IllegalArgumentException("Element id is not EHT_CAPABILITIES: " + ie.id);
             }
             mPresent = true;
-
-            //TODO Add code to parse the IE
+            mEhtMacCapabilitiesInformation.from(Arrays.copyOfRange(ie.bytes,
+                    mEhtMacCapabilitiesInformation.startOffset,
+                    mEhtMacCapabilitiesInformation.endOffset));
         }
     }
 
@@ -956,6 +1243,47 @@
             return commonInfoLength;
         }
 
+        /** Parse per STA sub element (not fragmented) of Multi link element. */
+        private Boolean parsePerStaSubElement(byte[] bytes, int start, int len) {
+            MloLink link = new MloLink();
+            link.setLinkId(
+                    bytes[start + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET]
+                            & PER_STA_SUB_ELEMENT_LINK_ID_MASK);
+
+            int staInfoLength =
+                    bytes[start + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET] & Constants.BYTE_MASK;
+            if (len < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) {
+                if (DBG) {
+                    Log.w(TAG, "Invalid sta info length: " + staInfoLength);
+                }
+                // Skipping parsing of the IE
+                return false;
+            }
+
+            // Check if MAC Address is present
+            if ((bytes[start + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET]
+                            & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK)
+                    != 0) {
+                if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) {
+                    if (DBG) {
+                        Log.w(TAG, "Invalid sta info length: " + staInfoLength);
+                    }
+                    // Skipping parsing of the IE
+                    return false;
+                }
+
+                int macAddressOffset =
+                        start
+                                + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET
+                                + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET;
+                link.setApMacAddress(
+                        MacAddress.fromBytes(
+                                Arrays.copyOfRange(bytes, macAddressOffset, macAddressOffset + 6)));
+            }
+            mAffiliatedLinks.add(link);
+            return true;
+        }
+
         /**
          * Parse Link Info field in Multi-Link Operation IE
          *
@@ -990,41 +1318,30 @@
                     continue;
                 }
 
-                MloLink link = new MloLink();
-                link.setLinkId(ie.bytes[startOffset + PER_STA_SUB_ELEMENT_LINK_ID_OFFSET]
-                        & PER_STA_SUB_ELEMENT_LINK_ID_MASK);
-
-                int staInfoLength = ie.bytes[startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET]
-                        & Constants.BYTE_MASK;
-                if (subElementLen < PER_STA_SUB_ELEMENT_STA_INFO_OFFSET + staInfoLength) {
-                    if (DBG) {
-                        Log.w(TAG, "Invalid sta info length: " + staInfoLength);
-                    }
-                    // Skipping parsing of the IE
-                    return false;
-                }
-
-                // Check if MAC Address is present
-                if ((ie.bytes[startOffset + PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_OFFSET]
-                        & PER_STA_SUB_ELEMENT_MAC_ADDRESS_PRESENT_MASK) != 0) {
-                    if (staInfoLength < 1 /*length*/ + 6 /*mac address*/) {
-                        if (DBG) {
-                            Log.w(TAG, "Invalid sta info length: " + staInfoLength);
-                        }
-                        // Skipping parsing of the IE
+                int bytesRead;
+                // Check for fragmentation before parsing per sta profile sub element
+                if (subElementLen == DefragmentElement.FRAG_MAX_LEN) {
+                    DefragmentElement defragment =
+                            new DefragmentElement(
+                                    ie.bytes,
+                                    startOffset,
+                                    PER_STA_SUB_ELEMENT_ID,
+                                    InformationElement.EID_FRAGMENT_SUB_ELEMENT_MULTI_LINK);
+                    bytesRead = defragment.bytesRead;
+                    if (defragment.bytesRead == 0 || defragment.bytes == null) {
                         return false;
                     }
-
-                    int macAddressOffset = startOffset + PER_STA_SUB_ELEMENT_STA_INFO_OFFSET
-                            + PER_STA_SUB_ELEMENT_STA_INFO_MAC_ADDRESS_OFFSET;
-                    link.setApMacAddress(MacAddress.fromBytes(Arrays.copyOfRange(ie.bytes,
-                            macAddressOffset, macAddressOffset + 6)));
+                    if (!parsePerStaSubElement(defragment.bytes, 0, defragment.bytes.length)) {
+                        return false;
+                    }
+                } else {
+                    bytesRead = subElementLen;
+                    if (!parsePerStaSubElement(ie.bytes, startOffset, subElementLen)) {
+                        return false;
+                    }
                 }
-
-                mAffiliatedLinks.add(link);
-
                 // Done with this sub-element
-                startOffset += subElementLen;
+                startOffset += bytesRead;
             }
 
             return true;
@@ -1207,6 +1524,17 @@
                         ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
             }
         }
+
+        @Override
+        public String toString() {
+            StringBuilder stringBuilder = new StringBuilder();
+            stringBuilder.append("RoamingConsortium [");
+            stringBuilder.append("anqpOICount: " + anqpOICount);
+            stringBuilder.append(", roamingConsortiums: " + (roamingConsortiums == null ? "null"
+                    : Arrays.toString(roamingConsortiums)));
+            stringBuilder.append("]");
+            return stringBuilder.toString();
+        }
     }
 
     public static class Vsa {
@@ -1358,10 +1686,51 @@
     public static class ExtendedCapabilities {
         private static final int RTT_RESP_ENABLE_BIT = 70;
         private static final int SSID_UTF8_BIT = 48;
+        private static final int FILS_CAPABILITY_BIT = 72;
+        private static final int TWT_REQUESTER_CAPABILITY_BIT = 77;
+        private static final int TWT_RESPONDER_CAPABILITY_BIT = 78;
+        private static final int NO_TB_RANGING_RESPONDER = 90;
+        private static final int TB_RANGING_RESPONDER = 91;
 
         public BitSet capabilitiesBitSet;
 
         /**
+         * @return true if Trigger based ranging responder supported. Refer P802.11az/D7.0,
+         * September 2022, section 9.4.2.26 Extended Capabilities element.
+         */
+        public boolean isTriggerBasedRangingRespSupported() {
+            return capabilitiesBitSet.get(TB_RANGING_RESPONDER);
+        }
+
+        /**
+         * @return true if Non trigger based ranging responder supported. Refer P802.11az/D7.0,
+         * September 2022, section 9.4.2.26 Extended Capabilities element.
+         */
+        public boolean isNonTriggerBasedRangingRespSupported() {
+            return capabilitiesBitSet.get(NO_TB_RANGING_RESPONDER);
+        }
+
+        /**
+         * @return true if TWT Requester capability is set
+         */
+        public boolean isTwtRequesterSupported() {
+            return capabilitiesBitSet.get(TWT_REQUESTER_CAPABILITY_BIT);
+        }
+
+        /**
+         * @return true if TWT Responder capability is set
+         */
+        public boolean isTwtResponderSupported() {
+            return capabilitiesBitSet.get(TWT_RESPONDER_CAPABILITY_BIT);
+        }
+        /**
+         * @return true if Fast Initial Link Setup (FILS) capable
+         */
+        public boolean isFilsCapable() {
+            return capabilitiesBitSet.get(FILS_CAPABILITY_BIT);
+        }
+
+        /**
          * @return true if SSID should be interpreted using UTF-8 encoding
          */
         public boolean isStrictUtf8() {
diff --git a/service/java/com/android/server/wifi/util/LastCallerInfoManager.java b/service/java/com/android/server/wifi/util/LastCallerInfoManager.java
index 4fd7465..0b8e76a 100644
--- a/service/java/com/android/server/wifi/util/LastCallerInfoManager.java
+++ b/service/java/com/android/server/wifi/util/LastCallerInfoManager.java
@@ -125,6 +125,8 @@
                 return "API_SET_TDLS_ENABLED";
             case WifiManager.API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS:
                 return "API_SET_TDLS_ENABLED_WITH_MAC_ADDRESS";
+            case WifiManager.API_SET_PNO_SCAN_ENABLED:
+                return "API_SET_PNO_SCAN_ENABLED";
             default:
                 return "Unknown";
         }
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
index be873eb..47da81f 100644
--- a/service/java/com/android/server/wifi/util/XmlUtil.java
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -366,6 +366,7 @@
         public static final String XML_TAG_CARRIER_ID = "CarrierId";
         public static final String XML_TAG_SUBSCRIPTION_ID = "SubscriptionId";
         public static final String XML_TAG_IS_AUTO_JOIN = "AutoJoinEnabled";
+        public static final String XML_TAG_PRIORITY = "Priority";
         public static final String XML_TAG_DELETION_PRIORITY = "DeletionPriority";
         public static final String XML_TAG_NUM_REBOOTS_SINCE_LAST_USE = "NumRebootsSinceLastUse";
 
@@ -591,6 +592,7 @@
                     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_PRIORITY, configuration.priority);
             XmlUtil.writeNextValue(
                     out, XML_TAG_DELETION_PRIORITY,
                     configuration.getDeletionPriority());
@@ -996,6 +998,9 @@
                         case XML_TAG_IS_AUTO_JOIN:
                             configuration.allowAutojoin = (boolean) value;
                             break;
+                        case XML_TAG_PRIORITY:
+                            configuration.priority = (int) value;
+                            break;
                         case XML_TAG_DELETION_PRIORITY:
                             configuration.setDeletionPriority((int) value);
                             break;
@@ -1374,6 +1379,8 @@
         public static final String XML_TAG_HAS_EVER_CONNECTED = "HasEverConnected";
         public static final String XML_TAG_IS_CAPTIVE_PORTAL_NEVER_DETECTED =
                 "CaptivePortalNeverDetected";
+        public static final String XML_TAG_HAS_EVER_VALIDATED_INTERNET_ACCESS =
+                "HasEverValidatedInternetAccess";
         public static final String XML_TAG_CONNECT_CHOICE_RSSI = "ConnectChoiceRssi";
 
         /**
@@ -1397,6 +1404,8 @@
                     out, XML_TAG_HAS_EVER_CONNECTED, selectionStatus.hasEverConnected());
             XmlUtil.writeNextValue(out, XML_TAG_IS_CAPTIVE_PORTAL_NEVER_DETECTED,
                     selectionStatus.hasNeverDetectedCaptivePortal());
+            XmlUtil.writeNextValue(out, XML_TAG_HAS_EVER_VALIDATED_INTERNET_ACCESS,
+                    selectionStatus.hasEverValidatedInternetAccess());
         }
 
         /**
@@ -1416,6 +1425,10 @@
             // which do not have the XML_TAG_IS_CAPTIVE_PORTAL_NEVER_DETECTED tag.
             selectionStatus.setHasNeverDetectedCaptivePortal(false);
 
+            // Initialize hasEverValidatedInternetAccess to "true" for existing configs which don't
+            // have any value stored.
+            selectionStatus.setHasEverValidatedInternetAccess(true);
+
             // Loop through and parse out all the elements from the stream within this section.
             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
                 String[] valueName = new String[1];
@@ -1441,6 +1454,10 @@
                         break;
                     case XML_TAG_IS_CAPTIVE_PORTAL_NEVER_DETECTED:
                         selectionStatus.setHasNeverDetectedCaptivePortal((boolean) value);
+                        break;
+                    case XML_TAG_HAS_EVER_VALIDATED_INTERNET_ACCESS:
+                        selectionStatus.setHasEverValidatedInternetAccess((boolean) value);
+                        break;
                     default:
                         Log.w(TAG, "Ignoring unknown value name found: " + valueName[0]);
                         break;
@@ -1506,6 +1523,8 @@
         public static final String XML_TAG_TRUST_ON_FIRST_USE = "TrustOnFirstUse";
         public static final String XML_TAG_USER_APPROVE_NO_CA_CERT = "UserApproveNoCaCert";
         public static final String XML_TAG_MINIMUM_TLS_VERSION = "MinimumTlsVersion";
+        public static final String XML_TAG_TOFU_DIALOG_STATE = "TofuDialogState";
+        public static final String XML_TAG_TOFU_CONNECTION_STATE = "TofuConnectionState";
 
         /**
          * Write password key to the XML stream.
@@ -1595,6 +1614,10 @@
                     enterpriseConfig.isUserApproveNoCaCert());
             XmlUtil.writeNextValue(out, XML_TAG_MINIMUM_TLS_VERSION,
                     enterpriseConfig.getMinimumTlsVersion());
+            XmlUtil.writeNextValue(out, XML_TAG_TOFU_DIALOG_STATE,
+                    enterpriseConfig.getTofuDialogState());
+            XmlUtil.writeNextValue(out, XML_TAG_TOFU_CONNECTION_STATE,
+                    enterpriseConfig.getTofuConnectionState());
         }
 
         /**
@@ -1720,6 +1743,12 @@
                         case XML_TAG_MINIMUM_TLS_VERSION:
                             enterpriseConfig.setMinimumTlsVersion((int) value);
                             break;
+                        case XML_TAG_TOFU_DIALOG_STATE:
+                            enterpriseConfig.setTofuDialogState((int) value);
+                            break;
+                        case XML_TAG_TOFU_CONNECTION_STATE:
+                            enterpriseConfig.setTofuConnectionState((int) value);
+                            break;
                         default:
                             Log.w(TAG, "Ignoring unknown value name found: " + valueName[0]);
                             break;
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 2755116..bbe67e1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi;
 
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
 
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY;
@@ -35,6 +36,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -118,6 +120,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -181,6 +184,7 @@
     @Mock LocalLog mLocalLog;
     @Mock WifiSettingsConfigStore mSettingsConfigStore;
     @Mock LastCallerInfoManager mLastCallerInfoManager;
+    @Mock WifiGlobals mWifiGlobals;
 
     Listener<ConcreteClientModeManager> mClientListener;
     Listener<SoftApManager> mSoftApListener;
@@ -239,6 +243,9 @@
         when(mSettingsConfigStore.get(
                 eq(WIFI_NATIVE_SUPPORTED_STA_BANDS))).thenReturn(
                 TEST_SUPPORTED_BANDS);
+        // Default force that WPA Personal is deprecated since the feature set is opposite to the
+        // API value.
+        when(mWifiGlobals.isWpaPersonalDeprecated()).thenReturn(true);
         doAnswer(new Answer<ClientModeManager>() {
             public ClientModeManager answer(InvocationOnMock invocation) {
                 Object[] args = invocation.getArguments();
@@ -310,12 +317,14 @@
                 mWifiPermissionsUtil,
                 mWifiMetrics,
                 mExternalScoreUpdateObserverProxy,
-                mDppManager);
+                mDppManager,
+                mWifiGlobals);
         // SelfRecovery is created in WifiInjector after ActiveModeWarden, so getSelfRecovery()
         // returns null when constructing ActiveModeWarden.
         when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)).thenReturn(true);
+        warden.setWifiStateForApiCalls(WIFI_STATE_ENABLED);
         return warden;
     }
 
@@ -349,13 +358,25 @@
      * @param isClientModeSwitch true if switching from another mode, false if creating a new one
      */
     private void enterClientModeActiveState(boolean isClientModeSwitch) throws Exception {
+        enterClientModeActiveState(isClientModeSwitch, TEST_FEATURE_SET);
+    }
+
+    /**
+     * Helper method with tested feature set to enter the EnabledState and set ClientModeManager
+     * in ConnectMode.
+     *
+     * @param isClientModeSwitch true if switching from another mode, false if creating a new one
+     * @param testFeatureSet a customized feature set to test
+     */
+    private void enterClientModeActiveState(boolean isClientModeSwitch, long testFeatureSet)
+            throws Exception {
         String fromState = mActiveModeWarden.getCurrentMode();
         when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
         mActiveModeWarden.wifiToggled(TEST_WORKSOURCE);
         mLooper.dispatchAll();
         when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY);
         when(mClientModeManager.getCurrentNetwork()).thenReturn(mNetwork);
-        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(TEST_FEATURE_SET);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(testFeatureSet);
         // ClientModeManager starts in SCAN_ONLY role.
         mClientListener.onRoleChanged(mClientModeManager);
         mLooper.dispatchAll();
@@ -376,7 +397,7 @@
         }
         verify(mClientModeManager, atLeastOnce()).getInterfaceName();
         verify(mWifiNative, atLeastOnce()).getSupportedFeatureSet(WIFI_IFACE_NAME);
-        assertEquals(TEST_FEATURE_SET, mActiveModeWarden.getSupportedFeatureSet());
+        assertEquals(testFeatureSet, mActiveModeWarden.getSupportedFeatureSet());
         verify(mScanRequestProxy, times(4)).enableScanning(true, true);
         assertEquals(mClientModeManager, mActiveModeWarden.getPrimaryClientModeManager());
         verify(mModeChangeCallback).onActiveModeManagerRoleChanged(mClientModeManager);
@@ -1541,6 +1562,54 @@
                 anyInt(), eq("android_apm"), eq(false));
     }
 
+    /** Wi-Fi state is restored properly when SoftAp is enabled during airplane mode. */
+    @Test
+    public void testWifiStateRestoredWhenSoftApEnabledDuringApm() throws Exception {
+        enableWifi();
+        assertInEnabledState();
+
+        // enabling airplane mode shuts down wifi
+        assertWifiShutDown(
+                () -> {
+                    when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+                    mActiveModeWarden.airplaneModeToggled();
+                    mLooper.dispatchAll();
+                });
+        verify(mLastCallerInfoManager)
+                .put(
+                        eq(WifiManager.API_WIFI_ENABLED),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        eq("android_apm"),
+                        eq(false));
+        mClientListener.onStopped(mClientModeManager);
+        mLooper.dispatchAll();
+
+        // start SoftAp
+        mActiveModeWarden.startSoftAp(
+                new SoftApModeConfiguration(
+                        WifiManager.IFACE_IP_MODE_LOCAL_ONLY,
+                        null,
+                        mSoftApCapability,
+                        TEST_COUNTRYCODE),
+                TEST_WORKSOURCE);
+        mLooper.dispatchAll();
+
+        // disabling airplane mode enables wifi
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+        mActiveModeWarden.airplaneModeToggled();
+        mLooper.dispatchAll();
+        verify(mLastCallerInfoManager)
+                .put(
+                        eq(WifiManager.API_WIFI_ENABLED),
+                        anyInt(),
+                        anyInt(),
+                        anyInt(),
+                        eq("android_apm"),
+                        eq(true));
+    }
+
     /**
      * Disabling location mode when in scan mode will disable wifi
      */
@@ -2593,6 +2662,7 @@
         assertInDisabledState();
 
         verify(mClientModeManager, atLeastOnce()).getInterfaceName();
+        verify(mClientModeManager, atLeastOnce()).getPreviousRole();
         verifyNoMoreInteractions(mClientModeManager, mSoftApManager);
     }
 
@@ -2942,6 +3012,7 @@
             String ssid, String bssid)
             throws Exception {
         enterClientModeActiveState();
+        when(additionalClientModeManager.getRequestorWs()).thenReturn(TEST_WORKSOURCE);
 
         Mutable<Listener<ConcreteClientModeManager>> additionalClientListener =
                 new Mutable<>();
@@ -3003,7 +3074,10 @@
         assertEquals(additionalClientModeManager, requestedClientModeManager.getValue());
         // the additional CMM never became primary
         verify(mPrimaryChangedCallback, never()).onChange(any(), eq(additionalClientModeManager));
-
+        if (additionaClientModeManagerRole == ROLE_CLIENT_LOCAL_ONLY
+                || additionaClientModeManagerRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
+            assertEquals(Set.of(TEST_WORKSOURCE), mActiveModeWarden.getSecondaryRequestWs());
+        }
         return additionalClientListener.value;
     }
 
@@ -3279,6 +3353,27 @@
     }
 
     @Test
+    public void testRequestSecondaryClientModeManagerWhenWifiIsDisabling()
+            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, false));
+
+        // Set wifi to disabling and verify secondary CMM is not obtained
+        mActiveModeWarden.setWifiStateForApiCalls(WIFI_STATE_DISABLING);
+        ExternalClientModeManagerRequestListener externalRequestListener = mock(
+                ExternalClientModeManagerRequestListener.class);
+        mActiveModeWarden.requestLocalOnlyClientModeManager(
+                externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false);
+        mLooper.dispatchAll();
+
+        verify(externalRequestListener).onAnswer(null);
+    }
+
+    @Test
     public void requestLocalOnlyClientModeManagerWhenWifiIsOff() throws Exception {
         // Ensure that we can create more client ifaces.
         when(mWifiNative.isItPossibleToCreateStaIface(any())).thenReturn(true);
@@ -3976,6 +4071,23 @@
     }
 
     @Test
+    public void testGetActiveModeManagersOrder() throws Exception {
+        enableWifi();
+        enterSoftApActiveMode();
+        assertInEnabledState();
+
+        Collection<ActiveModeManager> activeModeManagers =
+                mActiveModeWarden.getActiveModeManagers();
+        if (activeModeManagers == null) {
+            fail("activeModeManagers list should not be null");
+        }
+        Object[] modeManagers = activeModeManagers.toArray();
+        assertEquals(2, modeManagers.length);
+        assertTrue(modeManagers[0] instanceof SoftApManager);
+        assertTrue(modeManagers[1] instanceof ConcreteClientModeManager);
+    }
+
+    @Test
     public void airplaneModeToggleOnDisablesSoftAp() throws Exception {
         enterSoftApActiveMode();
         assertInEnabledState();
@@ -4115,17 +4227,18 @@
     public void propagateConnectedWifiScorerToPrimaryClientModeManager() throws Exception {
         IBinder iBinder = mock(IBinder.class);
         IWifiConnectedNetworkScorer iScorer = mock(IWifiConnectedNetworkScorer.class);
-        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
         verify(iScorer).onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy);
         enterClientModeActiveState();
         assertInEnabledState();
-        verify(mClientModeManager).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        verify(mClientModeManager).setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
 
         mActiveModeWarden.clearWifiConnectedNetworkScorer();
         verify(mClientModeManager).clearWifiConnectedNetworkScorer();
 
-        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer);
-        verify(mClientModeManager, times(2)).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
+        verify(mClientModeManager, times(2)).setWifiConnectedNetworkScorer(iBinder, iScorer,
+                TEST_UID);
     }
 
     @Test
@@ -4133,11 +4246,11 @@
             throws Exception {
         IBinder iBinder = mock(IBinder.class);
         IWifiConnectedNetworkScorer iScorer = mock(IWifiConnectedNetworkScorer.class);
-        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
         verify(iScorer).onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy);
         enterClientModeActiveState();
         assertInEnabledState();
-        verify(mClientModeManager).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        verify(mClientModeManager).setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
 
         enterScanOnlyModeActiveState(true);
 
@@ -4149,12 +4262,13 @@
         IBinder iBinder = mock(IBinder.class);
         IWifiConnectedNetworkScorer iScorer = mock(IWifiConnectedNetworkScorer.class);
         doThrow(new RemoteException()).when(iScorer).onSetScoreUpdateObserver(any());
-        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mActiveModeWarden.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
         verify(iScorer).onSetScoreUpdateObserver(mExternalScoreUpdateObserverProxy);
         enterClientModeActiveState();
         assertInEnabledState();
         // Ensure we did not propagate the scorer.
-        verify(mClientModeManager, never()).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        verify(mClientModeManager, never()).setWifiConnectedNetworkScorer(iBinder, iScorer,
+                TEST_UID);
     }
 
     /** Verify that the primary changed callback is triggered when entering client mode. */
@@ -5124,4 +5238,26 @@
         assertInEnabledState();
     }
 
+    @Test
+    public void testOnIdleModeChanged() throws Exception {
+        enterClientModeActiveState();
+        List<ClientModeManager> currentCMMs = mActiveModeWarden.getClientModeManagers();
+        assertTrue(currentCMMs.size() >= 1);
+        mActiveModeWarden.onIdleModeChanged(true);
+        for (ClientModeManager cmm : currentCMMs) {
+            verify(cmm).onIdleModeChanged(true);
+        }
+    }
+
+    @Test
+    public void testWepNotDeprecated() throws Exception {
+        when(mWifiGlobals.isWepSupported()).thenReturn(true);
+        enterClientModeActiveState(false, TEST_FEATURE_SET | WifiManager.WIFI_FEATURE_WEP);
+    }
+
+    @Test
+    public void testWpaPersonalNotDeprecated() throws Exception {
+        when(mWifiGlobals.isWpaPersonalDeprecated()).thenReturn(false);
+        enterClientModeActiveState(false, TEST_FEATURE_SET | WifiManager.WIFI_FEATURE_WPA_PERSONAL);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/AfcClientTest.java b/service/tests/wifitests/src/com/android/server/wifi/AfcClientTest.java
new file mode 100644
index 0000000..b9fe0bd
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/AfcClientTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+import android.location.Location;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticInOrder;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.server.wifi.entitlement.http.HttpClient;
+import com.android.server.wifi.entitlement.http.HttpRequest;
+import com.android.server.wifi.entitlement.http.HttpResponse;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+import java.net.MalformedURLException;
+import java.util.Random;
+
+/**
+ * Unit tests for {@link AfcClient}.
+ */
+@SmallTest
+public class AfcClientTest {
+    @Captor ArgumentCaptor<AfcServerResponse> mAfcServerResponseCaptor;
+    @Captor ArgumentCaptor<HttpRequest> mHttpRequestCaptor;
+    @Mock private Location mLocation;
+    @Mock private Random mRandom;
+    @Mock AfcClient.Callback mCallback;
+    private AfcClient mAfcClient;
+    private MockitoSession mSession;
+    private AfcLocation mAfcLocation;
+    private TestLooper mTestLooper;
+    private Handler mWifiHandler;
+    private static final double LONGITUDE = -122;
+    private static final double LATITUDE = 37;
+    private static final int HTTP_RESPONSE_CODE = 200;
+    private static final String AVAILABILITY_EXPIRE_TIME = "2020-11-03T13:34:05Z";
+
+    @Before
+    public void setUp() throws MalformedURLException, ServiceEntitlementException, JSONException {
+        MockitoAnnotations.initMocks(this);
+
+        mAfcServerResponseCaptor = ArgumentCaptor.forClass(AfcServerResponse.class);
+        mHttpRequestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
+
+        mTestLooper = new TestLooper();
+        mWifiHandler = new Handler(mTestLooper.getLooper());
+
+        when(mRandom.nextDouble()).thenReturn(0.5);
+        when(mLocation.getLongitude()).thenReturn(LONGITUDE);
+        when(mLocation.getLatitude()).thenReturn(LATITUDE);
+        mAfcLocation = new AfcEllipseLocation(
+                AfcEllipseLocation.DEFAULT_SEMI_MINOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_SEMI_MAJOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_ORIENTATION,
+                AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES, mRandom, mLocation
+        );
+
+        HttpResponse httpResponse = HttpResponse.builder().setResponseCode(
+                HTTP_RESPONSE_CODE).setBody(
+                getHttpResponseJSONBody()).build();
+
+        // Start a mockitoSession to mock HttpClient's static request method
+        mSession = ExtendedMockito.mockitoSession()
+                .mockStatic(HttpClient.class, withSettings().lenient())
+                .startMocking();
+        when(HttpClient.request(any(HttpRequest.class))).thenReturn(httpResponse);
+
+        // Create new AfcClient with a testing server URL
+        Handler backgroundHandler = new Handler(mTestLooper.getLooper());
+        mAfcClient = new AfcClient(backgroundHandler);
+        mAfcClient.setServerURL("https://testingURL");
+    }
+
+    /**
+     * Create a test HttpResponse JSON body to be received by server queries.
+     */
+    private String getHttpResponseJSONBody() throws JSONException {
+        JSONObject responseObject = new JSONObject();
+        JSONArray inquiryResponses = new JSONArray();
+        JSONObject inquiryResponse = new JSONObject();
+        inquiryResponse.put("requestId", String.valueOf(0));
+        inquiryResponse.put("availabilityExpireTime", AVAILABILITY_EXPIRE_TIME);
+        JSONObject responseResultObject = new JSONObject();
+        responseResultObject.put("responseCode", 0);
+        responseResultObject.put("shortDescription", "Success");
+        inquiryResponse.put("response", responseResultObject);
+        inquiryResponses.put(0, inquiryResponse);
+        responseObject.put("availableSpectrumInquiryResponses", inquiryResponses);
+        return responseObject.toString();
+    }
+
+    /**
+     * Verify that the Http request has expected fields set and that the request ID in the HTTP
+     * request is incremented with every query to the AFC server.
+     */
+    @Test
+    public void testHTTPRequestInitialization() throws JSONException {
+        StaticInOrder inOrder = inOrder(staticMockMarker(HttpClient.class));
+
+        mAfcClient.queryAfcServer(mAfcLocation, mWifiHandler, mCallback);
+        mTestLooper.dispatchAll();
+
+        inOrder.verify(() -> HttpClient.request(mHttpRequestCaptor.capture()));
+        HttpRequest httpRequest1 = mHttpRequestCaptor.getValue();
+        assertThat(httpRequest1.postData().getJSONArray("availableSpectrumInquiryRequests")
+                .getJSONObject(0).getJSONObject("location").getJSONObject(
+                        "ellipse").getJSONObject("center").get("longitude"))
+                .isEqualTo(LONGITUDE);
+        assertThat(httpRequest1.postData().getJSONArray("availableSpectrumInquiryRequests")
+                .getJSONObject(0).getJSONObject("location").getJSONObject(
+                        "ellipse").getJSONObject("center").get("latitude"))
+                .isEqualTo(LATITUDE);
+
+        assertThat(httpRequest1.postData().getJSONArray("availableSpectrumInquiryRequests")
+                .getJSONObject(0).get("requestId")).isEqualTo("0");
+
+        mAfcClient.queryAfcServer(mAfcLocation, mWifiHandler, mCallback);
+        mTestLooper.dispatchAll();
+
+        inOrder.verify(() -> HttpClient.request(mHttpRequestCaptor.capture()));
+        HttpRequest httpRequest2 = mHttpRequestCaptor.getValue();
+
+        // Ensure that the request ID has been incremented
+        assertThat(httpRequest2.postData().getJSONArray("availableSpectrumInquiryRequests")
+                .getJSONObject(0).get("requestId")).isEqualTo("1");
+    }
+
+    /**
+     * Verify that the AfcClient retrieves the HTTP request object and that the background thread
+     * successfully sends a request to the AFC server. Checks that the response is passed to the
+     * Wi-Fi handler callback with expected fields.
+     */
+    @Test
+    public void testSendRequestAndReceiveResponse() {
+        mAfcClient.queryAfcServer(mAfcLocation, mWifiHandler, mCallback);
+        mTestLooper.dispatchAll();
+        assertThat(mAfcClient.getAfcHttpRequestObject(mAfcLocation).url()).isEqualTo(
+                "https://testingURL");
+        verify(mCallback).onResult(mAfcServerResponseCaptor.capture(), any(AfcLocation.class));
+        AfcServerResponse serverResponse = mAfcServerResponseCaptor.getValue();
+        assertThat(serverResponse.getAfcChannelAllowance().availabilityExpireTimeMs)
+                .isEqualTo(AfcServerResponse.convertExpireTimeStringToTimestamp(
+                        AVAILABILITY_EXPIRE_TIME));
+        assertThat(serverResponse.getHttpResponseCode()).isEqualTo(HTTP_RESPONSE_CODE);
+        assertThat(serverResponse.getAfcResponseCode()).isEqualTo(0);
+        assertThat(serverResponse.getAfcResponseDescription()).isEqualTo("Success");
+    }
+
+    /**
+     * Verify that sending the callback onFailure method is called with the correct reason code and
+     * message when the request sent to the server throws a ServiceEntitlementException.
+     */
+    @Test
+    public void testSendHTTPRequestFail() throws ServiceEntitlementException {
+        when(HttpClient.request(any(HttpRequest.class))).thenThrow(
+                new ServiceEntitlementException(-1, "Test message"));
+        mAfcClient.queryAfcServer(mAfcLocation, mWifiHandler, mCallback);
+        mTestLooper.dispatchAll();
+        verify(mCallback).onFailure(AfcClient.REASON_SERVICE_ENTITLEMENT_FAILURE,
+                "Encountered Service Entitlement Exception when sending request to server. "
+                        + "com.android.wifi.x.com.android.libraries.entitlement."
+                        + "ServiceEntitlementException: Test message");
+    }
+
+     /**
+     * Called after each test.
+     */
+    @After
+    public void cleanup() {
+        validateMockitoUsage();
+        if (mSession != null) {
+            mSession.finishMocking();
+        }
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/AfcEllipseLocationTest.java b/service/tests/wifitests/src/com/android/server/wifi/AfcEllipseLocationTest.java
new file mode 100644
index 0000000..ddfa3ed
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/AfcEllipseLocationTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.location.Location;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Random;
+
+/**
+ * Unit tests for {@link AfcLocation}.
+ */
+@SmallTest
+public class AfcEllipseLocationTest {
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+    @Mock
+    private Location mLocation;
+    @Mock
+    private Random mRandom;
+    private AfcEllipseLocation mAfcEllipseLocation;
+    private double mLongitudeCenterOfEllipse;
+    private double mLatitudeCenterOfEllipse;
+    private static final double RANDOM_DOUBLE = .5;
+    private static final double STARTING_LATITUDE = 9.9;
+    private static final double STARTING_LONGITUDE = 2.5;
+    private static final double SEMI_MINOR_AXIS_DEGREES = AfcEllipseLocation
+            .DEFAULT_SEMI_MINOR_AXIS_METERS / AfcEllipseLocation.ONE_DEGREE_LONGITUDE_IN_METERS;
+    private static final double SEMI_MAJOR_AXIS_DEGREES = AfcEllipseLocation
+            .DEFAULT_SEMI_MAJOR_AXIS_METERS / AfcEllipseLocation.ONE_DEGREE_LONGITUDE_IN_METERS;
+
+    @Before
+    public void setUp() {
+        when(mLocation.getLatitude()).thenReturn(STARTING_LATITUDE);
+        when(mLocation.getLongitude()).thenReturn(STARTING_LONGITUDE);
+        when(mRandom.nextDouble()).thenReturn(RANDOM_DOUBLE);
+
+        mAfcEllipseLocation = new AfcEllipseLocation(
+                AfcEllipseLocation.DEFAULT_SEMI_MINOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_SEMI_MAJOR_AXIS_METERS,
+                AfcEllipseLocation.DEFAULT_ORIENTATION,
+                AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES, mRandom, mLocation
+        );
+
+        mLongitudeCenterOfEllipse = mAfcEllipseLocation.mLongitude;
+        mLatitudeCenterOfEllipse = mAfcEllipseLocation.mLatitude;
+
+        // Center point of ellipse is at point (mLongitudeCenterOfEllipse, mLatitudeCenterOfEllipse)
+        when(mLocation.getLongitude()).thenReturn(mLongitudeCenterOfEllipse);
+        when(mLocation.getLatitude()).thenReturn(mLatitudeCenterOfEllipse);
+    }
+
+    /**
+     * Verify that the ellipse center longitude and latitude fields are equal to expected values.
+     */
+    @Test
+    public void verifyCenterValues() {
+        assertThat(mLongitudeCenterOfEllipse).isEqualTo(
+                RANDOM_DOUBLE * 2 * AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES + (
+                        STARTING_LONGITUDE
+                                - AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES));
+        assertThat(mLatitudeCenterOfEllipse).isEqualTo(
+                RANDOM_DOUBLE * 2 * AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES + (
+                        STARTING_LATITUDE
+                                - AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES));
+    }
+
+    /**
+     * Verify that moving the longitude center coordinate gradually out causes the correct
+     * return value in {@link AfcEllipseLocation#checkLocation(Location)}.
+     */
+    @Test
+    public void adjustLongitudeValue() {
+        // Center point of ellipse is at point (mLongitudeCenterOfEllipse, mLatitudeCenterOfEllipse)
+        when(mLocation.getLongitude()).thenReturn(mLongitudeCenterOfEllipse);
+        when(mLocation.getLatitude()).thenReturn(mLatitudeCenterOfEllipse);
+
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        double movingLongitudePoint =
+                mLongitudeCenterOfEllipse + SEMI_MAJOR_AXIS_DEGREES - .0004;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        movingLongitudePoint =
+                mLongitudeCenterOfEllipse + SEMI_MAJOR_AXIS_DEGREES;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+
+        movingLongitudePoint =
+                mLongitudeCenterOfEllipse - SEMI_MAJOR_AXIS_DEGREES;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+
+        movingLongitudePoint =
+                mLongitudeCenterOfEllipse + SEMI_MAJOR_AXIS_DEGREES + 2;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+
+        movingLongitudePoint =
+                mLongitudeCenterOfEllipse + SEMI_MAJOR_AXIS_DEGREES + 10;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+    }
+
+    /**
+     * Verify that moving the latitude center coordinate gradually out causes the correct
+     * return value in {@link AfcEllipseLocation#checkLocation(Location)}.
+     */
+    @Test
+    public void adjustLatitudeValue() {
+        // Center point of ellipse is at point (mLongitudeCenterOfEllipse, mLatitudeCenterOfEllipse)
+        when(mLocation.getLongitude()).thenReturn(mLongitudeCenterOfEllipse);
+        when(mLocation.getLatitude()).thenReturn(mLatitudeCenterOfEllipse);
+
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        double movingLatitudePoint =
+                mLatitudeCenterOfEllipse + SEMI_MINOR_AXIS_DEGREES - .0004;
+        when(mLocation.getLatitude()).thenReturn(movingLatitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        movingLatitudePoint =
+                mLatitudeCenterOfEllipse + SEMI_MINOR_AXIS_DEGREES;
+        when(mLocation.getLatitude()).thenReturn(movingLatitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+
+        movingLatitudePoint =
+                mLatitudeCenterOfEllipse - SEMI_MINOR_AXIS_DEGREES;
+        when(mLocation.getLatitude()).thenReturn(movingLatitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+
+        movingLatitudePoint =
+                mLatitudeCenterOfEllipse + SEMI_MINOR_AXIS_DEGREES + 2;
+        when(mLocation.getLatitude()).thenReturn(movingLatitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+
+        movingLatitudePoint =
+                mLatitudeCenterOfEllipse + SEMI_MINOR_AXIS_DEGREES + 10;
+        when(mLocation.getLatitude()).thenReturn(movingLatitudePoint);
+        assertThat(mAfcEllipseLocation.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+    }
+
+    /**
+     * Verify that an ellipse that goes across the maximum longitude accurately checks the bounds
+     * of locations.
+     */
+    @Test
+    public void testEllipseOverMaximumLongitude() {
+        when(mLocation.getLatitude()).thenReturn(mLatitudeCenterOfEllipse);
+        when(mLocation.getLongitude()).thenReturn(AfcEllipseLocation.MAX_LONGITUDE - 0.001);
+        AfcEllipseLocation afcEllipseOverPrimeMeridianEast = new AfcEllipseLocation(
+                AfcEllipseLocation.DEFAULT_SEMI_MINOR_AXIS_METERS, AfcEllipseLocation
+                .DEFAULT_SEMI_MAJOR_AXIS_METERS, AfcEllipseLocation.DEFAULT_ORIENTATION,
+                AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES, mRandom, mLocation);
+
+        double movingLongitudePoint = AfcEllipseLocation.MIN_LONGITUDE + 0.001;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(afcEllipseOverPrimeMeridianEast.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        movingLongitudePoint = AfcEllipseLocation.MIN_LONGITUDE + 4;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(afcEllipseOverPrimeMeridianEast.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+    }
+
+    /**
+     * Verify that an ellipse that goes across the minimum longitude accurately checks the bounds
+     * of locations.
+     */
+    @Test
+    public void testEllipseOverMinimumLongitude() {
+        when(mLocation.getLongitude()).thenReturn(AfcEllipseLocation.MIN_LONGITUDE + 0.001);
+        when(mLocation.getLatitude()).thenReturn(mLatitudeCenterOfEllipse);
+        AfcEllipseLocation afcEllipseOverPrimeMeridianWest = new AfcEllipseLocation(
+                AfcEllipseLocation.DEFAULT_SEMI_MINOR_AXIS_METERS, AfcEllipseLocation
+                .DEFAULT_SEMI_MAJOR_AXIS_METERS, AfcEllipseLocation.DEFAULT_ORIENTATION,
+                AfcEllipseLocation.DEFAULT_CENTER_LEEWAY_DEGREES, mRandom, mLocation);
+
+        double movingLongitudePoint = AfcEllipseLocation.MAX_LONGITUDE - 0.001;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(afcEllipseOverPrimeMeridianWest.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        movingLongitudePoint = AfcEllipseLocation.MAX_LONGITUDE - 5;
+        when(mLocation.getLongitude()).thenReturn(movingLongitudePoint);
+        assertThat(afcEllipseOverPrimeMeridianWest.checkLocation(mLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/AfcLocationUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/AfcLocationUtilTest.java
new file mode 100644
index 0000000..843e41b
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/AfcLocationUtilTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.location.Location;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Unit tests for {@link AfcLocationUtil}.
+ */
+@SmallTest
+
+public class AfcLocationUtilTest {
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+    @Mock
+    private AfcLocation mAfcLocation;
+    @Mock
+    private Location mComparingLocation;
+    private AfcLocationUtil mAfcLocationUtil;
+
+    @Before
+    public void setUp() {
+        mAfcLocationUtil = new AfcLocationUtil();
+    }
+
+    /**
+     * Verify that the checkLocation result for an AfcLocation object matches the checkLocation
+     * result in AfcLocationUtil.
+     */
+    @Test
+    public void checkLocationForAfcObject() {
+        when(mComparingLocation.getLongitude()).thenReturn(9.9);
+        when(mComparingLocation.getLatitude()).thenReturn(9.9);
+
+        when(mAfcLocation.checkLocation(mComparingLocation)).thenReturn(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+        assertThat(mAfcLocationUtil.checkLocation(mAfcLocation, mComparingLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+
+        when(mAfcLocation.checkLocation(mComparingLocation)).thenReturn(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+        assertThat(mAfcLocationUtil.checkLocation(mAfcLocation, mComparingLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+
+        when(mAfcLocation.checkLocation(mComparingLocation)).thenReturn(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+        assertThat(mAfcLocationUtil.checkLocation(mAfcLocation, mComparingLocation)).isEqualTo(
+                AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/AfcManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/AfcManagerTest.java
new file mode 100644
index 0000000..eea6326
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/AfcManagerTest.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.wifi.WifiContext;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.modules.utils.HandlerExecutor;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.AfcManager}.
+ */
+@SmallTest
+public class AfcManagerTest extends WifiBaseTest {
+    @Captor ArgumentCaptor<AfcClient.Callback> mAfcClientCallbackCaptor;
+    @Mock Clock mClock;
+    @Mock WifiContext mContext;
+    @Mock AfcLocation mAfcLocation;
+    @Mock LocationManager mLocationManager;
+    @Mock Location mLocation;
+    @Mock Looper mLooper;
+    @Mock HandlerThread mWifiHandlerThread;
+    @Mock WifiInjector mWifiInjector;
+    @Mock WifiNative mWifiNative;
+    @Mock WifiGlobals mWifiGlobals;
+    @Mock AfcLocationUtil mAfcLocationUtil;
+    @Mock AfcClient mAfcClient;
+    private AfcManager mAfcManager;
+    private static final String EXPIRATION_DATE = "2020-11-03T13:34:05Z";
+    private static String sAfcServerUrl1;
+    private static Map<String, String> sAfcRequestProperties1;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mAfcClientCallbackCaptor = ArgumentCaptor.forClass(AfcClient.Callback.class);
+
+        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
+        when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread);
+        when(mWifiInjector.getClock()).thenReturn(mClock);
+        when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
+        when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals);
+        when(mWifiInjector.getAfcLocationUtil()).thenReturn(mAfcLocationUtil);
+        when(mWifiInjector.getAfcClient()).thenReturn(mAfcClient);
+        when(mWifiHandlerThread.getLooper()).thenReturn(mLooper);
+        when(mWifiGlobals.isAfcSupportedOnDevice()).thenReturn(true);
+        when(mWifiGlobals.getAfcServerUrlsForCountry(anyString())).thenReturn(Arrays.asList(
+                sAfcServerUrl1));
+
+        when(mLocationManager.getProviders(anyBoolean())).thenReturn(List.of(
+                LocationManager.FUSED_PROVIDER, LocationManager.PASSIVE_PROVIDER,
+                LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER));
+        when(mAfcLocationUtil.createAfcLocation(mLocation)).thenReturn(mAfcLocation);
+
+        sAfcServerUrl1 = "https://example.com/";
+        sAfcRequestProperties1 = new HashMap<>();
+    }
+
+    private AfcManager makeAfcManager() {
+        return new AfcManager(mContext, mWifiInjector);
+    }
+
+    /**
+     * Returns a valid mock AfcServerResponse from the AFC sever.
+     */
+    private AfcServerResponse buildSuccessfulSpectrumInquiryResponse() throws JSONException {
+        JSONObject inquiryResponse = new JSONObject();
+        inquiryResponse.put("requestId", String.valueOf(0));
+
+        // This response's expire time in milliseconds is 1687463240000L as tested by
+        // AfcClientTest#testExpireTimeSetCorrectly
+        inquiryResponse.put("availabilityExpireTime", AfcManagerTest.EXPIRATION_DATE);
+
+        JSONObject responseResultObject = new JSONObject();
+        responseResultObject.put("responseCode", 0);
+        responseResultObject.put("shortDescription", "Success");
+        inquiryResponse.put("response", responseResultObject);
+
+        return AfcServerResponse.fromSpectrumInquiryResponse(200, inquiryResponse);
+    }
+
+    /**
+     * Verify that a crash does not occur if the AFC Manager is provided with a null location on
+     * a call to {@link AfcManager#onLocationChange}.
+     */
+    @Test
+    public void testNoCrashForNullLocation() {
+        mAfcManager = makeAfcManager();
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(9999L);
+
+        mAfcManager.onLocationChange(null, false);
+        assertThat(mAfcManager.getLastKnownLocation()).isEqualTo(null);
+    }
+
+    /**
+     * Verify that a when a location change occurs, we query the Afc server for the first time
+     * and update the lastKnownLocation variable.
+     */
+    @Test
+    public void testUpdateLastKnownLocationOnLocationChange() {
+        mAfcManager = makeAfcManager();
+        mAfcManager.setIsAfcSupportedInCurrentCountry(true);
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(9999L);
+
+        mAfcManager.onLocationChange(mLocation, false);
+
+        verify(mAfcClient).queryAfcServer(any(AfcLocation.class), any(Handler.class), any(AfcClient
+                .Callback.class));
+        assertThat(mAfcManager.getLastKnownLocation()).isEqualTo(mLocation);
+        assertThat(mAfcManager.getLocationManager()).isEqualTo(mLocationManager);
+    }
+
+    /**
+     * Verify that we fetch a new location and re-query the server when the country code changes.
+     */
+    @Test
+    public void testGetLocationAfterCountryCodeChange() {
+        mAfcManager = makeAfcManager();
+        long now = 9999L;
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(now);
+        mAfcManager.onCountryCodeChange("US");
+
+        ArgumentCaptor<Consumer<Location>> consumerArgumentCaptor =
+                ArgumentCaptor.forClass(Consumer.class);
+
+        assertEquals(sAfcServerUrl1, (mAfcManager).getAfcServerUrl());
+        // check that we fetch a new location
+        verify(mLocationManager).getCurrentLocation(anyString(), any(),
+                any(HandlerExecutor.class), consumerArgumentCaptor.capture());
+
+        // run the consumer's callback, then verify that we updated the lastKnownLocation
+        Consumer<Location> locationConsumer = consumerArgumentCaptor.getValue();
+        locationConsumer.accept(mLocation);
+        assertThat(mAfcManager.getLastKnownLocation()).isEqualTo(mLocation);
+
+        // check that the AfcClient makes a query
+        verify(mAfcClient).queryAfcServer(any(AfcLocation.class), any(Handler.class), any(AfcClient
+                .Callback.class));
+
+        // check that when a query is sent to the server, we are caching information
+        // about the request
+        assertThat(mAfcManager.getLastAfcServerQueryTime()).isEqualTo(now);
+        verify(mAfcLocationUtil).createAfcLocation(mLocation);
+
+        // verify we are setting lastKnownLocation to null when a null location is passed in
+        locationConsumer.accept(null);
+        assertThat(mAfcManager.getLastKnownLocation()).isEqualTo(null);
+    }
+
+    /**
+     * Verify that the AFC Manager is created correctly and loads the clock and handler thread from
+     * the {@link WifiInjector}
+     */
+    @Test
+    public void testAfcManagerCorrectlyCreated() {
+        mAfcManager = makeAfcManager();
+        verify(mWifiInjector).getClock();
+        verify(mWifiInjector).getWifiHandlerThread();
+        verify(mWifiInjector).getWifiGlobals();
+    }
+
+    /**
+     * Verify that when AFC is disabled, we do not query the server after a country code change.
+     */
+    @Test
+    public void testNoQueryWhenAfcNotEnabled() {
+        mAfcManager = makeAfcManager();
+        when(mWifiGlobals.isAfcSupportedOnDevice()).thenReturn(false);
+        mAfcManager.onCountryCodeChange("US");
+
+        verifyNoMoreInteractions(mLocationManager);
+    }
+
+    /**
+     * Verify that the network provider is used to get the current location if the fused provider
+     * is not available (it is only available from API level 31 onwards).
+     */
+    @Test
+    public void testCorrectProviderUsedForGetLocation() {
+        mAfcManager = makeAfcManager();
+        // list doesn't include the fused provider
+        when(mLocationManager.getProviders(anyBoolean())).thenReturn(List.of(
+                LocationManager.PASSIVE_PROVIDER, LocationManager.NETWORK_PROVIDER,
+                LocationManager.GPS_PROVIDER));
+
+        mAfcManager.onCountryCodeChange("US");
+        ArgumentCaptor<String> providerArgumentCaptor = ArgumentCaptor.forClass(String.class);
+        verify(mLocationManager).getCurrentLocation(providerArgumentCaptor.capture(), any(),
+                any(HandlerExecutor.class), any(Consumer.class));
+
+        // capture the provider, and check that it is the expected provider
+        String provider = providerArgumentCaptor.getValue();
+        assertThat(provider).isEqualTo(LocationManager.NETWORK_PROVIDER);
+    }
+
+    /**
+     * Verify that when AFC is not available in a country, the location listener is no longer
+     * informed of location updates.
+     */
+    @Test
+    public void testStopListeningForLocationUpdates() {
+        mAfcManager = makeAfcManager();
+        ArgumentCaptor<LocationListener> locationListenerCaptor =
+                ArgumentCaptor.forClass(LocationListener.class);
+        mAfcManager.onCountryCodeChange("US");
+
+        assertEquals(sAfcServerUrl1, (mAfcManager).getAfcServerUrl());
+        // check that we listen for location updates
+        verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(),
+                anyFloat(), locationListenerCaptor.capture(), any(Looper.class));
+        LocationListener locationListener = locationListenerCaptor.getValue();
+
+        // Mock another country code change, this time with AFC not available, and check that the
+        // location listener is removed from location updates.
+        when(mWifiGlobals.getAfcServerUrlsForCountry(anyString())).thenReturn(null);
+        mAfcManager.onCountryCodeChange("00");
+
+        assertEquals(null, (mAfcManager).getAfcServerUrl());
+        verify(mLocationManager).removeUpdates(locationListener);
+    }
+
+    /**
+     * Test that no query is executed if the location passed into AfcManager#onLocationChange is
+     * null.
+     */
+    @Test
+    public void testNullLocationChange() {
+        mAfcManager = makeAfcManager();
+        mAfcManager.onLocationChange(null, false);
+        verify(mAfcClient, never()).queryAfcServer(any(AfcLocation.class), any(Handler.class),
+                any(AfcClient.Callback.class));
+    }
+
+    /**
+     * Verify that the AFC Manager triggers a query of the AFC server upon a location change if the
+     * availability expiration time of the last server response has expired.
+     */
+    @Test
+    public void testQueryAfterExpiredTime() throws JSONException {
+        mAfcManager = makeAfcManager();
+        mAfcManager.setIsAfcSupportedInCurrentCountry(true);
+
+        mAfcManager.onLocationChange(mLocation, false);
+
+        verify(mAfcClient).queryAfcServer(any(AfcLocation.class), any(Handler.class),
+                mAfcClientCallbackCaptor.capture());
+        AfcClient.Callback afcClientCallback = mAfcClientCallbackCaptor.getValue();
+
+        // Set the response to the test AFC server response which has an expiration time that is
+        // expired.
+        afcClientCallback.onResult(buildSuccessfulSpectrumInquiryResponse(), mAfcLocation);
+
+        // Ensure that the current time is before the expiration date
+        when(mClock.getWallClockMillis()).thenReturn(AfcServerResponse
+                .convertExpireTimeStringToTimestamp(EXPIRATION_DATE) - 10);
+        mAfcManager.onLocationChange(mLocation, false);
+        // Ensure that a query is not executed because the expiration time has not passed
+        verify(mAfcClient, times(1)).queryAfcServer(any(AfcLocation.class),
+                any(Handler.class), any(AfcClient.Callback.class));
+
+        // Ensure that the current time is past the expiration date
+        when(mClock.getWallClockMillis()).thenReturn(AfcServerResponse
+                .convertExpireTimeStringToTimestamp(EXPIRATION_DATE) + 10);
+        mAfcManager.onLocationChange(mLocation, false);
+        // Ensure that a query is executed because the expiration time has passed
+        verify(mAfcClient, times(2)).queryAfcServer(any(AfcLocation.class),
+                any(Handler.class), any(AfcClient.Callback.class));
+    }
+
+    /**
+     * Verify that the AFC Manager triggers a query of the AFC server if the location
+     * change moves outside the bounds of the AfcLocation from the last successful AFC server query.
+     */
+    @Test
+    public void testQueryAfterLocationChange() throws JSONException {
+        mAfcManager = makeAfcManager();
+        mAfcManager.setIsAfcSupportedInCurrentCountry(true);
+        mAfcManager.onLocationChange(mLocation, false);
+
+        verify(mAfcClient).queryAfcServer(any(AfcLocation.class), any(Handler.class),
+                mAfcClientCallbackCaptor.capture());
+        AfcClient.Callback afcClientCallback = mAfcClientCallbackCaptor.getValue();
+
+        afcClientCallback.onResult(buildSuccessfulSpectrumInquiryResponse(), mAfcLocation);
+
+        // Ensure that the current time is before the expiration date
+        when(mClock.getWallClockMillis()).thenReturn(AfcServerResponse
+                .convertExpireTimeStringToTimestamp(EXPIRATION_DATE) - 10);
+
+        // Ensure that a query is not executed because the location is inside the AfcLocation
+        when(mAfcLocationUtil.checkLocation(any(AfcLocation.class), any(Location.class)))
+                .thenReturn(AfcLocationUtil.InBoundsCheckResult.INSIDE_AFC_LOCATION);
+        mAfcManager.onLocationChange(mLocation, false);
+        verify(mAfcLocationUtil).checkLocation(any(AfcLocation.class), any(Location.class));
+        verify(mAfcClient, times(1)).queryAfcServer(any(AfcLocation.class),
+                any(Handler.class), any(AfcClient.Callback.class));
+
+        // Ensure that a query is not executed because the location is on the border
+        when(mAfcLocationUtil.checkLocation(any(AfcLocation.class), any(Location.class)))
+                .thenReturn(AfcLocationUtil.InBoundsCheckResult.ON_BORDER);
+        mAfcManager.onLocationChange(mLocation, false);
+        verify(mAfcLocationUtil, times(2)).checkLocation(any(AfcLocation
+                .class), any(Location.class));
+        verify(mAfcClient, times(1)).queryAfcServer(any(AfcLocation.class),
+                any(Handler.class), any(AfcClient.Callback.class));
+
+        // Ensure that a query is executed because the location is outside the AfcLocation
+        when(mAfcLocationUtil.checkLocation(any(AfcLocation.class), any(Location.class)))
+                .thenReturn(AfcLocationUtil.InBoundsCheckResult.OUTSIDE_AFC_LOCATION);
+        mAfcManager.onLocationChange(mLocation, false);
+        verify(mAfcLocationUtil, times(3)).checkLocation(any(AfcLocation
+                .class), any(Location.class));
+        verify(mAfcClient, times(2)).queryAfcServer(any(AfcLocation.class),
+                any(Handler.class), any(AfcClient.Callback.class));
+    }
+
+    /*
+     * Verify that when a shell command triggers a location update, that a server query is made
+     * regardless of whether AFC is currently supported.
+     */
+    @Test
+    public void testQueryMadeFromShellCommand() {
+        mAfcManager = makeAfcManager();
+        mAfcManager.setServerUrlAndRequestPropertyPairs(sAfcServerUrl1, sAfcRequestProperties1);
+        when(mWifiGlobals.isAfcSupportedOnDevice()).thenReturn(false);
+        mAfcManager.onLocationChange(mLocation, true);
+        verify(mAfcClient).queryAfcServer(any(AfcLocation.class), any(Handler.class),
+                any(AfcClient.Callback.class));
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/AfcServerResponseTest.java b/service/tests/wifitests/src/com/android/server/wifi/AfcServerResponseTest.java
new file mode 100644
index 0000000..9978674
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/AfcServerResponseTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.AfcServerResponse}.
+ */
+public class AfcServerResponseTest {
+    static final int SUCCESS_RESPONSE_CODE = 0;
+    static final int GENERAL_FAILURE_RESPONSE_CODE = -1;
+    static final int PROTOCOL_ERROR_RESPONSE_CODE = 100;
+    static final int MESSAGE_EXCHANGE_ERROR_RESPONSE_CODE = 300;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Returns a mock response from the AFC sever with valid available AFC frequency and channel
+     * information.
+     */
+    private JSONObject buildSuccessfulSpectrumInquiryResponse() {
+        JSONObject availableSpectrumInquiryResponse = new JSONObject();
+        try {
+            availableSpectrumInquiryResponse.put("availableFrequencyInfo",
+                    buildAvailableFrequencyInfoArray());
+            availableSpectrumInquiryResponse.put("availableChannelInfo",
+                    buildAvailableChannelInfoArray());
+            availableSpectrumInquiryResponse.put("availabilityExpireTime",
+                    "2022-06-18T19:17:52.593206170Z");
+            availableSpectrumInquiryResponse.put("response", new JSONObject());
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "shortDescription", "Example description");
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "responseCode", SUCCESS_RESPONSE_CODE);
+        } catch (JSONException ignore) {
+            // should never occur as all JSON objects are mocks
+        }
+
+        return availableSpectrumInquiryResponse;
+    }
+
+    /**
+     * Creates a JSON array of allowed AFC frequencies.
+     */
+    public JSONArray buildAvailableFrequencyInfoArray() {
+        try {
+            JSONArray availableFrequencyInfo = new JSONArray();
+            JSONObject frequencyJSON1 = new JSONObject();
+            JSONObject frequencyJSON2 = new JSONObject();
+            JSONObject frequencyJSON3 = new JSONObject();
+            JSONObject frequencyRangeJSON1 = new JSONObject();
+            JSONObject frequencyRangeJSON2 = new JSONObject();
+            JSONObject frequencyRangeJSON3 = new JSONObject();
+            availableFrequencyInfo.put(frequencyJSON1);
+            availableFrequencyInfo.put(frequencyJSON2);
+            availableFrequencyInfo.put(frequencyJSON3);
+
+            frequencyJSON1.put("frequencyRange", frequencyRangeJSON1);
+            frequencyJSON2.put("frequencyRange", frequencyRangeJSON2);
+            frequencyJSON3.put("frequencyRange", frequencyRangeJSON3);
+            frequencyRangeJSON1.put("lowFrequency", 6109);
+            frequencyRangeJSON1.put("highFrequency", 6111);
+            frequencyJSON1.put("maxPsd", 17);
+            frequencyRangeJSON2.put("lowFrequency", 5925);
+            frequencyRangeJSON2.put("highFrequency", 5930);
+            frequencyJSON2.put("maxPsd", 23);
+            frequencyRangeJSON3.put("lowFrequency", 6177);
+            frequencyRangeJSON3.put("highFrequency", 6182);
+            frequencyJSON3.put("maxPsd", 23);
+
+            return availableFrequencyInfo;
+        } catch (JSONException ignore) { /* should never occur */ }
+
+        return null;
+    }
+
+    /**
+     * Creates a JSON array of allowed AFC channels.
+     */
+    private JSONArray buildAvailableChannelInfoArray() {
+        int[] channelCfis1 = {7, 23, 39, 55, 71, 87};
+        int[] channelCfis2 = {15, 47, 79};
+        int[] maxEirps1 = {31, 31, 34, 34, 31, 34};
+        int[] maxEirps2 = {34, 36, 34};
+        int globalOperatingClass1 = 133;
+        int globalOperatingClass2 = 134;
+
+        try {
+            JSONArray availableChannelInfo = new JSONArray();
+            JSONObject availableChannel1 = new JSONObject();
+            JSONObject availableChannel2 = new JSONObject();
+            availableChannelInfo.put(availableChannel1);
+            availableChannelInfo.put(availableChannel2);
+
+            JSONArray channelCfisJSON1 = new JSONArray();
+            JSONArray maxEirpsJSON1 = new JSONArray();
+            JSONArray channelCfisJSON2 = new JSONArray();
+            JSONArray maxEirpsJSON2 = new JSONArray();
+            availableChannel1.put("globalOperatingClass", globalOperatingClass1);
+            availableChannel2.put("globalOperatingClass", globalOperatingClass2);
+            availableChannel1.put("channelCfi", channelCfisJSON1);
+            availableChannel2.put("channelCfi", channelCfisJSON2);
+            availableChannel1.put("maxEirp", maxEirpsJSON1);
+            availableChannel2.put("maxEirp", maxEirpsJSON2);
+
+            for (int i = 0; i < channelCfis1.length; ++i) {
+                channelCfisJSON1.put(channelCfis1[i]);
+                maxEirpsJSON1.put(maxEirps1[i]);
+            }
+
+            for (int i = 0; i < channelCfis2.length; ++i) {
+                channelCfisJSON2.put(channelCfis2[i]);
+                maxEirpsJSON2.put(maxEirps2[i]);
+            }
+
+            return availableChannelInfo;
+        } catch (JSONException ignore) { /* should never occur */ }
+
+        return null;
+    }
+
+    /**
+     * Builds a successful inquiry response JSON object, but the available frequency info is an
+     * empty array.
+     */
+    JSONObject buildSuccessfulSpectrumInquiryResponseWithEmptyFrequencyInfo() {
+        JSONObject availableSpectrumInquiryResponse = new JSONObject();
+        try {
+            availableSpectrumInquiryResponse.put("availableFrequencyInfo", new JSONArray());
+            availableSpectrumInquiryResponse.put("availableChannelInfo",
+                    buildAvailableChannelInfoArray());
+            availableSpectrumInquiryResponse.put("availabilityExpireTime",
+                    "2022-06-18T19:17:52.593206170Z");
+            availableSpectrumInquiryResponse.put("response", new JSONObject());
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "shortDescription", "Example description");
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "responseCode", SUCCESS_RESPONSE_CODE);
+        } catch (JSONException ignore) {
+            // should never occur as all JSON objects are mocks
+        }
+
+        return availableSpectrumInquiryResponse;
+    }
+
+    /**
+     * Builds a successful inquiry response JSON object, but the available channel info is an
+     * empty array.
+     */
+    JSONObject buildSuccessfulSpectrumInquiryResponseWithEmptyChannelInfo() {
+        JSONObject availableSpectrumInquiryResponse = new JSONObject();
+        try {
+            availableSpectrumInquiryResponse.put("availableFrequencyInfo",
+                    buildAvailableFrequencyInfoArray());
+            availableSpectrumInquiryResponse.put("availableChannelInfo", new JSONArray());
+            availableSpectrumInquiryResponse.put("availabilityExpireTime",
+                    "2022-06-18T19:17:52.593206170Z");
+            availableSpectrumInquiryResponse.put("response", new JSONObject());
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "shortDescription", "Example description");
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "responseCode", SUCCESS_RESPONSE_CODE);
+        } catch (JSONException ignore) {
+            // should never occur as all JSON objects are mocks
+        }
+
+        return availableSpectrumInquiryResponse;
+    }
+
+    /**
+     * Create a response JSON object with a specified AFC response code. This allows the response
+     * to indicate if it was successful, or for what reason it failed.
+     *
+     * @param afcResponseCode the code indicating the status of the response from the AFC server
+     * @return a spectrum inquiry response with the desired response code
+     */
+    JSONObject buildSpectrumInquiryResponseWithSpecifiedCode(int afcResponseCode) {
+        JSONObject availableSpectrumInquiryResponse = new JSONObject();
+
+        try {
+            availableSpectrumInquiryResponse.put("availableFrequencyInfo",
+                    new JSONArray());
+            availableSpectrumInquiryResponse.put("availableChannelInfo",
+                    buildAvailableChannelInfoArray());
+            availableSpectrumInquiryResponse.put("availabilityExpireTime",
+                    "2022-06-18T19:17:52.593206170Z");
+            availableSpectrumInquiryResponse.put("response", new JSONObject());
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "shortDescription", "Example description");
+            availableSpectrumInquiryResponse.getJSONObject("response").put(
+                    "responseCode", afcResponseCode);
+        } catch (JSONException ignore) {
+            // should never occur as all JSON objects are mocks
+        }
+
+        return availableSpectrumInquiryResponse;
+    }
+
+    /**
+     * Verify that the correct values are set when the response is successful.
+     */
+    @Test
+    public void testSuccessfulResponse() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSuccessfulSpectrumInquiryResponse());
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(SUCCESS_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+        assertEquals(3, serverResponse.getAfcChannelAllowance().availableAfcFrequencyInfos.size());
+        assertEquals(9, serverResponse.getAfcChannelAllowance().availableAfcChannelInfos.size());
+    }
+
+    /**
+     * Verify that the availability expiration time is parsed and converted to a timestamp
+     * correctly.
+     */
+    @Test
+    public void testExpireTimeSetCorrectly() {
+        int httpResponseCode = 200;
+        String expireTimeString = "2023-06-22T19:47:20Z";
+        long expireTimestampMs = 1687463240000L;
+
+        try {
+            JSONObject availableSpectrumInquiryResponse = buildSuccessfulSpectrumInquiryResponse();
+            availableSpectrumInquiryResponse.put("availabilityExpireTime", expireTimeString);
+            AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                    httpResponseCode, availableSpectrumInquiryResponse);
+
+            assertEquals(expireTimestampMs,
+                    serverResponse.getAfcChannelAllowance().availabilityExpireTimeMs);
+        } catch (JSONException ignore) {
+            // should never occur
+        }
+    }
+
+    /**
+     * Verify that when the response is successful and contains an empty frequency info array,
+     * the AfcChannelAllowance allowed frequency list is empty.
+     */
+    @Test
+    public void testResponseWithEmptyFrequencyArray() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSuccessfulSpectrumInquiryResponseWithEmptyFrequencyInfo());
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(SUCCESS_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+
+        // the allowed frequency array should be empty
+        assertEquals(0, serverResponse.getAfcChannelAllowance().availableAfcFrequencyInfos.size());
+    }
+
+    /**
+     * Verify that when the response is successful and contains an empty channel info array,
+     * the AfcChannelAllowance allowed channel list is empty.
+     */
+    @Test
+    public void testResponseWithEmptyChannelArray() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSuccessfulSpectrumInquiryResponseWithEmptyChannelInfo());
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(SUCCESS_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+
+        // the allowed frequency array should be empty
+        assertEquals(0, serverResponse.getAfcChannelAllowance().availableAfcChannelInfos.size());
+    }
+
+    /**
+     * Verify that the case of a response of type GENERAL_FAILURE is handled correctly.
+     */
+    @Test
+    public void testGeneralFailureResponse() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSpectrumInquiryResponseWithSpecifiedCode(
+                        GENERAL_FAILURE_RESPONSE_CODE));
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(GENERAL_FAILURE_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+    }
+
+    /**
+     * Verify that the case of a response of type PROTOCOL_ERROR is handled correctly.
+     */
+    @Test
+    public void testProtocolErrorResponse() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSpectrumInquiryResponseWithSpecifiedCode(
+                        PROTOCOL_ERROR_RESPONSE_CODE));
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(PROTOCOL_ERROR_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+    }
+
+    /**
+     * Verify that the case of a response of type MESSAGE_EXCHANGE_ERROR is handled correctly.
+     */
+    @Test
+    public void testMessageExchangeErrorResponse() {
+        int httpResponseCode = 200;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, buildSpectrumInquiryResponseWithSpecifiedCode(
+                        MESSAGE_EXCHANGE_ERROR_RESPONSE_CODE));
+
+        assertEquals(httpResponseCode, serverResponse.getHttpResponseCode());
+        assertEquals(MESSAGE_EXCHANGE_ERROR_RESPONSE_CODE, serverResponse.getAfcResponseCode());
+    }
+
+    /**
+     * Verify that no crash occurs if a null spectrum inquiry response object is passed to the AFC
+     * server response.
+     */
+    @Test
+    public void testNoCrashOnNullInquiryResponse() {
+        int httpResponseCode = 500;
+        AfcServerResponse serverResponse = AfcServerResponse.fromSpectrumInquiryResponse(
+                httpResponseCode, null);
+        assertEquals(null, serverResponse);
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java b/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java
index 77b69f8..fd3edcb 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/CandidateScorerTest.java
@@ -293,6 +293,31 @@
     }
 
     /**
+     * With everything else the same, the current network and another candidate both without
+     * internet should get the same score.
+     */
+    @Test
+    public void testNoInternetNetworksEvaluateTheSame() throws Exception {
+        if (mExpectedExpId != ThroughputScorer.THROUGHPUT_SCORER_DEFAULT_EXPID) return;
+        double score1 = evaluate(mCandidate1.setScanRssi(-57)
+                        .setCurrentNetwork(true)
+                        .setPredictedThroughputMbps(560)
+                        .setNoInternetAccess(true)
+                        .setNoInternetAccessExpected(false));
+        double score2 = evaluate(mCandidate2.setScanRssi(-57)
+                        .setPredictedThroughputMbps(560)
+                        .setNoInternetAccess(true)
+                        .setNoInternetAccessExpected(false));
+
+        // Both networks no internet and have no reboot since last use. Expect same same.
+        assertEquals(score1, score2, TOL);
+
+        // score candidate 2 but after a reboot. It should have higher score.
+        double score3 = evaluate(mCandidate2.setNumRebootsSinceLastUse(1));
+        assertThat(score3, greaterThan(score1));
+    }
+
+    /**
      * Prefer to switch with a larger rssi difference.
      */
     @Test
@@ -488,19 +513,19 @@
     @Test
     public void testPreferCurrentNetworkWithInternetOverNetworkWithNoInternet() throws Exception {
         if (mExpectedExpId == ThroughputScorer.THROUGHPUT_SCORER_DEFAULT_EXPID) {
-            // First verify that when evaluated separately, mCandidate2 has a higher score due
-            // to it having better RSSI and throughput.
+            // mCandidate2 should have lower score due to not having internet
             mCandidate1.setScanRssi(-77)
                     .setPredictedThroughputMbps(30)
                     .setCurrentNetwork(true)
-                    .setNoInternetAccess(false);
+                    .setNoInternetAccess(false)
+                    .setNoInternetAccessExpected(false);
             mCandidate2.setScanRssi(-40)
                     .setPredictedThroughputMbps(100)
                     .setCurrentNetwork(false)
                     .setNoInternetAccess(true)
                     .setNoInternetAccessExpected(false);
             double score1 = evaluate(mCandidate1);
-            assertThat(evaluate(mCandidate2), greaterThan(score1));
+            assertThat(evaluate(mCandidate2), lessThan(score1));
 
             // Then verify that when evaluated together, mCandidate1 wins because it is the current
             // network and has internet
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 bc34e23..08f2aa0 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_SCREEN_ON;
 import static android.net.NetworkInfo.DetailedState.AUTHENTICATING;
 import static android.net.NetworkInfo.DetailedState.CONNECTED;
 import static android.net.NetworkInfo.DetailedState.CONNECTING;
@@ -31,6 +29,7 @@
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY;
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_UNWANTED_LOW_RSSI;
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_SUCCESS;
 
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
@@ -39,6 +38,7 @@
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT;
 import static com.android.server.wifi.ClientModeImpl.ARP_TABLE_PATH;
 import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION;
+import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION_COMPLETE;
 import static com.android.server.wifi.ClientModeImpl.CMD_UNWANTED_NETWORK;
 import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE;
 import static com.android.server.wifi.WifiSettingsConfigStore.SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS;
@@ -62,6 +62,7 @@
 import static org.mockito.Mockito.anyObject;
 import static org.mockito.Mockito.anyString;
 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;
@@ -94,6 +95,7 @@
 import android.net.CaptivePortalData;
 import android.net.DhcpResultsParcelable;
 import android.net.InetAddresses;
+import android.net.IpConfiguration;
 import android.net.IpPrefix;
 import android.net.Layer2InformationParcelable;
 import android.net.Layer2PacketParcelable;
@@ -131,6 +133,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkAgentSpecifier;
 import android.net.wifi.WifiNetworkSpecifier;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
@@ -173,6 +176,7 @@
 import com.android.server.wifi.hotspot2.PasspointManager;
 import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
 import com.android.server.wifi.hotspot2.WnmData;
+import com.android.server.wifi.p2p.WifiP2pServiceImpl;
 import com.android.server.wifi.proto.nano.WifiMetricsProto;
 import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent;
 import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent;
@@ -280,7 +284,6 @@
     private static final MacAddress TEST_MLO_LINK_ADDR =
             MacAddress.fromString(TEST_MLO_LINK_ADDR_STR);
 
-
     private static final String TEST_MLO_LINK_ADDR_1_STR = "02:03:04:05:06:0B";
     private static final MacAddress TEST_MLO_LINK_ADDR_1 =
             MacAddress.fromString(TEST_MLO_LINK_ADDR_1_STR);
@@ -288,6 +291,9 @@
     private static final int TEST_MLO_LINK_ID = 1;
     private static final int TEST_MLO_LINK_ID_1 = 2;
 
+    private static final int TEST_MLO_LINK_FREQ = 5160;
+    private static final int TEST_MLO_LINK_FREQ_1 = 2437;
+
     private static final String TEST_TDLS_PEER_ADDR_STR = "02:55:11:02:36:4C";
 
     private long mBinderToken;
@@ -487,6 +493,8 @@
     static final int TEST_IPV6_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 2;
     static final int TEST_IPV4_ONLY_NETWORK_MAX_DTIM_MULTIPLIER = 9;
     static final int TEST_DUAL_STACK_NETWORK_MAX_DTIM_MULTIPLIER = 2;
+    static final int TEST_CHANNEL = 6;
+    static final int TEST_CHANNEL_1 = 44;
 
     ClientModeImpl mCmi;
     HandlerThread mWifiCoreThread;
@@ -570,12 +578,16 @@
     @Mock WifiCarrierInfoManager mWifiCarrierInfoManager;
     @Mock WifiPseudonymManager mWifiPseudonymManager;
     @Mock WifiNotificationManager mWifiNotificationManager;
+
+    @Mock WifiConnectivityHelper mWifiConnectivityHelper;
     @Mock InsecureEapNetworkHandler mInsecureEapNetworkHandler;
     @Mock ScanResult mScanResult;
     @Mock HandlerThread mWifiHandlerThread;
     @Mock SsidTranslator mSsidTranslator;
     @Mock ApplicationQosPolicyRequestHandler mApplicationQosPolicyRequestHandler;
     @Mock LocalLog mLocalLog;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
+    @Mock WifiCountryCode mWifiCountryCode;
 
     @Captor ArgumentCaptor<WifiConfigManager.OnNetworkUpdateListener> mConfigUpdateListenerCaptor;
     @Captor ArgumentCaptor<WifiNetworkAgent.Callback> mWifiNetworkAgentCallbackCaptor;
@@ -583,6 +595,11 @@
             mOffloadDisabledListenerArgumentCaptor = ArgumentCaptor.forClass(
                     WifiCarrierInfoManager.OnCarrierOffloadDisabledListener.class);
     @Captor ArgumentCaptor<BroadcastReceiver> mScreenStateBroadcastReceiverCaptor;
+
+    @Captor
+    ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback>
+            mStateChangeCallbackArgumentCaptor;
+
     @Captor ArgumentCaptor<ProvisioningConfigurationParcelable> mProvisioningConfigurationCaptor;
     WifiInfo mPrimaryWifiInfo;
 
@@ -655,6 +672,7 @@
         when(mMultiInternetManager.hasPendingConnectionRequests()).thenReturn(true);
         when(mApplicationQosPolicyRequestHandler.isFeatureEnabled()).thenReturn(true);
         when(mWifiInjector.getWifiHandlerLocalLog()).thenReturn(mLocalLog);
+        when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode);
         when(mWifiInjector.getApplicationQosPolicyRequestHandler())
                 .thenReturn(mApplicationQosPolicyRequestHandler);
 
@@ -703,6 +721,8 @@
         when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals);
         when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread);
         when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator);
+        when(mWifiInjector.getWifiDeviceStateChangeManager())
+                .thenReturn(mWifiDeviceStateChangeManager);
         when(mWifiHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
         when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true);
         when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true);
@@ -792,7 +812,8 @@
                 mSimRequiredNotifier, mWifiScoreReport, mWifiP2pConnection, mWifiGlobals,
                 WIFI_IFACE_NAME, mClientModeManager, mCmiMonitor,
                 mBroadcastQueue, mWifiNetworkSelector, mTelephonyManager, mWifiInjector,
-                mSettingsConfigStore, false, mWifiNotificationManager);
+                mSettingsConfigStore, false, mWifiNotificationManager,
+                mWifiConnectivityHelper);
         mCmi.mInsecureEapNetworkHandler = mInsecureEapNetworkHandler;
 
         mWifiCoreThread = getCmiHandlerThread(mCmi);
@@ -814,9 +835,8 @@
         assertEquals("DisconnectedState", getCurrentState().getName());
         validateConnectionInfo();
 
-        verify(mContext, atLeastOnce()).registerReceiver(
-                mScreenStateBroadcastReceiverCaptor.capture(),
-                argThat(f -> f.hasAction(ACTION_SCREEN_ON) && f.hasAction(ACTION_SCREEN_OFF)));
+        verify(mWifiDeviceStateChangeManager, atLeastOnce())
+                .registerStateChangeCallback(mStateChangeCallbackArgumentCaptor.capture());
     }
 
     @After
@@ -1260,6 +1280,8 @@
                     }
                 }
             });
+
+        assertFalse(mCmi.isIpProvisioningTimedOut());
         // Ensure the connection stats for the network is updated.
         verify(mWifiConfigManager).updateNetworkAfterConnect(eq(FRAMEWORK_NETWORK_ID),
                 anyBoolean(), anyBoolean(), anyInt());
@@ -1281,6 +1303,172 @@
         validateConnectionInfo();
     }
 
+    private void connectWithIpProvisionTimeout(boolean lateDhcpResponse) throws Exception {
+        mResources.setBoolean(R.bool.config_wifiRemainConnectedAfterIpProvisionTimeout, true);
+        assertNull(mCmi.getConnectingWifiConfiguration());
+        assertNull(mCmi.getConnectedWifiConfiguration());
+
+        triggerConnect();
+        validateConnectionInfo();
+
+        assertNotNull(mCmi.getConnectingWifiConfiguration());
+        assertNull(mCmi.getConnectedWifiConfiguration());
+
+        WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID);
+        config.carrierId = CARRIER_ID_1;
+        when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
+                .thenReturn(mScanDetailCache);
+
+        when(mScanDetailCache.getScanDetail(TEST_BSSID_STR)).thenReturn(
+                getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq));
+        when(mScanDetailCache.getScanResult(TEST_BSSID_STR)).thenReturn(
+                getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
+        ScanResult scanResult = new ScanResult(WifiSsid.fromUtf8Text(sFilsSsid),
+                sFilsSsid, TEST_BSSID_STR, 1245, 0, "", -78, 2412, 1025, 22, 33, 20, 0, 0, true);
+        ScanResult.InformationElement ie = createIE(ScanResult.InformationElement.EID_SSID,
+                sFilsSsid.getBytes(StandardCharsets.UTF_8));
+        scanResult.informationElements = new ScanResult.InformationElement[]{ie};
+        when(mScanRequestProxy.getScanResult(eq(TEST_BSSID_STR))).thenReturn(scanResult);
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq,
+                        SupplicantState.ASSOCIATED));
+        mLooper.dispatchAll();
+        validateConnectionInfo();
+
+        WifiSsid wifiSsid = WifiSsid.fromBytes(
+                NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
+        mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT,
+                new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null));
+        mLooper.dispatchAll();
+        // WifiInfo should be updated if it is primary
+        validateConnectionInfo();
+
+        verify(mWifiMetrics).noteFirstL2ConnectionAfterBoot(true);
+
+        // L2 connected, but not L3 connected yet. So, still "Connecting"...
+        assertNotNull(mCmi.getConnectingWifiConfiguration());
+        assertNull(mCmi.getConnectedWifiConfiguration());
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq,
+                        SupplicantState.COMPLETED));
+        mLooper.dispatchAll();
+        validateConnectionInfo();
+
+        assertEquals("L3ProvisioningState", getCurrentState().getName());
+        verifyNetworkStateChangedBroadcast(times(1),
+                new NetworkStateChangedIntentMatcher(CONNECTING));
+        verifyNetworkStateChangedBroadcast(times(1),
+                new NetworkStateChangedIntentMatcher(OBTAINING_IPADDR));
+        mLooper.dispatchAll();
+
+        mLooper.moveTimeForward(mCmi.WAIT_FOR_L3_PROVISIONING_TIMEOUT_MS);
+        mLooper.dispatchAll();
+        verify(mWifiConnectivityManager).handleConnectionStateChanged(mClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_CONNECTED);
+        assertTrue(mCmi.isIpProvisioningTimedOut());
+        verify(mWifiConfigManager).setIpProvisioningTimedOut(eq(FRAMEWORK_NETWORK_ID), eq(true));
+
+        if (!lateDhcpResponse) {
+            return;
+        }
+
+        // Simulate the scenario of a late response of the dhcp after the network is labeled a local
+        // only due to ip provisioning timeout.
+
+        DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable();
+        dhcpResults.baseConfiguration = new StaticIpConfiguration();
+        dhcpResults.baseConfiguration.gateway = InetAddresses.parseNumericAddress("1.2.3.4");
+        dhcpResults.baseConfiguration.ipAddress =
+                new LinkAddress(InetAddresses.parseNumericAddress("192.168.1.100"), 0);
+        dhcpResults.baseConfiguration.dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8"));
+        dhcpResults.leaseDuration = 3600;
+
+        injectDhcpSuccess(dhcpResults);
+        mLooper.dispatchAll();
+        assertNull(mCmi.getConnectingWifiConfiguration());
+        assertNotNull(mCmi.getConnectedWifiConfiguration());
+
+        // Verify WifiMetrics logging for metered metrics based on DHCP results
+        verify(mWifiMetrics).addMeteredStat(any(), anyBoolean());
+        WifiInfo wifiInfo = mWifiInfo;
+        assertNotNull(wifiInfo);
+        assertEquals(TEST_BSSID_STR, wifiInfo.getBSSID());
+        assertEquals(sFreq, wifiInfo.getFrequency());
+        assertEquals(TEST_WIFI_SSID, wifiInfo.getWifiSsid());
+        assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, wifiInfo.getMacAddress());
+        assertEquals(mConnectedNetwork.getDefaultSecurityParams().getSecurityType(),
+                mWifiInfo.getCurrentSecurityType());
+
+        if (wifiInfo.isPasspointAp()) {
+            assertEquals(wifiInfo.getPasspointProviderFriendlyName(),
+                    WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME);
+        } else {
+            assertNull(wifiInfo.getPasspointProviderFriendlyName());
+        }
+        assertEquals(Arrays.asList(scanResult.informationElements),
+                wifiInfo.getInformationElements());
+        assertNotNull(wifiInfo.getNetworkKey());
+        expectRegisterNetworkAgent((na) -> {
+            if (!mConnectedNetwork.carrierMerged) {
+                assertNull(na.subscriberId);
+            }
+        }, (nc) -> {
+                if (SdkLevel.isAtLeastS()) {
+                    WifiInfo wifiInfoFromTi = (WifiInfo) nc.getTransportInfo();
+                    assertEquals(TEST_BSSID_STR, wifiInfoFromTi.getBSSID());
+                    assertEquals(sFreq, wifiInfoFromTi.getFrequency());
+                    assertEquals(TEST_WIFI_SSID, wifiInfoFromTi.getWifiSsid());
+                    if (wifiInfo.isPasspointAp()) {
+                        assertEquals(wifiInfoFromTi.getPasspointProviderFriendlyName(),
+                                WifiConfigurationTestUtil.TEST_PROVIDER_FRIENDLY_NAME);
+                    } else {
+                        assertNull(wifiInfoFromTi.getPasspointProviderFriendlyName());
+                    }
+                }
+            });
+
+        assertFalse(mCmi.isIpProvisioningTimedOut());
+        verify(mWifiConfigManager).setIpProvisioningTimedOut(eq(FRAMEWORK_NETWORK_ID), eq(false));
+
+        // Ensure the connection stats for the network is updated.
+        verify(mWifiConfigManager).updateRandomizedMacExpireTime(any(), anyLong());
+        verifyNetworkStateChangedBroadcast(atLeast(1),
+                new NetworkStateChangedIntentMatcher(CONNECTED));
+
+        //Ensure that the network is registered
+        verify(mWifiConfigManager).updateNetworkAfterConnect(
+                eq(FRAMEWORK_NETWORK_ID), anyBoolean(), anyBoolean(), anyInt());
+        verify(mWifiConnectivityManager, times(2)).handleConnectionStateChanged(mClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+
+        // Anonymous Identity is not set.
+        assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
+        verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.CONNECTED);
+        assertEquals("L3ConnectedState", getCurrentState().getName());
+        verify(mWifiMetrics).incrementNumOfCarrierWifiConnectionSuccess();
+        verify(mWifiLockManager).updateWifiClientConnected(mClientModeManager, true);
+        verify(mWifiNative).getConnectionCapabilities(any());
+        verify(mThroughputPredictor).predictMaxTxThroughput(any());
+        verify(mWifiMetrics).setConnectionMaxSupportedLinkSpeedMbps(WIFI_IFACE_NAME, 90, 80);
+        assertEquals(90, wifiInfo.getMaxSupportedTxLinkSpeedMbps());
+        verify(mWifiMetrics).noteFirstL3ConnectionAfterBoot(true);
+        validateConnectionInfo();
+
+    }
+
+    @Test
+    public void testConnectWithIpProvisioningTimeout() throws Exception {
+        connectWithIpProvisionTimeout(false);
+    }
+
+    @Test
+    public void testConnectWithIpProvisioningTimeoutLateDhcpResponse() throws Exception {
+        connectWithIpProvisionTimeout(true);
+    }
+
     private void verifyNetworkStateChangedBroadcast(VerificationMode mode,
             ArgumentCaptor<Intent> intentCaptor) {
         if (SdkLevel.isAtLeastU()) {
@@ -1351,16 +1539,41 @@
         String expectedDecoratedPseudonym = "abc-123@wlan.mnc456.mcc123.3gppnetwork.org";
         when(mWifiCarrierInfoManager.decoratePseudonymWith3GppRealm(any(), eq(expectedPseudonym)))
                 .thenReturn(expectedDecoratedPseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
         listenerCaptor.getValue().onUpdated(CARRIER_ID_1, expectedPseudonym);
         mLooper.dispatchAll();
 
-        if (SdkLevel.isAtLeastT()) {
-            verify(mWifiNative).setEapAnonymousIdentity(
-                    anyString(), eq(expectedDecoratedPseudonym), eq(true));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(
-                    anyString(), anyString(), anyBoolean());
-        }
+        verify(mWifiNative).setEapAnonymousIdentity(
+                anyString(), eq(expectedDecoratedPseudonym), eq(true));
+
+        mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT,
+                new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false));
+        mLooper.dispatchAll();
+
+        assertEquals("DisconnectedState", getCurrentState().getName());
+        verify(mWifiPseudonymManager)
+                .unregisterPseudonymUpdatingListener(eq(listenerCaptor.getValue()));
+    }
+
+    @Test
+    public void testNotUpdatingOobPseudonymToSupplicant() throws Exception {
+        when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(anyInt())).thenReturn(true);
+        when(mDeviceConfigFacade.isOobPseudonymEnabled()).thenReturn(true);
+
+        ArgumentCaptor<WifiPseudonymManager.PseudonymUpdatingListener> listenerCaptor =
+                ArgumentCaptor.forClass(WifiPseudonymManager.PseudonymUpdatingListener.class);
+        setupEapSimConnection();
+        verify(mWifiPseudonymManager).registerPseudonymUpdatingListener(listenerCaptor.capture());
+        String expectedPseudonym = "abc-123";
+        String expectedDecoratedPseudonym = "abc-123@wlan.mnc456.mcc123.3gppnetwork.org";
+        when(mWifiCarrierInfoManager.decoratePseudonymWith3GppRealm(any(), eq(expectedPseudonym)))
+                .thenReturn(expectedDecoratedPseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(false);
+        listenerCaptor.getValue().onUpdated(CARRIER_ID_1, expectedPseudonym);
+        mLooper.dispatchAll();
+
+        verify(mWifiNative, never()).setEapAnonymousIdentity(
+                anyString(), anyString(), anyBoolean());
 
         mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT,
                 new DisconnectEventInfo(mConnectedNetwork.SSID, TEST_BSSID_STR, 0, false));
@@ -1605,6 +1818,7 @@
                 getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
         when(mWifiNative.getEapAnonymousIdentity(anyString()))
                 .thenReturn(pseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
 
         WifiSsid wifiSsid = WifiSsid.fromBytes(
                 NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
@@ -1613,12 +1827,8 @@
         mLooper.dispatchAll();
 
         verify(mWifiNative).getEapAnonymousIdentity(any());
-        if (SdkLevel.isAtLeastT()) {
-            // No decorated pseudonum, only update the cached data.
-            verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(any(), any(), anyBoolean());
-        }
+        // No decorated pseudonum, only update the cached data.
+        verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
         assertEquals(pseudonym,
                 mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
         // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
@@ -1667,6 +1877,7 @@
                 getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
         when(mWifiNative.getEapAnonymousIdentity(anyString()))
                 .thenReturn(pseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
 
         WifiSsid wifiSsid = WifiSsid.fromBytes(
                 NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
@@ -1675,12 +1886,8 @@
         mLooper.dispatchAll();
 
         verify(mWifiNative).getEapAnonymousIdentity(any());
-        if (SdkLevel.isAtLeastT()) {
-            verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym + "@" + realm),
-                    eq(true));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(any(), any(), anyBoolean());
-        }
+        verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym + "@" + realm),
+                eq(true));
         assertEquals(pseudonym + "@" + realm,
                 mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
         // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
@@ -1726,6 +1933,7 @@
                 getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
         when(mWifiNative.getEapAnonymousIdentity(anyString()))
                 .thenReturn(pseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
 
         WifiSsid wifiSsid = WifiSsid.fromBytes(
                 NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
@@ -1734,12 +1942,8 @@
         mLooper.dispatchAll();
 
         verify(mWifiNative).getEapAnonymousIdentity(any());
-        if (SdkLevel.isAtLeastT()) {
-            // No decorated pseudonum, only update the cached data.
-            verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(any(), any(), anyBoolean());
-        }
+        // No decorated pseudonum, only update the cached data.
+        verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
         assertEquals(pseudonym,
                 mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
         // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
@@ -1785,6 +1989,7 @@
                 getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
         when(mWifiNative.getEapAnonymousIdentity(anyString()))
                 .thenReturn(pseudonymFromSupplicant);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
 
         WifiSsid wifiSsid = WifiSsid.fromBytes(
                 NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
@@ -1793,13 +1998,9 @@
         mLooper.dispatchAll();
 
         verify(mWifiNative).getEapAnonymousIdentity(any());
-        if (SdkLevel.isAtLeastT()) {
-            // No decorated pseudonum, only update the cached data.
-            verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonymFromSupplicant),
-                    eq(false));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(any(), any(), anyBoolean());
-        }
+        // No decorated pseudonum, only update the cached data.
+        verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonymFromSupplicant),
+                eq(false));
         verify(mWifiPseudonymManager).setInBandPseudonym(anyInt(), eq(pseudonymFromSupplicant));
         assertEquals(pseudonymFromSupplicant,
                 mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
@@ -1843,6 +2044,7 @@
                 getGoogleGuestScanDetail(TEST_RSSI, TEST_BSSID_STR, sFreq).getScanResult());
         when(mWifiNative.getEapAnonymousIdentity(anyString()))
                 .thenReturn(pseudonym);
+        when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(1)).thenReturn(true);
 
         WifiSsid wifiSsid = WifiSsid.fromBytes(
                 NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
@@ -1851,12 +2053,8 @@
         mLooper.dispatchAll();
 
         verify(mWifiNative).getEapAnonymousIdentity(any());
-        if (SdkLevel.isAtLeastT()) {
-            // No decorated pseudonum, only update the cached data.
-            verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
-        } else {
-            verify(mWifiNative, never()).setEapAnonymousIdentity(any(), any(), anyBoolean());
-        }
+        // No decorated pseudonum, only update the cached data.
+        verify(mWifiNative).setEapAnonymousIdentity(any(), eq(pseudonym), eq(false));
         assertEquals(pseudonym,
                 mConnectedNetwork.enterpriseConfig.getAnonymousIdentity());
         // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a
@@ -2036,6 +2234,51 @@
         verify(mWifiStateTracker).updateState(WIFI_IFACE_NAME, WifiStateTracker.DISCONNECTED);
     }
 
+    @Test
+    public void testIdleModeChanged_firmwareRoaming() throws Exception {
+        // verify no-op when either the feature flag is disabled or firmware roaming is not
+        // supported
+        when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(false);
+        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+        mCmi.onIdleModeChanged(true);
+        verify(mWifiNative, never()).enableFirmwareRoaming(anyString(), anyInt());
+        when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true);
+        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
+        mCmi.onIdleModeChanged(true);
+        verify(mWifiNative, never()).enableFirmwareRoaming(anyString(), anyInt());
+
+        // Enable both, then verify firmware roaming is disabled when idle mode is entered
+        when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true);
+        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+        mCmi.onIdleModeChanged(true);
+        verify(mWifiNative).enableFirmwareRoaming(anyString(),
+                eq(WifiNative.DISABLE_FIRMWARE_ROAMING));
+
+        // Verify firmware roaming is enabled when idle mode exited
+        mCmi.onIdleModeChanged(false);
+        verify(mWifiNative).enableFirmwareRoaming(anyString(),
+                eq(WifiNative.ENABLE_FIRMWARE_ROAMING));
+    }
+
+    @Test
+    public void testIdleModeChanged_firmwareRoamingLocalOnlyCase() throws Exception {
+        // mock connected network to be local only
+        connect();
+        mConnectedNetwork.fromWifiNetworkSpecifier = true;
+
+        // Enable feature, then verify firmware roaming is disabled when idle mode is entered
+        when(mWifiGlobals.isDisableFirmwareRoamingInIdleMode()).thenReturn(true);
+        when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+        mCmi.onIdleModeChanged(true);
+        verify(mWifiNative).enableFirmwareRoaming(anyString(),
+                eq(WifiNative.DISABLE_FIRMWARE_ROAMING));
+
+        // Verify firmware roaming is not enabled when idle mode exited
+        mCmi.onIdleModeChanged(false);
+        verify(mWifiNative, never()).enableFirmwareRoaming(anyString(),
+                eq(WifiNative.ENABLE_FIRMWARE_ROAMING));
+    }
+
     /**
      * Tests the network connection initiation sequence with no network request pending from
      * from WifiNetworkFactory when we're already connected to a different network.
@@ -2330,7 +2573,7 @@
         // Ensure we don't end the new connection event.
         verify(mWifiMetrics, never()).endConnectionEvent(
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
-                anyInt(), anyInt(), anyInt());
+                anyInt(), anyInt(), anyInt(), anyInt());
         verify(mWifiConnectivityManager).prepareForForcedConnection(FRAMEWORK_NETWORK_ID + 1);
     }
 
@@ -2587,7 +2830,7 @@
 
         verify(mWifiMetrics).endConnectionEvent(
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
-                anyInt(), anyInt(), anyInt());
+                anyInt(), anyInt(), anyInt(), anyInt());
         verify(mWifiConnectivityManager).handleConnectionAttemptEnded(
                 any(), anyInt(), anyInt(), any(), any());
         assertEquals(WifiInfo.SECURITY_TYPE_UNKNOWN, mWifiInfo.getCurrentSecurityType());
@@ -3651,13 +3894,15 @@
         llStats.rxmpdu_bk = 2000;
         WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
         signalPollResults.addEntry(0, -42, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
         when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
         when(mClock.getWallClockMillis()).thenReturn(startMillis + 0);
         mCmi.enableRssiPolling(true);
         connect();
         mLooper.dispatchAll();
-        assertRssiChangeBroadcastSent();
+        assertRssiChangeBroadcastSent(1);
         when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333);
         mLooper.dispatchAll();
         WifiInfo wifiInfo = mWifiInfo;
@@ -3674,6 +3919,92 @@
     }
 
     /**
+     * Verify that RSSI polling will send RSSI broadcasts if the RSSI signal level has changed
+     */
+    @Test
+    public void verifyConnectedModeRssiPollingWithSameSignalLevel() throws Exception {
+        final long startMillis = 1_500_000_000_100L;
+        WifiLinkLayerStats llStats = new WifiLinkLayerStats();
+        llStats.txmpdu_be = 1000;
+        llStats.rxmpdu_bk = 2000;
+        WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
+        signalPollResults.addEntry(0, -42, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
+        when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
+        when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
+        when(mClock.getWallClockMillis()).thenReturn(startMillis + 0);
+        mCmi.enableRssiPolling(true);
+        connect();
+        mLooper.dispatchAll();
+        when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        // Two broadcasts, one when enabling RSSI polling and the other for RSSI polling after
+        // IP configuration success resets the current level.
+        assertRssiChangeBroadcastSent(2);
+
+        // Same RSSI should not send another broadcast
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        assertRssiChangeBroadcastSent(2);
+
+        // Same signal level should not send another broadcast
+        signalPollResults.addEntry(0, -43, 65, 54, sFreq);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        assertRssiChangeBroadcastSent(2);
+
+        // Different signal level should send another broadcast
+        signalPollResults.addEntry(0, -70, 65, 54, sFreq);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        assertRssiChangeBroadcastSent(3);
+    }
+
+    /**
+     * Verify that RSSI polling with verbose logging enabled by the user will send RSSI broadcasts
+     * if the RSSI has changed at all.
+     */
+    @Test
+    public void verifyConnectedModeRssiPollingWithSameSignalLevelVerboseLoggingEnabled()
+            throws Exception {
+        when(mWifiGlobals.getVerboseLoggingLevel())
+                .thenReturn(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED);
+        final long startMillis = 1_500_000_000_100L;
+        WifiLinkLayerStats llStats = new WifiLinkLayerStats();
+        llStats.txmpdu_be = 1000;
+        llStats.rxmpdu_bk = 2000;
+        WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
+        signalPollResults.addEntry(0, -42, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
+        when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
+        when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
+        when(mClock.getWallClockMillis()).thenReturn(startMillis + 0);
+        mCmi.enableRssiPolling(true);
+        connect();
+        mLooper.dispatchAll();
+        when(mClock.getWallClockMillis()).thenReturn(startMillis + 3333);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        // Two broadcasts, one when enabling RSSI polling and the other for RSSI polling after
+        // IP configuration success resets the current level.
+        assertRssiChangeBroadcastSent(2);
+
+        // Same RSSI should not send another broadcast
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        assertRssiChangeBroadcastSent(2);
+
+        // Different RSSI but same signal level should send another broadcast since we're verbose.
+        signalPollResults.addEntry(0, -43, 65, 54, sFreq);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        assertRssiChangeBroadcastSent(3);
+    }
+
+    /**
      * Verify link bandwidth update in connected mode
      */
     @Test
@@ -3706,6 +4037,8 @@
         WifiLinkLayerStats llStats = new WifiLinkLayerStats();
         WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
         signalPollResults.addEntry(0, -42, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
         when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
         when(mClock.getWallClockMillis()).thenReturn(startMillis + 0);
@@ -4245,6 +4578,8 @@
         llStats.rxmpdu_bk = 2000;
         WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
         signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
         when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
 
@@ -4270,6 +4605,8 @@
         llStats.rxmpdu_bk = 2000;
         WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
         signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
         when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
 
@@ -4279,7 +4616,7 @@
         mLooper.dispatchAll();
         DisconnectEventInfo disconnectEventInfo =
                 new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST);
+        mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST");
         mLooper.dispatchAll();
 
         verify(mWifiBlocklistMonitor).handleBssidConnectionFailure(eq(TEST_BSSID_STR),
@@ -4478,11 +4815,14 @@
         connect();
         // Disconnection with reason = DISASSOC_AP_BUSY
         DisconnectEventInfo disconnectEventInfo =
-                new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 5, false);
+                new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR,
+                        SupplicantStaIfaceHal.StaIfaceReasonCode.DISASSOC_AP_BUSY, false);
         mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo);
         mLooper.dispatchAll();
         verify(mWifiConfigManager).setRecentFailureAssociationStatus(anyInt(),
                 eq(WifiConfiguration.RECENT_FAILURE_DISCONNECTION_AP_BUSY));
+        verify(mWifiBlocklistMonitor).blockBssidForDurationMs(any(), any(),
+                anyLong(), eq(WifiBlocklistMonitor.REASON_AP_UNABLE_TO_HANDLE_NEW_STA), anyInt());
     }
 
     /**
@@ -4985,6 +5325,8 @@
         llStats.rxmpdu_bk = 2000;
         WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
         signalPollResults.addEntry(0, RSSI_THRESHOLD_BREACH_MIN, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
         when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
 
@@ -4997,6 +5339,7 @@
         currentNetwork.SSID = DEFAULT_TEST_SSID;
         currentNetwork.noInternetAccessExpected = false;
         currentNetwork.numNoInternetAccessReports = 1;
+        currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(true);
 
         // not user selected
         when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID))
@@ -5094,7 +5437,26 @@
     }
 
     @Test
-    public void testInternetValidationFailureUserSelectedRecentlyExpectNotDisabled()
+    public void testInternetValidationFailureUserSelectedRecently_ExpectDisabled()
+            throws Exception {
+        testInternetValidationUserSelectedRecently(false);
+        // expect disabled because the network has never had internet validation passed
+        verify(mWifiConfigManager).updateNetworkSelectionStatus(
+                FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_PERMANENT);
+    }
+
+    @Test
+    public void testInternetValidationFailureUserSelectedRecently_ExpectNotDisabled()
+            throws Exception {
+        testInternetValidationUserSelectedRecently(true);
+        // expect not disabled
+        verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(
+                FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_PERMANENT);
+        verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(
+                FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY);
+    }
+
+    private void testInternetValidationUserSelectedRecently(boolean hasEverValidatedInternetAccess)
             throws Exception {
         connect();
         verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(),
@@ -5104,6 +5466,8 @@
         currentNetwork.networkId = FRAMEWORK_NETWORK_ID;
         currentNetwork.noInternetAccessExpected = false;
         currentNetwork.numNoInternetAccessReports = 1;
+        currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(
+                hasEverValidatedInternetAccess);
 
         // user last picked this network
         when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID))
@@ -5121,9 +5485,6 @@
 
         verify(mWifiConfigManager)
                 .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID);
-        // expect not disabled
-        verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(
-                FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY);
     }
 
     @Test
@@ -5137,6 +5498,7 @@
         currentNetwork.networkId = FRAMEWORK_NETWORK_ID;
         currentNetwork.noInternetAccessExpected = false;
         currentNetwork.numNoInternetAccessReports = 1;
+        currentNetwork.getNetworkSelectionStatus().setHasEverValidatedInternetAccess(true);
 
         // user last picked this network
         when(mWifiConfigManager.getConfiguredNetwork(FRAMEWORK_NETWORK_ID))
@@ -5161,7 +5523,7 @@
     }
 
     @Test
-    public void testNetworkInternetValidationFailureNotUserSelectedExpectNotDisabled()
+    public void testNetworkInternetValidationFailureNoInternetExpected_ExpectNotDisabled()
             throws Exception {
         connect();
         verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(),
@@ -5187,7 +5549,7 @@
 
         verify(mWifiConfigManager)
                 .incrementNetworkNoInternetAccessReports(FRAMEWORK_NETWORK_ID);
-        // expect not disabled
+        // expect not disabled since no internet is expected on this network
         verify(mWifiConfigManager, never()).updateNetworkSelectionStatus(
                 FRAMEWORK_NETWORK_ID, DISABLED_NO_INTERNET_TEMPORARY);
     }
@@ -5553,13 +5915,15 @@
     /**
      * Verifies that RSSI change broadcast is sent.
      */
-    private void assertRssiChangeBroadcastSent() throws Exception {
+    private void assertRssiChangeBroadcastSent(int times) throws Exception {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext).sendBroadcastAsUser(intentCaptor.capture(),
+        verify(mContext, times(times)).sendBroadcastAsUser(intentCaptor.capture(),
                 eq(UserHandle.ALL), eq(Manifest.permission.ACCESS_WIFI_STATE), any());
-        Intent intent = intentCaptor.getValue();
-        assertNotNull(intent);
-        assertEquals(WifiManager.RSSI_CHANGED_ACTION, intent.getAction());
+        if (times > 0) {
+            Intent intent = intentCaptor.getValue();
+            assertNotNull(intent);
+            assertEquals(WifiManager.RSSI_CHANGED_ACTION, intent.getAction());
+        }
     }
 
     /**
@@ -5572,6 +5936,8 @@
         connect();
 
         failOnRssiChangeBroadcast();
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats();
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats);
         mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
@@ -5597,6 +5963,8 @@
         connect();
 
         failOnRssiChangeBroadcast();
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         WifiLinkLayerStats stats = new WifiLinkLayerStats();
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats);
         when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(),
@@ -5933,7 +6301,7 @@
     public void testTakebugreportbyIpReachabilityLost() throws Exception {
         connect();
 
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST);
+        mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST");
         mLooper.dispatchAll();
         verify(mWifiDiagnostics).triggerBugReportDataCapture(
                 eq(WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST));
@@ -5949,7 +6317,7 @@
 
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiDiagnostics).triggerBugReportDataCapture(
                 eq(WifiDiagnostics.REPORT_REASON_REACHABILITY_FAILURE));
@@ -6037,7 +6405,7 @@
     public void verifyIpReachabilityLostMsgUpdatesWifiUsabilityMetrics() throws Exception {
         connect();
 
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST);
+        mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST");
         mLooper.dispatchAll();
         verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME,
                 WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
@@ -6058,7 +6426,7 @@
 
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME,
                 WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
@@ -6232,7 +6600,7 @@
      * over the threshold.
      */
     @Test
-    public void testHighPrababilityInternetTemporarilyDisableNetwork() throws Exception {
+    public void testHighProbabilityInternetTemporarilyDisableNetwork() throws Exception {
         connect();
         when(mPerBssid.estimatePercentInternetAvailability()).thenReturn(
                 ClientModeImpl.PROBABILITY_WITH_INTERNET_TO_PERMANENTLY_DISABLE_NETWORK);
@@ -6489,8 +6857,7 @@
         l2Packet.dstMacAddress = TEST_GLOBAL_MAC_ADDRESS;
         l2Packet.payload = new byte[] {0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13,
                 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x00, 0x12, 0x13, 0x55, 0x66};
-        mCmi.sendMessage(ClientModeImpl.CMD_START_FILS_CONNECTION, 0, 0,
-                Collections.singletonList(l2Packet));
+        mIpClientCallback.onPreconnectionStart(Collections.singletonList(l2Packet));
         mLooper.dispatchAll();
         assertEquals("L2ConnectingState", mCmi.getCurrentState().getName());
     }
@@ -6712,7 +7079,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
@@ -6737,7 +7104,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -6770,7 +7137,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
@@ -6798,7 +7165,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -6830,7 +7197,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -6860,7 +7227,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -6962,7 +7329,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, true);
         }
         mLooper.dispatchAll();
         verify(mWifiNative).removeNetworkCachedData(eq(oldConfig.networkId));
@@ -7056,14 +7423,96 @@
         expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { });
         reset(mWifiNetworkAgent);
 
+        // normal behavior w/o overlay
+        when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(false);
+        when(mWifiCountryCode.getCountryCode()).thenReturn("CN");
+
         // Trigger ip reachability failure and ensure we trigger a disconnect.
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
     }
 
+    @Test
+    public void testIpReachabilityFailureConfirmDoesNotTriggersDisconnectionForWapi()
+            throws Exception {
+        mConnectedNetwork = spy(WifiConfigurationTestUtil.createWapiPskNetwork());
+
+        assumeTrue(SdkLevel.isAtLeastT());
+        connect();
+        expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { });
+        reset(mWifiNetworkAgent);
+
+        when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true);
+        when(mWifiCountryCode.getCountryCode()).thenReturn("CN");
+
+        // Trigger ip reachability failure and ensure we trigger a disconnect.
+        ReachabilityLossInfoParcelable lossInfo =
+                new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
+        mLooper.dispatchAll();
+        if (SdkLevel.isAtLeastV()) {
+            verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
+        } else {
+            verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
+        }
+    }
+
+    @Test
+    public void testIpReachabilityFailureStaticIpOrganicTriggersDisconnection() throws Exception {
+        when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true);
+        assumeTrue(SdkLevel.isAtLeastT());
+
+        final List<InetAddress> dnsServers = new ArrayList<>();
+        dnsServers.add(InetAddresses.parseNumericAddress("8.8.8.8"));
+        dnsServers.add(InetAddresses.parseNumericAddress("4.4.4.4"));
+        final StaticIpConfiguration staticIpConfig =
+                new StaticIpConfiguration.Builder()
+                        .setIpAddress(new LinkAddress("192.0.2.2/25"))
+                        .setGateway(InetAddresses.parseNumericAddress("192.0.2.1"))
+                        .setDnsServers(dnsServers)
+                        .build();
+        final IpConfiguration ipConfig = new IpConfiguration();
+        ipConfig.setStaticIpConfiguration(staticIpConfig);
+        ipConfig.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
+        mConnectedNetwork.setIpConfiguration(ipConfig);
+
+        triggerConnect();
+        validateConnectionInfo();
+
+        // Simulate L2 connection.
+        final WifiSsid wifiSsid =
+                WifiSsid.fromBytes(
+                        NativeUtil.byteArrayFromArrayList(
+                                NativeUtil.decodeSsid(mConnectedNetwork.SSID)));
+        mCmi.sendMessage(
+                WifiMonitor.NETWORK_CONNECTION_EVENT,
+                new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, null));
+        mLooper.dispatchAll();
+
+        // Simulate L3 connection.
+        final DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable();
+        dhcpResults.baseConfiguration = staticIpConfig;
+        injectDhcpSuccess(dhcpResults);
+        mLooper.dispatchAll();
+        expectRegisterNetworkAgent((agentConfig) -> {}, (cap) -> {});
+        reset(mWifiNetworkAgent);
+
+        // normal behavior outside specific CC
+        when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true);
+        when(mWifiCountryCode.getCountryCode()).thenReturn("US");
+
+        // Trigger IP reachability failure and ensure we trigger a disconnection due to static IP.
+        ReachabilityLossInfoParcelable lossInfo =
+                new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ORGANIC);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
+        mLooper.dispatchAll();
+        verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
+        verify(mWifiNetworkAgent, never()).unregisterAfterReplacement(anyInt());
+    }
+
     private void doIpReachabilityFailureTest(int lossReason, boolean shouldWifiDisconnect)
             throws Exception {
         assumeTrue(SdkLevel.isAtLeastU());
@@ -7076,7 +7525,7 @@
         // on U and above, but wifi should still disconnect on previous platforms.
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", lossReason);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         if (!shouldWifiDisconnect) {
             verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
@@ -7100,6 +7549,11 @@
             throws Exception {
         when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(false);
 
+        // should still disconnect in following scenario
+        mConnectedNetwork = spy(WifiConfigurationTestUtil.createPskNetwork());
+        when(mWifiGlobals.disableNudDisconnectsForWapiInSpecificCc()).thenReturn(true);
+        when(mWifiCountryCode.getCurrentDriverCountryCode()).thenReturn("CN");
+
         doIpReachabilityFailureTest(ReachabilityLossReason.CONFIRM,
                 true /* shouldWifiDisconnect */);
     }
@@ -7114,7 +7568,7 @@
         // Trigger ip reachability failure and ensure we trigger a disconnect.
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ORGANIC);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
     }
@@ -7150,7 +7604,7 @@
         // Trigger ip reachability failure and ensure we disconnect.
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
     }
@@ -7165,7 +7619,7 @@
         // Trigger ip reachability failure and ensure we do not trigger a disconnect.
         ReachabilityLossInfoParcelable lossInfo =
                 new ReachabilityLossInfoParcelable("", ReachabilityLossReason.ROAM);
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_FAILURE, lossInfo);
+        mIpClientCallback.onReachabilityFailure(lossInfo);
         mLooper.dispatchAll();
         verify(mWifiNetworkAgent).unregisterAfterReplacement(anyInt());
         verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
@@ -7207,6 +7661,66 @@
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
     }
 
+    private void runProvisioningFailureCalledAfterReachabilityFailureTest(int lossReason)
+            throws Exception {
+        assumeTrue(SdkLevel.isAtLeastT());
+        connect();
+        expectRegisterNetworkAgent((agentConfig) -> { }, (cap) -> { });
+        reset(mWifiNetworkAgent);
+
+        // Save current IpClientCallbacks instance and verify an immediate IP provisioning
+        // failure event from this instance after IP reachability failure still triggers a
+        // WiFi disconnection due to the race, for example:
+        // - IpClient sends onReachabilityFailure callback, wifi module receives this callback
+        //   and post CMD_IP_REACHABILITY_FAILURE, return;
+        // - IpClient sends onProvisioningFailure callback, wifi module receives this callback
+        //   and post CMD_IP_CONFIGURATION_LOST, return;
+        // - state machine processes CMD_IP_REACHABILITY_FAILURE first and eventually transition
+        //   to WaitBeforeL3ProvisioningState and recreate an IpClient instance there.
+        // - state machine receives CMD_IP_CONFIGURATION_LOST from the old IpClientCallbacks
+        //   instanceat at WaitBeforeL3ProvisioningState, but defer the command to its parent state
+        //   until transition to L3ProvisioningState.
+        // - CMD_IP_CONFIGURATION_LOST is processed at L2ConnectedState eventually, and trigger
+        //   WiFi disconnection there.
+        final IpClientCallbacks callback = mIpClientCallback;
+        final ReachabilityLossInfoParcelable lossInfo =
+                new ReachabilityLossInfoParcelable("", lossReason);
+        callback.onReachabilityFailure(lossInfo);
+        callback.onProvisioningFailure(new LinkProperties());
+        mLooper.dispatchAll();
+        verify(mWifiNetworkAgent).unregisterAfterReplacement(anyInt());
+        verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
+        assertEquals("L3ProvisioningState", getCurrentState().getName());
+
+        // Verify that onProvisioningFailure from the current IpClientCallbacks instance
+        // triggers wifi disconnection.
+        mIpClientCallback.onProvisioningFailure(new LinkProperties());
+        mLooper.dispatchAll();
+        verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
+    }
+
+    @Test
+    public void testProvisioningFailureCalledAfterReachabilityFailure_postRoam()
+            throws Exception {
+        runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.ROAM);
+    }
+
+    @Test
+    public void testProvisioningFailureCalledAfterReachabilityFailure_rssiConfirm()
+            throws Exception {
+        when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true);
+
+        runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.CONFIRM);
+    }
+
+    @Test
+    public void testProvisioningFailureCalledAfterReachabilityFailure_organicKernel()
+            throws Exception {
+        when(mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled()).thenReturn(true);
+
+        runProvisioningFailureCalledAfterReachabilityFailureTest(ReachabilityLossReason.ORGANIC);
+    }
+
     @Test
     public void testIpReachabilityLostAndRoamEventsRace() throws Exception {
         connect();
@@ -7214,7 +7728,7 @@
         reset(mWifiNetworkAgent);
 
         // Trigger ip reachability loss and ensure we trigger a disconnect.
-        mCmi.sendMessage(ClientModeImpl.CMD_IP_REACHABILITY_LOST);
+        mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST");
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
 
@@ -7320,6 +7834,8 @@
      */
     @Test
     public void testWifiScoreReportDump() throws Exception {
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         InOrder inOrder = inOrder(mWifiNative, mWifiScoreReport);
         inOrder.verify(mWifiNative, never()).getWifiLinkLayerStats(any());
         connect();
@@ -7625,7 +8141,7 @@
     public void testCarrierEapFailure() throws Exception {
         initializeAndAddNetworkAndVerifySuccess();
         WifiBlocklistMonitor.CarrierSpecificEapFailureConfig eapFailureConfig =
-                new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(1, -1);
+                new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(1, -1, true);
 
         startConnectSuccess();
 
@@ -8310,10 +8826,10 @@
     }
 
     private void setScreenState(boolean screenOn) {
-        BroadcastReceiver broadcastReceiver = mScreenStateBroadcastReceiverCaptor.getValue();
-        assertNotNull(broadcastReceiver);
-        Intent intent = new Intent(screenOn  ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
-        broadcastReceiver.onReceive(mContext, intent);
+        WifiDeviceStateChangeManager.StateChangeCallback callback =
+                mStateChangeCallbackArgumentCaptor.getValue();
+        assertNotNull(callback);
+        callback.onScreenStateChanged(screenOn);
     }
 
     @Test
@@ -8322,6 +8838,8 @@
         connect();
         clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall);
 
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats();
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats);
         mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
@@ -8364,6 +8882,8 @@
 
         verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall);
 
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats());
 
         // No link layer stats collection on secondary CMM.
@@ -8381,6 +8901,8 @@
         connect();
         clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall);
 
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats());
 
         // No link layer stats collection on secondary CMM.
@@ -8412,6 +8934,8 @@
         clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall);
 
         // RSSI polling is enabled on primary.
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
         WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats();
         when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats);
         mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
@@ -8435,6 +8959,23 @@
     }
 
     @Test
+    public void verifyRssiPollWhenLinkLayerStatsIsNotSupported() throws Exception {
+        setScreenState(true);
+        connect();
+        clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall);
+
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(0L);
+        WifiLinkLayerStats stats = new WifiLinkLayerStats();
+        when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats);
+        mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis());
+        mLooper.dispatchAll();
+        verify(mWifiNative, never()).getWifiLinkLayerStats(any());
+        verify(mWifiDataStall).checkDataStallAndThroughputSufficiency(WIFI_IFACE_NAME,
+                mConnectionCapabilities, null, null, mWifiInfo, TEST_TX_BYTES, TEST_RX_BYTES);
+        verify(mWifiMetrics).incrementWifiLinkLayerUsageStats(WIFI_IFACE_NAME, null);
+    }
+
+    @Test
     public void testClientModeImplWhenIpClientIsNotReady() throws Exception {
         WifiConfiguration config = mConnectedNetwork;
         config.networkId = FRAMEWORK_NETWORK_ID;
@@ -8852,7 +9393,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         verify(mWifiNative).disconnect(WIFI_IFACE_NAME);
@@ -8878,7 +9419,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -8905,7 +9446,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
@@ -8931,7 +9472,7 @@
 
         for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor
                 .getAllValues()) {
-            listener.onNetworkUpdated(mConnectedNetwork, oldConfig);
+            listener.onNetworkUpdated(mConnectedNetwork, oldConfig, false);
         }
         mLooper.dispatchAll();
         assertEquals("L3ConnectedState", getCurrentState().getName());
@@ -9087,7 +9628,7 @@
                     FRAMEWORK_NETWORK_ID, 0, certificateEventInfo);
             mLooper.dispatchAll();
             verify(mInsecureEapNetworkHandler).addPendingCertificate(
-                    eq(eapTlsConfig.SSID), eq(0), eq(certificateEventInfo));
+                    eq(eapTlsConfig.networkId), eq(0), eq(certificateEventInfo));
 
             // Adding a certificate in depth 0 will cause a disconnection when TOFU is supported
             DisconnectEventInfo disconnectEventInfo =
@@ -9146,7 +9687,7 @@
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
                 eq(WifiMetricsProto.ConnectionEvent.HLF_NONE),
                 eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN),
-                anyInt());
+                anyInt(), anyInt());
         ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor =
                 ArgumentCaptor.forClass(WifiConfiguration.class);
 
@@ -9175,7 +9716,7 @@
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
                 eq(WifiMetricsProto.ConnectionEvent.HLF_NONE),
                 eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN),
-                anyInt());
+                anyInt(), anyInt());
     }
 
    /**
@@ -9220,7 +9761,7 @@
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
                 eq(WifiMetricsProto.ConnectionEvent.HLF_NONE),
                 eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN),
-                anyInt());
+                anyInt(), anyInt());
         ArgumentCaptor<WifiConfiguration> wifiConfigurationArgumentCaptor =
                 ArgumentCaptor.forClass(WifiConfiguration.class);
 
@@ -9248,7 +9789,7 @@
                 any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION),
                 eq(WifiMetricsProto.ConnectionEvent.HLF_NONE),
                 eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN),
-                anyInt());
+                anyInt(), anyInt());
     }
 
     /**
@@ -9275,7 +9816,7 @@
                 testConfig.networkId);
         mLooper.dispatchAll();
         verify(mWifiMetrics, never()).endConnectionEvent(
-                any(), anyInt(), anyInt(), anyInt(), anyInt());
+                any(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
     }
 
     /**
@@ -9309,7 +9850,7 @@
                 testConfig.networkId);
         mLooper.dispatchAll();
         verify(mWifiMetrics, never()).endConnectionEvent(
-                any(), anyInt(), anyInt(), anyInt(), anyInt());
+                any(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
     }
 
     /**
@@ -9333,9 +9874,13 @@
     private void setScanResultWithMloInfo() {
         List<MloLink> mloLinks = new ArrayList<>();
         MloLink link1 = new MloLink();
+        link1.setBand(WifiScanner.WIFI_BAND_24_GHZ);
+        link1.setChannel(TEST_CHANNEL);
         link1.setApMacAddress(MacAddress.fromString(TEST_BSSID_STR));
         link1.setLinkId(TEST_MLO_LINK_ID);
         MloLink link2 = new MloLink();
+        link2.setBand(WifiScanner.WIFI_BAND_5_GHZ);
+        link2.setChannel(TEST_CHANNEL_1);
         link2.setApMacAddress(MacAddress.fromString(TEST_BSSID_STR1));
         link2.setLinkId(TEST_MLO_LINK_ID_1);
         mloLinks.add(link1);
@@ -9564,6 +10109,15 @@
         assertEquals(mWifiInfo.getAssociatedMloLinks().size(),
                 mWifiInfo.getAffiliatedMloLinks().size());
 
+        // verify that affiliated link state the scan results are still in unassociated state.
+        for (MloLink link : mScanResult.getAffiliatedMloLinks()) {
+            assertEquals(MloLink.MLO_LINK_STATE_UNASSOCIATED, link.getState());
+        }
+        // verify that affiliated link state are active
+        for (MloLink link : mWifiInfo.getAffiliatedMloLinks()) {
+            assertEquals(MloLink.MLO_LINK_STATE_ACTIVE, link.getState());
+        }
+
         setScanResultWithMloInfo();
         // Send FOUR_WAY_HANDSHAKE, GROUP_HANDSHAKE and COMPLETED and verify all links are still
         // asscoaited.
@@ -10030,6 +10584,40 @@
     }
 
     @Test
+    public void testUpdateWpa3EnterpriseSecurityTypeByConnectionInfo() throws Exception {
+        // Create a WifiConfiguration with WPA2 enterprise and WPA3 enterprise security params
+        WifiConfiguration config = spy(WifiConfigurationTestUtil.createWpa2Wpa3EnterpriseNetwork());
+        config.networkId = FRAMEWORK_NETWORK_ID;
+
+        SecurityParams wpa3EnterpriseParams =
+                config.getSecurityParams(SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+        assertNotNull(wpa3EnterpriseParams);
+        // Trigger connection with security params as WPA3 enterprise where PMF is mandatory
+        config.getNetworkSelectionStatus().setLastUsedSecurityParams(wpa3EnterpriseParams);
+        setupAndStartConnectSequence(config);
+        validateSuccessfulConnectSequence(config);
+
+        // WifiInfo is not updated yet.
+        assertEquals(WifiInfo.SECURITY_TYPE_UNKNOWN, mWifiInfo.getCurrentSecurityType());
+
+        WifiSsid wifiSsid =
+                WifiSsid.fromBytes(
+                        NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(config.SSID)));
+
+        BitSet akm = new BitSet();
+        akm.set(WifiConfiguration.KeyMgmt.WPA_EAP_SHA256);
+        // Send network connection event with WPA_EAP_SHA256 AKM
+        mCmi.sendMessage(
+                WifiMonitor.NETWORK_CONNECTION_EVENT,
+                new NetworkConnectionEventInfo(0, wifiSsid, TEST_BSSID_STR, false, akm));
+        mLooper.dispatchAll();
+        // WifiInfo is updated with WPA3-Enterprise security type derived from the AKM sent in
+        // network connection event and the PMF settings used in connection.
+        assertEquals(
+                WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, mWifiInfo.getCurrentSecurityType());
+    }
+
+    @Test
     public void testEnableTdls() throws Exception {
         connect();
         when(mWifiNative.getMaxSupportedConcurrentTdlsSessions(WIFI_IFACE_NAME)).thenReturn(5);
@@ -10085,4 +10673,161 @@
         WifiInfo wifiInfo = mWifiInfo;
         assertEquals(sFreq1, wifiInfo.getFrequency());
     }
+
+    /**
+     * Verify that static attributes (channel, state, link id and band) of the affiliated MLO links
+     * are not changed after signal poll.
+     */
+    @Test
+    public void testMloLinkAttributesAfterSignalPoll() throws Exception {
+        connect();
+        setScanResultWithMloInfo();
+
+        // Associate to one link only
+        mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE;
+        WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo();
+        info.links = new WifiNative.ConnectionMloLink[1];
+        info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR,
+                TEST_AP_MLD_MAC_ADDRESS, Byte.MAX_VALUE, Byte.MAX_VALUE, 2437);
+        when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info);
+
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq,
+                        SupplicantState.ASSOCIATED));
+        mLooper.dispatchAll();
+
+        // Verify the link attributes
+        List<MloLink> links = mWifiInfo.getAffiliatedMloLinks();
+        assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE);
+        assertEquals(links.get(0).getBand(), WifiScanner.WIFI_BAND_24_GHZ);
+        assertEquals(links.get(0).getChannel(), TEST_CHANNEL);
+        assertEquals(links.get(0).getLinkId(), TEST_MLO_LINK_ID);
+        assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_UNASSOCIATED);
+        assertEquals(links.get(1).getBand(), WifiScanner.WIFI_BAND_5_GHZ);
+        assertEquals(links.get(1).getChannel(), TEST_CHANNEL_1);
+        assertEquals(links.get(1).getLinkId(), TEST_MLO_LINK_ID_1);
+
+        // Signal poll for associated link only
+        final long startMillis = 1_500_000_000_100L;
+        WifiLinkLayerStats llStats = new WifiLinkLayerStats();
+        llStats.txmpdu_be = 1000;
+        llStats.rxmpdu_bk = 2000;
+        WifiSignalPollResults signalPollResults = new WifiSignalPollResults();
+        signalPollResults.addEntry(TEST_MLO_LINK_ID, -42, 65, 54, sFreq);
+        when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(
+                WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
+        when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats);
+        when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults);
+        when(mClock.getWallClockMillis()).thenReturn(startMillis + 0);
+        mCmi.enableRssiPolling(true);
+        mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1);
+        mLooper.dispatchAll();
+
+        // Make sure static link attributes has not changed
+        links = mWifiInfo.getAffiliatedMloLinks();
+        assertEquals(links.get(0).getState(), MloLink.MLO_LINK_STATE_ACTIVE);
+        assertEquals(links.get(0).getBand(), WifiScanner.WIFI_BAND_24_GHZ);
+        assertEquals(links.get(0).getChannel(), TEST_CHANNEL);
+        assertEquals(links.get(0).getLinkId(), TEST_MLO_LINK_ID);
+        assertEquals(links.get(1).getState(), MloLink.MLO_LINK_STATE_UNASSOCIATED);
+        assertEquals(links.get(1).getBand(), WifiScanner.WIFI_BAND_5_GHZ);
+        assertEquals(links.get(1).getChannel(), TEST_CHANNEL_1);
+        assertEquals(links.get(1).getLinkId(), TEST_MLO_LINK_ID_1);
+    }
+
+    /**
+     * Verify that during DHCP process, 1. If P2P is in waiting state, clientModeImpl doesn't send a
+     * message to block P2P discovery. 2. If P2P is not in waiting state, clientModeImpl sends a
+     * message to block P2P discovery. 3. On DHCP completion, clientModeImpl sends a message to
+     * unblock P2P discovery.
+     */
+    @Test
+    public void testP2pBlockDiscoveryDuringDhcp() throws Exception {
+        initializeAndAddNetworkAndVerifySuccess();
+
+        startConnectSuccess();
+
+        mCmi.sendMessage(
+                WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
+                0,
+                0,
+                new StateChangeResult(
+                        0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, SupplicantState.ASSOCIATED));
+        mLooper.dispatchAll();
+
+        mCmi.sendMessage(
+                WifiMonitor.NETWORK_CONNECTION_EVENT,
+                new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false, null));
+        mLooper.dispatchAll();
+        verify(mWifiBlocklistMonitor).handleBssidConnectionSuccess(TEST_BSSID_STR, TEST_SSID);
+
+        mCmi.sendMessage(
+                WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
+                0,
+                0,
+                new StateChangeResult(
+                        0, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq, SupplicantState.COMPLETED));
+        mLooper.dispatchAll();
+
+        assertEquals("L3ProvisioningState", getCurrentState().getName());
+
+        when(mWifiP2pConnection.isConnected()).thenReturn(true);
+        when(mWifiP2pConnection.isP2pInWaitingState()).thenReturn(true);
+
+        mIpClientCallback.onPreDhcpAction();
+        mLooper.dispatchAll();
+        verify(mWifiP2pConnection, never()).sendMessage(anyInt(), anyInt(), anyInt());
+        verify(mIpClient).completedPreDhcpAction();
+
+        when(mWifiP2pConnection.isConnected()).thenReturn(true);
+        when(mWifiP2pConnection.isP2pInWaitingState()).thenReturn(false);
+
+        mIpClientCallback.onPreDhcpAction();
+        mLooper.dispatchAll();
+        verify(mWifiP2pConnection)
+                .sendMessage(
+                        eq(WifiP2pServiceImpl.BLOCK_DISCOVERY),
+                        eq(WifiP2pServiceImpl.ENABLED),
+                        eq(CMD_PRE_DHCP_ACTION_COMPLETE));
+        verify(mIpClient).completedPreDhcpAction();
+
+        mIpClientCallback.onPostDhcpAction();
+        mLooper.dispatchAll();
+        verify(mWifiP2pConnection)
+                .sendMessage(
+                        eq(WifiP2pServiceImpl.BLOCK_DISCOVERY), eq(WifiP2pServiceImpl.DISABLED));
+    }
+
+    /**
+     * Verify that MLO link will be updated on receiving BSS_FREQUENCY_CHANGED_EVENT event.
+     */
+    @Test
+    public void testBssFrequencyChangedUpdatesMloLink() throws Exception {
+        connect();
+        mLooper.dispatchAll();
+
+        mConnectionCapabilities.wifiStandard = ScanResult.WIFI_STANDARD_11BE;
+        WifiNative.ConnectionMloLinksInfo info = new WifiNative.ConnectionMloLinksInfo();
+        info.links = new WifiNative.ConnectionMloLink[2];
+        info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR,
+                TEST_AP_MLD_MAC_ADDRESS, Byte.MIN_VALUE, Byte.MAX_VALUE, TEST_MLO_LINK_FREQ);
+        info.links[1] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID_1, TEST_MLO_LINK_ADDR_1,
+                TEST_AP_MLD_MAC_ADDRESS, Byte.MAX_VALUE, Byte.MIN_VALUE, TEST_MLO_LINK_FREQ_1);
+        when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info);
+        mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(FRAMEWORK_NETWORK_ID, TEST_WIFI_SSID, TEST_BSSID_STR, sFreq,
+                        SupplicantState.ASSOCIATED));
+        mLooper.dispatchAll();
+        assertEquals(mWifiInfo.getAssociatedMloLinks().size(),
+                mWifiInfo.getAffiliatedMloLinks().size());
+
+        info.links[0] = new WifiNative.ConnectionMloLink(TEST_MLO_LINK_ID, TEST_MLO_LINK_ADDR,
+                TEST_AP_MLD_MAC_ADDRESS, Byte.MIN_VALUE, Byte.MAX_VALUE, 6215);
+        when(mWifiNative.getConnectionMloLinksInfo(WIFI_IFACE_NAME)).thenReturn(info);
+        mCmi.sendMessage(WifiMonitor.BSS_FREQUENCY_CHANGED_EVENT, 6215);
+        mLooper.dispatchAll();
+        List<MloLink> links = mWifiInfo.getAffiliatedMloLinks();
+        assertEquals(53, links.get(0).getChannel());
+        assertEquals(WifiScanner.WIFI_BAND_6_GHZ, links.get(0).getBand());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java b/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java
index 8160b66..00a3564 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ConcreteCandidate.java
@@ -50,6 +50,9 @@
     private int mEstimatedPercentInternetAvailability = 50;
     private int mPredictedMultiLinkThroughputMbps = 0;
     private MacAddress mApMldMacAddress;
+    private int mNumRebootsSinceLastUse;
+
+    private boolean mIpProvisioningTimedOut;
 
     private final Map<WifiScoreCardProto.Event, WifiScoreCardProto.Signal>
             mEventStatisticsMap = new ArrayMap<>();
@@ -88,7 +91,9 @@
             }
         }
         mPredictedMultiLinkThroughputMbps = candidate.getPredictedMultiLinkThroughputMbps();
+        mNumRebootsSinceLastUse = candidate.getNumRebootsSinceLastUse();
         mApMldMacAddress = candidate.getApMldMacAddress();
+        mIpProvisioningTimedOut = candidate.isIpProvisioningTimedOut();
     }
 
     public ConcreteCandidate setKey(WifiCandidates.Key key) {
@@ -321,6 +326,19 @@
         return mApMldMacAddress;
     }
 
+    @Override
+    public int getNumRebootsSinceLastUse() {
+        return mNumRebootsSinceLastUse;
+    }
+
+    /**
+     * Setter for mNumRebootsSinceLastUse.
+     */
+    public ConcreteCandidate setNumRebootsSinceLastUse(int numRebootsSinceLastUse) {
+        mNumRebootsSinceLastUse = numRebootsSinceLastUse;
+        return this;
+    }
+
     public ConcreteCandidate setEventStatistics(
             WifiScoreCardProto.Event event,
             WifiScoreCardProto.Signal signal) {
@@ -342,4 +360,9 @@
     public boolean isMultiLinkCapable() {
         return (mApMldMacAddress != null);
     }
+
+    @Override
+    public boolean isIpProvisioningTimedOut() {
+        return mIpProvisioningTimedOut;
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
index d185269..a823f64 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
@@ -94,6 +94,7 @@
     private static final int TEST_ACTIVE_SUBSCRIPTION_ID = 1;
     private static final WorkSource TEST_WORKSOURCE = new WorkSource();
     private static final WorkSource TEST_WORKSOURCE2 = new WorkSource();
+    private static final int TEST_UID = 435546654;
 
     TestLooper mLooper;
 
@@ -487,6 +488,37 @@
                 WIFI_STATE_ENABLING);
     }
 
+    @Test
+    public void testSwitchFromConnectModeToScanOnlyModeFail() throws Exception {
+        startClientInConnectModeAndVerifyEnabled();
+
+        // mock wifi native role switch failure
+        when(mWifiNative.switchClientInterfaceToScanMode(any(), any()))
+                .thenReturn(false);
+        InOrder inOrder = inOrder(mContext);
+        mClientModeManager.setRole(ROLE_CLIENT_SCAN_ONLY, TEST_WORKSOURCE);
+        mLooper.dispatchAll();
+
+        // Verify callback received for failed transition
+        verify(mListener).onStartFailure(mClientModeManager);
+
+        // First check WIFI_STATE_DISABLING is broadcast as the CMM tries to switch to scan only
+        // mode
+        inOrder.verify(mContext, atLeastOnce()).sendStickyBroadcastAsUser(
+                argThat(intent ->
+                        intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1)
+                                == WifiManager.WIFI_STATE_DISABLING),
+                any());
+
+        // Then verify WIFI_STATE_DISABLED is broadcast due to WifiNative failing
+        // to switch CMM role.
+        inOrder.verify(mContext).sendStickyBroadcastAsUser(
+                argThat(intent ->
+                        intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1)
+                                == WIFI_STATE_DISABLED),
+                any());
+    }
+
     /**
      * Switch ClientModeManager from Connect mode to ScanOnly mode.
      */
@@ -1534,6 +1566,12 @@
 
         mClientModeManager.setShouldReduceNetworkScore(false);
         verify(mClientModeImpl, times(2)).setShouldReduceNetworkScore(false);
+
+        mClientModeManager.onIdleModeChanged(true);
+        verify(mClientModeImpl).onIdleModeChanged(true);
+
+        mClientModeManager.onIdleModeChanged(false);
+        verify(mClientModeImpl).onIdleModeChanged(false);
     }
 
     @Test
@@ -1542,14 +1580,14 @@
 
         IBinder iBinder = mock(IBinder.class);
         IWifiConnectedNetworkScorer iScorer = mock(IWifiConnectedNetworkScorer.class);
-        mClientModeManager.setWifiConnectedNetworkScorer(iBinder, iScorer);
-        verify(mClientModeImpl).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mClientModeManager.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
+        verify(mClientModeImpl).setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
 
         mClientModeManager.clearWifiConnectedNetworkScorer();
         verify(mClientModeImpl).clearWifiConnectedNetworkScorer();
 
-        mClientModeManager.setWifiConnectedNetworkScorer(iBinder, iScorer);
-        verify(mClientModeImpl, times(2)).setWifiConnectedNetworkScorer(iBinder, iScorer);
+        mClientModeManager.setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
+        verify(mClientModeImpl, times(2)).setWifiConnectedNetworkScorer(iBinder, iScorer, TEST_UID);
 
         mClientModeManager.onNetworkSwitchAccepted(1, "macAddress");
         verify(mClientModeImpl).onNetworkSwitchAccepted(1, "macAddress");
diff --git a/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java b/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java
index 5e8064d..9353e6a 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/DeviceConfigFacadeTest.java
@@ -230,9 +230,9 @@
         assertEquals(true, mDeviceConfigFacade.isOobPseudonymEnabled());
         mLooper.dispatchAll();
         verify(mOobPseudonymFeatureFlagChangedListener, never()).accept(anyBoolean());
-        assertEquals(false, mDeviceConfigFacade.isApplicationQosPolicyApiEnabled());
-        assertEquals(true, mDeviceConfigFacade.isAdjustPollRssiIntervalEnabled());
-        assertEquals(false, mDeviceConfigFacade.includePasspointSsidsInPnoScans());
+        assertEquals(true, mDeviceConfigFacade.isApplicationQosPolicyApiEnabled());
+        assertEquals(false, mDeviceConfigFacade.isAdjustPollRssiIntervalEnabled());
+        assertEquals(true, mDeviceConfigFacade.includePasspointSsidsInPnoScans());
         assertEquals(true, mDeviceConfigFacade.isHandleRssiOrganicKernelFailuresEnabled());
     }
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
index 6960da4..f060cdd 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java
@@ -105,11 +105,11 @@
     private static final int TEST_PEER_ID = 1;
     private static final int TEST_BOOTSTRAP_ID = 1;
     private static final int TEST_LISTEN_CHANNEL = 6;
-    private static final String TEST_SSID = "\"Test_SSID\"";
+    private static final String TEST_SSID = "\"テストSSID\""; // Make sure we test with non-ASCII
     private static final String TEST_SSID_NO_QUOTE = TEST_SSID.replace("\"", "");
-    private static final String TEST_SSID_ENCODED = "546573745f53534944";
-    private static final String TEST_PASSWORD = "\"secretPassword\"";
-    private static final String TEST_PASSWORD_ENCODED = "73656372657450617373776f7264";
+    private static final String TEST_SSID_ENCODED = "e38386e382b9e3838853534944";
+    private static final String TEST_PASSWORD = "\"テストPassword\"";
+    private static final String TEST_PASSWORD_ENCODED = "e38386e382b9e3838850617373776f7264";
     private static final int TEST_NETWORK_ID = 1;
     private static final String TEST_BSSID = "01:02:03:04:05:06";
     private static final String TEST_PACKAGE_NAME = "TestPackage";
diff --git a/service/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java b/service/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
index c365970..e257c2d 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/EapFailureNotifierTest.java
@@ -73,6 +73,7 @@
     @Mock
     WifiCarrierInfoManager mWifiCarrierInfoManager;
     @Mock WifiConfiguration mWifiConfiguration;
+    @Mock WifiGlobals mWifiGlobals;
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Notification.Builder mNotificationBuilder;
     private static final int KNOWN_ERROR_CODE = 32764;
@@ -85,7 +86,7 @@
             "Error Message Unknown Error Code";
     private final WifiBlocklistMonitor.CarrierSpecificEapFailureConfig
             mExpectedEapFailureConfig = new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(
-                    1, -1);
+                    1, -1, true);
 
     EapFailureNotifier mEapFailureNotifier;
 
@@ -118,7 +119,7 @@
         when(mFrameworkFacade.getSettingsPackageName(any())).thenReturn(TEST_SETTINGS_PACKAGE);
         mEapFailureNotifier =
                 new EapFailureNotifier(mContext, mFrameworkFacade, mWifiCarrierInfoManager,
-                        mWifiNotificationManager);
+                        mWifiNotificationManager, mWifiGlobals);
     }
 
     @After
@@ -127,6 +128,52 @@
     }
 
     /**
+     * Verify Android 14 and later EapFailureConfig override behavior based on config file.
+     */
+    @Test
+    public void onEapFailureEapFailureConfigWithOverride() {
+        WifiBlocklistMonitor.CarrierSpecificEapFailureConfig carrierOverride =
+                new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(
+                        mExpectedEapFailureConfig.threshold + 1,
+                        mExpectedEapFailureConfig.durationMs + 1000,
+                        false);
+        when(mWifiGlobals.getCarrierSpecificEapFailureConfig(anyInt(), anyInt())).thenReturn(
+                carrierOverride);
+
+        when(mFrameworkFacade.makeNotificationBuilder(any(),
+                eq(WifiService.NOTIFICATION_NETWORK_ALERTS))).thenReturn(mNotificationBuilder);
+        StatusBarNotification[] activeNotifications = new StatusBarNotification[1];
+        activeNotifications[0] = new StatusBarNotification("android", "", 56, "", 0, 0, 0,
+                mNotification, android.os.Process.myUserHandle(), 0);
+        when(mWifiNotificationManager.getActiveNotifications()).thenReturn(activeNotifications);
+        mWifiConfiguration.SSID = SSID_2;
+        WifiBlocklistMonitor.CarrierSpecificEapFailureConfig failureConfig =
+                mEapFailureNotifier.onEapFailure(KNOWN_ERROR_CODE, mWifiConfiguration, true);
+        if (SdkLevel.isAtLeastU()) {
+            // New Android U behavior will override CarrierSpecificEapFailureConfig
+            assertEquals(carrierOverride, failureConfig);
+            // Should not try to send notification since it is disabled in the overlay
+            verify(mFrameworkFacade, never()).makeNotificationBuilder(any(), any());
+
+            // Now enable displayNotification and then verify send notification is attempted
+            carrierOverride =
+                    new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(
+                            carrierOverride.threshold,
+                            carrierOverride.durationMs,
+                            true);
+            when(mWifiGlobals.getCarrierSpecificEapFailureConfig(anyInt(), anyInt())).thenReturn(
+                    carrierOverride);
+            mEapFailureNotifier.onEapFailure(KNOWN_ERROR_CODE, mWifiConfiguration, true);
+            verify(mFrameworkFacade).makeNotificationBuilder(any(), any());
+        } else {
+            // Before Android U, will try to send notification send default showNotification is
+            // true, and will not apply the eapFailureConfig override.
+            verify(mFrameworkFacade).makeNotificationBuilder(any(), any());
+            assertEquals(mExpectedEapFailureConfig, failureConfig);
+        }
+    }
+
+    /**
      * Verify that a eap failure notification will be generated with the following conditions :
      * No eap failure notification of eap failure is displayed now.
      * Error code is defined by carrier
@@ -309,7 +356,7 @@
                 mEapFailureNotifier.onEapFailure(KNOWN_ERROR_CODE, mWifiConfiguration, false);
         verify(mWifiNotificationManager, never()).notify(anyInt(), any());
         WifiBlocklistMonitor.CarrierSpecificEapFailureConfig expected =
-                new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(2, 14400000);
+                new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig(2, 14400000, true);
         assertEquals(expected, failureConfig);
     }
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 43c6d44..39b82d8 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -36,6 +36,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -52,12 +53,14 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.test.MockAnswerUtil;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
@@ -83,6 +86,7 @@
 import com.android.server.wifi.hal.WifiRttController;
 import com.android.server.wifi.hal.WifiStaIface;
 import com.android.server.wifi.util.WorkSourceHelper;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import com.google.common.collect.ImmutableList;
@@ -135,6 +139,8 @@
     @Mock private WorkSourceHelper mWorkSourceHelper0;
     @Mock private WorkSourceHelper mWorkSourceHelper1;
     @Mock private WorkSourceHelper mWorkSourceHelper2;
+    @Mock private DeviceConfigFacade mDeviceConfigFacade;
+    @Mock private FeatureFlags mFeatureFlags;
     private TestLooper mTestLooper;
     private Handler mHandler;
     private ArgumentCaptor<WifiHal.Callback> mWifiEventCallbackCaptor = ArgumentCaptor.forClass(
@@ -186,6 +192,11 @@
         when(mWorkSourceHelper1.getWorkSource()).thenReturn(TEST_WORKSOURCE_1);
         when(mWorkSourceHelper2.getWorkSource()).thenReturn(TEST_WORKSOURCE_2);
         when(mWifiInjector.getSettingsConfigStore()).thenReturn(mWifiSettingsConfigStore);
+        when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
+        when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags);
+        when(mFeatureFlags.singleWifiThread()).thenReturn(true);
+        when(mConcreteClientModeManager.getRole()).thenReturn(
+                ClientModeManager.ROLE_CLIENT_PRIMARY);
 
         when(mWifiMock.registerEventCallback(any(WifiHal.Callback.class))).thenReturn(true);
         when(mWifiMock.start()).thenReturn(WifiHal.WIFI_STATUS_SUCCESS);
@@ -843,10 +854,13 @@
                 HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_2);
         assertNull("Should not create this AP", apDetails);
         WifiInterface apIface = mDut.createApIface(
-                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_2, false, mSoftApManager);
+                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_2, false, mSoftApManager,
+                new ArrayList<>());
         collector.checkThat("AP was created", apIface, IsNull.nullValue());
 
         // Switch STA to secondary internet. Foreground AP can be created now.
+        when(mConcreteClientModeManager.getRole()).thenReturn(
+                ClientModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED);
         when(mConcreteClientModeManager.isSecondaryInternet()).thenReturn(true);
         apDetails = mDut.reportImpactToCreateIface(
                 HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_2);
@@ -944,6 +958,9 @@
         networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
         connectionIntent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo);
         brCaptor.getValue().onReceive(mContext, connectionIntent);
+        assertFalse("Should not treat disconnected P2P as privileged iface",
+                mDut.creatingIfaceWillDeletePrivilegedIface(
+                        HDM_CREATE_IFACE_NAN, TEST_WORKSOURCE_2));
         nanDetails = mDut.reportImpactToCreateIface(
                 HDM_CREATE_IFACE_NAN, true, TEST_WORKSOURCE_2);
         assertNotNull("Should create this NAN", nanDetails);
@@ -1195,6 +1212,7 @@
      */
     @Test
     public void testOnDestroyedWithHandlerTriggeredOnDifferentThread() throws Exception {
+        when(mFeatureFlags.singleWifiThread()).thenReturn(false);
         long currentThreadId = 983757; // arbitrary current thread ID
         when(mWifiInjector.getCurrentThreadId()).thenReturn(currentThreadId);
         // RETURNS_DEEP_STUBS allows mocking nested method calls
@@ -1226,6 +1244,7 @@
      */
     @Test
     public void testOnDestroyedWaitingWithHandlerTriggeredOnDifferentThread() throws Exception {
+        when(mFeatureFlags.singleWifiThread()).thenReturn(false);
         // Enable waiting for destroy listeners
         mWaitForDestroyedListeners = true;
         // Setup a separate thread for destroy
@@ -1350,7 +1369,7 @@
         WifiApIface apIface = mock(WifiApIface.class);
         doAnswer(new GetNameAnswer("wlan0")).when(apIface).getName();
         doAnswer(new CreateApIfaceAnswer(chipMock, true, apIface))
-                .when(chipMock.chip).createApIface();
+                .when(chipMock.chip).createApIface(anyList());
         List<Pair<Integer, WorkSource>> apDetails = mDut.reportImpactToCreateIface(
                 HDM_CREATE_IFACE_AP, false, TEST_WORKSOURCE_0);
         assertEquals("Should get STA destroy details", 1, apDetails.size());
@@ -1361,7 +1380,8 @@
         assertEquals("Need to destroy the STA",
                 Pair.create(WifiChip.IFACE_TYPE_STA, TEST_WORKSOURCE_0), apDetails.get(0));
         assertEquals(apIface, mDut.createApIface(
-                CHIP_CAPABILITY_ANY, apIdl, mHandler, TEST_WORKSOURCE_0, false, mSoftApManager));
+                CHIP_CAPABILITY_ANY, apIdl, mHandler, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>()));
         mInOrder.verify(chipMock.chip).removeStaIface(getName(staIface));
         mInOrder.verify(staIdl).onDestroyed(getName(staIface));
         mInOrder.verify(chipMock.chip).configureChip(TestChipV1.AP_CHIP_MODE_ID);
@@ -1424,7 +1444,7 @@
         WifiApIface apIface = mock(WifiApIface.class);
         doAnswer(new GetNameAnswer("wlan0")).when(apIface).getName();
         doAnswer(new CreateApIfaceAnswer(chipMock, true, apIface))
-                .when(chipMock.chip).createApIface();
+                .when(chipMock.chip).createApIface(anyList());
         List<Pair<Integer, WorkSource>> apDetails = mDut.reportImpactToCreateIface(
                 HDM_CREATE_IFACE_AP, false, TEST_WORKSOURCE_0);
         assertEquals("Should get STA destroy details", 1, apDetails.size());
@@ -1435,7 +1455,8 @@
         assertEquals("Need to destroy the STA",
                 Pair.create(WifiChip.IFACE_TYPE_STA, TEST_WORKSOURCE_0), apDetails.get(0));
         assertEquals(apIface, mDut.createApIface(
-                CHIP_CAPABILITY_ANY, apIdl, mHandler, TEST_WORKSOURCE_0, false, mSoftApManager));
+                CHIP_CAPABILITY_ANY, apIdl, mHandler, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>()));
         mInOrder.verify(chipMock.chip).removeStaIface(getName(staIface));
         mInOrder.verify(staIdl).onDestroyed(getName(staIface));
         mInOrder.verify(chipMock.chip).configureChip(TestChipV1.AP_CHIP_MODE_ID);
@@ -1479,9 +1500,10 @@
         WifiApIface apIface = mock(WifiApIface.class);
         doAnswer(new GetNameAnswer("wlan0")).when(apIface).getName();
         doAnswer(new CreateApIfaceAnswer(chipMock, true, apIface))
-                .when(chipMock.chip).createApIface();
+                .when(chipMock.chip).createApIface(anyList());
         assertNull(mDut.createApIface(
-                CHIP_CAPABILITY_ANY, idl, null, TEST_WORKSOURCE_0, false, mSoftApManager));
+                CHIP_CAPABILITY_ANY, idl, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>()));
 
         // Create NAN Iface will be failure because null handler.
         WifiNanIface nanIface = mock(WifiNanIface.class);
@@ -1663,7 +1685,6 @@
     public void testCanDeviceSupportCreateTypeComboChipV1() throws Exception {
         TestChipV1 chipMock = new TestChipV1();
         chipMock.initialize();
-        chipMock.allowGetCapsBeforeIfaceCreated = false;
         mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock);
 
         // Try to query iface support before starting the HAL. Should return false without any
@@ -1800,6 +1821,100 @@
         verifyNoMoreInteractions(mManagerStatusListenerMock);
     }
 
+    /**
+     * Validates that {@link HalDeviceManager#canDeviceSupportCreateTypeCombo(SparseArray)} with
+     * outdated stored static chip info will be updated once we load the chip info when the driver
+     * is up.
+     */
+    @Test
+    public void testCanDeviceSupportCreateTypeComboChipV1WithOutdatedStoredStaticChipInfo()
+            throws Exception {
+        TestChipV1 chipMock = new TestChipV1();
+        chipMock.initialize();
+        mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock);
+
+        // Try to query iface support before starting the HAL. Should return false with the outdated
+        // stored static chip info that's missing AP capabilities.
+        String outdatedStaticChipInfo =
+                        "["
+                        + "    {"
+                        + "        \"chipId\": 10,"
+                        + "        \"chipCapabilities\": -1,"
+                        + "        \"availableModes\": ["
+                        + "            {"
+                        + "                \"id\": 0,"
+                        + "                \"availableCombinations\": ["
+                        + "                    {"
+                        + "                        \"limits\": ["
+                        + "                            {"
+                        + "                                \"maxIfaces\": 1,"
+                        + "                                \"types\": [0]"
+                        + "                            },"
+                        + "                            {"
+                        + "                                \"maxIfaces\": 1,"
+                        + "                                \"types\": [3, 4]"
+                        + "                            }"
+                        + "                        ]"
+                        + "                    }"
+                        + "                ]"
+                        + "            }"
+                        + "        ]"
+                        + "    }"
+                        + "]";
+        when(mWifiMock.isStarted()).thenReturn(false);
+        when(mWifiSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO))
+                .thenReturn(outdatedStaticChipInfo);
+
+        // Can create a NAN but not an AP from the stored static chip info
+        assertTrue(
+                mDut.canDeviceSupportCreateTypeCombo(
+                        new SparseArray<Integer>() {
+                            {
+                                put(WifiChip.IFACE_CONCURRENCY_TYPE_NAN, 1);
+                            }
+                        }));
+        assertFalse(
+                mDut.canDeviceSupportCreateTypeCombo(
+                        new SparseArray<Integer>() {
+                            {
+                                put(WifiChip.IFACE_CONCURRENCY_TYPE_AP, 1);
+                            }
+                        }));
+
+        verify(mWifiMock, never()).getChipIds();
+        when(mWifiMock.isStarted()).thenReturn(true);
+        executeAndValidateStartupSequence();
+        clearInvocations(mWifiMock);
+
+        // Create a STA to get the static chip info from driver and save it to store.
+        validateInterfaceSequence(
+                chipMock,
+                false, // chipModeValid
+                -1000, // chipModeId (only used if chipModeValid is true)
+                HDM_CREATE_IFACE_STA, // ifaceTypeToCreate
+                "wlan0", // ifaceName
+                TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                mock(InterfaceDestroyedListener.class), // destroyedListener
+                TEST_WORKSOURCE_0 // requestorWs
+        );
+
+        // Verify that the latest static chip info is saved to store.
+        verify(mWifiSettingsConfigStore)
+                .put(
+                        eq(WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO),
+                        eq(new JSONArray(TestChipV1.STATIC_CHIP_INFO_JSON_STRING).toString()));
+
+        // Now we can create an AP.
+        assertTrue(
+                mDut.canDeviceSupportCreateTypeCombo(
+                        new SparseArray<Integer>() {
+                            {
+                                put(WifiChip.IFACE_CONCURRENCY_TYPE_AP, 1);
+                            }
+                        }));
+    }
+
     @Test
     public void testIsItPossibleToCreateIfaceTestChipV1() throws Exception {
         assumeTrue(SdkLevel.isAtLeastS());
@@ -2006,7 +2121,8 @@
         apDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_0);
         assertNull("Should not be able to create a new AP", apDetails);
         WifiInterface apIface2 = mDut.createApIface(
-                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager);
+                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>());
         collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
 
         // tear down AP
@@ -2319,8 +2435,9 @@
      * - request P2P (privileged app): failure
      * - tear down AP
      * - create STA (system app)
+     * - create P2P (system app): will get refused
      * - create STA (system app): will get refused
-     * - create NAN (privileged app): should tear down last created STA
+     * - make STA secondary, create P2P (privileged app): should tear down STA
      * - create STA (foreground app): will get refused
      */
     @Test
@@ -2424,7 +2541,8 @@
         apDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_0);
         assertNull("Should not create this AP", apDetails);
         WifiInterface apIface2 = mDut.createApIface(
-                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager);
+                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>());
         collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
 
         // request P2P (system app): should fail
@@ -2462,6 +2580,12 @@
         );
         collector.checkThat("STA 2 interface wasn't created", staIface2, IsNull.notNullValue());
 
+        // request P2P (system app): should fail even though both are privileged
+        p2pDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_P2P, true, TEST_WORKSOURCE_0);
+        assertNull("should not create this p2p", p2pDetails);
+        p2pIfaceName = mDut.createP2pIface(null, null, TEST_WORKSOURCE_0);
+        collector.checkThat("P2P should not be created", p2pIfaceName, IsNull.nullValue());
+
         // request STA3 (system app): should fail
         staDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_STA, false, TEST_WORKSOURCE_0);
         assertNotNull("should not fail when asking for same STA", staDetails);
@@ -2472,20 +2596,22 @@
                 mConcreteClientModeManager);
         collector.checkThat("STA3 should not be created", staIface3, IsNull.nullValue());
 
-        // create NAN (privileged app): should destroy the last created STA (STA2)
-        nanIface = validateInterfaceSequence(chipMock,
+        // Make STA2 secondary and create P2P (privileged app): should destroy the STA
+        when(mConcreteClientModeManager.getRole())
+                .thenReturn(ClientModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED);
+        p2pIface = validateInterfaceSequence(chipMock,
                 true, // chipModeValid
                 TestChipV3.CHIP_MODE_ID, // chipModeId
-                HDM_CREATE_IFACE_NAN, // ifaceTypeToCreate
+                HDM_CREATE_IFACE_P2P, // ifaceTypeToCreate
                 "wlan0", // ifaceName
                 TestChipV3.CHIP_MODE_ID, // finalChipMode
                 new WifiInterface[]{staIface2}, // tearDownList
-                nanDestroyedListener, // destroyedListener
+                p2pDestroyedListener, // destroyedListener
                 TEST_WORKSOURCE_2, // requestorWs
                 new InterfaceDestroyedListenerWithIfaceName(
                         getName(staIface2), staDestroyedListener2)
         );
-        collector.checkThat("NAN interface wasn't created", nanIface, IsNull.notNullValue());
+        collector.checkThat("P2P interface wasn't created", p2pIface, IsNull.notNullValue());
 
         verify(chipMock.chip).removeStaIface("wlan1");
         verify(staDestroyedListener2).onDestroyed(getName(staIface2));
@@ -2755,7 +2881,8 @@
         apDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_0);
         assertNull("Should not create this AP", apDetails);
         WifiInterface apIface2 = mDut.createApIface(
-                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager);
+                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>());
         collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
 
         // request P2P (system app): should fail
@@ -2920,7 +3047,8 @@
         apDetails = mDut.reportImpactToCreateIface(HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_0);
         assertNull("Should not create this AP", apDetails);
         WifiInterface apIface2 = mDut.createApIface(
-                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager);
+                CHIP_CAPABILITY_ANY, null, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                new ArrayList<>());
         collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
 
         // request P2P: should fail
@@ -3235,9 +3363,8 @@
             );
             collector.checkThat("STA created", staIface, IsNull.notNullValue());
         } else {
-            List<Pair<Integer, WorkSource>> staDetails = mDut.reportImpactToCreateIface(
-                    HDM_CREATE_IFACE_STA, true, requiredChipCapabilities, TEST_WORKSOURCE_1);
-            assertNull("Should not create this STA", staDetails);
+            assertFalse("Should not be able to create this STA", mDut.isItPossibleToCreateIface(
+                    HDM_CREATE_IFACE_STA, requiredChipCapabilities, TEST_WORKSOURCE_1));
             staIface = mDut.createStaIface(
                     requiredChipCapabilities, null, null, TEST_WORKSOURCE_1,
                     mConcreteClientModeManager);
@@ -3263,11 +3390,11 @@
             );
             collector.checkThat("AP created", apIface, IsNull.notNullValue());
         } else {
-            List<Pair<Integer, WorkSource>> apDetails = mDut.reportImpactToCreateIface(
-                    HDM_CREATE_IFACE_AP, true, requiredChipCapabilities, TEST_WORKSOURCE_0);
-            assertNull("Should not create this AP", apDetails);
+            assertFalse("Should not be able to create this AP", mDut.isItPossibleToCreateIface(
+                    HDM_CREATE_IFACE_AP, requiredChipCapabilities, TEST_WORKSOURCE_0));
             apIface = mDut.createApIface(
-                    requiredChipCapabilities, null, null, TEST_WORKSOURCE_0, false, mSoftApManager);
+                    requiredChipCapabilities, null, null, TEST_WORKSOURCE_0, false, mSoftApManager,
+                    new ArrayList<>());
             collector.checkThat("AP should not be created", apIface, IsNull.nullValue());
         }
         if (SdkLevel.isAtLeastS()) {
@@ -4133,7 +4260,7 @@
 
         // check if can create interface
         List<Pair<Integer, WorkSource>> details = mDut.reportImpactToCreateIface(
-                createIfaceType, true, requiredChipCapabilities, requestorWs);
+                createIfaceType, true, requestorWs);
         if (tearDownList == null || tearDownList.length == 0) {
             assertTrue("Details list must be empty - can create" + details, details.isEmpty());
         } else { // TODO: assumes that at most a single entry - which is the current usage
@@ -4164,12 +4291,13 @@
                             .when((WifiApIface) iface).getBridgedInstances();
                 }
                 doAnswer(new CreateApIfaceAnswer(chipMock, true, iface))
-                        .when(chipMock.chip).createApIface();
+                        .when(chipMock.chip).createApIface(anyList());
                 doAnswer(new CreateApIfaceAnswer(chipMock, true, iface))
-                        .when(chipMock.chip).createBridgedApIface();
+                        .when(chipMock.chip).createBridgedApIface(anyList());
                 mDut.createApIface(requiredChipCapabilities,
                         destroyedListener, mHandler, requestorWs,
-                        createIfaceType == HDM_CREATE_IFACE_AP_BRIDGE, mSoftApManager);
+                        createIfaceType == HDM_CREATE_IFACE_AP_BRIDGE, mSoftApManager,
+                        new ArrayList<>());
                 break;
             case HDM_CREATE_IFACE_P2P:
                 iface = mock(WifiP2pIface.class);
@@ -4221,10 +4349,10 @@
                 mInOrder.verify(chipMock.chip).createStaIface();
                 break;
             case HDM_CREATE_IFACE_AP_BRIDGE:
-                mInOrder.verify(chipMock.chip).createBridgedApIface();
+                mInOrder.verify(chipMock.chip).createBridgedApIface(anyList());
                 break;
             case HDM_CREATE_IFACE_AP:
-                mInOrder.verify(chipMock.chip).createApIface();
+                mInOrder.verify(chipMock.chip).createApIface(anyList());
                 break;
             case HDM_CREATE_IFACE_P2P:
                 mInOrder.verify(chipMock.chip).createP2pIface();
@@ -4489,7 +4617,7 @@
             super(chipMockBase, success, wifiIface, WifiChip.IFACE_TYPE_AP);
         }
 
-        public WifiApIface answer() {
+        public WifiApIface answer(@NonNull List<OuiKeyedData> vendorData) {
             addInterfaceInfo();
             return mSuccess ? (WifiApIface) mWifiIface : null;
         }
@@ -4712,7 +4840,7 @@
         static final String STATIC_CHIP_INFO_JSON_STRING = "["
                 + "    {"
                 + "        \"chipId\": 10,"
-                + "        \"chipCapabilities\": -1,"
+                + "        \"chipCapabilities\": 0,"
                 + "        \"availableModes\": ["
                 + "            {"
                 + "                \"id\": 0,"
diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java
index 4656d43..8fd5c3e 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java
@@ -17,9 +17,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -30,6 +32,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -50,13 +53,16 @@
 import android.hardware.wifi.hostapd.IfaceParams;
 import android.hardware.wifi.hostapd.NetworkParams;
 import android.net.MacAddress;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.SoftApConfiguration.Builder;
 import android.net.wifi.WifiManager;
+import android.net.wifi.util.PersistableBundleUtils;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.test.TestLooper;
@@ -76,6 +82,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Unit tests for HostapdHal
@@ -83,6 +90,7 @@
 @SmallTest
 public class HostapdHalAidlImpTest extends WifiBaseTest {
     private static final String IFACE_NAME = "mock-wlan0";
+    private static final String IFACE_NAME_1 = "mock-wlan1";
     private static final String NETWORK_SSID = "test-ssid";
     private static final String NETWORK_PSK = "test-psk";
     private static final String TEST_CLIENT_MAC = "11:22:33:44:55:66";
@@ -101,6 +109,7 @@
     private @Mock IBinder mServiceBinderMock;
     private @Mock WifiNative.HostapdDeathEventHandler mHostapdHalDeathHandler;
     private @Mock WifiNative.SoftApHalCallback mSoftApHalCallback;
+    private @Mock WifiNative.SoftApHalCallback mSoftApHalCallback1;
 
     private IHostapdCallback mIHostapdCallback;
     private MockResources mResources;
@@ -140,11 +149,12 @@
         }
     }
 
-    private void mockApInfoChangedAndVerify(int numOfApInfo, IHostapdCallback mockHostapdCallback,
+    private void mockApInfoChangedAndVerify(String ifaceName, int numOfApInfo,
+            IHostapdCallback mockHostapdCallback,
             WifiNative.SoftApHalCallback mockSoftApHalCallback) throws Exception {
         // Trigger on info changed.
         ApInfo apInfo = new ApInfo();
-        apInfo.ifaceName = IFACE_NAME;
+        apInfo.ifaceName = ifaceName;
         apInfo.apIfaceInstance = TEST_AP_INSTANCE;
         apInfo.freqMhz = TEST_FREQ_24G;
         apInfo.channelBandwidth = TEST_BANDWIDTH;
@@ -866,15 +876,24 @@
                 configurationBuilder.build(), true,
                 () -> mSoftApHalCallback.onFailure()));
         verify(mIHostapdMock).addAccessPoint(any(), any());
+        // Register SoftApManager callback
+        mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback);
+
+        // Add second AP to test that the callbacks are triggered for the correct iface.
+        assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME_1,
+                configurationBuilder.build(), true,
+                () -> mSoftApHalCallback1.onFailure()));
+        mHostapdHal.registerApCallback(IFACE_NAME_1, mSoftApHalCallback1);
 
         // Trigger on failure.
         mIHostapdCallback.onFailure(IFACE_NAME, IFACE_NAME);
         verify(mSoftApHalCallback).onFailure();
-        // Register SoftApManager callback
-        mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback);
+        verify(mSoftApHalCallback1, never()).onFailure();
 
         // Trigger on info changed and verify.
-        mockApInfoChangedAndVerify(1, mIHostapdCallback, mSoftApHalCallback);
+        mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback);
+        verify(mSoftApHalCallback1, never()).onInfoChanged(anyString(), anyInt(), anyInt(),
+                anyInt(), any());
 
         // Trigger on client connected.
         ClientInfo clientInfo = new ClientInfo();
@@ -885,6 +904,8 @@
         mIHostapdCallback.onConnectedClientsChanged(clientInfo);
         verify(mSoftApHalCallback).onConnectedClientsChanged(eq(TEST_AP_INSTANCE),
                 eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true));
+        verify(mSoftApHalCallback1, never()).onConnectedClientsChanged(
+                anyString(), any(), anyBoolean());
     }
 
     /**
@@ -1172,7 +1193,7 @@
         mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback);
 
         // Trigger on info changed and verify.
-        mockApInfoChangedAndVerify(2, mIHostapdCallback, mSoftApHalCallback);
+        mockApInfoChangedAndVerify(IFACE_NAME, 2, mIHostapdCallback, mSoftApHalCallback);
 
         // Trigger on failure from first instance.
         mIHostapdCallback.onFailure(IFACE_NAME, TEST_AP_INSTANCE);
@@ -1183,4 +1204,43 @@
         verify(mSoftApHalCallback).onFailure();
 
     }
+
+    /**
+     * Verifies that SoftApConfigurations containing OEM-specific vendor data
+     * are handled currently in addAccessPoint.
+     */
+    @Test
+    public void testAddAccessPointWithVendorData() throws Exception {
+        assumeTrue(SdkLevel.isAtLeastV());
+        when(mIHostapdMock.getInterfaceVersion()).thenReturn(2);
+        executeAndValidateInitializationSequence(true);
+
+        Builder configurationBuilder = new SoftApConfiguration.Builder();
+        configurationBuilder.setSsid(NETWORK_SSID);
+        doNothing().when(mIHostapdMock).addAccessPoint(mIfaceParamsCaptor.capture(), any());
+
+        // SoftApConfig does not contain vendor data.
+        assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME,
+                configurationBuilder.build(), true,
+                () -> mSoftApHalCallback.onFailure()));
+        verify(mIHostapdMock).addAccessPoint(any(), any());
+        assertNull(mIfaceParamsCaptor.getValue().vendorData);
+
+        int oui = 0x00114477;
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString("fieldKey", "someStringValue");
+        OuiKeyedData frameworkData = new OuiKeyedData.Builder(oui, bundle).build();
+        configurationBuilder.setVendorData(Arrays.asList(frameworkData));
+
+        // SoftApConfig contains vendor data.
+        assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME,
+                configurationBuilder.build(), true,
+                () -> mSoftApHalCallback.onFailure()));
+        verify(mIHostapdMock, times(2)).addAccessPoint(any(), any());
+        android.hardware.wifi.common.OuiKeyedData[] halDataList =
+                mIfaceParamsCaptor.getValue().vendorData;
+        assertEquals(1, halDataList.length);
+        assertEquals(oui, halDataList[0].oui);
+        assertTrue(PersistableBundleUtils.isEqual(bundle, halDataList[0].vendorData));
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java
index 27bb496..44dc151 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java
@@ -66,6 +66,7 @@
 @SmallTest
 public class HostapdHalHidlImpTest extends WifiBaseTest {
     private static final String IFACE_NAME = "mock-wlan0";
+    private static final String IFACE_NAME_1 = "mock-wlan1";
     private static final String NETWORK_SSID = "test-ssid";
     private static final String NETWORK_PSK = "test-psk";
     private static final String TEST_CLIENT_MAC = "11:22:33:44:55:66";
@@ -79,6 +80,7 @@
     private @Mock IHostapd mIHostapdMock;
     private @Mock WifiNative.HostapdDeathEventHandler mHostapdHalDeathHandler;
     private @Mock WifiNative.SoftApHalCallback mSoftApHalCallback;
+    private @Mock WifiNative.SoftApHalCallback mSoftApHalCallback1;
     private android.hardware.wifi.hostapd.V1_1.IHostapd mIHostapdMockV11;
     private android.hardware.wifi.hostapd.V1_2.IHostapd mIHostapdMockV12;
     private android.hardware.wifi.hostapd.V1_3.IHostapd mIHostapdMockV13;
@@ -1295,12 +1297,19 @@
                 configurationBuilder.build(), true,
                 () -> mSoftApHalCallback.onFailure()));
         verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any());
+        // Register SoftApManager callback
+        mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback);
+
+        // Add second AP to test that the callbacks are triggered for the correct iface.
+        assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME_1,
+                configurationBuilder.build(), true,
+                () -> mSoftApHalCallback1.onFailure()));
+        mHostapdHal.registerApCallback(IFACE_NAME_1, mSoftApHalCallback1);
 
         // Trigger on failure.
         mIHostapdCallback13.onFailure(IFACE_NAME);
         verify(mSoftApHalCallback).onFailure();
-        // Register SoftApManager callback
-        mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback);
+        verify(mSoftApHalCallback1, never()).onFailure();
 
         int testFreq = 2412;
         int testBandwidth = Bandwidth.WIFI_BANDWIDTH_20;
@@ -1313,12 +1322,16 @@
                 eq(mHostapdHal.mapHalBandwidthToSoftApInfo(testBandwidth)),
                 eq(mHostapdHal.mapHalGenerationToWifiStandard(testGeneration)),
                 eq(MacAddress.fromString(TEST_CLIENT_MAC)));
+        verify(mSoftApHalCallback1, never()).onInfoChanged(anyString(), anyInt(), anyInt(),
+                anyInt(), any());
 
         // Trigger on client connected.
         mIHostapdCallback13.onConnectedClientsChanged(IFACE_NAME, TEST_AP_INSTANCE,
                 MacAddress.fromString(TEST_CLIENT_MAC).toByteArray(), true);
         verify(mSoftApHalCallback).onConnectedClientsChanged(eq(TEST_AP_INSTANCE),
                 eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true));
+        verify(mSoftApHalCallback1, never()).onConnectedClientsChanged(anyString(), any(),
+                anyBoolean());
     }
 
     /**
diff --git a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java
index 2e1d3e9..af60b42 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java
@@ -19,6 +19,7 @@
 import static com.android.server.wifi.InsecureEapNetworkHandler.TOFU_ANONYMOUS_IDENTITY;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
@@ -597,7 +598,7 @@
         setupTest(config, true, true);
 
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(type);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
         verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, true,
                 true, false, null, mockServerCert.getCert(),
                 null);
@@ -621,8 +622,8 @@
         // CA_CERT is also in the trust store
         CertificateEventInfo mockCaCert = generateMockCertEventInfo(TEST_GEN_CA_CERT);
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(TEST_GEN_SERVER_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1, mockCaCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
 
         List<String> aliases = new ArrayList<>();
         aliases.add("TEST_GEN_CA_CERT");
@@ -763,8 +764,8 @@
 
         CertificateEventInfo mockCaCert = generateMockCertEventInfo(TEST_GEN_CA_CERT);
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(TEST_GEN_SERVER_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1, mockCaCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
 
         verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported,
                 isUserSelected, needUserApproval, mockCaCert.getCert(), mockServerCert.getCert(),
@@ -788,7 +789,7 @@
 
         CertificateEventInfo mockSelfSignedCert =
                 generateMockCertEventInfo(TEST_GEN_SELF_SIGNED_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockSelfSignedCert);
 
         verifyTrustOnFirstUseFlow(config, ACTION_FORGET, isTrustOnFirstUseSupported,
                 isUserSelected, needUserApproval, mockSelfSignedCert.getCert(),
@@ -832,9 +833,9 @@
         config.enterpriseConfig.enableTrustOnFirstUse(false);
         setupTest(config, isAtLeastT, isTrustOnFirstUseSupported);
 
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1,
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1,
                 generateMockCertEventInfo(TEST_GEN_CA_CERT));
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0,
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0,
                 generateMockCertEventInfo(TEST_GEN_SERVER_CERT));
 
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
@@ -860,9 +861,9 @@
         setupTest(config, isAtLeastT, isTrustOnFirstUseSupported,
                 isInsecureEnterpriseConfigurationAllowed);
 
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1,
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1,
                 generateMockCertEventInfo(TEST_GEN_CA_CERT));
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0,
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0,
                 generateMockCertEventInfo(TEST_GEN_SERVER_CERT));
 
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
@@ -883,8 +884,8 @@
         CertificateEventInfo mockCaCert = generateMockCertEventInfo(TEST_GEN_CA2_CERT);
         // Missing intermediate cert.
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(TEST_GEN_SERVER_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1, mockCaCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
 
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
         verify(mCallbacks).onError(eq(config.SSID));
@@ -909,8 +910,8 @@
         // Fake Root CA that didn't sign the server cert
         CertificateEventInfo mockCaCert = generateMockCertEventInfo(TEST_GEN_FAKE_CA_CERT);
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(TEST_GEN_SERVER_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1, mockCaCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
 
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
         verify(mCallbacks).onError(eq(config.SSID));
@@ -940,7 +941,9 @@
                 mHandler);
         CertificateEventInfo mockSelfSignedCert =
                 generateMockCertEventInfo(TEST_GEN_SELF_SIGNED_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate("NotExist", 0, mockSelfSignedCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(WifiConfiguration.INVALID_NETWORK_ID, 0,
+                mockSelfSignedCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(0xF1AF, 0, mockSelfSignedCert);
     }
 
     @Test
@@ -952,7 +955,7 @@
         setupTest(config, isAtLeastT, isTrustOnFirstUseSupported);
 
         // Missing root CA cert.
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0,
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0,
                 generateMockCertEventInfo(TEST_GEN_SERVER_CERT));
 
         // The wrong cert chain should be cleared after this call.
@@ -960,7 +963,7 @@
 
         CertificateEventInfo mockSelfSignedCert =
                 generateMockCertEventInfo(TEST_GEN_SELF_SIGNED_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockSelfSignedCert);
 
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
         verify(mCallbacks, never()).onError(any());
@@ -976,7 +979,7 @@
 
         CertificateEventInfo mockSelfSignedCert =
                 generateMockCertEventInfo(TEST_GEN_SELF_SIGNED_CERT);
-        mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert);
+        mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockSelfSignedCert);
 
         // Pass another PSK config which is not the same as the current one.
         WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork();
@@ -989,6 +992,8 @@
         WifiConfiguration anotherEapConfig = spy(WifiConfigurationTestUtil.createEapNetwork(
                 WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE));
         anotherEapConfig.networkId = FRAMEWORK_NETWORK_ID + 1;
+        assertFalse(mInsecureEapNetworkHandler.addPendingCertificate(anotherEapConfig.networkId, 0,
+                mockSelfSignedCert));
         mInsecureEapNetworkHandler.prepareConnection(anotherEapConfig);
         mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected);
         verify(mCallbacks, never()).onError(any());
@@ -1000,8 +1005,8 @@
         CertificateEventInfo mockCaCert = generateMockCertEventInfo(TEST_GEN_CA_CERT);
         CertificateEventInfo mockServerCert = generateMockCertEventInfo(TEST_GEN_SERVER_CERT);
         if (isTrustOnFirstUseSupported) {
-            mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert);
-            mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert);
+            mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 1, mockCaCert);
+            mInsecureEapNetworkHandler.addPendingCertificate(config.networkId, 0, mockServerCert);
         }
         verifyTrustOnFirstUseFlow(config, action, isTrustOnFirstUseSupported,
                 isUserSelected, needUserApproval, mockCaCert.getCert(), mockServerCert.getCert(),
@@ -1075,27 +1080,36 @@
             verify(mWifiConfigManager).updateNetworkSelectionStatus(eq(config.networkId),
                     eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE));
             if (isTrustOnFirstUseSupported) {
+                int postConnectionMethod;
                 if (!TextUtils.isEmpty(expectedCaPath)) {
                     // Simulate Root CA from trust store
+                    postConnectionMethod = WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA;
                     verify(mWifiConfigManager).updateCaCertificate(
                             eq(config.networkId), eq(expectedCaCert), eq(expectedServerCert),
                             eq(null), eq(true));
                 } else if (expectedCaCert == null) {
                     // Simulate server cert pinning case where there is no Root CA
+                    postConnectionMethod = WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING;
                     verify(mWifiConfigManager).updateCaCertificate(
                             eq(config.networkId), eq(expectedServerCert), eq(expectedServerCert),
                             eq("12345678"), eq(false)); // Server certificate hash
                 } else {
+                    postConnectionMethod = WifiEnterpriseConfig.TOFU_STATE_CONFIGURE_ROOT_CA;
                     verify(mWifiConfigManager).updateCaCertificate(
                             eq(config.networkId), eq(expectedCaCert), eq(expectedServerCert),
                             eq(null), eq(false)); // Cert pinning not used
                 }
+                verify(mWifiConfigManager).setTofuPostConnectionState(
+                        eq(config.networkId), eq(postConnectionMethod));
             } else {
                 verify(mWifiConfigManager, never()).updateCaCertificate(
                         anyInt(), any(), any(), any(), anyBoolean());
+                verify(mWifiConfigManager, never()).setTofuPostConnectionState(anyInt(), anyInt());
             }
+            verify(mWifiConfigManager).setTofuDialogApproved(eq(config.networkId), eq(true));
             verify(mCallbacks).onAccept(eq(config.SSID), eq(config.networkId));
         } else if (action == ACTION_REJECT) {
+            verify(mWifiConfigManager).setTofuDialogApproved(eq(config.networkId), eq(false));
             verify(mWifiConfigManager, atLeastOnce())
                     .updateNetworkSelectionStatus(eq(config.networkId),
                             eq(WifiConfiguration.NetworkSelectionStatus
diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
index 23a58b2..3598cf1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
@@ -99,6 +99,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -160,6 +161,7 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">DHCP</string>\n"
@@ -186,6 +188,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -247,15 +250,20 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">DHCP</string>\n"
                     + "<string name=\"ProxySettings\">NONE</string>\n"
                     + "</IpConfiguration>\n"
                     + "<WifiEnterpriseConfiguration>\n"
-                    + "<string name=\"Identity\">" + TEST_IDENTITY + "</string>\n"
+                    + "<string name=\"Identity\">"
+                    + TEST_IDENTITY
+                    + "</string>\n"
                     + "<string name=\"AnonIdentity\"></string>\n"
-                    + "<string name=\"Password\">" + TEST_EAP_PASSWORD + "</string>\n"
+                    + "<string name=\"Password\">"
+                    + TEST_EAP_PASSWORD
+                    + "</string>\n"
                     + "<string name=\"ClientCert\"></string>\n"
                     + "<string name=\"CaCert\"></string>\n"
                     + "<string name=\"SubjectMatch\"></string>\n"
@@ -274,13 +282,14 @@
                     + "<boolean name=\"AppInstalledRootCaCert\" value=\"false\" />\n"
                     + "<boolean name=\"AppInstalledPrivateKey\" value=\"false\" />\n"
                     + "<null name=\"KeyChainAlias\" />\n"
-                    + (SdkLevel.isAtLeastS()
-                    ? "<null name=\"DecoratedIdentityPrefix\" />\n" : "")
+                    + (SdkLevel.isAtLeastS() ? "<null name=\"DecoratedIdentityPrefix\" />\n" : "")
                     + "<boolean name=\"TrustOnFirstUse\" value=\"false\" />\n"
                     + "<boolean name=\"UserApproveNoCaCert\" value=\"false\" />\n"
                     + "<int name=\"MinimumTlsVersion\" value=\"3\" />\n"
+                    + "<int name=\"TofuDialogState\" value=\"0\" />\n"
+                    + "<int name=\"TofuConnectionState\" value=\"0\" />\n"
                     + "</WifiEnterpriseConfiguration>\n"
-                    + "</Network>\n";;
+                    + "</Network>\n";
 
     private static final String SINGLE_SAE_NETWORK_DATA_XML_STRING_FORMAT =
             "<Network>\n"
@@ -302,6 +311,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -355,6 +365,7 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">DHCP</string>\n"
@@ -381,6 +392,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -441,15 +453,20 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">DHCP</string>\n"
                     + "<string name=\"ProxySettings\">NONE</string>\n"
                     + "</IpConfiguration>\n"
                     + "<WifiEnterpriseConfiguration>\n"
-                    + "<string name=\"Identity\">" + TEST_IDENTITY + "</string>\n"
+                    + "<string name=\"Identity\">"
+                    + TEST_IDENTITY
+                    + "</string>\n"
                     + "<string name=\"AnonIdentity\"></string>\n"
-                    + "<string name=\"Password\">" + TEST_EAP_PASSWORD + "</string>\n"
+                    + "<string name=\"Password\">"
+                    + TEST_EAP_PASSWORD
+                    + "</string>\n"
                     + "<string name=\"ClientCert\"></string>\n"
                     + "<string name=\"CaCert\"></string>\n"
                     + "<string name=\"SubjectMatch\"></string>\n"
@@ -468,20 +485,18 @@
                     + "<boolean name=\"AppInstalledRootCaCert\" value=\"false\" />\n"
                     + "<boolean name=\"AppInstalledPrivateKey\" value=\"false\" />\n"
                     + "<null name=\"KeyChainAlias\" />\n"
-                    + (SdkLevel.isAtLeastS()
-                    ? "<null name=\"DecoratedIdentityPrefix\" />\n" : "")
+                    + (SdkLevel.isAtLeastS() ? "<null name=\"DecoratedIdentityPrefix\" />\n" : "")
                     + "<boolean name=\"TrustOnFirstUse\" value=\"false\" />\n"
                     + "<boolean name=\"UserApproveNoCaCert\" value=\"false\" />\n"
                     + "<int name=\"MinimumTlsVersion\" value=\"0\" />\n"
+                    + "<int name=\"TofuDialogState\" value=\"0\" />\n"
+                    + "<int name=\"TofuConnectionState\" value=\"0\" />\n"
                     + "</WifiEnterpriseConfiguration>\n"
-                    + "</Network>\n";;
+                    + "</Network>\n";
 
     /**
-     * Repro'es the scenario in b/153435438.
-     * Network has
-     *  - Valid preSharedKey
-     *  - KeyMgmt set to KeyMgmt.OSEN
-     *  - ConfigKey set to "SSID"NONE
+     * Repro'es the scenario in b/153435438. Network has - Valid preSharedKey - KeyMgmt set to
+     * KeyMgmt.OSEN - ConfigKey set to "SSID"NONE
      */
     private static final String SINGLE_INVALID_NETWORK_DATA_XML_STRING_FORMAT =
             "<Network>\n"
@@ -502,6 +517,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -536,6 +552,7 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">UNASSIGNED</string>\n"
@@ -790,6 +807,7 @@
                         + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                         + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                         + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                        + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                         + "</NetworkStatus>\n"
                         + "<IpConfiguration>\n"
                         + "<string name=\"IpAssignment\">DHCP</string>\n"
diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java
index 819f198..b89a150 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionNominatorTest.java
@@ -22,8 +22,21 @@
 import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
 import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
@@ -39,7 +52,6 @@
 import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion;
 import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo;
 import com.android.server.wifi.entitlement.PseudonymInfo;
-import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -74,19 +86,20 @@
 
     private @Mock WifiConfigManager mWifiConfigManager;
     private @Mock WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
-    private @Mock PasspointNetworkNominateHelper mPasspointNetworkNominateHelper;
     private @Mock Clock mClock;
     private @Mock WifiCarrierInfoManager mWifiCarrierInfoManager;
     private @Mock WifiPseudonymManager mWifiPseudonymManager;
     private @Mock WifiMetrics mWifiMetrics;
     private NetworkSuggestionNominator mNetworkSuggestionNominator;
+    private List<Pair<ScanDetail, WifiConfiguration>> mPasspointCandidates =
+            Collections.emptyList();
 
     /** Sets up test. */
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mNetworkSuggestionNominator = new NetworkSuggestionNominator(
-                mWifiNetworkSuggestionsManager, mWifiConfigManager, mPasspointNetworkNominateHelper,
+                mWifiNetworkSuggestionsManager, mWifiConfigManager,
                 new LocalLog(100), mWifiCarrierInfoManager, mWifiPseudonymManager,
                 mWifiMetrics);
         when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any())).thenReturn(
@@ -126,7 +139,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -169,7 +183,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -211,7 +226,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -256,7 +272,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -301,7 +318,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -350,7 +368,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -396,7 +415,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -451,7 +471,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -495,7 +516,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -553,7 +575,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -618,7 +641,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) ->
                         connectableNetworks.add(Pair.create(scanDetail, configuration)));
 
@@ -675,7 +699,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -744,7 +769,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -804,14 +830,12 @@
 
         passpointCandidates.add(Pair.create(scanDetails[0],
                 suggestions[0].createInternalWifiConfiguration(mWifiCarrierInfoManager)));
-        when(mPasspointNetworkNominateHelper
-                .getPasspointNetworkCandidates(Arrays.asList(scanDetails), true))
-                .thenReturn(passpointCandidates);
         when(mWifiNetworkSuggestionsManager.getNetworkSuggestionsForFqdn(TEST_FQDN))
                 .thenReturn(matchedExtSuggestions);
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), passpointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -857,15 +881,13 @@
 
         passpointCandidates.add(Pair.create(scanDetails[0],
                 suggestions[0].createInternalWifiConfiguration(mWifiCarrierInfoManager)));
-        when(mPasspointNetworkNominateHelper
-                .getPasspointNetworkCandidates(Arrays.asList(scanDetails), true))
-                .thenReturn(passpointCandidates);
         // As user haven't approved this suggestion, return null
         when(mWifiNetworkSuggestionsManager.getNetworkSuggestionsForFqdn(TEST_FQDN))
                 .thenReturn(Set.of());
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), passpointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -909,7 +931,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -955,7 +978,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1005,7 +1029,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1060,7 +1085,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1111,7 +1137,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1157,7 +1184,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1203,7 +1231,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), true, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, true, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1247,7 +1276,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1294,7 +1324,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, true, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, true, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1338,7 +1369,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1385,7 +1417,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, true, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, true,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1435,7 +1468,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, true, true, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, true, true,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1484,7 +1518,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1534,7 +1569,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, true, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, true, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1584,7 +1620,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, true, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, true,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1632,7 +1669,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1679,7 +1717,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1723,7 +1762,8 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), false, false, false, Collections.emptySet(),
+                Arrays.asList(scanDetails), mPasspointCandidates, false, false, false,
+                Collections.emptySet(),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
                 });
@@ -1769,7 +1809,7 @@
 
         List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
         mNetworkSuggestionNominator.nominateNetworks(
-                Arrays.asList(scanDetails), true, false, false,
+                Arrays.asList(scanDetails), mPasspointCandidates, true, false, false,
                 Set.of(suggestions[0].wns.wifiConfiguration.creatorUid),
                 (ScanDetail scanDetail, WifiConfiguration configuration) -> {
                     connectableNetworks.add(Pair.create(scanDetail, configuration));
diff --git a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
index c86e7a0..f7549cf 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/NetworkSuggestionStoreDataTest.java
@@ -298,6 +298,7 @@
         WifiConfiguration configuration = WifiConfigurationTestUtil.createEapNetwork();
         configuration.enterpriseConfig =
                 WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+        configuration.priority = 5;
         WifiNetworkSuggestion networkSuggestion =
                 new WifiNetworkSuggestion(configuration, null, false, false, true, true,
                         TEST_PRIORITY_GROUP);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java
index 215e39e..867396d 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SarManagerTest.java
@@ -73,6 +73,7 @@
     @Mock private ApplicationInfo mMockApplInfo;
     @Mock WifiNative mWifiNative;
     @Mock PowerManager mPowerManager;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
 
     @Before
     public void setUp() throws Exception {
@@ -123,8 +124,13 @@
                 R.bool.config_wifi_framework_enable_soft_ap_sar_tx_power_limit,
                 isSarSapEnabled);
 
-        mSarMgr = new SarManager(mContext, mTelephonyManager, mLooper.getLooper(),
-                mWifiNative);
+        mSarMgr =
+                new SarManager(
+                        mContext,
+                        mTelephonyManager,
+                        mLooper.getLooper(),
+                        mWifiNative,
+                        mWifiDeviceStateChangeManager);
 
         mSarMgr.handleBootCompleted();
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java
index 648f34d..025973c 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java
@@ -73,7 +73,7 @@
 
         mLocalLog = new LocalLog(512);
         mSavedNetworkNominator = new SavedNetworkNominator(mWifiConfigManager,
-                mPasspointNetworkNominateHelper, mLocalLog, mWifiCarrierInfoManager,
+                mLocalLog, mWifiCarrierInfoManager,
                 mWifiPseudonymManager, mWifiPermissionsUtil, mWifiNetworkSuggestionsManager);
         when(mWifiCarrierInfoManager.isSimReady(anyInt())).thenReturn(true);
         when(mWifiCarrierInfoManager.getBestMatchSubscriptionId(any())).thenReturn(VALID_SUBID);
@@ -118,6 +118,8 @@
     private @Mock WifiGlobals mWifiGlobals;
     private MockitoSession mStaticMockSession = null;
     private LocalLog mLocalLog;
+    private List<Pair<ScanDetail, WifiConfiguration>> mPasspointCandidates =
+            Collections.emptyList();
 
     /**
      * Do not evaluate networks that {@link WifiConfiguration#useExternalScores}.
@@ -141,7 +143,8 @@
         }
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, null, false, true, true, Collections.emptySet(), mOnConnectableListener
+        );
 
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
     }
@@ -167,7 +170,9 @@
         when(mWifiCarrierInfoManager.isSimReady(eq(INVALID_SUBID))).thenReturn(false);
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
 
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
     }
@@ -194,7 +199,9 @@
         }
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
 
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
     }
@@ -219,13 +226,17 @@
         WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, null, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
 
         verify(mOnConnectableListener, times(2)).onConnectable(any(), any());
         reset(mOnConnectableListener);
         savedConfigs[1].allowAutojoin = false;
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(any(),
                 mWifiConfigurationArgumentCaptor.capture());
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0],
@@ -253,7 +264,9 @@
             wifiConfiguration.allowAutojoin = false;
         }
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
     }
 
@@ -272,10 +285,10 @@
         List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates =
                 Arrays.asList(Pair.create(scanDetail1, configuration1), Pair.create(scanDetail2,
                         configuration2));
-        when(mPasspointNetworkNominateHelper.getPasspointNetworkCandidates(scanDetails, false))
-                .thenReturn(passpointCandidates);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, passpointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(scanDetail1, configuration1);
         verify(mOnConnectableListener, never()).onConnectable(scanDetail2, configuration2);
     }
@@ -299,7 +312,9 @@
         when(mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(savedConfigs[0]))
                 .thenReturn(false);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(any(), any());
         reset(mOnConnectableListener);
         when(mWifiCarrierInfoManager.isCarrierNetworkFromNonDefaultDataSim(savedConfigs[0]))
@@ -332,7 +347,9 @@
         when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID))
                 .thenReturn(false);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
         verify(mWifiCarrierInfoManager)
                 .sendImsiProtectionExemptionNotificationIfRequired(TEST_CARRIER_ID);
@@ -340,7 +357,9 @@
         when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID))
                 .thenReturn(true);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(any(), any());
         // If from settings app, will bypass the IMSI check.
         when(mWifiCarrierInfoManager.hasUserApprovedImsiPrivacyExemptionForCarrier(TEST_CARRIER_ID))
@@ -376,7 +395,9 @@
                 .thenReturn(Optional.of(mock(PseudonymInfo.class)));
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
 
         verify(mWifiPseudonymManager).updateWifiConfiguration(any());
         verify(mOnConnectableListener).onConnectable(any(), any());
@@ -406,7 +427,9 @@
                 .thenReturn(Optional.empty());
 
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
 
         verify(mWifiPseudonymManager).retrievePseudonymOnFailureTimeoutExpired(any());
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
@@ -431,14 +454,18 @@
                 .shouldBeIgnoredBySecureSuggestionFromSameCarrier(any(), any()))
                 .thenReturn(true);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
 
         when(mWifiNetworkSuggestionsManager
                 .shouldBeIgnoredBySecureSuggestionFromSameCarrier(any(), any()))
                 .thenReturn(false);
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, mPasspointCandidates, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(any(), any());
     }
 
@@ -462,7 +489,9 @@
         WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
         savedConfigs[0].setBssidAllowlist(List.of(MacAddress.fromString(bssids[0])));
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, null, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener).onConnectable(captor.capture(), any());
         assertEquals(bssids[0], captor.getValue().getBSSIDString());
     }
@@ -486,7 +515,9 @@
         WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
         savedConfigs[0].setBssidAllowlist(Collections.emptyList());
         mSavedNetworkNominator.nominateNetworks(
-                scanDetails, false, true, true, Collections.emptySet(), mOnConnectableListener);
+                scanDetails, null, false, true, true, Collections.emptySet(),
+                mOnConnectableListener
+        );
         verify(mOnConnectableListener, never()).onConnectable(any(), any());
     }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
index 3f609ce..9c87b0d 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ScanRequestProxyTest.java
@@ -315,6 +315,28 @@
         verifyScanMetricsDataWasSet();
     }
 
+    @Test
+    public void testPartialScanIsCached() throws Exception {
+        mScanRequestProxy.registerScanResultsCallback(mScanResultsCallback);
+        // Make a scan request.
+        testStartScanSuccess();
+
+        // Verify scan requests cache is initially empty
+        assertEquals(0, mScanRequestProxy.getScanResults().size());
+
+        // Create scan data that only for 2.4 and 5 without DFS - not a full band scan.
+        WifiScanner.ScanData[] partialScanData =
+                ScanTestUtil.createScanDatas(new int[][]{{ 2417, 2427, 5180}},
+                        new int[]{0},
+                        new int[]{WifiScanner.WIFI_BAND_BOTH});
+
+        // Verify scan result available is not sent but cache is updated
+        mGlobalScanListenerArgumentCaptor.getValue().onResults(partialScanData);
+        mLooper.dispatchAll();
+        verify(mScanResultsCallback, never()).onScanResultsAvailable();
+        assertEquals(3, mScanRequestProxy.getScanResults().size());
+    }
+
     /**
      * Verify a successful scan request and processing of scan results.
      */
@@ -970,7 +992,7 @@
      * internal scans.
      */
     @Test
-    public void testPartialInternalScanResultsDoesNotOverwritePreviousResults() throws Exception {
+    public void testPartialInternalScanResultsAppendToPreviousResults() throws Exception {
         enableScanning();
         // Make scan request 1.
         assertTrue(mScanRequestProxy.startScan(TEST_UID, TEST_PACKAGE_NAME_1));
@@ -980,6 +1002,7 @@
         mLooper.dispatchAll();
         validateScanResultsAvailableBroadcastSent(true);
         // Validate the scan results in the cache.
+        int scanResultsSize = mScanRequestProxy.getScanResults().size();
         ScanTestUtil.assertScanResultsEqualsAnyOrder(
                 mTestScanDatas1[0].getResults(),
                 mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
@@ -991,10 +1014,8 @@
         // Verify the scan failure processing.
         mGlobalScanListenerArgumentCaptor.getValue().onResults(mTestScanDatas2);
         mLooper.dispatchAll();
-        // Validate the scan results from a previous successful scan in the cache.
-        ScanTestUtil.assertScanResultsEqualsAnyOrder(
-                mTestScanDatas1[0].getResults(),
-                mScanRequestProxy.getScanResults().stream().toArray(ScanResult[]::new));
+        // Validate the scan results get appended
+        assertTrue(mScanRequestProxy.getScanResults().size() > scanResultsSize);
 
         verifyScanMetricsDataWasSet();
     }
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 bfae635..8cfd632 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -40,6 +40,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -80,6 +81,7 @@
 import android.os.WorkSource;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -308,13 +310,15 @@
         MockitoAnnotations.initMocks(this);
         mLooper = new TestLooper();
 
+        when(mWifiNative.isItPossibleToCreateApIface(any())).thenReturn(true);
         when(mWifiNative.isItPossibleToCreateBridgedApIface(any())).thenReturn(true);
         when(mWifiNative.isApSetMacAddressSupported(any())).thenReturn(true);
         when(mWifiNative.setApMacAddress(any(), any())).thenReturn(true);
         when(mWifiNative.startSoftAp(eq(TEST_INTERFACE_NAME), any(), anyBoolean(),
-                any(WifiNative.SoftApHalCallback.class))).thenReturn(true);
+                any(WifiNative.SoftApHalCallback.class)))
+                .thenReturn(SoftApManager.START_RESULT_SUCCESS);
         when(mWifiNative.setupInterfaceForSoftApMode(any(), any(), anyInt(), anyBoolean(),
-                any()))
+                any(), anyList()))
                 .thenReturn(TEST_INTERFACE_NAME);
         when(mFrameworkFacade.getIntegerSetting(
                 mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
@@ -331,6 +335,14 @@
                 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond))
                 .thenReturn(
                 (int) TEST_DEFAULT_SHUTDOWN_IDLE_INSTANCE_IN_BRIDGED_MODE_TIMEOUT_MILLIS);
+        when(mResources.getBoolean(R.bool.config_wifiBridgedSoftApSupported))
+                .thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported))
+                .thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifi24ghzSupport)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifiSoftap24ghzSupported)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifi5ghzSupport)).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_wifiSoftap5ghzSupported)).thenReturn(true);
         when(mWifiNative.setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
                 .thenReturn(true);
@@ -378,7 +390,6 @@
         when(mWifiNative.forceClientDisconnect(any(), any(), anyInt())).thenReturn(true);
         when(mWifiInjector.getWifiHandlerLocalLog()).thenReturn(mLocalLog);
         when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode);
-        when(mContext.getResources()).thenReturn(mResources);
 
         // Init Test SoftAp infos
         mTestSoftApInfo = new SoftApInfo();
@@ -403,7 +414,8 @@
                 | SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
                 | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD
                 | SoftApCapability.SOFTAP_FEATURE_WPA3_SAE
-                | SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION;
+                | SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION
+                | SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE;
         mTestSoftApCapability = new SoftApCapability(testSoftApFeature);
         mTestSoftApCapability.setMaxSupportedClients(10);
         mTestSoftApCapability.setSupportedChannelList(
@@ -501,6 +513,10 @@
         verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
                 WifiManager.SAP_START_FAILURE_USER_REJECTED);
         verify(mListener).onStartFailure(mSoftApManager);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_INTERFACE_CONFLICT_USER_REJECTED),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /** Verifies startSoftAp will skip checking for user approval for the Tethering case. */
@@ -574,6 +590,10 @@
         checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_FAILED,
                 WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
                 nullApConfig.getTargetMode());
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_GENERAL),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /**
@@ -584,7 +604,7 @@
     public void testSetupForSoftApModeNullApInterfaceNameFailureIncrementsMetrics()
             throws Exception {
         when(mWifiNative.setupInterfaceForSoftApMode(
-                    any(), any(), anyInt(), anyBoolean(), any())).thenReturn(null);
+                    any(), any(), anyInt(), anyBoolean(), any(), anyList())).thenReturn(null);
         when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
         SoftApModeConfiguration nullApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null,
@@ -608,6 +628,49 @@
 
         verify(mWifiMetrics).incrementSoftApStartResult(false,
                 WifiManager.SAP_START_FAILURE_GENERAL);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_GENERAL),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
+    }
+
+    /**
+     * Test that not being able to create a SoftAp interface is a failure and increments the
+     * corresponding metrics and proper state updates are sent out.
+     */
+    @Test
+    public void testStartSoftApNotPossibleToCreateApInterfaceIncrementsMetrics()
+            throws Exception {
+        when(mWifiNative.isItPossibleToCreateApIface(any())).thenReturn(false);
+        Builder configBuilder = new SoftApConfiguration.Builder();
+        configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
+        configBuilder.setSsid(TEST_SSID);
+        SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
+                IFACE_IP_MODE_LOCAL_ONLY, configBuilder.build(),
+                mTestSoftApCapability, TEST_COUNTRY_CODE);
+        mSoftApManager = createSoftApManager(apConfig, ROLE_SOFTAP_TETHERED);
+        verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        verify(mListener).onStartFailure(mSoftApManager);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        if (SdkLevel.isAtLeastSv2()) {
+            verify(mContext).sendBroadcastAsUser(intentCaptor.capture(),
+                    eq(UserHandle.ALL), eq(android.Manifest.permission.ACCESS_WIFI_STATE));
+        } else {
+            verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                    eq(UserHandle.ALL));
+        }
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
+                apConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_INTERFACE_CONFLICT),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /**
@@ -618,7 +681,7 @@
     public void testSetupForSoftApModeEmptyInterfaceNameFailureIncrementsMetrics()
             throws Exception {
         when(mWifiNative.setupInterfaceForSoftApMode(
-                    any(), any(), anyInt(), anyBoolean(), any())).thenReturn("");
+                    any(), any(), anyInt(), anyBoolean(), any(), anyList())).thenReturn("");
         SoftApModeConfiguration nullApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null,
                 mTestSoftApCapability, TEST_COUNTRY_CODE);
@@ -641,6 +704,10 @@
 
         verify(mWifiMetrics).incrementSoftApStartResult(false,
                 WifiManager.SAP_START_FAILURE_GENERAL);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_CREATE_INTERFACE),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /**
@@ -675,6 +742,10 @@
         checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_FAILED,
                 WIFI_AP_STATE_ENABLING, WifiManager.SAP_START_FAILURE_GENERAL, TEST_INTERFACE_NAME,
                 softApConfig.getTargetMode());
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_SET_COUNTRY_CODE),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /**
@@ -877,6 +948,7 @@
     @Test
     public void startSoftApFailNoChannel() throws Exception {
         SoftApCapability noAcsCapability = new SoftApCapability(0);
+        noAcsCapability.setCountryCode(TEST_COUNTRY_CODE);
         Builder configBuilder = new SoftApConfiguration.Builder();
         configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
         configBuilder.setSsid(TEST_SSID);
@@ -914,7 +986,8 @@
     @Test
     public void startSoftApApInterfaceFailedToStart() throws Exception {
         when(mWifiNative.startSoftAp(eq(TEST_INTERFACE_NAME), any(), anyBoolean(),
-                any(WifiNative.SoftApHalCallback.class))).thenReturn(false);
+                any(WifiNative.SoftApHalCallback.class))).thenReturn(
+                        SoftApManager.START_RESULT_FAILURE_ADD_AP_HOSTAPD);
 
         SoftApModeConfiguration softApModeConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mDefaultApConfig,
@@ -927,6 +1000,10 @@
                 WifiManager.SAP_START_FAILURE_GENERAL);
         verify(mListener).onStartFailure(mSoftApManager);
         verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_ADD_AP_HOSTAPD),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     /**
@@ -989,6 +1066,7 @@
 
         // reset to clear verified Intents for ap state change updates
         reset(mContext);
+        when(mContext.getResources()).thenReturn(mResources);
 
         InOrder order = inOrder(mCallback, mListener, mContext);
 
@@ -1060,6 +1138,7 @@
 
         // reset to clear verified Intents for ap state change updates
         reset(mContext, mCallback, mWifiNative);
+        when(mContext.getResources()).thenReturn(mResources);
 
         InOrder order = inOrder(mCallback, mListener, mContext);
 
@@ -1089,6 +1168,9 @@
         checkApStateChangedBroadcast(capturedIntents.get(2), WIFI_AP_STATE_DISABLED,
                 WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
                 softApModeConfig.getTargetMode());
+        verify(mWifiMetrics).writeSoftApStoppedEvent(eq(SoftApManager.STOP_EVENT_INTERFACE_DOWN),
+                any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt(), any());
     }
 
     /**
@@ -1123,6 +1205,7 @@
 
         // reset to clear verified Intents for ap state change updates
         reset(mContext, mCallback, mWifiNative);
+        when(mContext.getResources()).thenReturn(mResources);
 
         InOrder order = inOrder(mCallback, mListener, mContext);
         mSoftApHalCallbackCaptor.getValue().onFailure();
@@ -1150,6 +1233,9 @@
         checkApStateChangedBroadcast(capturedIntents.get(2), WIFI_AP_STATE_DISABLED,
                 WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
                 softApModeConfig.getTargetMode());
+        verify(mWifiMetrics).writeSoftApStoppedEvent(eq(SoftApManager.STOP_EVENT_HOSTAPD_FAILURE),
+                any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt(), any());
     }
 
     /**
@@ -1958,6 +2044,9 @@
 
         verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME);
         verify(mFakeSoftApNotifier).showSoftApShutdownTimeoutExpiredNotification();
+        verify(mWifiMetrics).writeSoftApStoppedEvent(eq(SoftApManager.STOP_EVENT_NO_USAGE_TIMEOUT),
+                any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt(), any());
     }
 
     @Test
@@ -2094,6 +2183,7 @@
                 | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD
                 | SoftApCapability.SOFTAP_FEATURE_WPA3_SAE;
         SoftApCapability testSoftApCapability = new SoftApCapability(testSoftApFeature);
+        testSoftApCapability.setCountryCode(TEST_COUNTRY_CODE);
         Builder configBuilder = new SoftApConfiguration.Builder();
         configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
         configBuilder.setSsid(TEST_SSID);
@@ -2328,10 +2418,17 @@
                 randomizedBssidConfig = randomizedBssidConfigBuilder.build();
                 when(mWifiApConfigStore.randomizeBssidIfUnset(any(), any())).thenReturn(
                         randomizedBssidConfig);
-                expectedConfig = new SoftApConfiguration.Builder(randomizedBssidConfig)
-                        .build();
+
+                expectedConfig = randomizedBssidConfig;
             } else {
-                expectedConfig = new SoftApConfiguration.Builder(config)
+                expectedConfig = config;
+            }
+            if (SdkLevel.isAtLeastT()
+                    && expectedConfig.isIeee80211beEnabled()
+                    && !softApConfig.getCapability().areFeaturesSupported(
+                            SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE)) {
+                expectedConfig = new SoftApConfiguration.Builder(expectedConfig)
+                        .setIeee80211beEnabled(false)
                         .build();
             }
         }
@@ -2365,7 +2462,7 @@
             verify(mWifiNative, never()).setupInterfaceForSoftApMode(
                     mWifiNativeInterfaceCallbackCaptor.capture(), eq(TEST_WORKSOURCE),
                     eq(expectedConfig.getBand()), eq(expectedConfig.getBands().length > 1),
-                    eq(mSoftApManager));
+                    eq(mSoftApManager), anyList());
 
             // Simulate user approval
             ArgumentCaptor<StateMachine> stateMachineCaptor =
@@ -2385,11 +2482,14 @@
         order.verify(mWifiNative).setupInterfaceForSoftApMode(
                 mWifiNativeInterfaceCallbackCaptor.capture(), eq(TEST_WORKSOURCE),
                 eq(expectedConfig.getBand()), eq(expectedConfig.getBands().length > 1),
-                eq(mSoftApManager));
+                eq(mSoftApManager), anyList());
         ArgumentCaptor<SoftApConfiguration> configCaptor =
                 ArgumentCaptor.forClass(SoftApConfiguration.class);
         order.verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
-        if (mResources.getBoolean(R.bool.config_wifiDriverSupportedNl80211RegChangedEvent)) {
+        if (!TextUtils.isEmpty(softApConfig.getCountryCode())
+                && !TextUtils.equals(
+                        softApConfig.getCountryCode(),
+                        softApConfig.getCapability().getCountryCode())) {
             // Don't start SoftAP before driver country code change.
             verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any());
 
@@ -2442,12 +2542,29 @@
         verify(mListener).onStarted(mSoftApManager);
         verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.getTargetMode(),
                 TEST_DEFAULT_SHUTDOWN_TIMEOUT_MILLIS, expectedConfig.getBands().length > 1);
-        verify(mWifiMetrics).updateSoftApConfiguration(config == null
-                ? randomizedBssidConfig : expectedConfig, softApConfig.getTargetMode(),
+        verify(mWifiMetrics).updateSoftApConfiguration(expectedConfig, softApConfig.getTargetMode(),
                 expectedConfig.getBands().length > 1);
-        verify(mWifiMetrics).updateSoftApCapability(softApConfig.getCapability(),
-                softApConfig.getTargetMode(), expectedConfig.getBands().length > 1);
+        verify(mWifiMetrics)
+                .updateSoftApCapability(
+                        any(),
+                        eq(softApConfig.getTargetMode()),
+                        eq(expectedConfig.getBands().length > 1));
+        // Verify the bands we get from getSoftApModeConfiguration() match the original bands
+        // we passed in.
+        assertThat(mSoftApManager.getSoftApModeConfiguration().getSoftApConfiguration().getBands())
+                .isEqualTo(config != null ? config.getBands() : mDefaultApConfig.getBands());
         if (SdkLevel.isAtLeastS()) {
+            SparseIntArray actualChannels =
+                    mSoftApManager
+                            .getSoftApModeConfiguration()
+                            .getSoftApConfiguration()
+                            .getChannels();
+            SparseIntArray expectedChannels =
+                    config != null ? config.getChannels() : mDefaultApConfig.getChannels();
+            assertThat(actualChannels.size()).isEqualTo(expectedChannels.size());
+            for (int band : actualChannels.copyKeys()) {
+                assertThat(actualChannels.get(band)).isEqualTo(actualChannels.get(band));
+            }
             verify(mCoexManager).registerCoexListener(mCoexListenerCaptor.capture());
         }
     }
@@ -2475,6 +2592,7 @@
                 | SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION;
         SoftApCapability noClientControlCapability = new SoftApCapability(testSoftApFeature);
         noClientControlCapability.setMaxSupportedClients(1);
+        noClientControlCapability.setCountryCode(TEST_COUNTRY_CODE);
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null,
                 noClientControlCapability, TEST_COUNTRY_CODE);
@@ -2509,6 +2627,7 @@
                 | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
         SoftApCapability noClientControlCapability = new SoftApCapability(testSoftApFeature);
         noClientControlCapability.setMaxSupportedClients(1);
+        noClientControlCapability.setCountryCode(TEST_COUNTRY_CODE);
         SoftApConfiguration softApConfig = new SoftApConfiguration.Builder(
                 mDefaultApConfig).setMaxNumberOfClients(1).build();
 
@@ -2523,6 +2642,10 @@
         verify(mWifiMetrics).incrementSoftApStartResult(false,
                 WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION);
         verify(mListener).onStartFailure(mSoftApManager);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_UNSUPPORTED_CONFIG),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     @Test
@@ -2531,6 +2654,7 @@
         long testSoftApFeature = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT
                 | SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD;
         SoftApCapability noSaeCapability = new SoftApCapability(testSoftApFeature);
+        noSaeCapability.setCountryCode(TEST_COUNTRY_CODE);
         SoftApConfiguration softApConfig = new SoftApConfiguration.Builder(
                 mDefaultApConfig).setPassphrase(TEST_PASSWORD,
                 SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build();
@@ -2544,6 +2668,10 @@
         verify(mCallback).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
                 WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION);
         verify(mListener).onStartFailure(mSoftApManager);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_UNSUPPORTED_CONFIG),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     @Test
@@ -2560,6 +2688,7 @@
                 SoftApConfiguration.BAND_2GHZ, TEST_SUPPORTED_24G_CHANNELS);
         testCapability.setSupportedChannelList(
                 SoftApConfiguration.BAND_5GHZ, TEST_SUPPORTED_5G_CHANNELS);
+        testCapability.setCountryCode(TEST_COUNTRY_CODE);
         SoftApConfiguration softApConfig = generateBridgedModeSoftApConfig(null);
         SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
                 WifiManager.IFACE_IP_MODE_TETHERED, softApConfig,
@@ -2573,6 +2702,10 @@
         verify(mWifiMetrics).incrementSoftApStartResult(false,
                 WifiManager.SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION);
         verify(mListener).onStartFailure(mSoftApManager);
+        verify(mWifiMetrics).writeSoftApStartedEvent(
+                eq(SoftApManager.START_RESULT_FAILURE_UNSUPPORTED_CONFIG),
+                any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(),
+                anyInt());
     }
 
     @Test
@@ -2930,7 +3063,8 @@
                 mTestSoftApCapability, TEST_COUNTRY_CODE);
         mSoftApManager = createSoftApManager(dualBandConfig, ROLE_SOFTAP_TETHERED);
         verify(mWifiNative).setupInterfaceForSoftApMode(
-                any(), any(), eq(SoftApConfiguration.BAND_2GHZ), eq(true), eq(mSoftApManager));
+                any(), any(), eq(SoftApConfiguration.BAND_2GHZ), eq(true), eq(mSoftApManager),
+                anyList());
     }
 
     @Test
@@ -3313,6 +3447,25 @@
     }
 
     @Test
+    public void testBridgedModeFallbackToSingleModeDueToCountryCodeChangedToWorldMode()
+            throws Exception {
+        assumeTrue(SdkLevel.isAtLeastS());
+        String worldModeCC = "00";
+        when(mContext.getResources()
+                .getString(R.string.config_wifiDriverWorldModeCountryCode)).thenReturn(worldModeCC);
+
+        SoftApCapability testCapability = new SoftApCapability(mTestSoftApCapability);
+        Builder configBuilder = new SoftApConfiguration.Builder(
+                generateBridgedModeSoftApConfig(null));
+        SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
+                testCapability, worldModeCC);
+        // Reset band to 2.4G | 5G to generate expected configuration
+        configBuilder.setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
+        startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false);
+    }
+
+    @Test
     public void testBridgedModeKeepWhenCoexChangedToSoftUnsafe()
             throws Exception {
         assumeTrue(SdkLevel.isAtLeastS());
@@ -3503,6 +3656,30 @@
     }
 
     @Test
+    public void testBridgedModeKeepIfMovingFromUnsupportedCCtoSupportedCC() throws Exception {
+        assumeTrue(SdkLevel.isAtLeastS());
+        String worldModeCC = "00";
+        when(mContext.getResources().getString(R.string.config_wifiDriverWorldModeCountryCode))
+                .thenReturn(worldModeCC);
+
+        // Simulate stale world mode CC without 5GHz band
+        SoftApCapability staleCapability = new SoftApCapability(mTestSoftApCapability);
+        staleCapability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, new int[0]);
+        staleCapability.setCountryCode(worldModeCC);
+        Builder configBuilder =
+                new SoftApConfiguration.Builder(generateBridgedModeSoftApConfig(null));
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(
+                        WifiManager.IFACE_IP_MODE_TETHERED,
+                        configBuilder.build(),
+                        staleCapability,
+                        TEST_COUNTRY_CODE);
+        // Started bands should include both 2.4GHz and 5GHz since we get the updated capabilities
+        // after waiting for the driver CC event.
+        startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false);
+    }
+
+    @Test
     public void testWaitForDriverCountryCode() throws Exception {
         when(mResources.getBoolean(
                 R.bool.config_wifiDriverSupportedNl80211RegChangedEvent)).thenReturn(true);
@@ -3739,5 +3916,24 @@
         startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false);
     }
 
+    /**
+     * Tests that 11BE is set to disabled in the SoftApConfiguration if it isn't supported by
+     * SoftApCapability.
+     */
+    @Test
+    public void testStartSoftApRemoves11BEIfNotSupported() throws Exception {
+        assumeTrue(SdkLevel.isAtLeastT());
+        mTestSoftApCapability.setSupportedFeatures(false,
+                SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE);
+        Builder configBuilder = new SoftApConfiguration.Builder();
+        configBuilder.setBand(SoftApConfiguration.BAND_2GHZ);
+        configBuilder.setSsid(TEST_SSID);
+        configBuilder.setIeee80211beEnabled(true);
+        SoftApModeConfiguration apConfig = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
+                mTestSoftApCapability, TEST_COUNTRY_CODE);
+        SoftApConfiguration expectedConfig = configBuilder.setIeee80211beEnabled(false).build();
+        startSoftApAndVerifyEnabled(apConfig, expectedConfig, false);
+    }
 }
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java b/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java
index ee630ab..556f7d5 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java
@@ -104,25 +104,25 @@
         when(mResource.getBoolean(R.bool.config_wifiEnable6GhzBeaconRssiBoost)).thenReturn(false);
         int predicted_5Ghz_80MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_80MHZ, -70, 5160, 1,
-                0, 0, false);
+                0, 0, false, null);
         int predicted_6Ghz_20MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_20MHZ, -70, 5975, 1,
-                0, 0, false);
+                0, 0, false, null);
         int predicted_6Ghz_80MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_80MHZ, -70, 5975, 1,
-                0, 0, false);
+                0, 0, false, null);
 
         // verify that after the boost is enabled, only the 6Ghz 80MHz bandwidth score is increased.
         when(mResource.getBoolean(R.bool.config_wifiEnable6GhzBeaconRssiBoost)).thenReturn(true);
         int newPredicted_5Ghz_80MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_80MHZ, -70, 5160, 1,
-                0, 0, false);
+                0, 0, false, null);
         int newPredicted_6Ghz_20MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_20MHZ, -70, 5975, 1,
-                0, 0, false);
+                0, 0, false, null);
         int newPredicted_6Ghz_80MHz = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_80MHZ, -70, 5975, 1,
-                0, 0, false);
+                0, 0, false, null);
 
         assertEquals("5Ghz AP should not get RSSI boost",
                 predicted_5Ghz_80MHz, newPredicted_5Ghz_80MHz);
@@ -136,7 +136,7 @@
     public void verifyVeryLowRssi() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -200, 2412, 1,
-                0, 0, false);
+                0, 0, false, null);
 
         assertEquals(0, predictedThroughputMbps);
     }
@@ -145,7 +145,7 @@
     public void verifyMaxChannelUtilizationBssLoad() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, 0, 2412, 1,
-                MAX_CHANNEL_UTILIZATION, 0, false);
+                MAX_CHANNEL_UTILIZATION, 0, false, null);
 
         assertEquals(0, predictedThroughputMbps);
     }
@@ -154,7 +154,7 @@
     public void verifyMaxChannelUtilizationLinkLayerStats() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, 0, 5210, 1,
-                INVALID, MAX_CHANNEL_UTILIZATION, false);
+                INVALID, MAX_CHANNEL_UTILIZATION, false, null);
 
         assertEquals(0, predictedThroughputMbps);
     }
@@ -163,7 +163,7 @@
     public void verifyHighRssiMinChannelUtilizationAc5g80Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, 0, 5180, 2,
-                MIN_CHANNEL_UTILIZATION, 50, false);
+                MIN_CHANNEL_UTILIZATION, 50, false, null);
 
         assertEquals(866, predictedThroughputMbps);
     }
@@ -178,7 +178,7 @@
                 .thenReturn(1);
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, 0, 5180, 2,
-                MIN_CHANNEL_UTILIZATION, 50, false);
+                MIN_CHANNEL_UTILIZATION, 50, false, null);
 
         assertEquals(433, predictedThroughputMbps);
     }
@@ -194,7 +194,7 @@
 
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_320MHZ, 0, 6180, 4,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(11529, predictedThroughputMbps);
     }
@@ -213,7 +213,7 @@
 
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5180, 4,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(2401, predictedThroughputMbps);
     }
@@ -223,7 +223,7 @@
     public void verifyMidRssiMinChannelUtilizationAc5g80Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -50, 5180, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(866, predictedThroughputMbps);
     }
@@ -232,7 +232,7 @@
     public void verifyLowRssiMinChannelUtilizationAc5g80Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -80, 5180, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(41, predictedThroughputMbps);
     }
@@ -241,7 +241,7 @@
     public void verifyLowRssiDefaultChannelUtilizationAc5g80Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -80, 5180, 2,
-                INVALID, INVALID, false);
+                INVALID, INVALID, false, null);
 
         assertEquals(31, predictedThroughputMbps);
     }
@@ -250,7 +250,7 @@
     public void verifyHighRssiMinChannelUtilizationAc2g20Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -20, 2437, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(192, predictedThroughputMbps);
     }
@@ -259,7 +259,7 @@
     public void verifyHighRssiMinChannelUtilizationAc2g20Mhz2ssBluetoothConnected() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -20, 2437, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, true);
+                MIN_CHANNEL_UTILIZATION, INVALID, true, null);
 
         assertEquals(144, predictedThroughputMbps);
     }
@@ -268,7 +268,7 @@
     public void verifyHighRssiMinChannelUtilizationLegacy5g20Mhz() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_LEGACY, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 5180,
-                1, MIN_CHANNEL_UTILIZATION, INVALID, false);
+                1, MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(54, predictedThroughputMbps);
     }
@@ -277,7 +277,7 @@
     public void verifyLowRssiDefaultChannelUtilizationLegacy5g20Mhz() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_LEGACY, ScanResult.CHANNEL_WIDTH_20MHZ, -80, 5180,
-                2, INVALID, INVALID, false);
+                2, INVALID, INVALID, false, null);
 
         assertEquals(11, predictedThroughputMbps);
     }
@@ -286,7 +286,7 @@
     public void verifyHighRssiMinChannelUtilizationHt2g20Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11N, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 2437, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
 
         assertEquals(144, predictedThroughputMbps);
     }
@@ -297,7 +297,7 @@
         when(mDeviceCapabilities.getMaxNumberRxSpatialStreams()).thenReturn(0);
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11N, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 2437, 2,
-                MIN_CHANNEL_UTILIZATION, INVALID, false);
+                MIN_CHANNEL_UTILIZATION, INVALID, false, null);
         // Expect to 1SS peak rate because maxNumberSpatialStream is overridden to 1.
         assertEquals(72, predictedThroughputMbps);
     }
@@ -306,7 +306,7 @@
     public void verifyLowRssiDefaultChannelUtilizationHt2g20Mhz1ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11N, ScanResult.CHANNEL_WIDTH_20MHZ, -80, 2437, 1,
-                INVALID, INVALID, true);
+                INVALID, INVALID, true, null);
 
         assertEquals(5, predictedThroughputMbps);
     }
@@ -315,7 +315,7 @@
     public void verifyHighRssiHighChannelUtilizationAc2g20Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 2437, 2,
-                INVALID, 80, true);
+                INVALID, 80, true, null);
 
         assertEquals(84, predictedThroughputMbps);
     }
@@ -324,7 +324,7 @@
     public void verifyRssiBoundaryHighChannelUtilizationAc2g20Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -69, 2437, 2,
-                INVALID, 80, true);
+                INVALID, 80, true, null);
 
         assertEquals(46, predictedThroughputMbps);
     }
@@ -333,7 +333,7 @@
     public void verifyRssiBoundaryHighChannelUtilizationAc5g40Mhz2ss() {
         int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
                 ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_40MHZ, -66, 5180, 2,
-                INVALID, 80, false);
+                INVALID, 80, false, null);
 
         assertEquals(103, predictedThroughputMbps);
     }
@@ -440,4 +440,23 @@
         assertEquals(2881, mThroughputPredictor.predictRxThroughput(mConnectionCap,
                 -10, 5180, INVALID));
     }
+
+    @Test
+    public void verifyThroughputBe5g160Mhz4ssDisabledSubchannelBitmap() {
+        when(mDeviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)).thenReturn(
+                true);
+        when(mDeviceCapabilities.isChannelWidthSupported(
+                ScanResult.CHANNEL_WIDTH_160MHZ)).thenReturn(true);
+        when(mDeviceCapabilities.getMaxNumberTxSpatialStreams()).thenReturn(4);
+        when(mDeviceCapabilities.getMaxNumberRxSpatialStreams()).thenReturn(4);
+        int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
+                ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5240, 4,
+                MIN_CHANNEL_UTILIZATION, 50, false, null);
+        assertEquals(5764, predictedThroughputMbps);
+
+        predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities,
+                ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5240, 4,
+                MIN_CHANNEL_UTILIZATION, 50, false, new byte[]{(byte) 0x3, (byte) 0x0});
+        assertEquals(4388, predictedThroughputMbps);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 74c351a..644d022 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -47,6 +47,7 @@
 import android.net.wifi.SoftApCapability;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.SoftApConfiguration.Builder;
+import android.net.wifi.SoftApInfo;
 import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Handler;
@@ -313,6 +314,7 @@
     public void initWithDefaultConfiguration() throws Exception {
         WifiApConfigStore store = createWifiApConfigStore();
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
     }
 
@@ -341,6 +343,7 @@
         store.setApConfiguration(null);
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
         verifyDefaultApConfig(mDataStoreSource.toSerialize(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
         verify(mBackupManagerProxy).notifyDataChanged();
         assertFalse(store.getApConfiguration().isUserConfigurationInternal());
@@ -359,6 +362,7 @@
 
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
         assertFalse(store.getApConfiguration().isUserConfigurationInternal());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
 
         /* Update with a valid configuration. */
@@ -375,6 +379,7 @@
         assertEquals(TEST_RANDOMIZED_MAC, store.getApConfiguration()
                 .getPersistentRandomizedMacAddress());
         verifyApConfig(expectedConfig, mDataStoreSource.toSerialize());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager, times(2)).saveToStore(true);
         verify(mBackupManagerProxy, times(2)).notifyDataChanged();
         assertTrue(store.getApConfiguration().isUserConfigurationInternal());
@@ -390,6 +395,7 @@
         /* Initialize WifiApConfigStore with default configuration. */
         WifiApConfigStore store = createWifiApConfigStore();
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
 
         /* Update with a valid configuration. */
@@ -412,6 +418,7 @@
         store.setApConfiguration(providedConfig);
         verifyApConfig(expectedConfig, store.getApConfiguration());
         verifyApConfig(expectedConfig, mDataStoreSource.toSerialize());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager, times(2)).saveToStore(true);
         verify(mBackupManagerProxy, times(2)).notifyDataChanged();
     }
@@ -426,6 +433,7 @@
         /* Initialize WifiApConfigStore with default configuration. */
         WifiApConfigStore store = createWifiApConfigStore();
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
 
         /* Update with a valid configuration. */
@@ -439,6 +447,7 @@
         store.setApConfiguration(expectedConfig);
         verifyApConfig(expectedConfig, store.getApConfiguration());
         verifyApConfig(expectedConfig, mDataStoreSource.toSerialize());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager, times(2)).saveToStore(true);
         verify(mBackupManagerProxy, times(2)).notifyDataChanged();
     }
@@ -452,6 +461,7 @@
         /* Initialize WifiApConfigStore with default configuration. */
         WifiApConfigStore store = createWifiApConfigStore();
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
 
         /* Update with a valid configuration. */
@@ -465,6 +475,7 @@
         store.setApConfiguration(expectedConfig);
         verifyApConfig(expectedConfig, store.getApConfiguration());
         verifyApConfig(expectedConfig, mDataStoreSource.toSerialize());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager, times(2)).saveToStore(true);
         verify(mBackupManagerProxy, times(2)).notifyDataChanged();
     }
@@ -495,6 +506,7 @@
         mDataStoreSource.fromDeserialized(persistedConfig);
         verifyApConfig(expectedConfig, store.getApConfiguration());
         verifyApConfig(expectedConfig, mDataStoreSource.toSerialize());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
         verify(mBackupManagerProxy).notifyDataChanged();
     }
@@ -518,6 +530,7 @@
         WifiApConfigStore store = createWifiApConfigStore();
         mDataStoreSource.fromDeserialized(persistedConfig);
         verifyApConfig(persistedConfig, store.getApConfiguration());
+        mLooper.dispatchAll();
         verify(mWifiConfigManager, never()).saveToStore(true);
         verify(mBackupManagerProxy, never()).notifyDataChanged();
     }
@@ -553,7 +566,6 @@
 
     /**
      * Verify a proper local only hotspot config is generated for 5Ghz band when overlay configured
-     * and there are available 5g channels.
      */
     @Test
     public void generateLocalOnlyHotspotConfigWhenOverlayConfigureTo5G() throws Exception {
@@ -566,7 +578,7 @@
         SoftApConfiguration config = store
                 .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability);
         verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
-                SoftApConfiguration.BAND_5GHZ);
+                SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
 
         // verify that the config passes the validateApWifiConfiguration check
         assertTrue(WifiApConfigStore.validateApWifiConfiguration(config, true, mContext,
@@ -586,7 +598,7 @@
         SoftApConfiguration config = store
                 .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability);
         verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
-                SoftApConfiguration.BAND_2GHZ);
+                SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
 
         // verify that the config passes the validateApWifiConfiguration check
         assertTrue(WifiApConfigStore.validateApWifiConfiguration(config, true, mContext,
@@ -595,19 +607,16 @@
 
     /**
      * Verify a proper local only hotspot config is generated for 6Ghz band when overlay configured
-     * and there are available 6g channels.
      */
     @Test
     public void generateLocalOnlyHotspotConfigWhenOverlayConfigureTo6G() throws Exception {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
-        when(mSoftApCapability.getSupportedChannelList(SoftApConfiguration.BAND_6GHZ))
-                .thenReturn(new int[] {1});
         mResources.setBoolean(R.bool.config_wifiLocalOnlyHotspot6ghz, true);
         WifiApConfigStore store = createWifiApConfigStore();
         SoftApConfiguration config = store
                 .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability);
         verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
-                SoftApConfiguration.BAND_6GHZ);
+                SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_6GHZ);
 
         SoftApConfiguration updatedConfig = new SoftApConfiguration.Builder(config)
                 .setPassphrase("somepassword", SECURITY_TYPE_WPA3_SAE)
@@ -619,19 +628,24 @@
     }
 
     /**
-     * Verify a proper local only hotspot config is generated for 2Ghz band when overlay configured
-     * but there is no available 6g channel.
+     * Verify a proper local only hotspot config is generated for 5Ghz and 6Ghz band when overlay
+     * configured
      */
     @Test
-    public void generateLocalOnlyHotspotConfigWhenOverlayConfigureTo6GButNoAvailable6GChannel()
-            throws Exception {
+    public void generateLocalOnlyHotspotConfigWhenOverlayConfigureTo5GAnd6G() throws Exception {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true);
         mResources.setBoolean(R.bool.config_wifiLocalOnlyHotspot6ghz, true);
+        mResources.setBoolean(R.bool.config_wifi_local_only_hotspot_5ghz, true);
         WifiApConfigStore store = createWifiApConfigStore();
         SoftApConfiguration config = store
                 .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability);
         verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID,
-                SoftApConfiguration.BAND_2GHZ);
+                SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ
+                        | SoftApConfiguration.BAND_6GHZ);
+
+        SoftApConfiguration updatedConfig = new SoftApConfiguration.Builder(config)
+                .setPassphrase("somepassword", SECURITY_TYPE_WPA3_SAE)
+                .build();
 
         // verify that the config passes the validateApWifiConfiguration check
         assertTrue(WifiApConfigStore.validateApWifiConfiguration(config, true, mContext,
@@ -1341,34 +1355,47 @@
     }
 
     @Test
-    public void testForceApBaneChannel() throws Exception {
+    public void testForceApBandChannel() throws Exception {
         int testBand = SoftApConfiguration.BAND_5GHZ; // Not default
         int testChannal = 149;
         WifiApConfigStore store = createWifiApConfigStore();
         verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
+        mLooper.dispatchAll();
         verify(mWifiConfigManager).saveToStore(true);
 
         // Test to enable forced AP band
-        store.enableForceSoftApBandOrChannel(testBand, 0);
+        store.enableForceSoftApBandOrChannel(testBand, 0, SoftApInfo.CHANNEL_WIDTH_AUTO);
         SoftApConfiguration expectedConfig = store.getApConfiguration();
-        assertEquals(expectedConfig.getBand(), testBand);
-        assertEquals(expectedConfig.getChannel(), 0);
+        assertEquals(testBand, expectedConfig.getBand());
+        assertEquals(0, expectedConfig.getChannel());
+        if (SdkLevel.isAtLeastT()) {
+            assertEquals(expectedConfig.getMaxChannelBandwidth(), SoftApInfo.CHANNEL_WIDTH_AUTO);
+        }
         // Disable forced AP band
         store.disableForceSoftApBandOrChannel();
         expectedConfig = store.getApConfiguration();
-        assertEquals(expectedConfig.getBand(), SoftApConfiguration.BAND_2GHZ);
-        assertEquals(expectedConfig.getChannel(), 0);
+        assertEquals(SoftApConfiguration.BAND_2GHZ, expectedConfig.getBand());
+        assertEquals(0, expectedConfig.getChannel());
+        if (SdkLevel.isAtLeastT()) {
+            assertEquals(SoftApInfo.CHANNEL_WIDTH_AUTO, expectedConfig.getMaxChannelBandwidth());
+        }
 
         // Test to enable forced AP band
-        store.enableForceSoftApBandOrChannel(testBand, testChannal);
+        store.enableForceSoftApBandOrChannel(testBand, testChannal, SoftApInfo.CHANNEL_WIDTH_40MHZ);
         expectedConfig = store.getApConfiguration();
-        assertEquals(expectedConfig.getBand(), testBand);
-        assertEquals(expectedConfig.getChannel(), testChannal);
+        assertEquals(testBand, expectedConfig.getBand());
+        assertEquals(testChannal, expectedConfig.getChannel());
+        if (SdkLevel.isAtLeastT()) {
+            assertEquals(SoftApInfo.CHANNEL_WIDTH_40MHZ, expectedConfig.getMaxChannelBandwidth());
+        }
         // Disable forced AP band
         store.disableForceSoftApBandOrChannel();
         expectedConfig = store.getApConfiguration();
-        assertEquals(expectedConfig.getBand(), SoftApConfiguration.BAND_2GHZ);
-        assertEquals(expectedConfig.getChannel(), 0);
+        assertEquals(SoftApConfiguration.BAND_2GHZ, expectedConfig.getBand());
+        assertEquals(0, expectedConfig.getChannel());
+        if (SdkLevel.isAtLeastT()) {
+            assertEquals(SoftApInfo.CHANNEL_WIDTH_AUTO, expectedConfig.getMaxChannelBandwidth());
+        }
     }
 
     private void verifyUpgradeConfiguration(WifiApConfigStore store, boolean isBridgedSupported,
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
index 5ea7586..c500a42 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
@@ -233,10 +233,12 @@
                     + "<NetworkList>\n"
                     + "<Network>\n"
                     + "<WifiConfiguration>\n"
-                    + "<string name=\"ConfigKey\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
-                        + "&quot;WPA_PSK</string>\n"
-                    + "<string name=\"SSID\">&quot;" + WifiConfigurationTestUtil.TEST_SSID
-                        + "&quot;</string>\n"
+                    + "<string name=\"ConfigKey\">&quot;"
+                    + WifiConfigurationTestUtil.TEST_SSID
+                    + "&quot;WPA_PSK</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"
@@ -251,6 +253,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"true\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"false\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java
index 64e266c..c44bcad 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java
@@ -78,6 +78,8 @@
             WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_MBO_OCE;
     private static final int TEST_L2_FAILURE = WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION;
     private static final int TEST_DHCP_FAILURE = WifiBlocklistMonitor.REASON_DHCP_FAILURE;
+    private static final long TEST_MAX_DISABLE_DURATION_MILLIS =
+            TimeUnit.HOURS.toMillis(18); // 18 hours
     private static final long BASE_BLOCKLIST_DURATION = TimeUnit.MINUTES.toMillis(5); // 5 minutes
     private static final long BASE_CONNECTED_SCORE_BLOCKLIST_DURATION =
             TimeUnit.SECONDS.toMillis(30);
@@ -141,6 +143,7 @@
     @Mock private ScanRequestProxy mScanRequestProxy;
     @Mock private WifiScoreCard.PerNetwork mPerNetwork;
     @Mock private WifiScoreCard.NetworkConnectionStats mRecentStats;
+    @Mock private WifiGlobals mWifiGlobals;
 
     private MockResources mResources;
     private WifiBlocklistMonitor mWifiBlocklistMonitor;
@@ -156,6 +159,8 @@
                 .thenReturn(TEST_NUM_MAX_FIRMWARE_SUPPORT_SSIDS);
         when(mScoringParams.getSufficientRssi(anyInt())).thenReturn(TEST_SUFFICIENT_RSSI);
         when(mScoringParams.getGoodRssi(anyInt())).thenReturn(TEST_GOOD_RSSI);
+        when(mWifiGlobals.getWifiConfigMaxDisableDurationMs())
+                .thenReturn(TEST_MAX_DISABLE_DURATION_MILLIS);
         mResources = new MockResources();
         mResources.setInteger(R.integer.config_wifiBssidBlocklistMonitorBaseBlockDurationMs,
                 (int) BASE_BLOCKLIST_DURATION);
@@ -248,7 +253,7 @@
 
         mWifiBlocklistMonitor = new WifiBlocklistMonitor(mContext, mWifiConnectivityHelper,
                 mWifiLastResortWatchdog, mClock, mLocalLog, mWifiScoreCard, mScoringParams,
-                mWifiMetrics, mWifiPermissionsUtil);
+                mWifiMetrics, mWifiPermissionsUtil, mWifiGlobals);
         mWifiBlocklistMonitor.setScanRequestProxy(mScanRequestProxy);
     }
 
@@ -1600,7 +1605,7 @@
 
     /**
      * Verify the disable duration for a network is capped at
-     * WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS.
+     * WifiConfigMaxDisableDurationMs.
      */
     @Test
     public void testTryEnableNetworkExponentialBackoffCapped() {
@@ -1610,10 +1615,10 @@
                 Integer.MAX_VALUE);
         verifyDisableNetwork(openNetwork, disableReason);
 
-        // verify the exponential backoff is capped at WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS
+        // verify the exponential backoff is capped at WifiConfigMaxDisableDurationMs
         verifyNetworkIsEnabledAfter(openNetwork,
                 TEST_ELAPSED_UPDATE_NETWORK_SELECTION_TIME_MILLIS
-                        + WifiBlocklistMonitor.WIFI_CONFIG_MAX_DISABLE_DURATION_MILLIS);
+                        + TEST_MAX_DISABLE_DURATION_MILLIS);
     }
 
     /**
@@ -1677,7 +1682,7 @@
             mResources.setInteger(entry.getValue(), newThreshold);
             mWifiBlocklistMonitor = new WifiBlocklistMonitor(mContext, mWifiConnectivityHelper,
                     mWifiLastResortWatchdog, mClock, mLocalLog, mWifiScoreCard, mScoringParams,
-                    mWifiMetrics, mWifiPermissionsUtil);
+                    mWifiMetrics, mWifiPermissionsUtil, mWifiGlobals);
             mWifiBlocklistMonitor.setScanRequestProxy(mScanRequestProxy);
 
             // Verify that the threshold is updated in the copied version
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index 96d8d6b..84e12e6 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -18,6 +18,8 @@
 
 import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION_ENTERPRISE;
 
+import static com.android.server.wifi.WifiConfigManager.BUFFERED_WRITE_ALARM_TAG;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -51,7 +53,9 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.app.test.TestAlarmManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -89,6 +93,7 @@
 import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
 import com.android.server.wifi.util.LruConnectionTracker;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import org.junit.After;
@@ -189,6 +194,7 @@
     @Mock private WifiConfigManager.OnNetworkUpdateListener mWcmListener;
     @Mock private FrameworkFacade mFrameworkFacade;
     @Mock private DeviceConfigFacade mDeviceConfigFacade;
+    @Mock private FeatureFlags mFeatureFlags;
     @Mock private MacAddressUtil mMacAddressUtil;
     @Mock private WifiBlocklistMonitor mWifiBlocklistMonitor;
     @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
@@ -211,7 +217,7 @@
     private TestLooper mLooper = new TestLooper();
     private MockitoSession mSession;
     private WifiCarrierInfoManager mWifiCarrierInfoManager;
-
+    private TestAlarmManager mAlarmManager;
 
     /**
      * Setup the mocks and an instance of WifiConfigManager before each test.
@@ -228,6 +234,10 @@
 
         // Set up the package name stuff & permission override.
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mAlarmManager = new TestAlarmManager();
+        mLooper = new TestLooper();
+        when(mContext.getSystemService(AlarmManager.class))
+                .thenReturn(mAlarmManager.getAlarmManager());
         mResources = new MockResources();
         mResources.setBoolean(
                 R.bool.config_wifi_only_link_same_credential_configurations, false);
@@ -321,6 +331,8 @@
         when(mPackageManager.getPackagesHoldingPermissions(any(String[].class), anyInt()))
                 .thenReturn(Collections.emptyList());
         when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
+        when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags);
+        when(mFeatureFlags.delaySaveToStore()).thenReturn(true);
         mWifiCarrierInfoManager = spy(new WifiCarrierInfoManager(mTelephonyManager,
                 mSubscriptionManager, mWifiInjector, mock(FrameworkFacade.class),
                 wifiContext, mock(WifiConfigStore.class), mock(Handler.class),
@@ -442,7 +454,10 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
 
         assertTrue(mWifiConfigManager.saveToStore(true));
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiMetrics).wifiConfigStored(anyInt());
     }
 
@@ -569,6 +584,9 @@
         verifyNetworkRemoveBroadcast();
         verifyNetworkAddBroadcast();
 
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(openNetwork);
 
@@ -757,7 +775,7 @@
         WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
                 networks, retrievedNetworks);
         verify(mWcmListener).onNetworkUpdated(
-                wifiConfigCaptor.capture(), wifiConfigCaptor.capture());
+                wifiConfigCaptor.capture(), wifiConfigCaptor.capture(), anyBoolean());
         WifiConfiguration newConfig = wifiConfigCaptor.getAllValues().get(1);
         WifiConfiguration oldConfig = wifiConfigCaptor.getAllValues().get(0);
         assertEquals(openNetwork.networkId, newConfig.networkId);
@@ -823,7 +841,7 @@
         WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
                 networks, retrievedNetworks);
         verify(mWcmListener).onNetworkUpdated(
-                wifiConfigCaptor.capture(), wifiConfigCaptor.capture());
+                wifiConfigCaptor.capture(), wifiConfigCaptor.capture(), anyBoolean());
         WifiConfiguration newConfig = wifiConfigCaptor.getAllValues().get(1);
         WifiConfiguration oldConfig = wifiConfigCaptor.getAllValues().get(0);
         assertEquals(openNetwork.networkId, newConfig.networkId);
@@ -943,7 +961,7 @@
                 mWifiConfigManager.addOrUpdateNetwork(openNetwork, TEST_CREATOR_UID);
         assertNotEquals(WifiConfiguration.INVALID_NETWORK_ID, networkUpdateResult.getNetworkId());
         verify(mWcmListener).onNetworkUpdated(
-                wifiConfigCaptor.capture(), wifiConfigCaptor.capture());
+                wifiConfigCaptor.capture(), wifiConfigCaptor.capture(), anyBoolean());
         WifiConfiguration newConfig = wifiConfigCaptor.getAllValues().get(1);
         assertEquals(WifiConfiguration.RANDOMIZATION_AUTO, newConfig.macRandomizationSetting);
         assertEquals(openNetwork.networkId, newConfig.networkId);
@@ -1176,7 +1194,9 @@
 
         assertTrue(addNetworkToWifiConfigManager(ephemeralNetwork).isSuccess());
         assertTrue(addNetworkToWifiConfigManager(openNetwork).isSuccess());
-
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         // The open network addition should trigger a store write.
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkListStoreData =
                 captureWriteNetworksListStoreData();
@@ -1675,7 +1695,10 @@
         NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
         assertTrue(retrievedStatus.isNetworkEnabled());
         verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiBlocklistMonitor, times(2)).clearBssidBlocklistForSsid(openNetwork.SSID);
 
         // Now set it disabled.
@@ -1685,7 +1708,10 @@
         retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
         assertTrue(retrievedStatus.isNetworkPermanentlyDisabled());
         verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.DISABLED);
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiMetrics, atLeast(2)).wifiConfigStored(anyInt());
     }
 
@@ -1764,14 +1790,20 @@
         WifiConfiguration retrievedNetwork =
                 mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
         assertTrue(retrievedNetwork.allowAutojoin);
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
 
         // Now set it disallow auto-join.
         assertTrue(mWifiConfigManager.allowAutojoin(
                 result.getNetworkId(), false));
         retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
         assertFalse(retrievedNetwork.allowAutojoin);
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiMetrics, atLeast(2)).wifiConfigStored(anyInt());
     }
 
@@ -1924,6 +1956,101 @@
     }
 
     /**
+     * Verify that the protected WifiEnterpriseConfig fields are set correctly.
+     */
+    @Test
+    public void testWifiEnterpriseConfigProtectedFields() {
+        // Add an external config. Expect the internal config to have the default values.
+        WifiConfiguration externalConfig = WifiConfigurationTestUtil.createEapNetwork();
+        externalConfig.enterpriseConfig.setUserApproveNoCaCert(true);
+        externalConfig.enterpriseConfig.setTofuDialogState(
+                WifiEnterpriseConfig.TOFU_DIALOG_STATE_ACCEPTED);
+
+        NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(externalConfig);
+        WifiConfiguration internalConfig = mWifiConfigManager.getConfiguredNetwork(
+                result.getNetworkId());
+        assertFalse(internalConfig.enterpriseConfig.isUserApproveNoCaCert());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_DIALOG_STATE_UNSPECIFIED,
+                internalConfig.enterpriseConfig.getTofuDialogState());
+
+        // Update using an external config. Expect internal config to retain the default values.
+        result = verifyUpdateNetworkToWifiConfigManager(externalConfig);
+        internalConfig = mWifiConfigManager.getConfiguredNetwork(
+                result.getNetworkId());
+        assertFalse(internalConfig.enterpriseConfig.isUserApproveNoCaCert());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_DIALOG_STATE_UNSPECIFIED,
+                internalConfig.enterpriseConfig.getTofuDialogState());
+
+        // If the internal config's values are updated by the framework, merging
+        // with an external config should not overwrite the internal values.
+        mWifiConfigManager.setUserApproveNoCaCert(externalConfig.networkId, true);
+        mWifiConfigManager.setTofuDialogApproved(externalConfig.networkId, true);
+        externalConfig.enterpriseConfig.setUserApproveNoCaCert(false);
+        externalConfig.enterpriseConfig.setTofuDialogApproved(false);
+
+        result = verifyUpdateNetworkToWifiConfigManager(externalConfig);
+        internalConfig = mWifiConfigManager.getConfiguredNetwork(
+                result.getNetworkId());
+        assertTrue(internalConfig.enterpriseConfig.isUserApproveNoCaCert());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_DIALOG_STATE_ACCEPTED,
+                internalConfig.enterpriseConfig.getTofuDialogState());
+    }
+
+    /**
+     * Verify that the TOFU connection state is set correctly when an Enterprise config is added or
+     * updated.
+     */
+    @Test
+    public void testEnterpriseConfigTofuStateMerge() {
+        long featureSet = WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE;
+        when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn(featureSet);
+
+        // If the configuration has never connected, the merged TOFU connection state
+        // should be set based on the latest external configuration.
+        WifiConfiguration config =
+                prepareTofuEapConfig(
+                        WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE);
+        config.enterpriseConfig.enableTrustOnFirstUse(false);
+        NetworkUpdateResult result = addNetworkToWifiConfigManager(config);
+        WifiConfiguration internalConfig =
+                mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_STATE_NOT_ENABLED,
+                internalConfig.enterpriseConfig.getTofuConnectionState());
+
+        // Enabling TOFU in the external config should lead to the Enabled Pre-Connection state.
+        config.enterpriseConfig.enableTrustOnFirstUse(true);
+        config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.PEAP);
+        result = updateNetworkToWifiConfigManager(config);
+        internalConfig = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_STATE_ENABLED_PRE_CONNECTION,
+                internalConfig.enterpriseConfig.getTofuConnectionState());
+
+        // Invalid post-connection values in the external config should be ignored,
+        // since external configs should not be setting their own TOFU connection state.
+        config.enterpriseConfig.setTofuConnectionState(
+                WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING);
+        result = updateNetworkToWifiConfigManager(config);
+        internalConfig = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_STATE_ENABLED_PRE_CONNECTION,
+                internalConfig.enterpriseConfig.getTofuConnectionState());
+
+        // Post-connection states in the internal config should always persist.
+        mWifiConfigManager.setTofuPostConnectionState(
+                result.getNetworkId(), WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING);
+        result = updateNetworkToWifiConfigManager(config);
+        internalConfig = mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING,
+                internalConfig.enterpriseConfig.getTofuConnectionState());
+    }
+
+    /**
      * Verifies the modification of a single network using
      * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} by passing in nulls
      * in all the publicly exposed fields.
@@ -1950,6 +2077,9 @@
 
         // Verify no changes to the original network configuration.
         verifyNetworkUpdateBroadcast();
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         verifyNetworkInConfigStoreData(originalNetwork);
         assertFalse(result.hasIpChanged());
         assertFalse(result.hasProxyChanged());
@@ -3665,6 +3795,7 @@
                 .thenReturn(true);
         mWifiConfigManager.handleUserUnlock(user1);
         verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class));
+        assertFalse(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
         // Capture the written data for the user 1 and ensure that it corresponds to what was
         // setup.
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList =
@@ -3745,6 +3876,7 @@
                 .thenReturn(true);
         mWifiConfigManager.handleUserUnlock(user1);
         verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class));
+        assertFalse(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList =
                 captureWriteNetworksListStoreData();
         assertTrue(writtenNetworkList.first.isEmpty());
@@ -3834,7 +3966,7 @@
         mWifiConfigManager.handleUserStop(user1);
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
                 .switchUserStoresAndRead(any(List.class));
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiMetrics).wifiConfigStored(anyInt());
     }
 
@@ -3920,8 +4052,11 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
         mContextConfigStoreMockOrder.verify(mWifiConfigStore)
                 .switchUserStoresAndRead(any(List.class));
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
         verify(mWifiMetrics).wifiConfigStored(anyInt());
+        // Verify shut down handling
+        mWifiConfigManager.handleShutDown();
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
     }
 
     /**
@@ -4291,6 +4426,9 @@
         mWifiConfigManager.addOnNetworkUpdateListener(mListener);
         // Set connect choice of network 2 over network 1.
         verifySetUserConnectChoice(network2.networkId, network1.networkId);
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
 
         WifiConfiguration retrievedNetwork =
                 mWifiConfigManager.getConfiguredNetwork(network1.networkId);
@@ -4313,10 +4451,13 @@
         assertNotEquals(
                 network2.getProfileKey(),
                 retrievedNetwork.getNetworkSelectionStatus().getConnectChoice());
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
 
         // This should have triggered 2 buffered writes. 1 for setting the connect choice, 1 for
         // clearing it after network removal.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, times(2)).write(eq(false));
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, times(2)).write(true);
         verify(mListener, times(2)).onConnectChoiceRemoved(anyString());
         verify(mWifiMetrics, atLeast(2)).wifiConfigStored(anyInt());
     }
@@ -5623,9 +5764,15 @@
     private void createWifiConfigManager() {
         mWifiConfigManager =
                 new WifiConfigManager(
-                        mContext, mWifiKeyStore, mWifiConfigStore,
-                        mNetworkListSharedStoreData, mNetworkListUserStoreData,
-                        mRandomizedMacStoreData, mLruConnectionTracker, mWifiInjector);
+                        mContext,
+                        mWifiKeyStore,
+                        mWifiConfigStore,
+                        mNetworkListSharedStoreData,
+                        mNetworkListUserStoreData,
+                        mRandomizedMacStoreData,
+                        mLruConnectionTracker,
+                        mWifiInjector,
+                        new Handler(mLooper.getLooper()));
         mWifiConfigManager.enableVerboseLogging(true);
     }
 
@@ -5729,7 +5876,7 @@
                     .setConfigurations(sharedConfigsCaptor.capture());
             mNetworkListStoreDataMockOrder.verify(mNetworkListUserStoreData)
                     .setConfigurations(userConfigsCaptor.capture());
-            mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+            mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(true);
             return Pair.create(sharedConfigsCaptor.getValue(), userConfigsCaptor.getValue());
         } catch (Exception e) {
             fail("Exception encountered during write " + e);
@@ -5964,6 +6111,9 @@
         assertTrue(result.hasProxyChanged());
 
         verifyNetworkAddBroadcast();
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(configuration);
         return result;
@@ -5982,7 +6132,7 @@
 
         verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for ephemeral network addition.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(true);
         return result;
     }
 
@@ -6000,7 +6150,7 @@
 
         verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for ephemeral network addition.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(true);
         return result;
     }
 
@@ -6020,7 +6170,7 @@
                 any(WifiConfiguration.class));
         verifyNetworkAddBroadcast();
         // Ensure that the write was not invoked for Passpoint network addition.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(true);
         return result;
     }
 
@@ -6050,6 +6200,9 @@
         assertFalse(result.isNewNetwork());
 
         verifyNetworkUpdateBroadcast();
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         // Verify that the config store write was triggered with this new configuration.
         verifyNetworkInConfigStoreData(configuration);
         return result;
@@ -6088,6 +6241,9 @@
                 configuration.networkId, TEST_CREATOR_UID, TEST_CREATOR_NAME));
 
         verifyNetworkRemoveBroadcast();
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         // Verify if the config store write was triggered without this new configuration.
         verifyNetworkNotInConfigStoreData(configuration);
         verify(mWifiBlocklistMonitor, atLeastOnce()).handleNetworkRemoved(configuration.SSID);
@@ -6103,7 +6259,7 @@
 
         verifyNetworkRemoveBroadcast();
         // Ensure that the write was not invoked for ephemeral network remove.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(true);
     }
 
     /**
@@ -6118,7 +6274,7 @@
         verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class), eq(false));
         verifyNetworkRemoveBroadcast();
         // Ensure that the write was not invoked for Passpoint network remove.
-        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+        mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(true);
     }
 
     /**
@@ -6328,7 +6484,9 @@
         verifyAddNetworkToWifiConfigManager(testNetwork2);
         mWifiConfigManager.updateNetworkAfterConnect(
                 testNetwork2.networkId, false, false, TEST_RSSI);
-        mWifiConfigManager.saveToStore(true);
+        assertTrue(mAlarmManager.isPending(BUFFERED_WRITE_ALARM_TAG));
+        mAlarmManager.dispatch(BUFFERED_WRITE_ALARM_TAG);
+        mLooper.dispatchAll();
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkStoreData =
                 captureWriteNetworksListStoreData();
         List<WifiConfiguration> sharedNetwork = networkStoreData.first;
@@ -6707,6 +6865,7 @@
         assertTrue(mergedNetwork.isSecurityType(upgradableSecurityType));
         assertEquals(upgradableConfig.getDefaultSecurityParams().isAddedByAutoUpgrade(),
                 mergedNetwork.getSecurityParams(upgradableSecurityType).isAddedByAutoUpgrade());
+        assertEquals(upgradableConfig.hiddenSSID, mergedNetwork.hiddenSSID);
     }
 
     /**
@@ -6748,6 +6907,47 @@
     }
 
     /**
+     * Verifies that adding an unhidden upgraded config will update the existing network to
+     * unhidden.
+     * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+     */
+    @Test
+    public void testAddUnhiddenUpgradedNetworkOverwritesHiddenSsidValue() {
+        WifiConfiguration baseConfig = new WifiConfiguration();
+        baseConfig.SSID = "\"upgradableNetwork\"";
+        baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+        baseConfig.preSharedKey = "\"Passw0rd\"";
+        baseConfig.hiddenSSID = true;
+        WifiConfiguration upgradedConfig = new WifiConfiguration();
+        upgradedConfig.SSID = "\"upgradableNetwork\"";
+        upgradedConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+        upgradedConfig.preSharedKey = "\"Passw0rd\"";
+        upgradedConfig.hiddenSSID = false;
+
+        verifyAddUpgradableNetwork(baseConfig, upgradedConfig);
+    }
+
+    /**
+     * Verifies that adding a hidden upgraded config will update the existing network to hidden.
+     * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+     */
+    @Test
+    public void testAddHiddenUpgradedNetworkOverwritesHiddenSsidValue() {
+        WifiConfiguration baseConfig = new WifiConfiguration();
+        baseConfig.SSID = "\"upgradableNetwork\"";
+        baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+        baseConfig.preSharedKey = "\"Passw0rd\"";
+        baseConfig.hiddenSSID = false;
+        WifiConfiguration upgradedConfig = new WifiConfiguration();
+        upgradedConfig.SSID = "\"upgradableNetwork\"";
+        upgradedConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+        upgradedConfig.preSharedKey = "\"Passw0rd\"";
+        upgradedConfig.hiddenSSID = true;
+
+        verifyAddUpgradableNetwork(baseConfig, upgradedConfig);
+    }
+
+    /**
      * Verifies the addition of an upgradable network using
      * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
      */
@@ -6816,7 +7016,14 @@
         assertTrue(mergedNetwork.isSecurityType(downgradableSecurityType));
         assertFalse(mergedNetwork.getSecurityParams(baseSecurityType)
                 .isAddedByAutoUpgrade());
+        if (baseConfig.hiddenSSID && !downgradableConfig.hiddenSSID) {
+            // Merged network should still be hidden if the base config is hidden.
+            assertTrue(mergedNetwork.hiddenSSID);
+        } else {
+            assertEquals(downgradableConfig.hiddenSSID, mergedNetwork.hiddenSSID);
+        }
     }
+
     /**
      * Verifies the addition of a downgradable network using
      * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
@@ -6875,6 +7082,48 @@
                 downgradableConfig);
     }
 
+    /**
+     * Verifies that adding an unhidden downgraded config won't update a hidden existing network to
+     * unhidden. This is to prevent the case where a user adds a downgraded network that's only
+     * visible due to scanning for a hidden SSID from an existing upgradeable config.
+     * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+     */
+    @Test
+    public void testAddUnhiddenDowngradedNetworkDoesntOverwriteHiddenSsidValue() {
+        WifiConfiguration baseConfig = new WifiConfiguration();
+        baseConfig.SSID = "\"downgradableConfig\"";
+        baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+        baseConfig.preSharedKey = "\"Passw0rd\"";
+        baseConfig.hiddenSSID = true;
+        WifiConfiguration downgradableConfig = new WifiConfiguration();
+        downgradableConfig.SSID = "\"downgradableConfig\"";
+        downgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+        downgradableConfig.preSharedKey = "\"Passw0rd\"";
+        downgradableConfig.hiddenSSID = false;
+
+        verifyAddDowngradableNetwork(baseConfig, downgradableConfig);
+    }
+
+    /**
+     * Verifies that adding an hidden downgraded config updates an existing network to hidden.
+     * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+     */
+    @Test
+    public void testAddHiddenDowngradedNetworkOverwritesHiddenSsidValueToTrue() {
+        WifiConfiguration baseConfig = new WifiConfiguration();
+        baseConfig.SSID = "\"downgradableConfig\"";
+        baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+        baseConfig.preSharedKey = "\"Passw0rd\"";
+        baseConfig.hiddenSSID = false;
+        WifiConfiguration downgradableConfig = new WifiConfiguration();
+        downgradableConfig.SSID = "\"downgradableConfig\"";
+        downgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+        downgradableConfig.preSharedKey = "\"Passw0rd\"";
+        downgradableConfig.hiddenSSID = true;
+
+        verifyAddDowngradableNetwork(baseConfig, downgradableConfig);
+    }
+
     private void verifyLoadFromStoreMergeUpgradableConfigurations(
             WifiConfiguration baseConfig,
             WifiConfiguration upgradableConfig) {
@@ -7179,6 +7428,36 @@
     }
 
     /**
+     * Verify that the protected WifiEnterpriseConfig fields are loaded correctly
+     * from the XML store.
+     */
+    @Test
+    public void testLoadEnterpriseConfigProtectedFields() throws Exception {
+        WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+        config.enterpriseConfig.setUserApproveNoCaCert(true);
+        config.enterpriseConfig.setTofuDialogState(WifiEnterpriseConfig.TOFU_DIALOG_STATE_ACCEPTED);
+        config.enterpriseConfig.setTofuConnectionState(
+                WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING);
+        List<WifiConfiguration> storedConfigs = Arrays.asList(config);
+
+        // Setup xml storage
+        setupStoreDataForRead(storedConfigs, Arrays.asList());
+        assertTrue(mWifiConfigManager.loadFromStore());
+        verify(mWifiConfigStore).read();
+
+        List<WifiConfiguration> retrievedNetworks =
+                mWifiConfigManager.getConfiguredNetworksWithPasswords();
+        assertEquals(1, retrievedNetworks.size());
+        assertTrue(retrievedNetworks.get(0).enterpriseConfig.isUserApproveNoCaCert());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_DIALOG_STATE_ACCEPTED,
+                retrievedNetworks.get(0).enterpriseConfig.getTofuDialogState());
+        assertEquals(
+                WifiEnterpriseConfig.TOFU_STATE_CERT_PINNING,
+                retrievedNetworks.get(0).enterpriseConfig.getTofuConnectionState());
+    }
+
+    /**
      * Verify that updating an existing config to a incompatible type works well.
      */
     @Test
@@ -7490,6 +7769,128 @@
     }
 
     /**
+     * Verifies that excess app-added networks are only considered for removal
+     * if the total number of networks reaches the app-added limit.
+     *
+     * This directly tests an optimization for enforcing the app-added limit. Retrieving the
+     * list of app-added configs is expensive (since we need to check the permissions on
+     * all the stored configs), so we should only retrieve it when necessary.
+     */
+    @Test
+    public void testFilterAtAppAddedLimit() {
+        final int maxTotalConfigs = 4;
+        final int maxAppAddedConfigs = 3;
+        mResources.setInteger(R.integer.config_wifiMaxNumWifiConfigurations, maxTotalConfigs);
+        mResources.setInteger(R.integer.config_wifiMaxNumWifiConfigurationsAddedByAllApps,
+                maxAppAddedConfigs);
+
+        when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())).thenReturn(false);
+        when(mWifiPermissionsUtil.isProfileOwner(anyInt(), any())).thenReturn(false);
+        when(mWifiPermissionsUtil.isSystem(any(), eq(TEST_CREATOR_UID)))
+                .thenReturn(true);
+        when(mWifiPermissionsUtil.isSystem(any(), eq(TEST_OTHER_USER_UID)))
+                .thenReturn(false);
+
+        // Add the maximum number of app-added configs.
+        for (int i = 0; i < maxAppAddedConfigs; i++) {
+            WifiConfiguration appAddedConfig = WifiConfigurationTestUtil.createPskNetwork();
+            appAddedConfig.creatorUid = TEST_OTHER_USER_UID;
+            verifyAddNetworkToWifiConfigManager(appAddedConfig);
+        }
+        assertEquals(maxAppAddedConfigs, mWifiConfigManager.getConfiguredNetworks().size());
+
+        // Since the app-added limit was not exceeded, the app-added config list was not retrieved.
+        // We only expect a single permission check per add.
+        int expectedNumPermissionChecks = maxAppAddedConfigs;
+        verify(mWifiPermissionsUtil, times(expectedNumPermissionChecks))
+                .isProfileOwner(anyInt(), any());
+
+        // The next app-added config will trigger the retrieval of the app-added config list.
+        // Expect the normal permission check, plus one additional from when
+        // the app-added config list is generated.
+        WifiConfiguration appAddedConfig = WifiConfigurationTestUtil.createPskNetwork();
+        appAddedConfig.creatorUid = TEST_OTHER_USER_UID;
+        verifyAddNetworkToWifiConfigManager(appAddedConfig);
+        assertEquals(maxAppAddedConfigs, mWifiConfigManager.getConfiguredNetworks().size());
+
+        expectedNumPermissionChecks += 2;
+        verify(mWifiPermissionsUtil, times(expectedNumPermissionChecks))
+                .isProfileOwner(anyInt(), any());
+
+        // Adding a system config will not trigger the retrieval of the app-added config list.
+        // We only expect a single permission check for this add.
+        WifiConfiguration systemConfig = WifiConfigurationTestUtil.createPskNetwork();
+        systemConfig.creatorUid = TEST_CREATOR_UID;
+        verifyAddNetworkToWifiConfigManager(systemConfig);
+        assertEquals(maxTotalConfigs, mWifiConfigManager.getConfiguredNetworks().size());
+
+        expectedNumPermissionChecks += 1;
+        verify(mWifiPermissionsUtil, times(expectedNumPermissionChecks))
+                .isProfileOwner(anyInt(), any());
+    }
+
+    /**
+     * Verifies that {@link WifiConfigManager#filterNonAppAddedNetworks(List)} properly filters
+     * out non app-added networks. Also checks that the permissions are cached for networks
+     * with the same creator.
+     */
+    @Test
+    public void testFilterNonAppAddedNetworks() {
+        int totalConfigs = 3;
+        List<WifiConfiguration> configs = new ArrayList<>();
+        for (int i = 0; i < totalConfigs; i++) {
+            configs.add(WifiConfigurationTestUtil.createPskNetwork());
+        }
+
+        // Configs 0 and 1 will belong to the same creator.
+        configs.get(0).creatorUid = 1;
+        configs.get(1).creatorUid = 1;
+        configs.get(2).creatorUid = 2;
+
+        configs.get(0).creatorName = "common";
+        configs.get(1).creatorName = "common";
+        configs.get(2).creatorName = "different";
+
+        // UIDs 1 and 2 belong to a PO and an app, respectively.
+        when(mWifiPermissionsUtil.isProfileOwner(eq(1), anyString())).thenReturn(true);
+        when(mWifiPermissionsUtil.isProfileOwner(eq(2), anyString())).thenReturn(false);
+        when(mWifiPermissionsUtil.isDeviceOwner(anyInt(), any())).thenReturn(false);
+        when(mWifiPermissionsUtil.isSystem(any(), anyInt())).thenReturn(false);
+        when(mWifiPermissionsUtil.isSignedWithPlatformKey(anyInt())).thenReturn(false);
+
+        // Verify that the non app-added networks (those created by UID 1) are filtered out.
+        List<WifiConfiguration> appAddedNetworks =
+                mWifiConfigManager.filterNonAppAddedNetworks(configs);
+        assertEquals(1, appAddedNetworks.size());
+
+        // Permissions should only be checked once per unique creator.
+        verify(mWifiPermissionsUtil, times(2)).isProfileOwner(anyInt(), any());
+    }
+
+    @Test
+    public void testNetworkValidation() {
+        ArgumentCaptor<WifiConfiguration> wifiConfigCaptor =
+                ArgumentCaptor.forClass(WifiConfiguration.class);
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+        NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+        // Set internet to be validated and verify internet validation tracking is updated.
+        int networkId = result.getNetworkId();
+        mWifiConfigManager.setNetworkValidatedInternetAccess(networkId, true);
+        WifiConfiguration updatedConfig = mWifiConfigManager.getConfiguredNetwork(networkId);
+        assertTrue(updatedConfig.getNetworkSelectionStatus().hasEverValidatedInternetAccess());
+        assertTrue(updatedConfig.validatedInternetAccess);
+
+        // Set internet validation failed now and verify again. hasEverValidatedInternetAccess
+        // should still be true but validatedInternetAccess should be false.
+        mWifiConfigManager.setNetworkValidatedInternetAccess(networkId, false);
+        updatedConfig = mWifiConfigManager.getConfiguredNetwork(networkId);
+        assertTrue(updatedConfig.getNetworkSelectionStatus().hasEverValidatedInternetAccess());
+        assertFalse(updatedConfig.validatedInternetAccess);
+    }
+
+    /**
      * Verifies that adding a network fails if the amount of saved networks is maxed out and the
      * next network to be removed is the same as the added network.
      */
@@ -7670,7 +8071,6 @@
         mWifiConfigManager.addCustomDhcpOptions(WifiSsid.fromString(TEST_SSID), oui1, option1);
         mWifiConfigManager.addCustomDhcpOptions(WifiSsid.fromString(TEST_SSID), oui2, option2);
         mWifiConfigManager.addCustomDhcpOptions(WifiSsid.fromString(TEST_SSID), oui3, option3);
-        mLooper.dispatchAll();
         assertEquals(1, mWifiConfigManager.getCustomDhcpOptions(
                 WifiSsid.fromString(TEST_SSID), Collections.singletonList(oui1)).size());
         assertEquals(1, mWifiConfigManager.getCustomDhcpOptions(
@@ -7679,7 +8079,6 @@
                 WifiSsid.fromString(TEST_SSID), Arrays.asList(oui2, oui3)).size());
 
         mWifiConfigManager.removeCustomDhcpOptions(WifiSsid.fromString(TEST_SSID), oui1);
-        mLooper.dispatchAll();
         assertEquals(0, mWifiConfigManager.getCustomDhcpOptions(
                 WifiSsid.fromString(TEST_SSID), Collections.singletonList(oui1)).size());
         List<DhcpOption> actual2 = mWifiConfigManager.getCustomDhcpOptions(
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 4adede0..c7fa102 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -102,6 +102,7 @@
                     + "<byte-array name=\"AllowedSuiteBCiphers\" num=\"0\"></byte-array>\n"
                     + "<boolean name=\"Shared\" value=\"%s\" />\n"
                     + "<boolean name=\"AutoJoinEnabled\" value=\"true\" />\n"
+                    + "<int name=\"Priority\" value=\"0\" />\n"
                     + "<int name=\"DeletionPriority\" value=\"0\" />\n"
                     + "<int name=\"NumRebootsSinceLastUse\" value=\"0\" />\n"
                     + "<boolean name=\"RepeaterEnabled\" value=\"false\" />\n"
@@ -175,6 +176,7 @@
                     + "<int name=\"ConnectChoiceRssi\" value=\"0\" />\n"
                     + "<boolean name=\"HasEverConnected\" value=\"false\" />\n"
                     + "<boolean name=\"CaptivePortalNeverDetected\" value=\"true\" />\n"
+                    + "<boolean name=\"HasEverValidatedInternetAccess\" value=\"false\" />\n"
                     + "</NetworkStatus>\n"
                     + "<IpConfiguration>\n"
                     + "<string name=\"IpAssignment\">DHCP</string>\n"
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index 2e0fb5b..42f9c6a 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
@@ -381,6 +381,15 @@
         return configuration;
     }
 
+    public static WifiConfiguration createCaptivePortalNetwork() {
+        WifiConfiguration configuration = createPskNetwork();
+        NetworkSelectionStatus.Builder builder = new NetworkSelectionStatus.Builder();
+        NetworkSelectionStatus networkSelectionStatus = builder.build();
+        networkSelectionStatus.setHasNeverDetectedCaptivePortal(false);
+        configuration.setNetworkSelectionStatus(networkSelectionStatus);
+        return configuration;
+    }
+
     public static WifiConfiguration createWepNetwork() {
         WifiConfiguration configuration =
                 generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true, null,
@@ -715,6 +724,7 @@
         assertEquals(expected.requirePmf, actual.requirePmf);
         assertEquals(expected.allowedKeyManagement, actual.allowedKeyManagement);
         assertEquals(expected.allowedAuthAlgorithms, actual.allowedAuthAlgorithms);
+        assertEquals(expected.priority, actual.priority);
         // Supplicant backup does not include the following fields.
         if (!isSupplicantBackup) {
             assertEquals(expected.allowedProtocols, actual.allowedProtocols);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index 0c62816..f8e6396 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -1336,13 +1336,27 @@
         EnterpriseConfig eapConfig1 = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TLS);
         eapConfig1.enterpriseConfig.setDomainSuffixMatch("wlan.android.com");
 
-        EnterpriseConfig eapConfig2 = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS);
+        EnterpriseConfig eapConfig2 = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TLS);
         eapConfig1.enterpriseConfig.setDomainSuffixMatch("wifi.android.com");
 
         assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig1.enterpriseConfig,
                 eapConfig2.enterpriseConfig));
     }
 
+    /** Verify WifiEnterpriseConfig Minimum TLS Version changes are detected. */
+    @Test
+    public void testMinimumTlsVersionChangesDetected() {
+        EnterpriseConfig eapConfig1 = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TLS);
+        eapConfig1.enterpriseConfig.setMinimumTlsVersion(WifiEnterpriseConfig.TLS_V1_3);
+
+        EnterpriseConfig eapConfig2 = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TLS);
+        eapConfig2.enterpriseConfig.setMinimumTlsVersion(WifiEnterpriseConfig.TLS_V1_0);
+
+        assertTrue(
+                WifiConfigurationUtil.hasEnterpriseConfigChanged(
+                        eapConfig1.enterpriseConfig, eapConfig2.enterpriseConfig));
+    }
+
     /**
      * Verify WifiEnterpriseConfig subject match changes are detected.
      */
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 404b0ba..ece4ea1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_SCREEN_ON;
-
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY;
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED;
@@ -63,7 +60,6 @@
 import android.app.test.MockAnswerUtil.AnswerWithArguments;
 import android.app.test.TestAlarmManager;
 import android.content.BroadcastReceiver;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.IpConfiguration;
 import android.net.MacAddress;
@@ -106,6 +102,7 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.ActiveModeWarden.ExternalClientModeManagerRequestListener;
 import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.proto.nano.WifiMetricsProto;
 import com.android.server.wifi.scanner.WifiScannerInternal;
 import com.android.server.wifi.util.LruConnectionTracker;
@@ -213,6 +210,7 @@
         mSession = ExtendedMockito.mockitoSession()
                 .strictness(Strictness.LENIENT)
                 .mockStatic(WifiInjector.class, withSettings().lenient())
+                .mockStatic(WifiStatsLog.class)
                 .startMocking();
         WifiInjector wifiInjector = mock(WifiInjector.class);
         when(wifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden);
@@ -332,6 +330,7 @@
     @Mock WifiCandidates.Candidate mCandidate2;
     @Mock WifiCandidates.Candidate mCandidate3;
     @Mock WifiCandidates.Candidate mCandidate4;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
     private WifiConfiguration mCandidateWifiConfig1;
     private WifiConfiguration mCandidateWifiConfig2;
     private List<WifiCandidates.Candidate> mCandidateList;
@@ -342,6 +341,11 @@
             mSuggestionUpdateListenerCaptor;
     @Captor ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackCaptor;
     @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+
+    @Captor
+    ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback>
+            mStateChangeCallbackArgumentCaptor;
+
     @Captor ArgumentCaptor<MultiInternetManager.ConnectionStatusListener>
             mMultiInternetConnectionStatusListenerCaptor;
     @Captor ArgumentCaptor<WifiDialogManager.SimpleDialogCallback> mSimpleDialogCallbackCaptor;
@@ -415,7 +419,7 @@
         private Message mMessage;
 
         TestHandler(Looper looper) {
-            super(looper, 100, new LocalLog(128), mWifiMetrics);
+            super(looper, 100, new LocalLog(128));
         }
 
         public List<Long> getIntervals() {
@@ -604,21 +608,42 @@
     }
 
     WifiConnectivityManager createConnectivityManager() {
-        WifiConnectivityManager wCm = new WifiConnectivityManager(mContext, mScoringParams,
-                mWifiConfigManager, mWifiNetworkSuggestionsManager,
-                mWifiNS, mWifiConnectivityHelper,
-                mWifiLastResortWatchdog, mOpenNetworkNotifier,
-                mWifiMetrics, mTestHandler, mClock,
-                mLocalLog, mWifiScoreCard, mWifiBlocklistMonitor, mWifiChannelUtilization,
-                mPasspointManager, mMultiInternetManager, mDeviceConfigFacade, mActiveModeWarden,
-                mFacade, mWifiGlobals, mExternalPnoScanRequestManager, mSsidTranslator,
-                mWifiPermissionsUtil, mWifiCarrierInfoManager, mWifiCountryCode,
-                mWifiDialogManager);
+        WifiConnectivityManager wCm =
+                new WifiConnectivityManager(
+                        mContext,
+                        mScoringParams,
+                        mWifiConfigManager,
+                        mWifiNetworkSuggestionsManager,
+                        mWifiNS,
+                        mWifiConnectivityHelper,
+                        mWifiLastResortWatchdog,
+                        mOpenNetworkNotifier,
+                        mWifiMetrics,
+                        mTestHandler,
+                        mClock,
+                        mLocalLog,
+                        mWifiScoreCard,
+                        mWifiBlocklistMonitor,
+                        mWifiChannelUtilization,
+                        mPasspointManager,
+                        mMultiInternetManager,
+                        mDeviceConfigFacade,
+                        mActiveModeWarden,
+                        mFacade,
+                        mWifiGlobals,
+                        mExternalPnoScanRequestManager,
+                        mSsidTranslator,
+                        mWifiPermissionsUtil,
+                        mWifiCarrierInfoManager,
+                        mWifiCountryCode,
+                        mWifiDialogManager,
+                        mWifiDeviceStateChangeManager);
         mLooper.dispatchAll();
         verify(mActiveModeWarden, atLeastOnce()).registerModeChangeCallback(
                 mModeChangeCallbackCaptor.capture());
-        verify(mContext, atLeastOnce()).registerReceiver(
-                mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        verify(mWifiDeviceStateChangeManager, atLeastOnce())
+                .registerStateChangeCallback(mStateChangeCallbackArgumentCaptor.capture());
+        setScreenState(false);
         verify(mWifiConfigManager, atLeastOnce()).addOnNetworkUpdateListener(
                 mNetworkUpdateListenerCaptor.capture());
         verify(mWifiNetworkSuggestionsManager, atLeastOnce()).addOnSuggestionUpdateListener(
@@ -715,6 +740,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 mPrimaryClientModeManager,
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+        assertEquals(WifiConnectivityManager.WIFI_STATE_DISCONNECTED,
+                mWifiConnectivityManager.getWifiState());
         mLooper.dispatchAll();
         verify(mPrimaryClientModeManager).startConnectToNetwork(
                 CANDIDATE_NETWORK_ID, Process.WIFI_UID, "any");
@@ -722,6 +749,22 @@
         verify(mActiveModeWarden).stopAllClientModeManagersInRole(ROLE_CLIENT_SECONDARY_TRANSIENT);
         verify(mActiveModeWarden, never()).requestSecondaryTransientClientModeManager(
                 any(), any(), any(), any());
+
+        // Verify a state change from secondaryCmm will get ignored and not change wifi state
+        ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class);
+        when(secondaryCmm.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED);
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                secondaryCmm,
+                WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
+        assertEquals(WifiConnectivityManager.WIFI_STATE_DISCONNECTED,
+                mWifiConnectivityManager.getWifiState());
+
+        // Verify state change from primary updates the state correctly
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_CONNECTED);
+        assertEquals(WifiConnectivityManager.WIFI_STATE_CONNECTED,
+                mWifiConnectivityManager.getWifiState());
     }
 
     /** Don't crash if allocated a null ClientModeManager. */
@@ -1503,7 +1546,27 @@
     @Test
     public void multiInternetSecondaryConnectionRequestSucceedsWithDbsApOnlyFailOnStaticIp() {
         setupMocksForMultiInternetTests(true);
+        // mock network selection not needed for primary
+        when(mWifiNS.isNetworkSelectionNeededForCmm(any())).thenReturn(false);
         testMultiInternetSecondaryConnectionRequest(true, false, false, CANDIDATE_BSSID_3);
+
+        // network selection should be skipped on the primary
+        verify(mWifiNS).selectNetwork(any());
+    }
+
+    /**
+     * Verify that when no valid secondary internet candidate is found network selection fallback
+     * onto the primary when needed.
+     */
+    @Test
+    public void multiInternetSecondaryConnectionRequestFallbackOnPrimary() {
+        setupMocksForMultiInternetTests(true);
+        // mock network selection not needed for primary
+        when(mWifiNS.isNetworkSelectionNeededForCmm(any())).thenReturn(true);
+        testMultiInternetSecondaryConnectionRequest(true, false, false, CANDIDATE_BSSID_3);
+
+        // network selection happens on the primary
+        verify(mWifiNS, atLeastOnce()).selectNetwork(any());
     }
 
     @Test
@@ -4861,6 +4924,44 @@
         verify(mPrimaryClientModeManager, times(0)).startRoamToNetwork(anyInt(), anyObject());
     }
 
+    @Test
+    public void testMultiInternetSimultaneous5GHz() {
+        // Enable dual 5GHz multi-internet mode
+        when(mWifiGlobals.isSupportMultiInternetDual5G()).thenReturn(true);
+
+        // 2.4GHz + 5GHz should be allowed
+        assertTrue(mWifiConnectivityManager.filterMultiInternetFrequency(TEST_FREQUENCY,
+                TEST_FREQUENCY_5G));
+
+        // 5GHz low + 5GHz high should be allowed
+        assertTrue(mWifiConnectivityManager.filterMultiInternetFrequency(
+                ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ,
+                ScanResult.BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ));
+
+        // 5GHz low + other 5GHz (that's neither low nor high) should not be allowed
+        assertFalse(mWifiConnectivityManager.filterMultiInternetFrequency(
+                ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ,
+                ScanResult.BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ - 1));
+
+        // 2 frequencies in 5GHz low band should not be allowed
+        assertFalse(mWifiConnectivityManager.filterMultiInternetFrequency(
+                ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ,
+                ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ - 1));
+
+        // 2 frequencies in 5GHz high band should not be allowed
+        assertFalse(mWifiConnectivityManager.filterMultiInternetFrequency(
+                ScanResult.BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ,
+                ScanResult.BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ + 1));
+
+        // Disable dual 5GHz multi-internet mode
+        when(mWifiGlobals.isSupportMultiInternetDual5G()).thenReturn(false);
+
+        // 5GHz low + 5GHz high should no longer be allowed
+        assertFalse(mWifiConnectivityManager.filterMultiInternetFrequency(
+                ScanResult.BAND_5_GHZ_LOW_HIGHEST_FREQ_MHZ,
+                ScanResult.BAND_5_GHZ_HIGH_LOWEST_FREQ_MHZ));
+    }
+
     /**
      *  Dump local log buffer.
      *
@@ -5119,6 +5220,74 @@
         verify(mWifiScanner, times(3)).startPnoScan(any(), any(), any());
     }
 
+    @Test
+    public void verifySetPnoScanEnabledByFramework() {
+        mWifiConnectivityManager = createConnectivityManager();
+
+        // set wifi on & disconnected to trigger pno scans when PNO scan is enabled.
+        setWifiEnabled(true);
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+        // Enable trusted connection. This should trigger a pno scan for auto-join.
+        mWifiConnectivityManager.setTrustedConnectionAllowed(true);
+        verify(mWifiScanner).startPnoScan(any(), any(), any());
+
+        // Verify disabling PNO scan stops the on-going PNO scan
+        mWifiConnectivityManager.setPnoScanEnabledByFramework(false, false);
+        verify(mWifiScanner).stopPnoScan(any());
+
+        // Verify that PNO scan is no longer triggered
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+        verify(mWifiScanner).startPnoScan(any(), any(), any());
+
+        // Verify that PNO scan is not triggered after wifi toggle, since it's not configured to do
+        // so.
+        setWifiEnabled(false);
+        setWifiEnabled(true);
+        verify(mWifiScanner).startPnoScan(any(), any(), any());
+
+        // Verify that PNO scan is triggered again after being enabled explicitly
+        mWifiConnectivityManager.setPnoScanEnabledByFramework(true, false);
+        verify(mWifiScanner, times(2)).startPnoScan(any(), any(), any());
+    }
+
+    @Test
+    public void verifySetPnoScanEnabledAfterWifiToggle() {
+        mWifiConnectivityManager = createConnectivityManager();
+
+        // set wifi on & disconnected to trigger pno scans when PNO scan is enabled.
+        setWifiEnabled(true);
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+        // Enable trusted connection. This should trigger a pno scan for auto-join.
+        mWifiConnectivityManager.setTrustedConnectionAllowed(true);
+        verify(mWifiScanner).startPnoScan(any(), any(), any());
+
+        // Verify disabling PNO scan stops the on-going PNO scan
+        mWifiConnectivityManager.setPnoScanEnabledByFramework(false, true);
+        verify(mWifiScanner).stopPnoScan(any());
+
+        // Verify that PNO scan is no longer triggered
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+        verify(mWifiScanner).startPnoScan(any(), any(), any());
+
+        // Verify that PNO scan is triggered again after wifi toggle
+        setWifiEnabled(false);
+        setWifiEnabled(true);
+        mWifiConnectivityManager.handleConnectionStateChanged(
+                mPrimaryClientModeManager,
+                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+        verify(mWifiScanner, times(2)).startPnoScan(any(), any(), any());
+    }
+
     /**
      * Verify that the increased PNO interval is used when power save is on.
      */
@@ -5290,7 +5459,8 @@
                 mPrimaryClientModeManager,
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
         mWifiConnectivityManager.setTrustedConnectionAllowed(true);
-        inOrder.verify(mWifiMetrics).logPnoScanStart();
+        ExtendedMockito.verify(() -> WifiStatsLog.write(
+                eq(WifiStatsLog.PNO_SCAN_STARTED), anyBoolean()));
 
         // change to High Movement, which has the same scan interval as Low Movement
         mWifiConnectivityManager.setDeviceMobilityState(
@@ -5306,7 +5476,8 @@
         inOrder.verify(mWifiMetrics).logPnoScanStop();
         inOrder.verify(mWifiMetrics).enterDeviceMobilityState(
                 WifiManager.DEVICE_MOBILITY_STATE_STATIONARY);
-        inOrder.verify(mWifiMetrics).logPnoScanStart();
+        ExtendedMockito.verify(() -> WifiStatsLog.write(
+                eq(WifiStatsLog.PNO_SCAN_STARTED), anyBoolean()), times(2));
 
         // stops PNO scan
         mWifiConnectivityManager.setTrustedConnectionAllowed(false);
@@ -5649,13 +5820,16 @@
      */
     @Test
     public void testRetrievePnoListOrder() {
-        //Create 4 networks.
+        // Create 4 non-Passpoint and 3 Passpoint networks.
         WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork();
         WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
         WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork();
         WifiConfiguration network4 = WifiConfigurationTestUtil.createPskNetwork();
+        WifiConfiguration passpointNetwork1 = WifiConfigurationTestUtil.createPasspointNetwork();
+        WifiConfiguration passpointNetwork2 = WifiConfigurationTestUtil.createPasspointNetwork();
+        WifiConfiguration passpointNetwork3 = WifiConfigurationTestUtil.createPasspointNetwork();
 
-        // mark all networks except network4 as connected before
+        // Mark all non-Passpoint networks except network4 as connected before.
         network1.getNetworkSelectionStatus().setHasEverConnected(true);
         network2.getNetworkSelectionStatus().setHasEverConnected(true);
         network3.getNetworkSelectionStatus().setHasEverConnected(true);
@@ -5669,16 +5843,27 @@
         networkList.add(network2);
         networkList.add(network3);
         when(mWifiConfigManager.getSavedNetworks(anyInt())).thenReturn(networkList);
+
+        List<WifiConfiguration> passpointNetworkList = new ArrayList<>();
+        passpointNetworkList.add(passpointNetwork1);
+        passpointNetworkList.add(passpointNetwork2);
+        passpointNetworkList.add(passpointNetwork3);
+        when(mDeviceConfigFacade.includePasspointSsidsInPnoScans()).thenReturn(true);
+        when(mPasspointManager.getWifiConfigsForPasspointProfiles(anyBoolean()))
+                .thenReturn(passpointNetworkList);
         List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
                 mWifiConnectivityManager.retrievePnoNetworkList();
 
         // Verify correct order of networks. Note that network4 should not appear for PNO scan
         // since it had not been connected before.
-        assertEquals(4, pnoNetworks.size());
-        assertEquals(network3.SSID, pnoNetworks.get(0).ssid);
+        assertEquals(7, pnoNetworks.size());
+        assertEquals(passpointNetwork1.SSID, pnoNetworks.get(0).ssid);
         assertEquals(UNTRANSLATED_HEX_SSID, pnoNetworks.get(1).ssid); // Possible untranslated SSID
-        assertEquals(network2.SSID, pnoNetworks.get(2).ssid);
-        assertEquals(network1.SSID, pnoNetworks.get(3).ssid);
+        assertEquals(passpointNetwork2.SSID, pnoNetworks.get(2).ssid);
+        assertEquals(network3.SSID, pnoNetworks.get(3).ssid);
+        assertEquals(network2.SSID, pnoNetworks.get(4).ssid);
+        assertEquals(network1.SSID, pnoNetworks.get(5).ssid);
+        assertEquals(passpointNetwork3.SSID, pnoNetworks.get(6).ssid);
     }
 
     private List<List<Integer>> linkScoreCardFreqsToNetwork(WifiConfiguration... configs) {
@@ -5708,13 +5893,36 @@
         when(mWifiConfigManager.getSavedNetworks(anyInt()))
                 .thenReturn(Arrays.asList(configuration1, configuration2));
 
+        // linkScoreCardFreqsToNetwork creates 2 Lists of size 3 each.
         List<List<Integer>> freqs = linkScoreCardFreqsToNetwork(configuration1, configuration2);
 
         mLruConnectionTracker.addNetwork(configuration2);
         mLruConnectionTracker.addNetwork(configuration1);
 
-        assertEquals(new HashSet<>(freqs.get(0)), mWifiConnectivityManager
-                .fetchChannelSetForPartialScan(3, CHANNEL_CACHE_AGE_MINS));
+        // Max count is 3 with no per-network max count - should match the first freq List.
+        assertEquals(
+                new HashSet<>(freqs.get(0)),
+                mWifiConnectivityManager.fetchChannelSetForPartialScan(
+                        3, 0, CHANNEL_CACHE_AGE_MINS));
+
+        // Max count is 3 with per-network max count as 1 - should get the first freq from each
+        // network.
+        Set<Integer> channelSet =
+                mWifiConnectivityManager.fetchChannelSetForPartialScan(
+                        3, 1, CHANNEL_CACHE_AGE_MINS);
+        assertEquals(2, channelSet.size());
+        assertTrue(channelSet.contains(freqs.get(0).get(0)));
+        assertTrue(channelSet.contains(freqs.get(1).get(0)));
+
+        // Max count is 3 with per-network max count as 2 - should get the 2 freqs from
+        // network 1, and 1 freq from network 2.
+        channelSet =
+                mWifiConnectivityManager.fetchChannelSetForPartialScan(
+                        3, 2, CHANNEL_CACHE_AGE_MINS);
+        assertEquals(3, channelSet.size());
+        assertTrue(channelSet.contains(freqs.get(0).get(0)));
+        assertTrue(channelSet.contains(freqs.get(0).get(1)));
+        assertTrue(channelSet.contains(freqs.get(1).get(0)));
     }
 
     /**
@@ -5852,9 +6060,9 @@
         mLooper.dispatchAll();
         List<WifiNetworkSelector.ClientModeManagerState> expectedCmmStates =
                 Arrays.asList(new WifiNetworkSelector.ClientModeManagerState(
-                                "wlan0", false, true, wifiInfo1),
+                                "wlan0", false, true, wifiInfo1, false),
                         new WifiNetworkSelector.ClientModeManagerState(
-                                "wlan1", false, true, wifiInfo2));
+                                "wlan1", false, true, wifiInfo2, false));
         verify(mWifiNS).getCandidatesFromScan(any(), any(),
                 eq(expectedCmmStates), anyBoolean(), anyBoolean(), anyBoolean(), any(),
                 anyBoolean());
@@ -5889,9 +6097,9 @@
         mLooper.dispatchAll();
         List<WifiNetworkSelector.ClientModeManagerState> expectedCmmStates =
                 Arrays.asList(new WifiNetworkSelector.ClientModeManagerState(
-                        "wlan0", false, true, wifiInfo1),
+                        "wlan0", false, true, wifiInfo1, false),
                 new WifiNetworkSelector.ClientModeManagerState(
-                        "unknown", false, true, new WifiInfo()));
+                        "unknown", false, true, new WifiInfo(), false));
         verify(mWifiNS).getCandidatesFromScan(any(), any(),
                 eq(expectedCmmStates), anyBoolean(), anyBoolean(), anyBoolean(), any(),
                 anyBoolean());
@@ -5948,10 +6156,10 @@
 
     private void setScreenState(boolean screenOn) {
         InOrder inOrder = inOrder(mWifiNS);
-        BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
-        assertNotNull(broadcastReceiver);
-        Intent intent = new Intent(screenOn  ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
-        broadcastReceiver.onReceive(mContext, intent);
+        WifiDeviceStateChangeManager.StateChangeCallback callback =
+                mStateChangeCallbackArgumentCaptor.getValue();
+        assertNotNull(callback);
+        callback.onScreenStateChanged(screenOn);
         inOrder.verify(mWifiNS).setScreenState(screenOn);
     }
 }
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 59b6f64..5212b0f 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -669,25 +669,6 @@
     }
 
     @Test
-    public void testCountryCodeChangedWhenSoftApManagerActiveAndForceSoftApRestartButCCisWorld()
-            throws Exception {
-        mForcedSoftApRestateWhenCountryCodeChanged = true;
-        when(mSoftApManager.getSoftApModeConfiguration()).thenReturn(mSoftApModeConfiguration);
-        createWifiCountryCode();
-        // SoftApManager actived
-        mModeChangeCallbackCaptor.getValue().onActiveModeManagerAdded(mSoftApManager);
-        // Simulate the country code set succeeded via SoftApManager
-        mChangeListenerCaptor.getValue().onSetCountryCodeSucceeded(
-                mWorldModeCountryCode);
-        verify(mSoftApManager, never()).updateCountryCode(anyString());
-        mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode);
-        verify(mSoftApManager).updateCountryCode(mTelephonyCountryCode);
-        verify(mSoftApManager, never()).getSoftApModeConfiguration();
-        verify(mActiveModeWarden, never()).stopSoftAp(anyInt());
-        verify(mActiveModeWarden, never()).startSoftAp(any(), any());
-    }
-
-    @Test
     public void testCountryCodeChangedWhenSoftApManagerActiveAndForceSoftApRestart()
             throws Exception {
         mForcedSoftApRestateWhenCountryCodeChanged = true;
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
index 90cf717..61b6884 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java
@@ -640,13 +640,13 @@
         // because it hits mLastTxBytes == 0 || mLastRxBytes == 0
         mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME,
                 mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes);
-        verify(mWifiMetrics, times(1)).incrementConnectionDuration(
+        verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME,
                 1000, true, true, TEST_RSSI, 960, 9609);
 
         // Expect 2nd throughput sufficiency check to return false
         mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME,
                 mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes);
-        verify(mWifiMetrics, times(1)).incrementConnectionDuration(
+        verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME,
                 1000, false, true, TEST_RSSI, 960, 9609);
 
         mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + 2000;
@@ -655,7 +655,7 @@
         assertEquals(false, mWifiDataStall.isCellularDataAvailable());
         mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME,
                 mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes);
-        verify(mWifiMetrics, times(1)).incrementConnectionDuration(
+        verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME,
                 2000, false, false, TEST_RSSI, 960, 9609);
 
         // Expect this update to be ignored by connection duration counters due to its
@@ -663,7 +663,7 @@
         mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + 10000;
         mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME,
                 mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes);
-        verify(mWifiMetrics, never()).incrementConnectionDuration(
+        verify(mWifiMetrics, never()).incrementConnectionDuration(TEST_IFACE_NAME,
                 10000, false, false, TEST_RSSI, 960, 9609);
         setWifiEnabled(false);
     }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java
new file mode 100644
index 0000000..ed4910a
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class WifiDeviceStateChangeManagerTest extends WifiBaseTest {
+
+    @Mock Context mContext;
+    @Mock WifiDeviceStateChangeManager.StateChangeCallback mStateChangeCallback;
+    @Mock PowerManager mPowerManager;
+
+    @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+    private TestLooper mLooper;
+    private Handler mHandler;
+    private WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        mLooper = new TestLooper();
+        mHandler = new Handler(mLooper.getLooper());
+        mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManager(mContext, mHandler);
+
+    }
+
+    @Test
+    public void testRegisterBeforeBootCompleted() {
+        mWifiDeviceStateChangeManager.registerStateChangeCallback(mStateChangeCallback);
+        // Should be no callback event before the boot completed
+        verify(mStateChangeCallback, never()).onScreenStateChanged(anyBoolean());
+        mWifiDeviceStateChangeManager.handleBootCompleted();
+        verify(mContext, atLeastOnce())
+                .registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        verify(mStateChangeCallback).onScreenStateChanged(true);
+        reset(mStateChangeCallback);
+        setScreenState(true);
+        verify(mStateChangeCallback).onScreenStateChanged(true);
+        setScreenState(false);
+        verify(mStateChangeCallback).onScreenStateChanged(false);
+        reset(mStateChangeCallback);
+        mWifiDeviceStateChangeManager.unregisterStateChangeCallback(mStateChangeCallback);
+        setScreenState(true);
+        verify(mStateChangeCallback, never()).onScreenStateChanged(anyBoolean());
+    }
+
+    @Test
+    public void testRegisterAfterBootCompleted() {
+        mWifiDeviceStateChangeManager.handleBootCompleted();
+        verify(mContext, atLeastOnce())
+                .registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        mWifiDeviceStateChangeManager.registerStateChangeCallback(mStateChangeCallback);
+        // Register after boot completed should immediately get a callback
+        verify(mStateChangeCallback).onScreenStateChanged(true);
+    }
+
+    private void setScreenState(boolean screenOn) {
+        BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
+        assertNotNull(broadcastReceiver);
+        Intent intent = new Intent(screenOn ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
+        broadcastReceiver.onReceive(mContext, intent);
+    }
+}
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
index dc837eb..bf34763 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java
@@ -42,7 +42,6 @@
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
-import android.os.PowerManager;
 import android.os.UserHandle;
 import android.view.Display;
 import android.view.Window;
@@ -82,7 +81,6 @@
     @Mock WifiThreadRunner mWifiThreadRunner;
     @Mock FrameworkFacade mFrameworkFacade;
     @Mock Resources mResources;
-    @Mock PowerManager mPowerManager;
     @Mock ActivityManager mActivityManager;
     private WifiDialogManager mDialogManager;
 
@@ -90,10 +88,8 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mWifiContext.getWifiDialogApkPkgName()).thenReturn(WIFI_DIALOG_APK_PKG_NAME);
-        when(mWifiContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
         when(mWifiContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
         when(mWifiContext.getResources()).thenReturn(mResources);
-        when(mPowerManager.isInteractive()).thenReturn(true);
         doThrow(SecurityException.class).when(mWifiContext).startActivityAsUser(any(), any(),
                 any());
         mDialogManager =
@@ -765,18 +761,10 @@
 
         verify(dialog, never()).cancel();
 
-        // ACTION_CLOSE_SYSTEM_DIALOGS due to screen off should be ignored.
-        when(mPowerManager.isInteractive()).thenReturn(false);
-        broadcastReceiverCaptor.getValue().onReceive(mWifiContext,
-                new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-        dispatchMockWifiThreadRunner(mWifiThreadRunner);
-
-        verify(dialog, never()).cancel();
-
-        // ACTION_CLOSE_SYSTEM_DIALOGS while screen on should cancel the dialog.
-        when(mPowerManager.isInteractive()).thenReturn(true);
-        broadcastReceiverCaptor.getValue().onReceive(mWifiContext,
-                new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+        // ACTION_CLOSE_SYSTEM_DIALOGS without the extra should cancel the dialog.
+        broadcastReceiverCaptor
+                .getValue()
+                .onReceive(mWifiContext, new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
         dispatchMockWifiThreadRunner(mWifiThreadRunner);
 
         verify(dialog).cancel();
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java
index 295419b..919a7a2 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java
@@ -21,6 +21,7 @@
 import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
@@ -36,6 +37,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 
 /** Unit tests for {@link WifiGlobals} */
 @SmallTest
@@ -59,6 +62,7 @@
         mResources.setBoolean(R.bool.config_wifiSaveFactoryMacToWifiConfigStore, true);
         mResources.setStringArray(R.array.config_wifiForceDisableMacRandomizationSsidPrefixList,
                 new String[] {TEST_SSID});
+        mResources.setStringArray(R.array.config_wifiAfcServerUrlsForCountry, new String[] {});
         when(mContext.getResources()).thenReturn(mResources);
 
         mWifiGlobals = new WifiGlobals(mContext);
@@ -154,6 +158,38 @@
                 .contains(TEST_SSID.substring(0, TEST_SSID.length() - 1)));
     }
 
+    @Test
+    public void testLoadCarrierSpecificEapFailureConfigMap() throws Exception {
+        // Test by default there's no override data
+        assertEquals(0, mWifiGlobals.getCarrierSpecificEapFailureConfigMapSize());
+
+        // Test config with too few items don't get added.
+        mResources.setStringArray(R.array.config_wifiEapFailureConfig,
+                new String[] {"1, 2, 3"});
+        mWifiGlobals = new WifiGlobals(mContext);
+        assertEquals(0, mWifiGlobals.getCarrierSpecificEapFailureConfigMapSize());
+
+        // Test config that fail to parse to int don't get added.
+        mResources.setStringArray(R.array.config_wifiEapFailureConfig,
+                new String[] {"1839, bad_config,  1, 1, 1440"});
+        mWifiGlobals = new WifiGlobals(mContext);
+        assertEquals(0, mWifiGlobals.getCarrierSpecificEapFailureConfigMapSize());
+
+        // Test correct config
+        mResources.setStringArray(R.array.config_wifiEapFailureConfig,
+                new String[] {"1839, 1031,  1, 1, 1440"});
+        mWifiGlobals = new WifiGlobals(mContext);
+        assertEquals(1, mWifiGlobals.getCarrierSpecificEapFailureConfigMapSize());
+        WifiBlocklistMonitor.CarrierSpecificEapFailureConfig config =
+                mWifiGlobals.getCarrierSpecificEapFailureConfig(1839, 1031);
+        assertTrue(config.displayNotification);
+        assertEquals(1, config.threshold);
+        assertEquals(1440 * 60 * 1000, config.durationMs);
+
+        // Getting CarrierSpecificEapFailureConfig for an not added reason should return null.
+        assertNull(mWifiGlobals.getCarrierSpecificEapFailureConfig(1839, 999));
+    }
+
 
     /**
      * Test that isDeprecatedSecurityTypeNetwork returns true due to WEP network
@@ -194,6 +230,67 @@
     }
 
     /**
+     * Test that the correct AFC server URLs are returned for a country.
+     */
+    @Test
+    public void testAfcServerUrlByCountry() {
+        String afcServerUS1 = "https://example.com/";
+        String afcServerUS2 = "https://www.google.com/";
+        String afcServerUS3 = "https://www.android.com/";
+        mResources.setStringArray(R.array.config_wifiAfcServerUrlsForCountry,
+                new String[] {"US," + afcServerUS1 + "," + afcServerUS2 + "," + afcServerUS3});
+        mWifiGlobals = new WifiGlobals(mContext);
+
+        List<String> afcServersForUS = mWifiGlobals.getAfcServerUrlsForCountry("US");
+        assertEquals(3, afcServersForUS.size());
+        assertEquals(afcServerUS1, afcServersForUS.get(0));
+        assertEquals(afcServerUS2, afcServersForUS.get(1));
+        assertEquals(afcServerUS3, afcServersForUS.get(2));
+    }
+
+    /**
+     * Verify that null is returned when attempting to access the AFC server URL list of a country
+     * where AFC is not available.
+     */
+    @Test
+    public void testAfcServerUrlCountryUnavailable() {
+        mResources.setStringArray(R.array.config_wifiAfcServerUrlsForCountry, new String[] {});
+        mWifiGlobals = new WifiGlobals(mContext);
+        assertNull(mWifiGlobals.getAfcServerUrlsForCountry("US"));
+    }
+
+    @Test
+    public void testSetWepAllowedWhenWepIsDeprecated() {
+        mResources.setBoolean(R.bool.config_wifiWepDeprecated, true);
+        mWifiGlobals = new WifiGlobals(mContext);
+        assertTrue(mWifiGlobals.isWepDeprecated());
+        assertFalse(mWifiGlobals.isWepSupported());
+
+        mWifiGlobals.setWepAllowed(true);
+        assertTrue(mWifiGlobals.isWepDeprecated());
+        assertTrue(mWifiGlobals.isWepAllowed());
+
+        mWifiGlobals.setWepAllowed(false);
+        assertTrue(mWifiGlobals.isWepDeprecated());
+        assertFalse(mWifiGlobals.isWepAllowed());
+    }
+
+    @Test
+    public void testSetWepAllowedWhenWepIsNotDeprecated() {
+        assertTrue(mWifiGlobals.isWepSupported());
+        // Default is not allow
+        assertFalse(mWifiGlobals.isWepAllowed());
+        assertTrue(mWifiGlobals.isWepDeprecated());
+        mWifiGlobals.setWepAllowed(true);
+        assertFalse(mWifiGlobals.isWepDeprecated());
+        assertTrue(mWifiGlobals.isWepAllowed());
+
+        mWifiGlobals.setWepAllowed(false);
+        assertTrue(mWifiGlobals.isWepDeprecated());
+        assertFalse(mWifiGlobals.isWepAllowed());
+    }
+
+    /**
      * Verify Force Overlay Config Value
      */
     @Test
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiHealthMonitorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiHealthMonitorTest.java
index 8b2c513..4f55297 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiHealthMonitorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiHealthMonitorTest.java
@@ -239,10 +239,18 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getIntArray(R.array.config_wifiRssiLevelThresholds))
                 .thenReturn(new int[]{-88, -77, -66, -55});
-        mWifiHealthMonitor = new WifiHealthMonitor(mContext, mWifiInjector, mClock,
-                mWifiConfigManager, mWifiScoreCard,
-                new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128), mWifiMetrics),
-                mWifiNative, "some seed", mDeviceConfigFacade, mActiveModeWarden);
+        mWifiHealthMonitor =
+                new WifiHealthMonitor(
+                        mContext,
+                        mWifiInjector,
+                        mClock,
+                        mWifiConfigManager,
+                        mWifiScoreCard,
+                        new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128)),
+                        mWifiNative,
+                        "some seed",
+                        mDeviceConfigFacade,
+                        mActiveModeWarden);
         mWifiHealthMonitor.enableVerboseLogging(true);
         mLooper.dispatchAll();
         ArgumentCaptor<ModeChangeCallback> modeChangeCallbackArgumentCaptor =
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
index 3b2a0b1..a07a293 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
@@ -17,14 +17,21 @@
 package com.android.server.wifi;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
+import android.net.MacAddress;
+import android.net.wifi.MloLink;
+import android.net.wifi.WifiScanner;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Random;
 
 /**
@@ -48,6 +55,40 @@
         mWifiLinkLayerStats = new WifiLinkLayerStats();
     }
 
+    private void setupLinkStats() {
+        if (mWifiLinkLayerStats == null) return;
+        final int numLinks = 2;
+        int[] freqs = {2412, 5652};
+
+        mWifiLinkLayerStats.links = new WifiLinkLayerStats.LinkSpecificStats[numLinks];
+        for (int i = 0; i < numLinks; i++) {
+            mWifiLinkLayerStats.links[i] = new WifiLinkLayerStats.LinkSpecificStats();
+            mWifiLinkLayerStats.links[i].link_id = i + 1;
+            mWifiLinkLayerStats.links[i].radio_id = mRandom.nextInt(5);
+            mWifiLinkLayerStats.links[i].rssi_mgmt = mRandom.nextInt(127);
+            mWifiLinkLayerStats.links[i].beacon_rx = mRandom.nextInt(1000);
+            mWifiLinkLayerStats.links[i].frequencyMhz = freqs[i];
+        }
+    }
+
+    private void setupMloLinks() {
+        List<MloLink> mloLinks = new ArrayList<>();
+        MloLink link1 = new MloLink();
+        link1.setBand(WifiScanner.WIFI_BAND_24_GHZ);
+        link1.setChannel(6);
+        link1.setApMacAddress(MacAddress.fromString("01:02:03:04:05:06"));
+        link1.setLinkId(1);
+        MloLink link2 = new MloLink();
+        link2.setBand(WifiScanner.WIFI_BAND_5_GHZ);
+        link2.setChannel(44);
+        link2.setApMacAddress(MacAddress.fromString("01:02:03:04:05:07"));
+        link2.setLinkId(2);
+        mloLinks.add(link1);
+        mloLinks.add(link2);
+
+        mWifiInfo.setAffiliatedMloLinks(mloLinks);
+    }
+
     /**
      * Increments the counters
      *
@@ -81,6 +122,30 @@
         s.txmpdu_vo += txg & m3;
         s.lostmpdu_vo += txb & m3;
         s.retries_vo += txr & m3;
+
+        if (s.links == null) return;
+
+        for (WifiLinkLayerStats.LinkSpecificStats l : s.links) {
+            l.rxmpdu_be += rxg & m0;
+            l.txmpdu_be += txg & m0;
+            l.lostmpdu_be += txb & m0;
+            l.retries_be += txr & m0;
+
+            l.rxmpdu_bk += rxg & m1;
+            l.txmpdu_bk += txg & m1;
+            l.lostmpdu_bk += txb & m1;
+            l.retries_bk += txr & m1;
+
+            l.rxmpdu_vi += rxg & m2;
+            l.txmpdu_vi += txg & m2;
+            l.lostmpdu_vi += txb & m2;
+            l.retries_vi += txr & m2;
+
+            l.rxmpdu_vo += rxg & m3;
+            l.txmpdu_vo += txg & m3;
+            l.lostmpdu_vo += txb & m3;
+            l.retries_vo += txr & m3;
+        }
     }
 
     /**
@@ -96,6 +161,11 @@
         int txr = mRandom.nextInt(100);
         int txb = mRandom.nextInt(100);
         int rxg = mRandom.nextInt(1000);
+        setupLinkStats();
+        setupMloLinks();
+        assertNotNull(mWifiInfo.getAffiliatedMloLinks());
+        assertNotNull(mWifiLinkLayerStats.links);
+
         int n = 3 * 5; // Time constant is 3 seconds, 5 times time constant should get 99% there
         for (int i = 0; i < n; i++) {
             bumpCounters(mWifiLinkLayerStats, txg, txr, txb, rxg);
@@ -112,6 +182,18 @@
         assertEquals(mWifiInfo.txRetries, n * txr);
         assertEquals(mWifiInfo.txBad, n * txb);
         assertEquals(mWifiInfo.rxSuccess, n * rxg);
+
+        for (MloLink mloLink : mWifiInfo.getAffiliatedMloLinks()) {
+            assertEquals((double) txg, mloLink.getSuccessfulTxPacketsPerSecond(), txg * 0.02);
+            assertEquals((double) txr, mloLink.getRetriedTxPacketsPerSecond(), txr * 0.02);
+            assertEquals((double) txb, mloLink.getLostTxPacketsPerSecond(), txb * 0.02);
+            assertEquals((double) rxg, mloLink.getSuccessfulRxPacketsPerSecond(), rxg * 0.02);
+
+            assertEquals(mloLink.txSuccess, n * ((long) txg));
+            assertEquals(mloLink.txRetries, n * ((long) txr));
+            assertEquals(mloLink.txBad, n * ((long) txb));
+            assertEquals(mloLink.rxSuccess, n * ((long) rxg));
+        }
     }
 
     /**
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
index 0b1838d..6af6d0c 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
@@ -16,21 +16,31 @@
 
 package com.android.server.wifi;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_SCREEN_ON;
-
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY;
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT;
 
 import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.net.wifi.IWifiLowLatencyLockListener;
 import android.net.wifi.WifiManager;
@@ -61,6 +71,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Random;
 
 /** Unit tests for {@link WifiLockManager}. */
 @SmallTest
@@ -92,9 +103,14 @@
     @Mock PowerManager mPowerManager;
     @Mock DeviceConfigFacade mDeviceConfigFacade;
     @Mock WifiPermissionsUtil mWifiPermissionsUtil;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
     TestLooper mLooper;
     Handler mHandler;
-    @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+
+    @Captor
+    ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback>
+            mStateChangeCallbackArgumentCaptor;
+
     @Mock Resources mResources;
 
     /**
@@ -127,11 +143,20 @@
         when(mResources.getBoolean(
                 R.bool.config_wifiLowLatencyLockDisableChipPowerSave)).thenReturn(true);
 
-        mWifiLockManager = new WifiLockManager(mContext, mBatteryStats,
-                mActiveModeWarden, mFrameworkFacade, mHandler, mClock, mWifiMetrics,
-                mDeviceConfigFacade, mWifiPermissionsUtil);
-        verify(mContext, atLeastOnce()).registerReceiver(
-                mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        mWifiLockManager =
+                new WifiLockManager(
+                        mContext,
+                        mBatteryStats,
+                        mActiveModeWarden,
+                        mFrameworkFacade,
+                        mHandler,
+                        mClock,
+                        mWifiMetrics,
+                        mDeviceConfigFacade,
+                        mWifiPermissionsUtil,
+                        mWifiDeviceStateChangeManager);
+        verify(mWifiDeviceStateChangeManager)
+                .registerStateChangeCallback(mStateChangeCallbackArgumentCaptor.capture());
     }
 
     private void acquireWifiLockSuccessful(int lockMode, String tag, IBinder binder, WorkSource ws)
@@ -277,8 +302,9 @@
         verify(mWifiMetrics).addWifiLockAcqSession(
                 eq(mDeviceConfigFacade.isHighPerfLockDeprecated() && SdkLevel.isAtLeastU()
                         ? WifiManager.WIFI_MODE_FULL_LOW_LATENCY
-                        : WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                        : WifiManager.WIFI_MODE_FULL_HIGH_PERF), eq(new int[]{DEFAULT_TEST_UID_1}),
+                eq(new String[]{null}), anyInt(), anyLong(), anyBoolean(), anyBoolean(),
+                anyBoolean());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode());
     }
 
@@ -550,7 +576,9 @@
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
-        verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode), anyLong());
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -584,7 +612,9 @@
         // Release the first lock
         releaseWifiLockSuccessful(mBinder);
         verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(), anyLong(),
+                anyBoolean(), anyBoolean(),
+                anyBoolean());
 
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
@@ -594,13 +624,15 @@
         // Release the second lock
         releaseWifiLockSuccessful(mBinder2);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -727,12 +759,16 @@
                 false);
 
         releaseWifiLockSuccessful(mBinder);
-        verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode), anyLong());
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(), anyLong(),
+                anyBoolean(), anyBoolean(), anyBoolean());
         assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
-        verify(mWifiMetrics, never()).addWifiLockActiveSession(anyInt(), anyLong());
+        verify(mWifiMetrics, never()).addWifiLockActiveSession(
+                eq(expectedMode), eq(new int[]{DEFAULT_TEST_UID_1}),
+                eq(new String[]{null}), anyLong(), anyBoolean(), anyBoolean(), anyBoolean());
 
         // Now attempting adding some other lock, WifiLockManager should retry setPowerSave()
         when(mClientModeManager.setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
@@ -743,7 +779,9 @@
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
-        verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode), anyLong());
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -771,7 +809,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[0]), eq(new String[0]), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -800,7 +839,8 @@
 
         releaseWifiLockSuccessful(mBinder);
         verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(), anyLong(),
+                anyBoolean(), anyBoolean(), anyBoolean());
         assertEquals(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
                 mWifiLockManager.getStrongestLockMode());
         inOrder.verify(mClientModeManager, never()).setPowerSave(
@@ -812,7 +852,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1030,7 +1071,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1129,7 +1171,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1172,7 +1215,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1261,7 +1305,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
         inOrder.verify(mClientModeManager).setLowLatencyMode(true);
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 false);
@@ -1300,7 +1345,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[0]), eq(new String[0]), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1387,7 +1433,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(),
+                anyBoolean(), anyBoolean());
     }
 
     /**
@@ -1445,7 +1492,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF),
-                anyLong());
+                eq(new int[0]), eq(new String[0]), anyLong(), anyBoolean(), anyBoolean(),
+                anyBoolean());
         inOrder.verify(mClientModeManager).setLowLatencyMode(true);
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 false);
@@ -1479,7 +1527,8 @@
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 true);
         verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                anyLong());
+                eq(new int[0]), eq(new String[0]), anyLong(), anyBoolean(), anyBoolean(),
+                anyBoolean());
         inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK,
                 false);
     }
@@ -1668,15 +1717,18 @@
         when(mClock.getElapsedSinceBootMillis()).thenReturn(deactivationTime);
         mWifiLockManager.updateWifiClientConnected(mClientModeManager, false);
 
-        inOrder.verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode),
-                eq(deactivationTime - activationTime));
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}),
+                eq(deactivationTime - activationTime), eq(true), eq(false), eq(false));
+
 
         // Release the lock
         when(mClock.getElapsedSinceBootMillis()).thenReturn(releaseTime);
         releaseWifiLockSuccessful_noBatteryStats(mBinder);
 
-        inOrder.verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode),
-                eq(releaseTime - acquireTime));
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(),
+                eq(releaseTime - acquireTime), eq(true), eq(false), eq(false));
     }
 
     /**
@@ -1717,17 +1769,18 @@
         when(mClock.getElapsedSinceBootMillis()).thenReturn(deactivationTime);
         mWifiLockManager.updateWifiClientConnected(mClientModeManager, false);
 
-        inOrder.verify(mWifiMetrics).addWifiLockActiveSession(
-                eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                eq(deactivationTime - activationTime));
+        verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}),
+                eq(deactivationTime - activationTime), eq(true), eq(false), eq(false));
 
         // Release the lock
         when(mClock.getElapsedSinceBootMillis()).thenReturn(releaseTime);
         releaseWifiLockSuccessful_noBatteryStats(mBinder);
 
-        inOrder.verify(mWifiMetrics).addWifiLockAcqSession(
-                eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
-                eq(releaseTime - acquireTime));
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(WifiManager.WIFI_MODE_FULL_LOW_LATENCY),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(),
+                eq(releaseTime - acquireTime), eq(true), eq(false), eq(false));
+
     }
 
     /**
@@ -1767,7 +1820,9 @@
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TEST_WIFI_LOCK_TAG,
                 mBinder, mWorkSource);
         releaseWifiLockSuccessful(mBinder);
-        verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode), anyLong());
+        verify(mWifiMetrics).addWifiLockAcqSession(eq(expectedMode),
+                eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyInt(), anyLong(),
+                anyBoolean(), anyBoolean(), anyBoolean());
         acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TEST_WIFI_LOCK_TAG,
                 mBinder, mWorkSource);
 
@@ -1821,10 +1876,10 @@
     }
 
     private void setScreenState(boolean screenOn) {
-        BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
-        assertNotNull(broadcastReceiver);
-        Intent intent = new Intent(screenOn  ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
-        broadcastReceiver.onReceive(mContext, intent);
+        WifiDeviceStateChangeManager.StateChangeCallback callback =
+                mStateChangeCallbackArgumentCaptor.getValue();
+        assertNotNull(callback);
+        callback.onScreenStateChanged(screenOn);
     }
 
     /**
@@ -1951,4 +2006,195 @@
         inOrder.verify(testListener, never()).onActivatedStateChanged(anyBoolean());
         inOrder.verify(testListener, never()).onActiveUsersChanged(any());
     }
+
+    /**
+     * Verify that low latency lock listeners are triggered even when chip is not supporting low
+     * latency mode
+     */
+    @Test
+    public void testWifiLowLatencyLockListenerWithNoChipSupport() throws Exception {
+        // Setup mock listener.
+        IWifiLowLatencyLockListener testListener = mock(IWifiLowLatencyLockListener.class);
+        when(testListener.asBinder()).thenReturn(mock(IBinder.class));
+        InOrder inOrder = inOrder(testListener);
+
+        // Register the listener and test current state and ownership are notified immediately after
+        // registration. Active users is not notified since the lock is not activated.
+        mWifiLockManager.addWifiLowLatencyLockListener(testListener);
+        inOrder.verify(testListener).onActivatedStateChanged(false);
+        inOrder.verify(testListener).onOwnershipChanged(eq(new int[0]));
+        inOrder.verify(testListener, never()).onActiveUsersChanged(any());
+
+        // Test notification when the chip does not support low latency mode.
+        setScreenState(true);
+        when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK),
+                anyBoolean())).thenReturn(true);
+        when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn(
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeManager.getSupportedFeatures()).thenReturn(
+                ~WifiManager.WIFI_FEATURE_LOW_LATENCY);
+        acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource);
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+        inOrder.verify(testListener).onOwnershipChanged(eq(new int[]{DEFAULT_TEST_UID_1}));
+        inOrder.verify(testListener).onActivatedStateChanged(true);
+        inOrder.verify(testListener).onActiveUsersChanged(eq(new int[]{DEFAULT_TEST_UID_1}));
+    }
+
+    /**
+     * Test if a 'Screen ON' exempted app which is in foreground acquires a low-latency lock,
+     * that lock becomes active even when screen is OFF and reported to the battery stats. A
+     * change in screen state (ON or OFF) should not trigger further battery stats report.
+     */
+    @Test
+    public void testScreenOffExemptionBlaming() throws Exception {
+        // Setup for low latency lock for exempted app
+        setScreenState(false);
+        when(mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission(
+                DEFAULT_TEST_UID_1)).thenReturn(true);
+        when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK),
+                anyBoolean())).thenReturn(true);
+        when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn(
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeManager.getSupportedFeatures()).thenReturn(
+                WifiManager.WIFI_FEATURE_LOW_LATENCY);
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+
+        // Acquire the lock should report
+        assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
+                mBinder, mWorkSource));
+        assertThat(mWifiLockManager.getStrongestLockMode(),
+                not(WifiManager.WIFI_MODE_NO_LOCKS_HELD));
+        InOrder inOrder = inOrder(mBinder, mBatteryStats);
+        inOrder.verify(mBatteryStats).reportFullWifiLockAcquiredFromSource(mWorkSource);
+        // Check for low latency
+        assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                mWifiLockManager.getStrongestLockMode());
+
+        // Screen on should not report for exempted UID
+        setScreenState(true);
+        inOrder.verify(mBatteryStats, never()).reportFullWifiLockAcquiredFromSource(mWorkSource);
+        // Screen off should not report for exempted UID
+        setScreenState(false);
+        inOrder.verify(mBatteryStats, never()).reportFullWifiLockReleasedFromSource(mWorkSource);
+        // Disconnect Wi-Fi should report un-blame
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, false);
+        inOrder.verify(mBatteryStats).reportFullWifiLockReleasedFromSource(mWorkSource);
+        // Connect Wi-Fi should report blame
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+        inOrder.verify(mBatteryStats).reportFullWifiLockAcquiredFromSource(mWorkSource);
+    }
+
+    /**
+     * Test if a 'foreground' exempted app which is in foreground-service acquires a low-latency
+     * lock, that lock becomes active and reported to the battery stats. A change in state from
+     * foreground to foreground service and vice versa  should not trigger further battery stats
+     * report.
+     */
+    @Test
+    public void testForegroundExemptionBlaming() throws Exception {
+        // Setup for low latency lock for foreground exempted app
+        setScreenState(true);
+        when(mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission(
+                DEFAULT_TEST_UID_1)).thenReturn(true);
+        when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK),
+                anyBoolean())).thenReturn(true);
+        when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn(
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeManager.getSupportedFeatures()).thenReturn(
+                WifiManager.WIFI_FEATURE_LOW_LATENCY);
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+
+        // Acquire the lock should report
+        assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
+                mBinder, mWorkSource));
+        captureUidImportanceListener();
+        assertThat(mWifiLockManager.getStrongestLockMode(),
+                not(WifiManager.WIFI_MODE_NO_LOCKS_HELD));
+        InOrder inOrder = inOrder(mBinder, mBatteryStats);
+        inOrder.verify(mBatteryStats).reportFullWifiLockAcquiredFromSource(mWorkSource);
+        // Check for low latency
+        assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                mWifiLockManager.getStrongestLockMode());
+
+        // App going to foreground service should not be reported
+        mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+        mLooper.dispatchAll();
+        inOrder.verify(mBatteryStats, never()).reportFullWifiLockReleasedFromSource(mWorkSource);
+        // App going to foreground should not be reported
+        mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        mLooper.dispatchAll();
+        inOrder.verify(mBatteryStats, never()).reportFullWifiLockReleasedFromSource(mWorkSource);
+        // App going to background should be reported
+        mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND);
+        mLooper.dispatchAll();
+        inOrder.verify(mBatteryStats).reportFullWifiLockReleasedFromSource(mWorkSource);
+    }
+
+    /**
+     * Test a series of low latency lock blaming and un-blaming with exempted app and make sure
+     * the reporting is balanced.
+     */
+    @Test
+    public void testExemptionBlaming() throws Exception {
+        // Setup for low latency lock for foreground exempted app
+        setScreenState(true);
+        when(mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission(
+                DEFAULT_TEST_UID_1)).thenReturn(true);
+        when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK),
+                anyBoolean())).thenReturn(true);
+        when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn(
+                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+        when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true);
+        when(mClientModeManager.getSupportedFeatures()).thenReturn(
+                WifiManager.WIFI_FEATURE_LOW_LATENCY);
+        mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+
+        // Acquire --> reportFullWifiLockAcquiredFromSource
+        assertTrue(mWifiLockManager.acquireWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "",
+                mBinder, mWorkSource));
+        captureUidImportanceListener();
+        assertThat(mWifiLockManager.getStrongestLockMode(),
+                not(WifiManager.WIFI_MODE_NO_LOCKS_HELD));
+        // Check for low latency
+        assertEquals(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                mWifiLockManager.getStrongestLockMode());
+
+        Random mRandom = new Random();
+        int iterate;
+        for (iterate = 0; iterate < mRandom.nextInt(100); ++iterate) {
+            mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+            mLooper.dispatchAll();
+            // background --> reportFullWifiLockReleasedFromSource
+            mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND);
+            mLooper.dispatchAll();
+            // foreground --> reportFullWifiLockAcquiredFromSource
+            mUidImportanceListener.onUidImportance(DEFAULT_TEST_UID_1,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
+            mLooper.dispatchAll();
+            setScreenState(false);
+            setScreenState(true);
+            setScreenState(false);
+            // disconnected --> reportFullWifiLockReleasedFromSource
+            mWifiLockManager.updateWifiClientConnected(mClientModeManager, false);
+            // connected --> reportFullWifiLockAcquiredFromSource
+            mWifiLockManager.updateWifiClientConnected(mClientModeManager, true);
+            setScreenState(true);
+        }
+        // Release --> reportFullWifiLockReleasedFromSource
+        assertTrue(mWifiLockManager.releaseWifiLock(mBinder));
+
+        // number of reports = (number of activate/deactivate) * iterations + acquire/release
+        int numReports = 2 * iterate + 1;
+        // Check for balance
+        verify(mBatteryStats, times(numReports)).reportFullWifiLockAcquiredFromSource(mWorkSource);
+        verify(mBatteryStats, times(numReports)).reportFullWifiLockReleasedFromSource(mWorkSource);
+    }
 }
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 6f1f534..4736dfb 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -15,13 +15,14 @@
  */
 package com.android.server.wifi;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_SCREEN_ON;
 import static android.net.wifi.WifiManager.DEVICE_MOBILITY_STATE_HIGH_MVMT;
 import static android.net.wifi.WifiManager.DEVICE_MOBILITY_STATE_LOW_MVMT;
 import static android.net.wifi.WifiManager.DEVICE_MOBILITY_STATE_STATIONARY;
 import static android.net.wifi.WifiManager.DEVICE_MOBILITY_STATE_UNKNOWN;
 
+import static com.android.server.wifi.WifiMetrics.convertBandwidthEnumToUsabilityStatsType;
+import static com.android.server.wifi.WifiMetrics.convertPreambleTypeEnumToUsabilityStatsType;
+import static com.android.server.wifi.WifiMetrics.convertSpatialStreamEnumToUsabilityStatsType;
 import static com.android.server.wifi.WifiMetricsTestUtil.assertDeviceMobilityStatePnoScanStatsEqual;
 import static com.android.server.wifi.WifiMetricsTestUtil.assertExperimentProbeCountsEqual;
 import static com.android.server.wifi.WifiMetricsTestUtil.assertHistogramBucketsEqual;
@@ -36,6 +37,9 @@
 import static com.android.server.wifi.WifiMetricsTestUtil.buildLinkProbeFailureStaEvent;
 import static com.android.server.wifi.WifiMetricsTestUtil.buildLinkProbeSuccessStaEvent;
 import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONFIG_SAVED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE;
 import static com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent.TYPE_LINK_PROBE;
 
 import static org.junit.Assert.assertEquals;
@@ -60,12 +64,11 @@
 import static java.lang.StrictMath.toIntExact;
 
 import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
 import android.net.MacAddress;
 import android.net.wifi.EAPConstants;
 import android.net.wifi.IOnWifiUsabilityStatsListener;
+import android.net.wifi.MloLink;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SecurityParams;
 import android.net.wifi.SoftApCapability;
@@ -77,6 +80,7 @@
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.ProvisioningCallback;
@@ -86,11 +90,13 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.telephony.TelephonyManager;
 import android.util.Base64;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import androidx.test.filters.MediumTest;
@@ -174,6 +180,13 @@
     private static final int TEST_NETWORK_ID = 42;
     public static final String TEST_IFACE_NAME = "wlan0";
     public static final String TEST_IFACE_NAME2 = "wlan1";
+    private static final int TEST_UID = 52;
+    private static final String TEST_TAG = "TestTag";
+    private static final int TEST_CONNECTION_FAILURE_STATUS_CODE = -1;
+    private static final String MLO_LINK_STA_MAC_ADDRESS = "12:34:56:78:9a:bc";
+    private static final String MLO_LINK_AP_MAC_ADDRESS = "bc:9a:78:56:34:12";
+    private static final int TEST_CHANNEL = 36;
+
     private MockitoSession mSession;
     @Mock Context mContext;
     MockResources mResources;
@@ -199,10 +212,12 @@
     @Mock PowerManager mPowerManager;
     @Mock WifiMonitor mWifiMonitor;
     @Mock ActiveModeWarden mActiveModeWarden;
-
-    @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
     @Captor ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackArgumentCaptor;
     @Captor ArgumentCaptor<Handler> mHandlerCaptor;
+    @Captor
+    ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback>
+            mStateChangeCallbackArgumentCaptor;
 
     @Before
     public void setUp() throws Exception {
@@ -212,12 +227,19 @@
         mTestLooper = new TestLooper();
         mResources = new MockResources();
         when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
-        when(mPowerManager.isInteractive()).thenReturn(true);
-        mWifiMetrics = new WifiMetrics(mContext, mFacade, mClock, mTestLooper.getLooper(),
-                new WifiAwareMetrics(mClock), new RttMetrics(mClock), mWifiPowerMetrics,
-                mWifiP2pMetrics, mDppMetrics, mWifiMonitor);
-        mWifiMetrics.start();
+        mWifiMetrics =
+                new WifiMetrics(
+                        mContext,
+                        mFacade,
+                        mClock,
+                        mTestLooper.getLooper(),
+                        new WifiAwareMetrics(mClock),
+                        new RttMetrics(mClock),
+                        mWifiPowerMetrics,
+                        mWifiP2pMetrics,
+                        mDppMetrics,
+                        mWifiMonitor,
+                        mWifiDeviceStateChangeManager);
         mWifiMetrics.setWifiConfigManager(mWcm);
         mWifiMetrics.setWifiBlocklistMonitor(mWifiBlocklistMonitor);
         mWifiMetrics.setPasspointManager(mPpm);
@@ -231,8 +253,9 @@
         when(mOnWifiUsabilityStatsListener.asBinder()).thenReturn(mAppBinder);
         when(mWifiScoreCard.lookupNetwork(anyString())).thenReturn(mPerNetwork);
         when(mPerNetwork.getRecentStats()).thenReturn(mNetworkConnectionStats);
-        verify(mContext, atLeastOnce()).registerReceiver(
-                mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        verify(mWifiDeviceStateChangeManager)
+                .registerStateChangeCallback(mStateChangeCallbackArgumentCaptor.capture());
+        setScreenState(true);
 
         mWifiMetrics.registerForWifiMonitorEvents("wlan0");
         verify(mWifiMonitor, atLeastOnce())
@@ -272,12 +295,14 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         //end Connection event without starting one
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         //start two ConnectionEvents in a row
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "BLUE", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
@@ -1852,7 +1877,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Change configuration to open without randomization
         config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
@@ -1867,7 +1893,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Dump proto from mWifiMetrics and deserialize it to mDecodedProto
         dumpProtoAndDeserialize();
@@ -1952,7 +1979,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -1971,7 +1999,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Dump proto and deserialize
         //This should clear all the metrics in mWifiMetrics,
@@ -2006,7 +2035,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
 
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -2033,7 +2063,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
 
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -2065,7 +2096,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME2,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -2082,7 +2114,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -2119,12 +2152,14 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME2,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -2161,7 +2196,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         WifiConfiguration config2 = WifiConfigurationTestUtil.createOpenNetwork();
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME2, config2, "BLUE",
@@ -2173,7 +2209,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME2,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_TIMEOUT, 5745,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -2206,7 +2243,8 @@
         mWifiMetrics.endConnectionEvent("nonexistentIface",
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 2412,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
     }
 
     /**
@@ -2222,7 +2260,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
 
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -2254,7 +2293,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         // Second network is created by a carrier app
         config.fromWifiNetworkSuggestion = true;
@@ -2265,7 +2305,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         // Third network is created by an unknown app
         config.fromWifiNetworkSuggestion = true;
@@ -2276,7 +2317,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
 
@@ -2300,7 +2342,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 0);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_WRONG_PSWD, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Dump proto and deserialize
         //This should clear all the metrics in mWifiMetrics,
@@ -2375,28 +2418,32 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "YELLOW", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "GREEN", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "ORANGE", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Dump proto and deserialize
         //This should clear all the metrics in mWifiMetrics,
@@ -2413,14 +2460,16 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "RED", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         //Dump proto and deserialize
         dumpProtoAndDeserialize();
@@ -2440,7 +2489,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), eq(false),
@@ -2453,7 +2503,8 @@
                 eq(0),
                 eq(true),
                 eq(false),
-                eq(1)));
+                eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt()));
     }
 
     /**
@@ -2469,14 +2520,16 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "YELLOW", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, null,
                 "GREEN", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
@@ -2490,7 +2543,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         dumpProtoAndDeserialize();
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -3343,7 +3397,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
         assertEquals(id, mDecodedProto.connectionEvent[0].networkSelectorExperimentId);
     }
@@ -3360,7 +3415,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
         assertEquals(true, mDecodedProto.connectionEvent[0].routerFingerprint.pmkCacheEnabled);
     }
@@ -3381,7 +3437,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
         assertEquals(MAX_SUPPORTED_TX_LINK_SPEED_MBPS, mDecodedProto.connectionEvent[0]
                 .routerFingerprint.maxSupportedTxLinkSpeedMbps);
@@ -3464,12 +3521,14 @@
                 mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                         WifiMetrics.ConnectionEvent.FAILURE_NONE,
                         WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                        WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                        WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                        TEST_CONNECTION_FAILURE_STATUS_CODE);
             } else {
                 mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                         WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                         WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                        WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                        WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                        TEST_CONNECTION_FAILURE_STATUS_CODE);
             }
         }
         when(mClock.getElapsedSinceBootMillis()).thenReturn(interArrivalTime);
@@ -3679,6 +3738,41 @@
     }
 
     /**
+     * Generate WifiIsUnUsableReported and verify that they are logged correctly when no external
+     * scorer is ON.
+     */
+    @Test
+    public void testWifiIsUnUsableReportedWithNoExternalScorer() throws Exception {
+        mResources.setBoolean(R.bool.config_wifiIsUnusableEventMetricsEnabled, true);
+        generateAllUnusableEvents(mWifiMetrics);
+        for (int i = 0; i < mTestUnusableEvents.length; i++) {
+            int index = i;
+            ExtendedMockito.verify(() -> WifiStatsLog.write(WIFI_IS_UNUSABLE_REPORTED,
+                    mTestUnusableEvents[index][0], Process.WIFI_UID, false,
+                    WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN));
+        }
+    }
+
+    /**
+     * Generate WifiIsUnUsableReported and verify that they are logged correctly when external
+     * scorer is ON.
+     */
+    @Test
+    public void testWifiIsUnUsableReportedWithExternalScorer() throws Exception {
+        mResources.setBoolean(R.bool.config_wifiIsUnusableEventMetricsEnabled, true);
+        mWifiMetrics.setIsExternalWifiScorerOn(true, TEST_UID);
+        mWifiMetrics.setScorerPredictedWifiUsabilityState(TEST_IFACE_NAME,
+                WifiMetrics.WifiUsabilityState.USABLE);
+        generateAllUnusableEvents(mWifiMetrics);
+        for (int i = 0; i < mTestUnusableEvents.length; i++) {
+            int index = i;
+            ExtendedMockito.verify(() -> WifiStatsLog.write(WIFI_IS_UNUSABLE_REPORTED,
+                    mTestUnusableEvents[index][0], TEST_UID, true,
+                    WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE));
+        }
+    }
+
+    /**
      * Verify that the number of WifiIsUnusableEvents does not exceed MAX_UNUSABLE_EVENTS
      */
     @Test
@@ -3842,13 +3936,13 @@
     private WifiLinkLayerStats nextRandomStats(WifiLinkLayerStats current) {
         WifiLinkLayerStats out = new WifiLinkLayerStats();
         final int numLinks = 2;
+        out.links = new WifiLinkLayerStats.LinkSpecificStats[numLinks];
         for (int i = 0; i < numLinks; i++) {
-            out.links = new WifiLinkLayerStats.LinkSpecificStats[numLinks];
             out.links[i] = new WifiLinkLayerStats.LinkSpecificStats();
-            out.links[i].link_id = nextRandInt() % 15;
+            out.links[i].link_id = i;
             out.links[i].txmpdu_vi = nextRandInt();
             out.links[i].txmpdu_bk = nextRandInt();
-            out.links[i].radio_id = nextRandInt() % 2;
+            out.links[i].radio_id = nextRandInt() % 5;
             out.links[i].rssi_mgmt = nextRandInt() % 127;
             out.links[i].beacon_rx = nextRandInt();
             out.links[i].frequencyMhz = nextRandInt();
@@ -3886,6 +3980,12 @@
             out.links[i].contentionNumSamplesVo = nextRandInt();
             out.links[i].timeSliceDutyCycleInPercent = (short) (nextRandInt() % 101);
             out.links[i].peerInfo = createNewPeerInfo(current.peerInfo);
+            // Channel Stats
+            WifiLinkLayerStats.ChannelStats cs = new WifiLinkLayerStats.ChannelStats();
+            cs.frequency = out.links[i].frequencyMhz;
+            cs.radioOnTimeMs = nextRandInt();
+            cs.ccaBusyTimeMs = nextRandInt();
+            out.channelStatsMap.put(out.links[i].frequencyMhz, cs);
         }
 
         out.timeStampInMs = current.timeStampInMs + nextRandInt();
@@ -4621,7 +4721,26 @@
         when(info.getRssi()).thenReturn(nextRandInt());
         when(info.getLinkSpeed()).thenReturn(nextRandInt());
 
-        WifiLinkLayerStats linkLayerStats = nextRandomStats(new WifiLinkLayerStats());
+
+        WifiLinkLayerStats linkLayerStats = nextRandomStats(createNewWifiLinkLayerStats());
+
+        // Add MLO links
+        List<MloLink> links = new ArrayList<>();
+        MloLink link;
+        for (WifiLinkLayerStats.LinkSpecificStats stat : linkLayerStats.links) {
+            link = new MloLink();
+            link.setStaMacAddress(MacAddress.fromString(MLO_LINK_STA_MAC_ADDRESS));
+            link.setApMacAddress(MacAddress.fromString(MLO_LINK_AP_MAC_ADDRESS));
+            link.setRssi(stat.rssi_mgmt);
+            link.setLinkId(stat.link_id);
+            link.setBand(WifiScanner.WIFI_BAND_5_GHZ);
+            link.setChannel(TEST_CHANNEL);
+            link.setRxLinkSpeedMbps(nextRandInt());
+            link.setTxLinkSpeedMbps(nextRandInt());
+            link.setState(nextRandInt() % MloLink.MLO_LINK_STATE_ACTIVE);
+            links.add(link);
+        }
+        when(info.getAffiliatedMloLinks()).thenReturn(links);
 
         // verify non-primary does not send wifi usability stats
         ConcreteClientModeManager concreteClientModeManager = mock(ConcreteClientModeManager.class);
@@ -4652,6 +4771,114 @@
         assertEquals(usabilityStats.getValue().getTimeStampMillis(), linkLayerStats.timeStampInMs);
         assertEquals(usabilityStats.getValue().getTotalRoamScanTimeMillis(),
                 linkLayerStats.on_time_roam_scan);
+
+        SparseArray<MloLink> mloLinks = new SparseArray<>();
+        for (MloLink mloLink: info.getAffiliatedMloLinks()) {
+            mloLinks.put(mloLink.getLinkId(), mloLink);
+        }
+
+        // Verify MLO stats
+        for (WifiLinkLayerStats.LinkSpecificStats linkStat : linkLayerStats.links) {
+            assertEquals(usabilityStats.getValue().getLinkState(linkStat.link_id), linkStat.state);
+            assertEquals(usabilityStats.getValue().getRadioId(linkStat.link_id), linkStat.radio_id);
+            assertEquals(usabilityStats.getValue().getRssi(linkStat.link_id), linkStat.rssi_mgmt);
+            assertEquals(usabilityStats.getValue().getTotalTxSuccess(linkStat.link_id),
+                    linkStat.txmpdu_be + linkStat.txmpdu_bk + linkStat.txmpdu_vi
+                            + linkStat.txmpdu_vo);
+            assertEquals(usabilityStats.getValue().getTxLinkSpeedMbps(linkStat.link_id),
+                    mloLinks.get(linkStat.link_id).getTxLinkSpeedMbps());
+            assertEquals(usabilityStats.getValue().getRxLinkSpeedMbps(linkStat.link_id),
+                    mloLinks.get(linkStat.link_id).getRxLinkSpeedMbps());
+
+            assertEquals(usabilityStats.getValue().getTotalTxRetries(linkStat.link_id),
+                    linkStat.retries_be + linkStat.retries_bk + linkStat.retries_vi
+                            + linkStat.retries_vo);
+            assertEquals(usabilityStats.getValue().getTotalCcaBusyFreqTimeMillis(linkStat.link_id),
+                    linkLayerStats.channelStatsMap.get(linkStat.frequencyMhz).ccaBusyTimeMs);
+            assertEquals(usabilityStats.getValue().getTotalRadioOnFreqTimeMillis(linkStat.link_id),
+                    linkLayerStats.channelStatsMap.get(linkStat.frequencyMhz).radioOnTimeMs);
+            assertEquals(usabilityStats.getValue().getTotalBeaconRx(linkStat.link_id),
+                    linkStat.beacon_rx);
+            assertEquals(usabilityStats.getValue().getTimeSliceDutyCycleInPercent(linkStat.link_id),
+                    linkStat.timeSliceDutyCycleInPercent);
+
+            // Verify contention time stats for each AC's
+            android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats contentionTimeStatsBe =
+                    usabilityStats.getValue().getContentionTimeStats(linkStat.link_id,
+                            android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE);
+            assertEquals(contentionTimeStatsBe.getContentionTimeMinMicros(),
+                    linkStat.contentionTimeMinBeInUsec);
+            assertEquals(contentionTimeStatsBe.getContentionTimeAvgMicros(),
+                    linkStat.contentionTimeAvgBeInUsec);
+            assertEquals(contentionTimeStatsBe.getContentionTimeMaxMicros(),
+                    linkStat.contentionTimeMaxBeInUsec);
+            assertEquals(contentionTimeStatsBe.getContentionNumSamples(),
+                    linkStat.contentionNumSamplesBe);
+
+            android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats contentionTimeStatsBk =
+                    usabilityStats.getValue().getContentionTimeStats(linkStat.link_id,
+                            android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK);
+            assertEquals(contentionTimeStatsBk.getContentionTimeMinMicros(),
+                    linkStat.contentionTimeMinBkInUsec);
+            assertEquals(contentionTimeStatsBk.getContentionTimeAvgMicros(),
+                    linkStat.contentionTimeAvgBkInUsec);
+            assertEquals(contentionTimeStatsBk.getContentionTimeMaxMicros(),
+                    linkStat.contentionTimeMaxBkInUsec);
+            assertEquals(contentionTimeStatsBk.getContentionNumSamples(),
+                    linkStat.contentionNumSamplesBk);
+
+            android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats contentionTimeStatsVo =
+                    usabilityStats.getValue().getContentionTimeStats(linkStat.link_id,
+                            android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO);
+            assertEquals(contentionTimeStatsVo.getContentionTimeMinMicros(),
+                    linkStat.contentionTimeMinVoInUsec);
+            assertEquals(contentionTimeStatsVo.getContentionTimeAvgMicros(),
+                    linkStat.contentionTimeAvgVoInUsec);
+            assertEquals(contentionTimeStatsVo.getContentionTimeMaxMicros(),
+                    linkStat.contentionTimeMaxVoInUsec);
+            assertEquals(contentionTimeStatsVo.getContentionNumSamples(),
+                    linkStat.contentionNumSamplesVo);
+
+            android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats contentionTimeStatsVi =
+                    usabilityStats.getValue().getContentionTimeStats(linkStat.link_id,
+                            android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI);
+            assertEquals(contentionTimeStatsVi.getContentionTimeMinMicros(),
+                    linkStat.contentionTimeMinViInUsec);
+            assertEquals(contentionTimeStatsVi.getContentionTimeAvgMicros(),
+                    linkStat.contentionTimeAvgViInUsec);
+            assertEquals(contentionTimeStatsVi.getContentionTimeMaxMicros(),
+                    linkStat.contentionTimeMaxViInUsec);
+            assertEquals(contentionTimeStatsVi.getContentionNumSamples(),
+                    linkStat.contentionNumSamplesVi);
+
+            // Verify Rate stats.
+            List<android.net.wifi.WifiUsabilityStatsEntry.RateStats> usabilityRateStats =
+                    usabilityStats.getValue().getRateStats(linkStat.link_id);
+            int i = 0;
+            for (RateStat rateStat : linkStat.peerInfo[0].rateStats) {
+                assertEquals(convertPreambleTypeEnumToUsabilityStatsType(rateStat.preamble),
+                        usabilityRateStats.get(i).getPreamble());
+                assertEquals(rateStat.bitRateInKbps,
+                        usabilityRateStats.get(i).getBitRateInKbps());
+                assertEquals(convertSpatialStreamEnumToUsabilityStatsType(rateStat.nss),
+                        usabilityRateStats.get(i).getNumberOfSpatialStreams());
+                assertEquals(convertBandwidthEnumToUsabilityStatsType(rateStat.bw),
+                        usabilityRateStats.get(i).getBandwidthInMhz());
+                assertEquals(rateStat.rateMcsIdx,
+                        usabilityRateStats.get(i).getRateMcsIdx());
+                assertEquals(rateStat.bitRateInKbps,
+                        usabilityRateStats.get(i).getBitRateInKbps());
+                assertEquals(rateStat.txMpdu,
+                        usabilityRateStats.get(i).getTxMpdu());
+                assertEquals(rateStat.rxMpdu,
+                        usabilityRateStats.get(i).getRxMpdu());
+                assertEquals(rateStat.mpduLost,
+                        usabilityRateStats.get(i).getMpduLost());
+                assertEquals(rateStat.retries,
+                        usabilityRateStats.get(i).getRetries());
+                i++;
+            }
+        }
     }
 
     /**
@@ -5283,15 +5510,69 @@
      */
     @Test
     public void testWifiLockActiveSession() throws Exception {
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 100000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 1000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 100000, true, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_HIGH_PERF, 100000,
+                        true, false, false));
 
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 90000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 900000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 9000);
-        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 20000000);
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 10000, true, true, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_HIGH_PERF, 10000,
+                        true, true, false));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 10000000, true, true, true);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_HIGH_PERF,
+                        10000000, true, true, true));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 1000, false, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_HIGH_PERF, 1000,
+                        false, false, false));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 90000, false, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 90000,
+                        false, false, false));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 900000, true, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_LOW_LATENCY,
+                        900000, true, false, false));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 9000, true, true, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 9000,
+                        true, true, false));
+
+        mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 20000000, true, true, true);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_DEACTIVATED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG},
+                        WifiStatsLog.WIFI_LOCK_DEACTIVATED__MODE__WIFI_MODE_FULL_LOW_LATENCY,
+                        20000000, true, true, true));
 
         dumpProtoAndDeserialize();
 
@@ -5324,15 +5605,70 @@
      */
     @Test
     public void testWifiLockAcqSession() throws Exception {
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 100000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 10000000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 1000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 100000, false, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_HIGH_PERF, 100000,
+                        false, false, false));
 
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 90000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 900000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 9000);
-        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 20000000);
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 10000, true, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_HIGH_PERF, 10000,
+                        true, false, false));
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 10000000, true, true, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_HIGH_PERF, 10000000,
+                        true, true, false));
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 1000, true, true, true);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_HIGH_PERF, 1000,
+                        true, true, true));
+
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 90000, false, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 90000,
+                        false, false, false));
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 900000, true, false, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 900000,
+                        true, false, false));
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 9000, true, true, false);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 9000,
+                        true, true, false));
+
+        mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY,
+                new int[]{TEST_UID}, new String[]{TEST_TAG}, 0, 20000000, true, true, true);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_RELEASED, new int[]{TEST_UID},
+                        new String[]{TEST_TAG}, 0,
+                        WifiStatsLog.WIFI_LOCK_RELEASED__MODE__WIFI_MODE_FULL_LOW_LATENCY, 20000000,
+                        true, true, true));
 
         dumpProtoAndDeserialize();
 
@@ -5692,12 +6028,17 @@
     public void testConnectionDurationStats() throws Exception {
         for (int i = 0; i < 2; i++) {
             mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, 52);
-            mWifiMetrics.incrementConnectionDuration(5000, false, true, -50, 10000, 10000);
+            mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 5000, false, true, -50, 10000,
+                    10000);
             mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, 40);
-            mWifiMetrics.incrementConnectionDuration(5000, false, true, -50, 10000, 10000);
-            mWifiMetrics.incrementConnectionDuration(3000, true, true, -50, 10000, 10000);
-            mWifiMetrics.incrementConnectionDuration(1000, false, false, -50, 10000, 10000);
-            mWifiMetrics.incrementConnectionDuration(500, true, false, -50, 10000, 10000);
+            mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 5000, false, true, -50, 10000,
+                    10000);
+            mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 3000, true, true, -50, 10000,
+                    10000);
+            mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 1000, false, false, -50,
+                    10000, 10000);
+            mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 500, true, false, -50, 10000,
+                    10000);
         }
         dumpProtoAndDeserialize();
 
@@ -5716,7 +6057,7 @@
      */
     @Test
     public void testIsExternalWifiScorerOn() throws Exception {
-        mWifiMetrics.setIsExternalWifiScorerOn(true);
+        mWifiMetrics.setIsExternalWifiScorerOn(true, TEST_UID);
         dumpProtoAndDeserialize();
         assertEquals(true, mDecodedProto.isExternalWifiScorerOn);
     }
@@ -5940,7 +6281,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 2000);
         // Connection event 3 doesn't overlap with 2
         assertEquals(0, mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, mTestWifiConfig,
@@ -5979,7 +6321,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
 
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -6079,8 +6422,9 @@
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
-                anyInt(), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()),
+                eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyBoolean(),
+                anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(0));
     }
 
@@ -6089,11 +6433,13 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
-                anyInt(), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()),
+                eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyBoolean(),
+                anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(0));
     }
 
@@ -6107,7 +6453,8 @@
             mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                     WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                     WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                    WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                    TEST_CONNECTION_FAILURE_STATUS_CODE);
         }
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
@@ -6118,7 +6465,8 @@
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT),
                 eq(true),
-                eq(0), eq(true), eq(false), eq(1)),
+                eq(0), eq(true), eq(false), eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(1));
     }
 
@@ -6142,7 +6490,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), eq(false),
@@ -6152,7 +6501,8 @@
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT),
                 eq(true),
-                eq(0),  eq(true), eq(true), eq(1)),
+                eq(0),  eq(true), eq(true), eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(1));
     }
 
@@ -6165,15 +6515,18 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
 
         mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, createComplexWifiConfig(),
                 "RED", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
@@ -6182,15 +6535,18 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__RECONNECT_SAME_NETWORK),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
 
         WifiConfiguration configOtherNetwork = createComplexWifiConfig();
         configOtherNetwork.networkId = 21;
@@ -6205,15 +6561,18 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_CONFIGURED_NETWORK),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
 
         WifiConfiguration config = createComplexWifiConfig();
         config.networkId = 42;
@@ -6227,13 +6586,16 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__MANUAL),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
     }
 
     @Test
@@ -6242,24 +6604,33 @@
                 "RED", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
 
+        long connectionEndTimeMs = 1000;
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(connectionEndTimeMs);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
+        long wifiDisconnectTimeMs = 2000;
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(wifiDisconnectTimeMs);
         int linkSpeed = 100;
         int reason = 42;
         mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, reason, TEST_CANDIDATE_LEVEL,
-                linkSpeed);
+                linkSpeed, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
-                WifiStatsLog.WIFI_DISCONNECT_REPORTED,
-                0,
-                reason,
-                WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__BAND__BAND_2G,
-                WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK,
-                TEST_CANDIDATE_LEVEL,
-                linkSpeed));
+                eq(WifiStatsLog.WIFI_DISCONNECT_REPORTED),
+                eq((int) (wifiDisconnectTimeMs - connectionEndTimeMs) / 1000),
+                eq(reason),
+                eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__BAND__BAND_2G),
+                eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__AUTH_TYPE__AUTH_TYPE_WPA2_PSK),
+                eq(TEST_CANDIDATE_LEVEL),
+                eq(linkSpeed),
+                eq((int) wifiDisconnectTimeMs / 1000),
+                eq((int) (wifiDisconnectTimeMs - connectionEndTimeMs) / 1000),
+                eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY),
+                anyInt(), anyInt(), anyInt(), anyInt()));
     }
 
     @Test
@@ -6268,30 +6639,37 @@
                 "RED", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false,
                 WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
 
+        long connectionEndTimeMs = 1000;
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(connectionEndTimeMs);
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
 
+        long wifiDisconnectTimeMs = 2000;
+        when(mClock.getElapsedSinceBootMillis()).thenReturn(wifiDisconnectTimeMs);
         int linkSpeed = 100;
         int reason = 42;
         mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, reason, TEST_CANDIDATE_LEVEL,
-                linkSpeed);
+                linkSpeed, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_DISCONNECT_REPORTED),
-                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(0));
     }
 
     @Test
     public void testWifiDisconnectAtomNotEmittedWithNoSession() {
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, TEST_CANDIDATE_LEVEL, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, TEST_CANDIDATE_LEVEL, 0, 0);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_DISCONNECT_REPORTED),
-                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()),
                 times(0));
     }
 
@@ -6304,7 +6682,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         // TRUE must be emitted
         ExtendedMockito.verify(() -> WifiStatsLog.write(
@@ -6316,7 +6695,7 @@
         int linkSpeed = 100;
         int reason = 42;
         mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, reason, TEST_CANDIDATE_LEVEL,
-                linkSpeed);
+                linkSpeed, 0);
 
         // FALSE must be emitted
         ExtendedMockito.verify(() -> WifiStatsLog.write(
@@ -6335,7 +6714,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
                 WifiMetricsProto.ConnectionEvent.HLF_DHCP,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         // TRUE must not be emitted
         ExtendedMockito.verify(() -> WifiStatsLog.write(
@@ -6346,7 +6726,7 @@
         int linkSpeed = 100;
         int reason = 42;
         mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, reason, TEST_CANDIDATE_LEVEL,
-                linkSpeed);
+                linkSpeed, 0);
 
         // But we still expect FALSE to be emitted
         ExtendedMockito.verify(() -> WifiStatsLog.write(
@@ -6367,15 +6747,18 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT),
-                anyBoolean(), eq(10), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), eq(10), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
 
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0);
 
         when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 30 * 1000);
 
@@ -6386,15 +6769,18 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_NONE,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ);
+                WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_NONE, TEST_CANDIDATE_FREQ,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
 
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(),
                 anyInt(), anyInt(), anyInt(), anyInt(), anyInt(),
                 eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__RECONNECT_SAME_NETWORK),
-                anyBoolean(), eq(20), anyBoolean(), anyBoolean(), anyInt()));
+                anyBoolean(), eq(20), anyBoolean(), anyBoolean(), anyInt(),
+                eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt()));
 
-        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0);
+        mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0);
     }
 
     @Test
@@ -6541,10 +6927,10 @@
     }
 
     private void setScreenState(boolean screenOn) {
-        BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
-        assertNotNull(broadcastReceiver);
-        Intent intent = new Intent(screenOn  ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
-        broadcastReceiver.onReceive(mContext, intent);
+        WifiDeviceStateChangeManager.StateChangeCallback callback =
+                mStateChangeCallbackArgumentCaptor.getValue();
+        assertNotNull(callback);
+        callback.onScreenStateChanged(screenOn);
     }
 
     @Test
@@ -6658,18 +7044,28 @@
         when(wifiInfo.getFrequency()).thenReturn(5810);
         mWifiMetrics.incrementWifiScoreCount("",  60);
         mWifiMetrics.handlePollResult(TEST_IFACE_NAME, wifiInfo);
-        mWifiMetrics.incrementConnectionDuration(3000, true, true, -50, 10002, 10001);
+        mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 3000, true, true, -50, 10002,
+                10001);
         ExtendedMockito.verify(() -> WifiStatsLog.write(
                 WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, 3000, true, true,
-                WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_HIGH, -50, 10002, 10001));
+                WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_HIGH, -50, 10002, 10001,
+                Process.WIFI_UID,
+                false,
+                WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN));
 
         when(wifiInfo.getFrequency()).thenReturn(2412);
+        mWifiMetrics.setIsExternalWifiScorerOn(true, TEST_UID);
+        mWifiMetrics.setScorerPredictedWifiUsabilityState(TEST_IFACE_NAME,
+                WifiMetrics.WifiUsabilityState.USABLE);
         mWifiMetrics.incrementWifiScoreCount("",  30);
         mWifiMetrics.handlePollResult(TEST_IFACE_NAME, wifiInfo);
-        mWifiMetrics.incrementConnectionDuration(2000, false, true, -55, 20002, 20001);
-        ExtendedMockito.verify(() -> WifiStatsLog.write(
-                WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, 2000, true, true,
-                WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_2G, -55, 20002, 20001));
+        mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 2000, false, true, -55, 20002,
+                20001);
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, 2000, true, true,
+                        WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_2G, -55, 20002, 20001,
+                        TEST_UID, true,
+                        WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE));
     }
 
     /**
@@ -6742,7 +7138,8 @@
         mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
                 WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_TIMED_OUT,
                 WifiMetricsProto.ConnectionEvent.HLF_NONE,
-                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0);
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
         dumpProtoAndDeserialize();
 
         assertEquals(1, mDecodedProto.connectionEvent.length);
@@ -6834,4 +7231,104 @@
         mWifiMetrics.wifiConfigStored(120);
         ExtendedMockito.verify(() -> WifiStatsLog.write(WIFI_CONFIG_SAVED, 120));
     }
+
+    @Test
+    public void testApCapabilitiesReported() throws Exception {
+        //Setup mock configs and scan details
+        NetworkDetail networkDetail = mock(NetworkDetail.class);
+        when(networkDetail.getWifiMode()).thenReturn(NETWORK_DETAIL_WIFIMODE);
+        when(networkDetail.getSSID()).thenReturn(SSID);
+        when(networkDetail.getDtimInterval()).thenReturn(NETWORK_DETAIL_DTIM);
+
+        ScanResult scanResult = mock(ScanResult.class);
+        scanResult.level = SCAN_RESULT_LEVEL;
+        scanResult.capabilities = "EAP/SHA1";
+        scanResult.frequency = TEST_CANDIDATE_FREQ;
+
+        WifiConfiguration config = mock(WifiConfiguration.class);
+        config.SSID = "\"" + SSID + "\"";
+        config.dtimInterval = CONFIG_DTIM;
+        config.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO;
+        config.allowedKeyManagement = new BitSet();
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+        config.enterpriseConfig = new WifiEnterpriseConfig();
+        config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+        config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2);
+        config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS);
+        config.hiddenSSID = true;
+
+        WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
+                mock(WifiConfiguration.NetworkSelectionStatus.class);
+        when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
+        when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStat);
+
+        ScanDetail scanDetail = mock(ScanDetail.class);
+        when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+        when(scanDetail.getScanResult()).thenReturn(scanResult);
+        when(networkDetail.isMboSupported()).thenReturn(true);
+        when(networkDetail.isOceSupported()).thenReturn(true);
+        when(networkDetail.getApType6GHz()).thenReturn(
+                InformationElementUtil.ApType6GHz.AP_TYPE_6GHZ_STANDARD_POWER);
+        when(networkDetail.isBroadcastTwtSupported()).thenReturn(true);
+        when(networkDetail.isRestrictedTwtSupported()).thenReturn(true);
+        when(networkDetail.isIndividualTwtSupported()).thenReturn(true);
+        when(networkDetail.isTwtRequired()).thenReturn(true);
+        when(networkDetail.isFilsCapable()).thenReturn(true);
+        when(networkDetail.is11azSupported()).thenReturn(true);
+        when(networkDetail.is80211McResponderSupport()).thenReturn(true);
+        when(networkDetail.isEpcsPriorityAccessSupported()).thenReturn(true);
+        when(networkDetail.getHSRelease()).thenReturn(NetworkDetail.HSRelease.Unknown);
+        when(networkDetail.isHiddenBeaconFrame()).thenReturn(false);
+        when(networkDetail.getWifiMode()).thenReturn(InformationElementUtil.WifiMode.MODE_11BE);
+
+        SecurityParams securityParams = mock(SecurityParams.class);
+        when(config.getDefaultSecurityParams()).thenReturn(securityParams);
+        when(securityParams.isEnterpriseSecurityType()).thenReturn(true);
+        when(config.isPasspoint()).thenReturn(false);
+        config.isHomeProviderNetwork = false;
+
+        //Create a connection event using only the config
+        mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, config,
+                "Red", WifiMetricsProto.ConnectionEvent.ROAM_NONE, false,
+                WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY);
+        mWifiMetrics.setConnectionScanDetail(TEST_IFACE_NAME, scanDetail);
+        mWifiMetrics.logBugReport();
+        mWifiMetrics.logStaEvent(TEST_IFACE_NAME, StaEvent.TYPE_CMD_START_ROAM,
+                StaEvent.DISCONNECT_UNKNOWN, null);
+        mWifiMetrics.endConnectionEvent(TEST_IFACE_NAME,
+                WifiMetrics.ConnectionEvent.FAILURE_NONE,
+                WifiMetricsProto.ConnectionEvent.HLF_NONE,
+                WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0,
+                TEST_CONNECTION_FAILURE_STATUS_CODE);
+
+        ExtendedMockito.verify(
+                () -> WifiStatsLog.write(eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED),
+                        eq(true), // mIsFrameworkInitiatedRoaming
+                        eq(TEST_CANDIDATE_FREQ),
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__BAND_MHZ__BAND_2G),
+                        eq(NETWORK_DETAIL_DTIM),
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CONNECTED_SECURITY_MODE__SECURITY_MODE_NONE),
+                        eq(true), // hidden
+                        eq(true), // mIsIncorrectlyConfiguredAsHidden
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__STANDARD__WIFI_STANDARD_11BE),
+                        eq(false), // mIs11bSupported
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_TYPE__TYPE_EAP_TTLS),
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__EAP_INNER_METHOD__METHOD_MSCHAP_V2),
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__OCSP_TYPE__TYPE_OCSP_REQUIRE_CERT_STATUS),
+                        eq(false), // pmkCacheEnabled
+                        eq(true), // mIsMboSupported
+                        eq(true), // mIsOceSupported
+                        eq(true), // mIsFilsSupported
+                        eq(true), // mIsTwtRequired
+                        eq(true), // mIsIndividualTwtSupported
+                        eq(true), // mIsBroadcastTwtSupported
+                        eq(true), // mIsRestrictedTwtSupported
+                        eq(true), // mIs11McSupported
+                        eq(true), // mIs11AzSupported
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__PASSPOINT_RELEASE__PASSPOINT_RELEASE_UNKNOWN),
+                        eq(false), // isPasspointHomeProvider
+                        eq(WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__AP_TYPE_6GHZ__AP_TYPE_6GHZ_STANDARD_POWER),
+                        eq(true))); // mIsEcpsPriorityAccessSupported
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
index c29bf34..0d48157 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -37,6 +38,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.test.MockAnswerUtil;
+import android.net.wifi.OuiKeyedData;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiScanner;
@@ -154,7 +156,7 @@
         when(mWifiVendorHal.startVendorHal()).thenReturn(true);
         when(mWifiVendorHal.createStaIface(any(), any(), any())).thenReturn(IFACE_NAME_0);
         when(mWifiVendorHal.createApIface(any(), any(), anyInt(),
-                anyBoolean(), any())).thenReturn(IFACE_NAME_0);
+                anyBoolean(), any(), anyList())).thenReturn(IFACE_NAME_0);
         when(mWifiVendorHal.getBridgedApInstances(any())).thenReturn(
                 List.of(IFACE_NAME_0));
         when(mWifiVendorHal.removeStaIface(any())).thenReturn(true);
@@ -527,13 +529,15 @@
         // The iface name will remain the same.
         doAnswer(new MockAnswerUtil.AnswerWithArguments() {
             public String answer(InterfaceDestroyedListener destroyedListener, WorkSource ws,
-                    int band, boolean isBridged, SoftApManager softApManager) {
+                    int band, boolean isBridged, SoftApManager softApManager,
+                    List<OuiKeyedData> vendorData) {
                 mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
                 return IFACE_NAME_0;
             }
-        }).when(mWifiVendorHal).createApIface(any(), any(), anyInt(), eq(false), any());
+        }).when(mWifiVendorHal).createApIface(any(), any(), anyInt(), eq(false), any(), anyList());
         assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1,
-                TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager,
+                new ArrayList<>()));
 
         validateHostApdStart();
         // Creation of AP interface should trigger the STA interface destroy
@@ -568,7 +572,7 @@
         mInOrder.verify(mWifiVendorHal).isVendorHalSupported();
         mInOrder.verify(mWifiVendorHal).createApIface(
                 mIfaceDestroyedListenerCaptor1.capture(), eq(TEST_WORKSOURCE), anyInt(), eq(false),
-                eq(mSoftApManager));
+                eq(mSoftApManager), anyList());
     }
 
     private void validateSetupInterfaceForScan(String ifaceName,
@@ -837,15 +841,17 @@
         // The iface name will remain the same.
         doAnswer(new MockAnswerUtil.AnswerWithArguments() {
             public String answer(InterfaceDestroyedListener destroyedListener, WorkSource ws,
-                    int band, boolean isBridged, SoftApManager softApManager) {
+                    int band, boolean isBridged, SoftApManager softApManager,
+                    List<OuiKeyedData> vendorData) {
                 mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
                 return IFACE_NAME_0;
             }
-        }).when(mWifiVendorHal).createApIface(any(), any(), anyInt(), eq(false), any());
+        }).when(mWifiVendorHal).createApIface(any(), any(), anyInt(), eq(false), any(), anyList());
         when(mWifiVendorHal.isVendorHalSupported()).thenReturn(true);
 
         assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1,
-                TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager,
+                new ArrayList<>()));
         validateHostApdStart();
         // Creation of AP interface should trigger the STA interface destroy
         validateOnDestroyedClientInterface(
@@ -912,8 +918,9 @@
         executeAndValidateSetupClientInterface(
                 false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
                 mNetworkObserverCaptor0);
-        // Trigger wificond death
+        // Trigger supplicant death
         mSupplicantDeathHandlerCaptor.getValue().onDeath();
+        mLooper.dispatchAll();
 
         mInOrder.verify(mWifiMetrics).incrementNumSupplicantCrashes();
 
@@ -932,8 +939,9 @@
                 mNetworkObserverCaptor0, false, true, 0);
 
         // Start softap
-        assertTrue(mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(),
-                true, mock(WifiNative.SoftApHalCallback.class)));
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
+                mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(),
+                        true, mock(WifiNative.SoftApHalCallback.class)));
 
         mInOrder.verify(mHostapdHal).isApInfoCallbackSupported();
         mInOrder.verify(mHostapdHal).registerApCallback(any(), any());
@@ -959,8 +967,9 @@
                 mNetworkObserverCaptor0, false, true, 0);
 
         // Start softap
-        assertTrue(mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(),
-                true, mock(WifiNative.SoftApHalCallback.class)));
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
+                mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(),
+                        true, mock(WifiNative.SoftApHalCallback.class)));
 
         mInOrder.verify(mHostapdHal).isApInfoCallbackSupported();
         mInOrder.verify(mWificondControl).registerApCallback(any(), any(), any());
@@ -1086,7 +1095,8 @@
     public void testSetupSoftApInterfaceFailureInStartHal() throws Exception {
         when(mWifiVendorHal.startVendorHal()).thenReturn(false);
         assertNull(mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback0, TEST_WORKSOURCE,
-                  SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager,
+                new ArrayList<>()));
 
         mInOrder.verify(mWifiVendorHal).isVendorHalSupported();
         mInOrder.verify(mWifiVendorHal).startVendorHal();
@@ -1591,11 +1601,11 @@
             ArgumentCaptor<InterfaceDestroyedListener> destroyedListenerCaptor,
             ArgumentCaptor<NetdEventObserver> networkObserverCaptor, boolean isBridged,
             boolean vendorHalSupported, int failureCode) throws Exception {
-        when(mWifiVendorHal.createApIface(any(), any(), anyInt(), eq(isBridged), any()))
+        when(mWifiVendorHal.createApIface(any(), any(), anyInt(), eq(isBridged), any(), anyList()))
                 .thenReturn(ifaceName);
         assertEquals(failureCode == 0 ? ifaceName : null, mWifiNative.setupInterfaceForSoftApMode(
                 callback, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, isBridged,
-                mSoftApManager));
+                mSoftApManager, new ArrayList<>()));
 
         validateSetupSoftApInterface(
                 hasStaIface, hasApIface, ifaceName, destroyedListenerCaptor,
@@ -1622,7 +1632,8 @@
         if (vendorHalSupported) {
             mInOrder.verify(mWifiVendorHal).createApIface(
                     destroyedListenerCaptor.capture(), eq(TEST_WORKSOURCE),
-                    eq(SoftApConfiguration.BAND_2GHZ), eq(isBridged), eq(mSoftApManager));
+                    eq(SoftApConfiguration.BAND_2GHZ), eq(isBridged), eq(mSoftApManager),
+                    anyList());
             if (failureCode == SOFTAP_FAILURE_CODE_CREATE_IFACE) {
                 mInOrder.verify(mWifiMetrics).incrementNumSetupSoftApInterfaceFailureDueToHal();
                 return;
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
index 81eaea3..63389f8 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
@@ -30,6 +30,7 @@
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.anyBoolean;
@@ -39,6 +40,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.validateMockitoUsage;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -62,12 +64,16 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.coex.CoexManager;
+import com.android.server.wifi.hal.WifiChip;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.util.NativeUtil;
 import com.android.server.wifi.util.NetdWrapper;
 import com.android.wifi.resources.R;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.AdditionalMatchers;
@@ -75,6 +81,8 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
@@ -272,7 +280,9 @@
     @Mock private SsidTranslator mSsidTranslator;
     @Mock private WifiGlobals mWifiGlobals;
     @Mock DeviceConfigFacade mDeviceConfigFacade;
+    @Mock WifiChip.AfcChannelAllowance mAfcChannelAllowance;
 
+    private MockitoSession mSession;
     ArgumentCaptor<WifiNl80211Manager.ScanEventCallback> mScanCallbackCaptor =
             ArgumentCaptor.forClass(WifiNl80211Manager.ScanEventCallback.class);
 
@@ -288,7 +298,7 @@
         when(mWifiVendorHal.startVendorHalSta(eq(mConcreteClientModeManager))).thenReturn(true);
         when(mWifiVendorHal.createStaIface(any(), any(), eq(mConcreteClientModeManager)))
                 .thenReturn(WIFI_IFACE_NAME);
-        when(mWifiVendorHal.createApIface(any(), any(), anyInt(), anyBoolean(), any()))
+        when(mWifiVendorHal.createApIface(any(), any(), anyInt(), anyBoolean(), any(), anyList()))
                 .thenReturn(WIFI_IFACE_NAME);
 
         when(mBuildProperties.isEngBuild()).thenReturn(false);
@@ -328,6 +338,12 @@
         when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
         when(mDeviceConfigFacade.isInterfaceFailureBugreportEnabled()).thenReturn(false);
 
+        // Mock static methods from WifiStatsLog.
+        mSession = ExtendedMockito.mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(WifiStatsLog.class)
+                .startMocking();
+
         mWifiNative = new WifiNative(
                 mWifiVendorHal, mStaIfaceHal, mHostapdHal, mWificondControl,
                 mWifiMonitor, mPropertyService, mWifiMetrics,
@@ -336,6 +352,14 @@
         mWifiNative.initialize();
     }
 
+    @After
+    public void tearDown() {
+        validateMockitoUsage();
+        if (mSession != null) {
+            mSession.finishMocking();
+        }
+    }
+
     /** Mock translating an SSID */
     private WifiSsid getTranslatedSsid(WifiSsid ssid) {
         byte[] ssidBytes = ssid.getBytes();
@@ -746,7 +770,10 @@
                 any(), mScanCallbackCaptor.capture());
 
         mScanCallbackCaptor.getValue().onScanFailed();
-        verify(mWifiMetrics).incrementPnoScanFailedCount();
+        ExtendedMockito.verify(() -> WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                0, false, false, false, false,
+                WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__WIFICOND_SCAN_FAILURE));
     }
 
     /**
@@ -776,11 +803,11 @@
         when(mWifiVendorHal.getBridgedApInstances(WIFI_IFACE_NAME))
                 .thenReturn(Arrays.asList(instance1, instance2));
         mWifiNative.setupInterfaceForSoftApMode(null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ
-                | SoftApConfiguration.BAND_5GHZ, true, mSoftApManager);
+                | SoftApConfiguration.BAND_5GHZ, true, mSoftApManager, new ArrayList<>());
         ArgumentCaptor<HalDeviceManager.InterfaceDestroyedListener> ifaceDestroyedListenerCaptor =
                 ArgumentCaptor.forClass(HalDeviceManager.InterfaceDestroyedListener.class);
         verify(mWifiVendorHal).createApIface(ifaceDestroyedListenerCaptor.capture(), any(),
-                anyInt(), anyBoolean(), any());
+                anyInt(), anyBoolean(), any(), anyList());
         verify(mWificondControl).setupInterfaceForSoftApMode(instance1);
 
         when(mWifiVendorHal.getBridgedApInstances(WIFI_IFACE_NAME)).thenReturn(null);
@@ -834,7 +861,10 @@
                 any(), mScanCallbackCaptor.capture());
 
         mScanCallbackCaptor.getValue().onScanFailed();
-        verify(mWifiMetrics).incrementPnoScanFailedCount();
+        ExtendedMockito.verify(() -> WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                0, false, false, false, false,
+                WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__WIFICOND_SCAN_FAILURE));
     }
 
     /**
@@ -862,7 +892,7 @@
 
         mWifiNative.teardownAllInterfaces();
         mWifiNative.setupInterfaceForSoftApMode(null, TEST_WORKSOURCE, WIFI_BAND_24_GHZ, false,
-                mSoftApManager);
+                mSoftApManager, new ArrayList<>());
         verify(mWifiVendorHal, times(4)).setCoexUnsafeChannels(unsafeChannels, restrictions);
     }
 
@@ -960,8 +990,10 @@
         verify(mWificondControl).startPnoScan(eq(WIFI_IFACE_NAME),
                 eq(TEST_PNO_SETTINGS.toNativePnoSettings()), any(), captor.capture());
         captor.getValue().onPnoRequestFailed();
-        verify(mWifiMetrics).incrementPnoScanStartAttemptCount();
-        verify(mWifiMetrics).incrementPnoScanFailedCount();
+        ExtendedMockito.verify(() -> WifiStatsLog.write(WifiStatsLog.PNO_SCAN_STOPPED,
+                WifiStatsLog.PNO_SCAN_STOPPED__STOP_REASON__SCAN_FAILED,
+                0, false, false, false, false,
+                WifiStatsLog.PNO_SCAN_STOPPED__FAILURE_CODE__WIFICOND_REQUEST_FAILURE));
     }
 
     /**
@@ -1618,4 +1650,13 @@
                 mConcreteClientModeManager);
         verify(mWifiVendorHal).enableStaChannelForPeerNetwork(true, true);
     }
+
+    /**
+     * Verifies that setAfcChannelAllowance() calls underlying WifiVendorHal.
+     */
+    @Test
+    public void testSetAfcChannelAllowance() {
+        mWifiNative.setAfcChannelAllowance(mAfcChannelAllowance);
+        verify(mWifiVendorHal).setAfcChannelAllowance(mAfcChannelAllowance);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
index 01ad16e..d4db378 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-import static android.content.Intent.ACTION_SCREEN_ON;
 import static android.net.wifi.WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_AUTHENTICATION;
 import static android.net.wifi.WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_IP_PROVISIONING;
 
@@ -63,7 +61,6 @@
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.AppOpsManager;
 import android.companion.CompanionDeviceManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -188,6 +185,7 @@
     private @Mock IBinder mBinder;
     private @Mock ClientModeManager mPrimaryClientModeManager;
     private @Mock WifiGlobals mWifiGlobals;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
     private MockitoSession mStaticMockSession = null;
     NetworkCapabilities mNetworkCapabilities;
     TestLooper mLooper;
@@ -210,7 +208,8 @@
             ArgumentCaptor.forClass(ActionListenerWrapper.class);
     ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackCaptor =
             ArgumentCaptor.forClass(ActiveModeWarden.ModeChangeCallback.class);
-    @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+    @Captor ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback>
+            mStateChangeCallbackArgumentCaptor;
     @Captor ArgumentCaptor<ClientModeImplListener> mCmiListenerCaptor;
     InOrder mInOrder;
 
@@ -267,6 +266,8 @@
         when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
         when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden);
         when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals);
+        when(mWifiInjector.getWifiDeviceStateChangeManager())
+                .thenReturn(mWifiDeviceStateChangeManager);
         when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true);
         when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true);
         when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt(), anyString(), eq(false)))
@@ -299,8 +300,8 @@
                 mWifiNative, mActiveModeWarden, mConnectHelper, mCmiMonitor, mFrameworkFacade,
                 mMultiInternetManager);
 
-        verify(mContext, atLeastOnce()).registerReceiver(
-                mBroadcastReceiverCaptor.capture(), any(), any(), any());
+        verify(mWifiDeviceStateChangeManager, atLeastOnce()).registerStateChangeCallback(
+                mStateChangeCallbackArgumentCaptor.capture());
 
         ArgumentCaptor<NetworkRequestStoreData.DataSource> dataSourceArgumentCaptor =
                 ArgumentCaptor.forClass(NetworkRequestStoreData.DataSource.class);
@@ -442,7 +443,21 @@
                 .thenReturn(true);
         doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
+
+        assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
+        mLooper.dispatchAll();
+        verify(mConnectivityManager).declareNetworkRequestUnfulfillable(eq(mNetworkRequest));
+    }
+
+    @Test
+    public void testRejectNetworkRequestWithWifiNetworkSpecifierTofuEnabled() throws Exception {
+        mockPackageImportance(TEST_PACKAGE_NAME_1, false, false);
+
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID_1)).thenReturn(true);
+        doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
+
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], true);
 
         assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
         mLooper.dispatchAll();
@@ -456,7 +471,7 @@
     public void testHandleAcceptNetworkRequestFromWithInternetCapability() throws Exception {
         mockPackageImportance(TEST_PACKAGE_NAME_1, true, true);
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mNetworkRequest.networkCapabilities.addCapability(
                 NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
@@ -473,7 +488,7 @@
     public void testHandleAcceptNetworkRequestFromNonFgAppOrSvcWithSpecifier() throws Exception {
         mockPackageImportance(TEST_PACKAGE_NAME_1, false, false);
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
         mLooper.dispatchAll();
@@ -484,7 +499,7 @@
     public void testNetworkRequestFromGuestUserWithSpecifier() {
         mockPackageImportance(TEST_PACKAGE_NAME_1, true, true);
         when(mWifiPermissionsUtil.isGuestUser()).thenReturn(true);
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
         mLooper.dispatchAll();
@@ -499,7 +514,7 @@
     public void testHandleAcceptNetworkRequestFromFgAppWithSpecifier() {
         mockPackageImportance(TEST_PACKAGE_NAME_1, true, true);
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
@@ -514,7 +529,7 @@
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(TEST_UID_1))
                 .thenReturn(true);
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
@@ -528,12 +543,12 @@
         mockPackageImportance(TEST_PACKAGE_NAME_2, true, true);
 
         // Handle request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Make request 2 which will be accepted because a fg app request can
         // override a fg service request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
 
@@ -545,12 +560,12 @@
     public void testHandleAcceptNetworkRequestFromFgSvcWithSpecifierWithPendingRequestFromFgSvc() {
 
         // Handle request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Make request 2 which will be accepted because a fg service request can
         // override an existing fg service request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
 
@@ -564,12 +579,12 @@
         mockPackageImportance(TEST_PACKAGE_NAME_2, true, true);
 
         // Handle request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Make request 2 which will be accepted because a fg app request can
         // override an existing fg app request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
 
@@ -583,12 +598,12 @@
         mockPackageImportance(TEST_PACKAGE_NAME_1, true, true);
 
         // Handle request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Make request 2 which will be rejected because a fg service request cannot
         // override a fg app request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
         mLooper.dispatchAll();
         verify(mConnectivityManager).declareNetworkRequestUnfulfillable(eq(mNetworkRequest));
@@ -607,7 +622,7 @@
         mockPackageImportance(TEST_PACKAGE_NAME_1, true, true);
 
         // Handle request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Resend the request from a fg service (should be accepted since it is already being
@@ -637,7 +652,7 @@
 
         // Make request 2 which will be accepted because a fg app request can
         // override an existing fg app request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertTrue(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
     }
 
@@ -661,7 +676,7 @@
 
         // Make request 2 which will be rejected because a fg service request cannot
         // override a fg app request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         assertFalse(mWifiNetworkFactory.acceptRequest(mNetworkRequest));
         mLooper.dispatchAll();
         verify(mConnectivityManager).declareNetworkRequestUnfulfillable(eq(mNetworkRequest));
@@ -696,7 +711,7 @@
      */
     @Test
     public void testHandleNetworkRequestWithSpecifier() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Verify UI start.
@@ -734,7 +749,7 @@
      */
     @Test
     public void testHandleNetworkRequestWithSpecifierAndInternetCapability() throws Exception {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mNetworkRequest.networkCapabilities.addCapability(
                 NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
@@ -748,7 +763,7 @@
      */
     @Test
     public void testHandleNetworkRequestWithSpecifierForHiddenNetwork() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, true, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, true, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Verify UI start.
@@ -773,7 +788,7 @@
     @Test
     public void testHandleNetworkRequestWithSpecifierAfterPreviousHiddenNetworkRequest() {
         // Hidden request 1.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, true, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, true, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
         // Verify scan settings.
         verify(mWifiScanner, times(1)).startScan(mScanSettingsArgumentCaptor.capture(), any(),
@@ -786,7 +801,7 @@
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
 
         // Regular request 2.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
         // Verify scan settings.
         verify(mWifiScanner, times(2)).startScan(mScanSettingsArgumentCaptor.capture(), any(),
@@ -804,7 +819,7 @@
         // Make a generic request first to ensure that we re-enable auto-join after release.
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         // Make the network request with specifier.
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
@@ -824,7 +839,7 @@
      */
     @Test
     public void testPeriodicScanNetworkRequestWithSpecifier() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         verifyPeriodicScans(0,
@@ -840,7 +855,7 @@
      */
     @Test
     public void testPeriodicScanCancelOnReleaseNetworkRequestWithSpecifier() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         verifyPeriodicScans(0,
@@ -858,7 +873,7 @@
      */
     @Test
     public void testPeriodicScanCancelOnUserSelectNetwork() throws Exception {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         mWifiNetworkFactory.addCallback(mNetworkRequestMatchCallback);
@@ -885,7 +900,7 @@
      */
     @Test
     public void testHandleCallbackRegistrationAndUnregistration() throws Exception {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         mWifiNetworkFactory.addCallback(mNetworkRequestMatchCallback);
@@ -905,7 +920,7 @@
      */
     @Test
     public void testHandleCallbackRegistrationWithNoActiveRequest() throws Exception {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
         mWifiNetworkFactory.releaseNetworkFor(mNetworkRequest);
 
@@ -1281,7 +1296,7 @@
         assertNotNull(networkRequestUserSelectionCallback);
 
         // Now send another network request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Now trigger user selection to some network.
@@ -2423,7 +2438,7 @@
 
         NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest);
         // Send second request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
         mLooper.dispatchAll();
 
@@ -2461,7 +2476,7 @@
      */
     @Test
     public void testHandleNewNetworkRequestWithSpecifierWhenScanning() throws Exception {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Register callback.
@@ -2470,7 +2485,7 @@
 
         NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest);
         // Send second request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
         mLooper.dispatchAll();
 
@@ -2506,7 +2521,7 @@
 
         NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest);
         // Send second request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Ensure we don't request a new ClientModeManager.
@@ -2547,7 +2562,7 @@
 
         NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest);
         // Send second request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Ensure we don't request a new ClientModeManager.
@@ -2595,7 +2610,7 @@
 
         NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest);
         // Send second request.
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_2, false, new int[0], false);
         mWifiNetworkFactory.needNetworkFor(mNetworkRequest);
 
         // Ensure we do request a new ClientModeManager.
@@ -2833,7 +2848,7 @@
      */
     @Test
     public void testHandleNetworkRequestWithSpecifierWhenWifiOff() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         // wifi off
         when(mActiveModeWarden.hasPrimaryClientModeManager()).thenReturn(false);
@@ -2888,7 +2903,7 @@
      */
     @Test
     public void testFullHandleNetworkRequestWithSpecifierWhenWifiOff() {
-        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0]);
+        attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, new int[0], false);
 
         // wifi off
         when(mActiveModeWarden.hasPrimaryClientModeManager()).thenReturn(false);
@@ -3853,18 +3868,19 @@
         mInOrder.verifyNoMoreInteractions();
     }
 
-    private void attachDefaultWifiNetworkSpecifierAndAppInfo(int uid, boolean isHidden,
-            int[] channels) {
+    private void attachDefaultWifiNetworkSpecifierAndAppInfo(
+            int uid, boolean isHidden, int[] channels, boolean isTofu) {
         PatternMatcher ssidPatternMatch =
                 new PatternMatcher(TEST_SSID_1, PatternMatcher.PATTERN_LITERAL);
         Pair<MacAddress, MacAddress> bssidPatternMatch =
                 Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
         WifiConfiguration wifiConfiguration;
-        if (isHidden) {
-            wifiConfiguration = WifiConfigurationTestUtil.createPskHiddenNetwork();
+        if (isTofu) {
+            wifiConfiguration = WifiConfigurationTestUtil.createWpa2Wpa3EnterpriseNetwork();
         } else {
             wifiConfiguration = WifiConfigurationTestUtil.createPskNetwork();
         }
+        wifiConfiguration.hiddenSSID = isHidden;
         String packageName = null;
         if (uid == TEST_UID_1) {
             packageName = TEST_PACKAGE_NAME_1;
@@ -4027,7 +4043,7 @@
     private void validateUiStartParams(boolean expectedIsReqForSingeNetwork) {
         ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, atLeastOnce()).startActivityAsUser(
-                intentArgumentCaptor.capture(), eq(UserHandle.getUserHandleForUid(TEST_UID_1)));
+                intentArgumentCaptor.capture(), eq(UserHandle.CURRENT));
         Intent intent = intentArgumentCaptor.getValue();
         assertNotNull(intent);
         assertEquals(intent.getAction(), WifiNetworkFactory.UI_START_INTENT_ACTION);
@@ -4126,9 +4142,9 @@
     }
 
     private void setScreenState(boolean screenOn) {
-        BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue();
-        assertNotNull(broadcastReceiver);
-        Intent intent = new Intent(screenOn  ? ACTION_SCREEN_ON : ACTION_SCREEN_OFF);
-        broadcastReceiver.onReceive(mContext, intent);
+        WifiDeviceStateChangeManager.StateChangeCallback callback =
+                mStateChangeCallbackArgumentCaptor.getValue();
+        assertNotNull(callback);
+        callback.onScreenStateChanged(screenOn);
     }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 4dae8a0..7d819b9 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -47,6 +47,7 @@
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.LocalLog;
+import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 
@@ -54,6 +55,7 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.server.wifi.WifiNetworkSelector.ClientModeManagerState;
 import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper;
 import com.android.server.wifi.proto.nano.WifiMetricsProto;
 import com.android.wifi.resources.R;
 
@@ -113,6 +115,8 @@
                 .mockStatic(WifiInfo.class, withSettings().lenient())
                 .startMocking();
         when(WifiInjector.getInstance()).thenReturn(mWifiInjector);
+        when(mWifiInjector.getPasspointNetworkNominateHelper())
+                .thenReturn(mPasspointNetworkNominateHelper);
         setupContext();
         setupResources();
         setupWifiConfigManager();
@@ -195,6 +199,7 @@
 
         @Override
         public void nominateNetworks(List<ScanDetail> scanDetails,
+                List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates,
                 boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed,
                 boolean oemPrivateNetworkAllowed,
                 Set<Integer> restrictedNetworkAllowedUids,
@@ -266,6 +271,7 @@
          */
         @Override
         public void nominateNetworks(List<ScanDetail> scanDetails,
+                List<Pair<ScanDetail, WifiConfiguration>> passpointCandidates,
                 boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed,
                 boolean oemPrivateNetworkAllowed,
                 Set<Integer> restrictedNetworkAllowedUids,
@@ -315,6 +321,7 @@
     @Mock private WifiGlobals mWifiGlobals;
     @Mock private ScanRequestProxy mScanRequestProxy;
     @Mock private DevicePolicyManager mDevicePolicyManager;
+    @Mock private PasspointNetworkNominateHelper mPasspointNetworkNominateHelper;
     private ScoringParams mScoringParams;
     private LocalLog mLocalLog;
     private int mThresholdMinimumRssi2G;
@@ -484,6 +491,42 @@
         }
     }
 
+    @Test
+    public void testNetworkInsufficientWhenIpProvisioningTimedOut() {
+        // mock current network to be connected
+        WifiConfiguration testConfig = WifiConfigurationTestUtil.createOpenNetwork();
+        when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED);
+        when(mWifiConfigManager.getConfiguredNetwork(anyInt()))
+                .thenReturn(testConfig);
+
+        // verify the current network is sufficient
+        assertTrue(mWifiNetworkSelector.isNetworkSufficient(mWifiInfo));
+
+        // verify the current network is no longer sufficient when "isIpProvisionTimeout" returns
+        // false.
+        testConfig.setIpProvisioningTimedOut(true);
+        assertFalse(mWifiNetworkSelector.isNetworkSufficient(mWifiInfo));
+    }
+
+    @Test
+    public void testIsNetworkSelectionNeededForCmmWhenIpProvisioningTimedOut() {
+        when(mClientModeManager.getInterfaceName()).thenReturn("test");
+        when(mClientModeManager.isConnected()).thenReturn(false);
+        when(mClientModeManager.isDisconnected()).thenReturn(false);
+        when(mClientModeManager.getInterfaceName()).thenReturn("test");
+        when(mClientModeManager.isIpProvisioningTimedOut()).thenReturn(false);
+
+        // Verify that if the cmm reports that the current network is neither in Connected nor
+        // in Disconnected state, network selection is not needed.
+        ClientModeManagerState cmmState = new ClientModeManagerState(mClientModeManager);
+        assertFalse(mWifiNetworkSelector.isNetworkSelectionNeededForCmm(cmmState));
+
+        // Verify that when the Ip Provisioning times out, network selection becomes needed.
+        when(mClientModeManager.isIpProvisioningTimedOut()).thenReturn(true);
+        cmmState = new ClientModeManagerState(mClientModeManager);
+        assertTrue(mWifiNetworkSelector.isNetworkSelectionNeededForCmm(cmmState));
+    }
+
     /**
      * No network selection if scan result is empty.
      *
@@ -508,7 +551,8 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -541,11 +585,14 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
         assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty());
+        verify(mPasspointNetworkNominateHelper).updatePasspointConfig(any());
+        verify(mPasspointNetworkNominateHelper, never()).getPasspointNetworkCandidates(any());
     }
 
     /**
@@ -575,7 +622,8 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -585,7 +633,8 @@
         // Do another network selection with CMI in CONNECTED state.
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -594,6 +643,8 @@
 
         verify(mWifiConfigManager, atLeast(2))
                 .updateScanDetailCacheFromScanDetailForSavedNetwork(any());
+        verify(mPasspointNetworkNominateHelper, times(2)).updatePasspointConfig(any());
+        verify(mPasspointNetworkNominateHelper).getPasspointNetworkCandidates(any());
     }
 
     /**
@@ -624,7 +675,8 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
@@ -635,7 +687,8 @@
         // Do another network selection with CMI in DISCONNECTED state.
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -674,7 +727,8 @@
         // connect to test1
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED);
@@ -691,13 +745,17 @@
         // Do another network selection.
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
         ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 chosenScanResult, candidate);
+        verify(mPasspointNetworkNominateHelper, times(2)).updatePasspointConfig(any());
+        verify(mPasspointNetworkNominateHelper, times(2)).getPasspointNetworkCandidates(any());
+
     }
 
     /**
@@ -729,7 +787,8 @@
         // connect to test1
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED);
@@ -754,13 +813,16 @@
         // Do another network selection.
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
         ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
         WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
                 chosenScanResult, candidate);
+        verify(mPasspointNetworkNominateHelper, times(2)).updatePasspointConfig(any());
+        verify(mPasspointNetworkNominateHelper, times(2)).getPasspointNetworkCandidates(any());
     }
     /**
      * Ensure that network selector update's network selection status for all configured
@@ -787,7 +849,8 @@
         // Do network selection.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         verify(mWifiMetrics).incrementNetworkSelectionFilteredBssidCount(0);
@@ -797,6 +860,8 @@
         verify(mWifiConfigManager, times(savedConfigs.length))
                 .clearNetworkCandidateScanResult(anyInt());
         verify(mWifiMetrics, atLeastOnce()).addMeteredStat(any(), anyBoolean());
+        verify(mPasspointNetworkNominateHelper).updatePasspointConfig(any());
+        verify(mPasspointNetworkNominateHelper).getPasspointNetworkCandidates(any());
     }
 
     /**
@@ -825,7 +890,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         verify(mWifiMetrics).incrementNetworkSelectionFilteredBssidCount(1);
@@ -864,7 +930,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -901,7 +968,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -940,7 +1008,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -982,7 +1051,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -1030,7 +1100,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
     }
@@ -1063,7 +1134,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -1097,7 +1169,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect null configuration", null, candidate);
@@ -1131,7 +1204,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals(ssids[0], candidate.SSID);
@@ -1164,7 +1238,8 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1189,7 +1264,8 @@
                 freqsNew, capsNew, levelsNew, mClock);
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1227,7 +1303,8 @@
         HashSet<String> blocklist = new HashSet<String>();
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1251,13 +1328,14 @@
                 + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
 
         when(mThroughputPredictor.predictThroughput(any(), anyInt(), anyInt(), anyInt(),
-                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean())).thenReturn(100);
+                anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), any())).thenReturn(100);
         // Force to return 2nd network in the network nominator
         mPlaceholderNominator.setNetworkIndexToReturn(1);
 
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
         assertEquals(100, candidates.get(0).getPredictedThroughputMbps());
@@ -1296,7 +1374,8 @@
         // With no user choice set, networkSelectorChoice should be chosen.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1321,7 +1400,8 @@
         // After user connect choice is set, userChoice should override networkSelectorChoice.
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1373,7 +1453,8 @@
         // Verify that the user connect choice network is chosen.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
@@ -1387,7 +1468,8 @@
                 + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate);
@@ -1427,7 +1509,8 @@
         // Verify that the user connect choice network is chosen.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
@@ -1439,7 +1522,8 @@
                 + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1480,7 +1564,8 @@
         // Verify that the userChoice network is chosen when override is enabled by default
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
@@ -1490,7 +1575,8 @@
         mWifiNetworkSelector.setUserConnectChoiceOverrideEnabled(false);
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
         WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate);
@@ -1524,7 +1610,8 @@
         // Verify that the last selection weight for the latest selected network is greater than 0
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
 
         assertFalse(candidates.isEmpty());
@@ -1540,7 +1627,8 @@
         // Verify that the last selection weight for the latest selected network is 0
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
 
         assertFalse(candidates.isEmpty());
@@ -1589,7 +1677,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -1804,7 +1893,8 @@
                 + TimeUnit.MINUTES.toMillis(expectedStickyTimeMinutes) - 1);
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         //WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals(1, candidates.size());
@@ -1815,7 +1905,8 @@
                 + TimeUnit.MINUTES.toMillis(expectedStickyTimeMinutes));
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         //WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals(1, candidates.size());
@@ -1930,7 +2021,8 @@
         // for connection, so this should connect to the first network.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 true, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertNotNull("Result should be not null", candidate);
@@ -1971,7 +2063,8 @@
 
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo,
+                        false)),
                 true, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -2008,7 +2101,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
@@ -2042,7 +2136,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 unSavedScanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertEquals("Expect open unsaved networks",
@@ -2056,7 +2151,8 @@
 
         candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 savedScanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         candidate = mWifiNetworkSelector.selectNetwork(candidates);
         // Saved networks are filtered out.
@@ -2085,7 +2181,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
@@ -2116,7 +2213,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty());
@@ -2157,7 +2255,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
@@ -2192,7 +2291,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>();
@@ -2222,7 +2322,8 @@
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 setUpTwoNetworks(-35, -40),
                 EMPTY_BLOCKLIST,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 true, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -2359,7 +2460,8 @@
                 new PlaceholderNominator(0, PLACEHOLDER_NOMINATOR_ID_2));
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 true, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         // Check if the wifiConfig is updated with the latest
@@ -2389,7 +2491,8 @@
                 new AllNetworkNominator(scanDetailsAndConfigs));
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // Expect one privileged and one regular candidate.
         assertEquals(2, candidates.size());
@@ -2438,7 +2541,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
         verify(mWifiMetrics, times(1))
@@ -2562,9 +2666,10 @@
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
                 Arrays.asList(new ClientModeManagerState(
-                        TEST_IFACE_NAME, true, false, mWifiInfo),
+                        TEST_IFACE_NAME, true, false, mWifiInfo, false),
                         new ClientModeManagerState(
-                                TEST_IFACE_NAME_SECONDARY, true, false, mSecondaryWifiInfo)),
+                                TEST_IFACE_NAME_SECONDARY, true, false, mSecondaryWifiInfo,
+                                false)),
                 false, true, true, Collections.emptySet(), false);
         WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates);
 
@@ -2629,9 +2734,10 @@
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
                 Arrays.asList(new ClientModeManagerState(
-                                TEST_IFACE_NAME, true, false, mWifiInfo),
+                                TEST_IFACE_NAME, true, false, mWifiInfo, false),
                         new ClientModeManagerState(
-                                TEST_IFACE_NAME_SECONDARY, true, false, mSecondaryWifiInfo)),
+                                TEST_IFACE_NAME_SECONDARY, true, false, mSecondaryWifiInfo,
+                                false)),
                 false, true, true, Collections.emptySet(), false);
         assertNull(mWifiNetworkSelector.selectNetwork(candidates));
     }
@@ -2640,7 +2746,8 @@
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetailsAndConfigs.getScanDetails(),
                 new HashSet<>(), // blocklist
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 true, // untrustedNetworkAllowed
                 true, // oemPaid
                 true, // oemPrivate
@@ -2698,7 +2805,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -2753,7 +2861,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // PSK type is disabled, PSK network is not matched.
         assertEquals(1, candidates.size());
@@ -2786,7 +2895,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -2818,7 +2928,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // The SAE-only network should be filtered.
         assertEquals(1, candidates.size());
@@ -2852,7 +2963,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -2895,7 +3007,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // OPEN type is disabled, OPEN network is not matched.
         assertEquals(1, candidates.size());
@@ -2927,7 +3040,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -2959,7 +3073,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // The OWE-only network should be filtered.
         assertEquals(1, candidates.size());
@@ -2991,7 +3106,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -3034,7 +3150,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         // WPA2 Enterprise type is disabled, WPA2 Enterprise network is not matched.
         assertEquals(1, candidates.size());
@@ -3105,7 +3222,8 @@
                 new AllNetworkNominator(scanDetailsAndConfigs));
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertNotNull(candidates);
         if (expectedSecurityParamType == -1) {
@@ -3143,7 +3261,8 @@
 
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, new HashSet<>(),
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         assertEquals(2, candidates.size());
 
@@ -3217,7 +3336,7 @@
         // Return predicted throughput's for each of the links identified by the frequency.
         for (int i = 0; i < throughputs.length; ++i) {
             when(mThroughputPredictor.predictThroughput(any(), anyInt(), anyInt(), anyInt(),
-                    eq(freqs[i]), anyInt(), anyInt(), anyInt(), anyBoolean())).thenReturn(
+                    eq(freqs[i]), anyInt(), anyInt(), anyInt(), anyBoolean(), any())).thenReturn(
                     throughputs[i]);
         }
         // Configure scan details and configs.
@@ -3245,7 +3364,8 @@
         // Select network.
         List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan(
                 scanDetails, blocklist,
-                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo)),
+                Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo,
+                        false)),
                 false, true, true, Collections.emptySet(), false);
         return candidates;
     }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
index 68b35a4..cbf3c06 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java
@@ -300,11 +300,18 @@
         when(mWifiKeyStore.updateNetworkKeys(any(), any())).thenReturn(true);
 
         mWifiNetworkSuggestionsManager =
-                new WifiNetworkSuggestionsManager(mContext, new RunnerHandler(mLooper.getLooper(),
-                        100, new LocalLog(128), mWifiMetrics),
-                        mWifiInjector, mWifiPermissionsUtil, mWifiConfigManager, mWifiConfigStore,
-                        mWifiMetrics, mWifiCarrierInfoManager, mWifiKeyStore,
-                        mLruConnectionTracker, mClock);
+                new WifiNetworkSuggestionsManager(
+                        mContext,
+                        new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128)),
+                        mWifiInjector,
+                        mWifiPermissionsUtil,
+                        mWifiConfigManager,
+                        mWifiConfigStore,
+                        mWifiMetrics,
+                        mWifiCarrierInfoManager,
+                        mWifiKeyStore,
+                        mLruConnectionTracker,
+                        mClock);
         mWifiNetworkSuggestionsManager.enableVerboseLogging(true);
         mLooper.dispatchAll();
         verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any());
@@ -4390,21 +4397,32 @@
     }
 
     /**
-     * Verify that getAllPasspointScanOptimizationSuggestionNetworks will only return
-     * user-approved Passpoint networks if they have a recent SSID.
+     * Verifies that getAllPasspointScanOptimizationSuggestionNetworks will return the expected
+     * user-approved Passpoint networks.
      */
     @Test
     public void testGetPasspointPnoAvailableSuggestions() {
         setupAndGetPnoAvailableSuggestions();
         mWifiNetworkSuggestionsManager.setHasUserApprovedForApp(true, TEST_UID_1, TEST_PACKAGE_1);
-        assertTrue(mWifiNetworkSuggestionsManager
-                .getAllPasspointScanOptimizationSuggestionNetworks().isEmpty());
 
-        // Expect that the Passpoint network is returned if it has a recent SSID.
+        // Suggestion should be available in the full list of results, but not
+        // in the list of Passpoint suggestions that include an SSID.
+        assertFalse(
+                mWifiNetworkSuggestionsManager
+                        .getAllPasspointScanOptimizationSuggestionNetworks(false)
+                        .isEmpty());
+        assertTrue(
+                mWifiNetworkSuggestionsManager
+                        .getAllPasspointScanOptimizationSuggestionNetworks(true)
+                        .isEmpty());
+
+        // Assign an SSID to the Passpoint suggestion. It should now be retrievable when
+        // requesting the list of Passpoint suggestions that include an SSID.
         final String ssid = "my-passpoint-network";
         when(mPasspointManager.getMostRecentSsidForProfile(any())).thenReturn(ssid);
-        List<WifiConfiguration> configs = mWifiNetworkSuggestionsManager
-                .getAllPasspointScanOptimizationSuggestionNetworks();
+        List<WifiConfiguration> configs =
+                mWifiNetworkSuggestionsManager.getAllPasspointScanOptimizationSuggestionNetworks(
+                        true);
         assertEquals(1, configs.size());
         assertEquals(ssid, configs.get(0).SSID);
     }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNotificationManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNotificationManagerTest.java
index 94dacb1..620bc4b 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiNotificationManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNotificationManagerTest.java
@@ -31,6 +31,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -54,6 +56,7 @@
     @Mock private Notification mNotification;
     @Mock private Resources mResources;
     @Mock private StatusBarNotification mStatusBarNotification;
+    @Mock private UserHandle mUser;
 
     @Before
     public void setUp() throws Exception {
@@ -114,4 +117,27 @@
                 any());
         verify(mNotificationManager, never()).notify(anyString(), anyInt(), any());
     }
+
+    @Test
+    public void testApmNotificationNotCancelled() {
+        mWifiNotificationManager.createNotificationChannels();
+        verify(mNotificationManager).createNotificationChannels(any());
+        mWifiNotificationManager.notify(TEST_MESSAGE_ID, mNotification);
+        mWifiNotificationManager.notify(SystemMessage.NOTE_WIFI_APM_NOTIFICATION, mNotification);
+        verify(mNotificationManager).notify(eq(NOTIFICATION_TAG), eq(TEST_MESSAGE_ID), any());
+        verify(mNotificationManager).notify(eq(NOTIFICATION_TAG),
+                eq(SystemMessage.NOTE_WIFI_APM_NOTIFICATION), any());
+        // clearing existing notifications doesn't cancel the APM notification
+        StatusBarNotification testNotification = new StatusBarNotification("pkg", "opPkg",
+                TEST_MESSAGE_ID, NOTIFICATION_TAG, 0, 0, 0, mNotification, mUser, 0);
+        StatusBarNotification apmNotification = new StatusBarNotification("pkg", "opPkg",
+                SystemMessage.NOTE_WIFI_APM_NOTIFICATION, NOTIFICATION_TAG, 0, 0, 0, mNotification,
+                mUser, 0);
+        when(mNotificationManager.getActiveNotifications()).thenReturn(
+                new StatusBarNotification[]{testNotification, apmNotification});
+        mWifiNotificationManager.createNotificationChannels();
+        verify(mNotificationManager).cancel(eq(NOTIFICATION_TAG), eq(TEST_MESSAGE_ID));
+        verify(mNotificationManager, never()).cancel(eq(NOTIFICATION_TAG),
+                eq(SystemMessage.NOTE_WIFI_APM_NOTIFICATION));
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiPseudonymManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiPseudonymManagerTest.java
index e3fe64a..7257f52 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiPseudonymManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiPseudonymManagerTest.java
@@ -17,23 +17,25 @@
 package com.android.server.wifi;
 
 import static com.android.server.wifi.WifiCarrierInfoManager.ANONYMOUS_IDENTITY;
+import static com.android.server.wifi.WifiPseudonymManager.RETRY_INTERVALS_FOR_CONNECTION_ERROR;
+import static com.android.server.wifi.WifiPseudonymManager.RETRY_INTERVALS_FOR_SERVER_ERROR;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiEnterpriseConfig;
-import android.os.Message;
-import android.os.test.TestLooper;
+import android.os.Looper;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.wifi.entitlement.CarrierSpecificServiceEntitlement;
@@ -42,6 +44,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -61,6 +64,10 @@
     private static final String IMSI = "imsi";
     private static final String DECORATED_PSEUDONYM = PSEUDONYM + "@";
     private static final String MCCMNC = "mccmnc";
+
+    // Same as PseudonymInfo.DEFAULT_PSEUDONYM_TTL_IN_MILLIS
+    private static final long DEFAULT_PSEUDONYM_TTL_IN_MILLIS = Duration.ofDays(2).toMillis();
+
     @Mock
     private WifiContext mWifiContext;
     @Mock
@@ -81,13 +88,18 @@
     private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
     @Mock
     Clock mClock;
+    @Mock AlarmManager mAlarmManager;
+    final ArgumentCaptor<WifiPseudonymManager.RetrieveListener> mRetrieveListenerArgumentCaptor =
+            ArgumentCaptor.forClass(WifiPseudonymManager.RetrieveListener.class);
+    final ArgumentCaptor<Integer> mAlarmTypeCaptor = ArgumentCaptor.forClass(Integer.class);
+    final ArgumentCaptor<Long> mWindowStartCaptor = ArgumentCaptor.forClass(Long.class);
+    final ArgumentCaptor<Long> mWindowLengthCaptor = ArgumentCaptor.forClass(Long.class);
 
     private WifiPseudonymManager mWifiPseudonymManager;
-    private TestLooper mTestLooper;
+    @Mock private Looper mLooper;
 
     @Before
     public void setUp() throws Exception {
-        mTestLooper = new TestLooper();
         MockitoAnnotations.initMocks(this);
         when(mClock.getWallClockMillis()).thenReturn(Instant.now().toEpochMilli());
         when(mWifiInjector.getWifiCarrierInfoManager()).thenReturn(mWifiCarrierInfoManager);
@@ -97,70 +109,73 @@
                 .thenReturn(mWifiNetworkSuggestionsManager);
         when(mWifiCarrierInfoManager.getSimInfo(anyInt())).thenReturn(
                 new WifiCarrierInfoManager.SimInfo(IMSI, MCCMNC, CARRIER_ID, CARRIER_ID));
+        mWifiPseudonymManager =
+                new WifiPseudonymManager(
+                        mWifiContext, mWifiInjector, mClock, mAlarmManager, mLooper);
     }
 
     @Test
-    public void getValidPseudonymInfoEmpty() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void getValidPseudonymInfo_byDefault_empty() {
         assertTrue(mWifiPseudonymManager.getValidPseudonymInfo(CARRIER_ID).isEmpty());
     }
 
     @Test
-    public void getValidPseudonymInfoEmptyExpired() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void getValidPseudonymInfo_expiredPseudonym_empty() {
         setAnExpiredPseudonym(mWifiPseudonymManager);
         assertTrue(mWifiPseudonymManager.getValidPseudonymInfo(WRONG_CARRIER_ID).isEmpty());
     }
 
     @Test
-    public void getValidPseudonymInfoEmptyWrongCarrierId() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        setAValidPseudonym(mWifiPseudonymManager);
+    public void getValidPseudonymInfo_wrongCarrierId_empty() {
+        setAFreshPseudonym(mWifiPseudonymManager);
         assertTrue(mWifiPseudonymManager.getValidPseudonymInfo(WRONG_CARRIER_ID).isEmpty());
     }
 
     @Test
-    public void getValidPseudonymInfoPresent() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        setAValidPseudonym(mWifiPseudonymManager);
+    public void getValidPseudonymInfo_freshPseudonym_present() {
+        setAFreshPseudonym(mWifiPseudonymManager);
         assertTrue(mWifiPseudonymManager.getValidPseudonymInfo(CARRIER_ID).isPresent());
     }
 
     @Test
-    public void retrievePseudonymOnFailureTimeoutExpiredSchedule() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void retrievePseudonymOnFailureTimeout_expiredPseudonym_scheduleRefresh() {
         long nowTime = Instant.now().toEpochMilli();
         when(mClock.getWallClockMillis()).thenReturn(nowTime);
         when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(CARRIER_ID)).thenReturn(true);
         mWifiPseudonymManager.mLastFailureTimestampArray.put(CARRIER_ID,
                 nowTime - Duration.ofDays(7).toMillis());
+
         mWifiPseudonymManager.retrievePseudonymOnFailureTimeoutExpired(CARRIER_ID);
-        assertTrue(mTestLooper.isIdle());
-        Message message = mTestLooper.nextMessage();
-        assertEquals(CARRIER_ID, message.what);
-        assertEquals(CARRIER_ID,
-                ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
+
+        verify(mAlarmManager, times(1))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        assertEquals(AlarmManager.RTC_WAKEUP, mAlarmTypeCaptor.getValue().intValue());
+        long maxStartTime =
+                nowTime
+                        + WifiPseudonymManager.TEN_SECONDS_IN_MILLIS
+                        + Duration.ofSeconds(1).toMillis();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
+        assertEquals(
+                WifiPseudonymManager.TEN_MINUTES_IN_MILLIS,
+                mWindowLengthCaptor.getValue().longValue());
+        assertEquals(CARRIER_ID, mRetrieveListenerArgumentCaptor.getValue().mCarrierId);
     }
 
     @Test
-    public void retrievePseudonymOnFailureTimeoutExpiredNotSchedule() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void retrievePseudonymOnFailureTimeout_noPseudonym_notScheduleRefresh() {
         when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(CARRIER_ID)).thenReturn(true);
         mWifiPseudonymManager.retrievePseudonymOnFailureTimeoutExpired(CARRIER_ID);
-        assertFalse(mTestLooper.isIdle());
+        verifyNoMoreInteractions(mAlarmManager);
     }
 
     @Test
-    public void setInBandPseudonymInfoAbsent() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
+    public void setInBandPseudonymInfo_noPseudonym_empty() {
         mWifiPseudonymManager.setInBandPseudonym(CARRIER_ID, PSEUDONYM);
         Optional<PseudonymInfo> pseudonymInfoOptional =
                 mWifiPseudonymManager.getValidPseudonymInfo(CARRIER_ID);
@@ -168,9 +183,7 @@
     }
 
     @Test
-    public void setInBandPseudonymInfoPresent() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void setInBandPseudonymInfo_expiredPseudonym_present() {
         setAnExpiredPseudonym(mWifiPseudonymManager);
 
         mWifiPseudonymManager.setInBandPseudonym(CARRIER_ID, PSEUDONYM);
@@ -181,9 +194,7 @@
     }
 
     @Test
-    public void updateWifiConfigurationWithExpiredPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void updateWifiConfiguration_expiredPseudonym_noUpdate() {
         mWifiConfiguration.enterpriseConfig = mEnterpriseConfig;
         mWifiConfiguration.carrierId = CARRIER_ID;
         when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(CARRIER_ID)).thenReturn(true);
@@ -196,9 +207,7 @@
     }
 
     @Test
-    public void updateWifiConfigurationPasspointWithValidPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void updateWifiConfiguration_freshPseudonymAndPasspoint_update() {
         mWifiConfiguration.enterpriseConfig = mEnterpriseConfig;
         mWifiConfiguration.carrierId = CARRIER_ID;
         when(mWifiCarrierInfoManager.isOobPseudonymFeatureEnabled(CARRIER_ID)).thenReturn(true);
@@ -223,9 +232,7 @@
     }
 
     @Test
-    public void updateWifiConfigurationNonPasspointNetworkSuggesetionWithValidPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void updateWifiConfiguration_freshPseudonymAndNonPasspointNetworkSuggestion_update() {
         mWifiConfiguration.enterpriseConfig = mEnterpriseConfig;
         mWifiConfiguration.carrierId = CARRIER_ID;
         mWifiConfiguration.fromWifiNetworkSuggestion = true;
@@ -250,10 +257,7 @@
     }
 
     @Test
-    public void updateWifiConfigurationNonPasspointNotNetworkSuggestion() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
+    public void updateWifiConfiguration_freshPseudonym_update() {
         mWifiConfiguration.enterpriseConfig = mEnterpriseConfig;
         mWifiConfiguration.carrierId = CARRIER_ID;
         mWifiConfiguration.fromWifiNetworkSuggestion = false;
@@ -280,10 +284,7 @@
     }
 
     @Test
-    public void updateWifiConfigurationNoNeedToUpdate() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
+    public void updateWifiConfiguration_freshPseudonymWithDecoratedIdentity_noUpdate() {
         mWifiConfiguration.enterpriseConfig = mEnterpriseConfig;
         mWifiConfiguration.carrierId = CARRIER_ID;
 
@@ -305,182 +306,224 @@
     }
 
     @Test
-    public void setPseudonymAndScheduleRefreshSchedule() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        mWifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID,
-                new PseudonymInfo(PSEUDONYM, IMSI));
-        assertFalse(mTestLooper.isIdle());
-        mTestLooper.moveTimeForward(Duration.ofHours(48).toMillis());
-        assertTrue(mTestLooper.isIdle());
-        Message message = mTestLooper.nextMessage();
-        assertEquals(CARRIER_ID, message.what);
-        assertEquals(CARRIER_ID,
-                ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
+    public void setPseudonymAndScheduleRefresh_freshPseudonym_schedule() {
+        PseudonymInfo pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI);
+        mWifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID, pseudonymInfo);
+        verify(mAlarmManager, times(1))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        assertEquals(AlarmManager.RTC_WAKEUP, mAlarmTypeCaptor.getValue().intValue());
+        long maxStartTime = Instant.now().toEpochMilli() + pseudonymInfo.getLttrInMillis();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
+        assertEquals(CARRIER_ID, mRetrieveListenerArgumentCaptor.getValue().mCarrierId);
         assertTrue(mWifiPseudonymManager.getValidPseudonymInfo(CARRIER_ID).isPresent());
     }
 
     @Test
-    public void retrieveOobPseudonymIfNeededEmptySchedule() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
+    public void retrieveOobPseudonymIfNeeded_noPseudonym_scheduleRefresh() {
         mWifiPseudonymManager.retrieveOobPseudonymIfNeeded(CARRIER_ID);
-        assertTrue(mTestLooper.isIdle());
-        Message message = mTestLooper.nextMessage();
-        assertEquals(CARRIER_ID, message.what);
-        assertEquals(CARRIER_ID,
-                ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
+
+        verify(mAlarmManager, times(1))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        assertEquals(AlarmManager.RTC_WAKEUP, mAlarmTypeCaptor.getValue().intValue());
+        long maxStartTime = Instant.now().toEpochMilli();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
+        assertEquals(CARRIER_ID, mRetrieveListenerArgumentCaptor.getValue().mCarrierId);
     }
 
     @Test
-    public void retrieveOobPseudonymIfNeededPresentNoScheduleWithFreshPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
-        when(mWifiCarrierInfoManager.getMatchingSubId(CARRIER_ID)).thenReturn(SUB_ID);
-        mWifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID,
-                new PseudonymInfo(PSEUDONYM, IMSI));
-        assertFalse(mTestLooper.isIdle());
-
-        when(mWifiCarrierInfoManager.getMatchingSubId(CARRIER_ID)).thenReturn(SUB_ID);
+    public void retrieveOobPseudonymIfNeeded_freshPseudonym_scheduleRefresh() {
+        setAFreshPseudonym(mWifiPseudonymManager);
         mWifiPseudonymManager.retrieveOobPseudonymIfNeeded(CARRIER_ID);
-        assertFalse(mTestLooper.isIdle());
+
+        verify(mAlarmManager, times(2))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        assertEquals(AlarmManager.RTC_WAKEUP, mAlarmTypeCaptor.getValue().intValue());
+        long maxStartTime =
+                Instant.now().toEpochMilli()
+                        + mWifiPseudonymManager
+                                .getValidPseudonymInfo(CARRIER_ID)
+                                .get()
+                                .getLttrInMillis();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
+        assertEquals(CARRIER_ID, mRetrieveListenerArgumentCaptor.getValue().mCarrierId);
     }
 
     @Test
-    public void retrieveOobPseudonymIfNeededScheduleWithExpiredPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-
+    public void retrieveOobPseudonymIfNeeded_expiredPseudonym_scheduleRefresh() {
         when(mWifiCarrierInfoManager.getMatchingSubId(CARRIER_ID)).thenReturn(SUB_ID);
         setAnExpiredPseudonym(mWifiPseudonymManager);
         when(mWifiCarrierInfoManager.getMatchingSubId(CARRIER_ID)).thenReturn(SUB_ID);
         mWifiPseudonymManager.retrieveOobPseudonymIfNeeded(CARRIER_ID);
-        assertTrue(mTestLooper.isIdle());
+
+        verify(mAlarmManager, times(2))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        assertEquals(AlarmManager.RTC_WAKEUP, mAlarmTypeCaptor.getValue().intValue());
+        long maxStartTime = Instant.now().toEpochMilli();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
+        assertEquals(CARRIER_ID, mRetrieveListenerArgumentCaptor.getValue().mCarrierId);
     }
 
     @Test
-    public void retrieveOobPseudonymWithRateLimitScheduleWithExpiredPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void retrieveOobPseudonymWithRateLimit_expiredPseudonym_scheduleRefresh() {
         setAnExpiredPseudonym(mWifiPseudonymManager);
         mWifiPseudonymManager.retrieveOobPseudonymWithRateLimit(CARRIER_ID);
-        assertFalse(mTestLooper.isIdle());
-        mTestLooper.moveTimeForward(Duration.ofSeconds(10).toMillis());
-        assertTrue(mTestLooper.isIdle());
-        Message message = mTestLooper.nextMessage();
-        assertEquals(CARRIER_ID, message.what);
-        assertEquals(CARRIER_ID,
-                ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
+        verify(mAlarmManager, times(2))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
     }
 
     @Test
-    public void retrieveOobPseudonymWithRateLimitNoScheduleWithEmptyPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void retrieveOobPseudonymWithRateLimit_emptyPseudonym_noScheduleRefresh() {
         mWifiPseudonymManager.retrieveOobPseudonymWithRateLimit(CARRIER_ID);
-        mTestLooper.moveTimeForward(Duration.ofSeconds(10).toMillis());
-        assertFalse(mTestLooper.isIdle());
+        verify(mAlarmManager, never())
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
     }
+
     @Test
-    public void retrieveOobPseudonymWithRateLimitNoScheduleWithFreshPseudonym() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
+    public void retrieveOobPseudonymWithRateLimit_freshPseudonym_noScheduleRefresh() {
         mWifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID,
                 new PseudonymInfo(PSEUDONYM, IMSI));
+        verify(mAlarmManager, times(1))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
         mWifiPseudonymManager.retrieveOobPseudonymWithRateLimit(CARRIER_ID);
-        mTestLooper.moveTimeForward(Duration.ofSeconds(10).toMillis());
-        assertFalse(mTestLooper.isIdle());
+        verifyNoMoreInteractions(mAlarmManager);
     }
 
     @Test
-    public void testRetrieveCallbackFailureByConnectionError() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        Message message;
-        String errorDescription = "HTTPS connection error";
+    public void mRetrieveCallback_transientFailureConnectionError_scheduleRetry() {
+        // Return failure more than the MAX_RETRY_TIMES(RETRY_INTERVALS_FOR_CONNECTION_ERROR.length)
         for (int retryCount = 0;
-                retryCount < WifiPseudonymManager.RETRY_INTERVALS_FOR_CONNECTION_ERROR.length;
+                retryCount < (RETRY_INTERVALS_FOR_CONNECTION_ERROR.length * 2);
                 retryCount++) {
-            mWifiPseudonymManager.mRetrieveCallback.onFailure(CARRIER_ID,
-                    CarrierSpecificServiceEntitlement.REASON_HTTPS_CONNECTION_FAILURE,
-                    errorDescription);
-            mTestLooper.moveTimeForward(
-                    WifiPseudonymManager.RETRY_INTERVALS_FOR_CONNECTION_ERROR[retryCount]);
-            assertTrue(mTestLooper.isIdle());
-
-            message = mTestLooper.nextMessage();
-            assertEquals(CARRIER_ID, message.what);
-            assertEquals(CARRIER_ID,
-                    ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
-            assertNull(mTestLooper.nextMessage());
+            mWifiPseudonymManager.mRetrieveCallback.onFailure(
+                    CARRIER_ID,
+                    CarrierSpecificServiceEntitlement.REASON_TRANSIENT_FAILURE,
+                    "Server error");
         }
 
-        mWifiPseudonymManager.mRetrieveCallback.onFailure(CARRIER_ID,
-                CarrierSpecificServiceEntitlement.REASON_HTTPS_CONNECTION_FAILURE,
-                errorDescription);
-        mTestLooper.moveTimeForward(Duration.ofDays(100).toMillis()); // Move forward long enough
-        assertFalse(mTestLooper.isIdle());
-        assertNull(mTestLooper.nextMessage());
+        /*
+         * Verify that the device only retries
+         * MAX_RETRY_TIMES(RETRY_INTERVALS_FOR_CONNECTION_ERROR.length)
+         */
+        verify(mAlarmManager, times(RETRY_INTERVALS_FOR_CONNECTION_ERROR.length))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        long maxStartTime =
+                Instant.now().toEpochMilli()
+                        + RETRY_INTERVALS_FOR_SERVER_ERROR[
+                                RETRY_INTERVALS_FOR_CONNECTION_ERROR.length - 1];
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
     }
 
     @Test
-    public void testRetrieveCallbackFailureTransient() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        Message message;
-        String errorDescription = "Server error";
+    public void mRetrieveCallback_transientFailureServerError_scheduleRetry() {
+        // Return failure more than the MAX_RETRY_TIMES(RETRY_INTERVALS_FOR_SERVER_ERROR.length).
         for (int retryCount = 0;
-                retryCount < WifiPseudonymManager.RETRY_INTERVALS_FOR_SERVER_ERROR.length;
+                retryCount < (RETRY_INTERVALS_FOR_SERVER_ERROR.length * 2);
                 retryCount++) {
-            mWifiPseudonymManager.mRetrieveCallback.onFailure(CARRIER_ID,
-                    CarrierSpecificServiceEntitlement.REASON_TRANSIENT_FAILURE, errorDescription);
-            mTestLooper.moveTimeForward(
-                    WifiPseudonymManager.RETRY_INTERVALS_FOR_SERVER_ERROR[retryCount]);
-            assertTrue(mTestLooper.isIdle());
-
-            message = mTestLooper.nextMessage();
-            assertEquals(CARRIER_ID, message.what);
-            assertEquals(CARRIER_ID,
-                    ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
-            assertNull(mTestLooper.nextMessage());
+            mWifiPseudonymManager.mRetrieveCallback.onFailure(
+                    CARRIER_ID,
+                    CarrierSpecificServiceEntitlement.REASON_TRANSIENT_FAILURE,
+                    "Server error");
         }
 
-        mWifiPseudonymManager.mRetrieveCallback.onFailure(CARRIER_ID,
-                CarrierSpecificServiceEntitlement.REASON_TRANSIENT_FAILURE, errorDescription);
-        mTestLooper.moveTimeForward(Duration.ofDays(100).toMillis()); // Move forward long enough
-        assertFalse(mTestLooper.isIdle());
-        assertNull(mTestLooper.nextMessage());
+        /*
+         * Verify that the device only retries
+         * MAX_RETRY_TIMES(RETRY_INTERVALS_FOR_SERVER_ERROR.length)
+         */
+        verify(mAlarmManager, times(RETRY_INTERVALS_FOR_SERVER_ERROR.length))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        long maxStartTime =
+                Instant.now().toEpochMilli()
+                        + RETRY_INTERVALS_FOR_SERVER_ERROR[
+                                RETRY_INTERVALS_FOR_SERVER_ERROR.length - 1];
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
     }
 
     @Test
-    public void testRetrieveCallbackFailureNonTransient() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        String errorDescription = "Authentication failed";
-        mWifiPseudonymManager.mRetrieveCallback.onFailure(CARRIER_ID,
-                CarrierSpecificServiceEntitlement.REASON_NON_TRANSIENT_FAILURE, errorDescription);
-        mTestLooper.moveTimeForward(Duration.ofDays(100).toMillis()); // Move forward long enough
-        assertFalse(mTestLooper.isIdle());
+    public void mRetrieveCallback_nonTransientFailure_noScheduleRetry() {
+        mWifiPseudonymManager.mRetrieveCallback.onFailure(
+                CARRIER_ID,
+                CarrierSpecificServiceEntitlement.REASON_NON_TRANSIENT_FAILURE,
+                "Authentication failed");
+        verify(mAlarmManager, never())
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
     }
 
     @Test
-    public void testRetrieveCallbackSuccess() {
-        mWifiPseudonymManager = new WifiPseudonymManager(mWifiContext, mWifiInjector, mClock,
-                mTestLooper.getLooper());
-        Message message;
+    public void mRetrieveCallback_success_scheduleRefresh() {
         PseudonymInfo pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI,
                 Duration.ofDays(2).toMillis(), Instant.now().toEpochMilli());
         mWifiPseudonymManager.mRetrieveCallback.onSuccess(CARRIER_ID, pseudonymInfo);
-        assertFalse(mTestLooper.isIdle());
-        mTestLooper.moveTimeForward(Duration.ofDays(2).toMillis());
-        assertTrue(mTestLooper.isIdle());
-        message = mTestLooper.nextMessage();
-        assertEquals(CARRIER_ID, message.what);
-        assertEquals(CARRIER_ID,
-                ((WifiPseudonymManager.RetrieveRunnable) message.getCallback()).mCarrierId);
-        assertNull(mTestLooper.nextMessage());
+
+        verify(mAlarmManager, times(1))
+                .setWindow(
+                        mAlarmTypeCaptor.capture(),
+                        mWindowStartCaptor.capture(),
+                        mWindowLengthCaptor.capture(),
+                        any(),
+                        mRetrieveListenerArgumentCaptor.capture(),
+                        any());
+        long maxStartTime = Instant.now().toEpochMilli() + pseudonymInfo.getLttrInMillis();
+        assertTrue(mWindowStartCaptor.getValue().longValue() <= maxStartTime);
     }
     private void setAnExpiredPseudonym(WifiPseudonymManager wifiPseudonymManager) {
         long ttl = Duration.ofDays(2).toMillis();
@@ -489,10 +532,13 @@
         wifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID, pseudonymInfo);
     }
 
-    private void setAValidPseudonym(WifiPseudonymManager wifiPseudonymManager) {
-        long ttl = Duration.ofDays(2).toMillis();
-        PseudonymInfo pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI, ttl,
-                Instant.now().toEpochMilli());
+    private void setAFreshPseudonym(WifiPseudonymManager wifiPseudonymManager) {
+        PseudonymInfo pseudonymInfo =
+                new PseudonymInfo(
+                        PSEUDONYM,
+                        IMSI,
+                        DEFAULT_PSEUDONYM_TTL_IN_MILLIS,
+                        Instant.now().toEpochMilli());
         wifiPseudonymManager.setPseudonymAndScheduleRefresh(CARRIER_ID, pseudonymInfo);
     }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java
index 3bc492d..adaddf5 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java
@@ -16,10 +16,13 @@
 
 package com.android.server.wifi;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -27,6 +30,9 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Handler;
 import android.os.test.TestLooper;
 import android.util.StatsEvent;
@@ -34,7 +40,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.wifi.hotspot2.PasspointManager;
 import com.android.server.wifi.proto.WifiStatsLog;
+import com.android.server.wifi.util.WifiPermissionsUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -47,17 +55,28 @@
 import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 
 @SmallTest
 public class WifiPulledAtomLoggerTest extends WifiBaseTest {
     public static final int TEST_INVALID_ATOM_ID = -1;
+
+    public static final int WIFI_VERSION_NUMBER = 340899999;
     private WifiPulledAtomLogger mWifiPulledAtomLogger;
     private MockitoSession mSession;
     private TestLooper mLooper;
     @Mock private StatsManager mStatsManager;
     @Mock private Context mContext;
+    @Mock private WifiInjector mWifiInjector;
     @Mock private PackageManager mPackageManager;
+    @Mock private WifiSettingsStore mWifiSettingsStore;
+    @Mock private WifiConfigManager mWifiConfigManager;
+    @Mock private WifiNetworkSuggestionsManager mWifiNetworkSuggestionsManager;
+    @Mock private PasspointManager mPasspointManager;
+    @Mock private SsidTranslator mSsidTranslator;
+    @Mock private WifiConfiguration mWifiConfiguration;
     @Captor ArgumentCaptor<StatsManager.StatsPullAtomCallback> mPullAtomCallbackArgumentCaptor;
 
     @Before
@@ -65,8 +84,9 @@
         MockitoAnnotations.initMocks(this);
         mLooper = new TestLooper();
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mWifiInjector.getWifiSettingsStore()).thenReturn(mWifiSettingsStore);
         mWifiPulledAtomLogger = new WifiPulledAtomLogger(mStatsManager,
-                new Handler(mLooper.getLooper()), mContext);
+                new Handler(mLooper.getLooper()), mContext, mWifiInjector);
 
         mSession = ExtendedMockito.mockitoSession()
                 .strictness(Strictness.LENIENT)
@@ -82,7 +102,7 @@
     @Test
     public void testNullStatsManagerNoCrash() {
         mWifiPulledAtomLogger = new WifiPulledAtomLogger(null,
-                new Handler(mLooper.getLooper()), mContext);
+                new Handler(mLooper.getLooper()), mContext, mWifiInjector);
         mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_MODULE_INFO);
     }
 
@@ -117,6 +137,7 @@
         List<PackageInfo> packageInfos = new ArrayList<>();
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = WifiPulledAtomLogger.WIFI_BUILD_FROM_SOURCE_PACKAGE_NAME;
+        packageInfo.setLongVersionCode(WIFI_VERSION_NUMBER);
         packageInfos.add(packageInfo);
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(packageInfos);
         assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
@@ -124,7 +145,7 @@
         assertEquals(1, data.size());
         ExtendedMockito.verify(() -> WifiStatsLog.buildStatsEvent(
                 WifiStatsLog.WIFI_MODULE_INFO,
-                WifiPulledAtomLogger.WIFI_VERSION_NUMBER,
+                WIFI_VERSION_NUMBER,
                 WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_BUILT_FROM_SOURCE));
     }
 
@@ -140,6 +161,7 @@
         List<PackageInfo> packageInfos = new ArrayList<>();
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "com.google.android.wifi";
+        packageInfo.setLongVersionCode(WIFI_VERSION_NUMBER);
         packageInfos.add(packageInfo);
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(packageInfos);
         assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
@@ -147,7 +169,97 @@
         assertEquals(1, data.size());
         ExtendedMockito.verify(() -> WifiStatsLog.buildStatsEvent(
                 WifiStatsLog.WIFI_MODULE_INFO,
-                WifiPulledAtomLogger.WIFI_VERSION_NUMBER,
+                WIFI_VERSION_NUMBER,
                 WifiStatsLog.WIFI_MODULE_INFO__BUILD_TYPE__TYPE_PREBUILT));
     }
+
+    @Test
+    public void testWifiSettingsPull() {
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_SETTING_INFO);
+        verify(mStatsManager).setPullAtomCallback(eq(WifiStatsLog.WIFI_SETTING_INFO), any(), any(),
+                mPullAtomCallbackArgumentCaptor.capture());
+        assertNotNull(mPullAtomCallbackArgumentCaptor.getValue());
+
+        when(mWifiInjector.getFrameworkFacade()).thenReturn(mock(FrameworkFacade.class));
+        when(mWifiInjector.getWakeupController()).thenReturn(mock(WakeupController.class));
+        when(mWifiInjector.getOpenNetworkNotifier()).thenReturn(mock(OpenNetworkNotifier.class));
+        when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mock(WifiPermissionsUtil.class));
+
+        // Verify that all 8 settings were retrieved.
+        List<StatsEvent> data = new ArrayList<>();
+        assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
+                .onPullAtom(WifiStatsLog.WIFI_SETTING_INFO, data));
+        assertEquals(8, data.size());
+    }
+
+    @Test
+    public void testWifiComplexSettingsPull_valid() {
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO);
+        verify(mStatsManager).setPullAtomCallback(eq(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO),
+                any(), any(), mPullAtomCallbackArgumentCaptor.capture());
+        assertNotNull(mPullAtomCallbackArgumentCaptor.getValue());
+
+        // Framework and atom should indicate DBS AP mode.
+        int frameworkMode = WifiManager.WIFI_MULTI_INTERNET_MODE_DBS_AP;
+        int atomMode = WifiStatsLog
+                .WIFI_COMPLEX_SETTING_INFO__MULTI_INTERNET_MODE__MULTI_INTERNET_MODE_DBS_AP;
+        when(mWifiSettingsStore.getWifiMultiInternetMode()).thenReturn(frameworkMode);
+
+        List<StatsEvent> data = new ArrayList<>();
+        assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
+                .onPullAtom(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO, data));
+        assertEquals(1, data.size());
+        ExtendedMockito.verify(() -> WifiStatsLog.buildStatsEvent(
+                WifiStatsLog.WIFI_COMPLEX_SETTING_INFO, atomMode));
+    }
+
+    @Test
+    public void testWifiComplexSettingsPull_invalid() {
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO);
+        verify(mStatsManager).setPullAtomCallback(eq(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO),
+                any(), any(), mPullAtomCallbackArgumentCaptor.capture());
+        assertNotNull(mPullAtomCallbackArgumentCaptor.getValue());
+
+        // Invalid WifiMultiInternetMode value should cause the pull to fail.
+        when(mWifiSettingsStore.getWifiMultiInternetMode()).thenReturn(9999);
+        assertEquals(StatsManager.PULL_SKIP, mPullAtomCallbackArgumentCaptor.getValue()
+                .onPullAtom(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO, new ArrayList<>()));
+    }
+
+    @Test
+    public void testWifiConfiguredNetworkInfoPull() {
+        mWifiPulledAtomLogger.setPullAtomCallback(WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO);
+        verify(mStatsManager).setPullAtomCallback(eq(WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO),
+                any(), any(), mPullAtomCallbackArgumentCaptor.capture());
+        assertNotNull(mPullAtomCallbackArgumentCaptor.getValue());
+
+        when(mWifiConfiguration.getNetworkKey()).thenReturn("someKey");
+        when(mWifiConfiguration.getNetworkSelectionStatus()).thenReturn(
+                mock(WifiConfiguration.NetworkSelectionStatus.class));
+        when(mWifiConfiguration.isPasspoint()).thenReturn(false);
+
+        WifiNetworkSuggestion mockSuggestion = mock(WifiNetworkSuggestion.class);
+        when(mockSuggestion.getWifiConfiguration()).thenReturn(mWifiConfiguration);
+
+        when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator);
+        when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
+        when(mWifiInjector.getWifiNetworkSuggestionsManager())
+                .thenReturn(mWifiNetworkSuggestionsManager);
+        when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager);
+
+        when(mWifiConfigManager.getSavedNetworks(anyInt()))
+                .thenReturn(Arrays.asList(mWifiConfiguration));
+        when(mWifiNetworkSuggestionsManager.getAllApprovedNetworkSuggestions())
+                .thenReturn(new HashSet<>(Arrays.asList(mockSuggestion)));
+        when(mWifiNetworkSuggestionsManager
+                .getAllPasspointScanOptimizationSuggestionNetworks(anyBoolean()))
+                .thenReturn(Arrays.asList(mWifiConfiguration));
+        when(mPasspointManager.getWifiConfigsForPasspointProfiles(anyBoolean()))
+                .thenReturn(Arrays.asList(mWifiConfiguration));
+
+        List<StatsEvent> data = new ArrayList<>();
+        assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
+                .onPullAtom(WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO, data));
+        assertEquals(4, data.size());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
index 74d156c..c974d69 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
@@ -41,14 +41,18 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.net.MacAddress;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkScore;
 import android.net.wifi.IScoreUpdateObserver;
 import android.net.wifi.IWifiConnectedNetworkScorer;
+import android.net.wifi.MloLink;
 import android.net.wifi.WifiConnectedSessionInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiUsabilityStatsEntry;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -73,6 +77,7 @@
 import org.mockito.verification.VerificationMode;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -106,6 +111,7 @@
     public static final String TEST_BSSID = "00:00:00:00:00:00";
     public static final boolean TEST_USER_SELECTED = true;
     public static final int TEST_NETWORK_SWITCH_DIALOG_DISABLED_MS = 300_000;
+    private static final int TEST_UID = 435546654;
 
     FakeClock mClock;
     WifiScoreReport mWifiScoreReport;
@@ -446,7 +452,8 @@
         mWifiScoreReport.onRoleChanged(ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED);
 
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -625,7 +632,8 @@
     @Test
     public void testAospScoreBreachNoScanWhenExternalScorerEnabled() throws Exception {
         // register external scorer
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mWifiGlobals).setUsingExternalScorer(true);
         mWifiInfo.setFrequency(5220);
         for (int rssi = -60; rssi >= -83; rssi -= 1) {
@@ -841,6 +849,61 @@
         verify(mPrintWriter, times(13)).println(anyString());
     }
 
+    /** Test data logging with MLO */
+    @Test
+    public void testDataLoggingMlo() throws Exception {
+        List<MloLink> mloLinks = new ArrayList<>();
+        MloLink link1 = new MloLink();
+        link1.setBand(WifiScanner.WIFI_BAND_24_GHZ);
+        link1.setChannel(6);
+        link1.setApMacAddress(MacAddress.fromString("01:02:03:04:05:06"));
+        link1.setTxLinkSpeedMbps(300);
+        link1.setState(MloLink.MLO_LINK_STATE_ACTIVE);
+        link1.setLinkId(1);
+
+        MloLink link2 = new MloLink();
+        link2.setBand(WifiScanner.WIFI_BAND_5_GHZ);
+        link2.setChannel(44);
+        link2.setApMacAddress(MacAddress.fromString("01:02:03:04:05:07"));
+        link2.setTxLinkSpeedMbps(600);
+        link2.setLinkId(2);
+        link2.setState(MloLink.MLO_LINK_STATE_ACTIVE);
+
+        mloLinks.add(link1);
+        mloLinks.add(link2);
+
+        when(mWifiMetrics.getTotalBeaconRxCount(1)).thenReturn(500L);
+        when(mWifiMetrics.getLinkUsageState(1))
+                .thenReturn(WifiUsabilityStatsEntry.LINK_STATE_IN_USE);
+        when(mWifiMetrics.getTotalBeaconRxCount(2)).thenReturn(0L);
+        when(mWifiMetrics.getLinkUsageState(2))
+                .thenReturn(WifiUsabilityStatsEntry.LINK_STATE_NOT_IN_USE);
+
+        mWifiInfo.setAffiliatedMloLinks(mloLinks);
+
+        for (int i = 0; i < 10; i++) {
+            mWifiInfo.setRssi(-65 + i);
+            mWifiInfo.setLinkSpeed(300);
+            mWifiInfo.setFrequency(5220);
+            mWifiInfo.setSuccessfulTxPacketsPerSecond(0.1 + i);
+            mWifiInfo.setRetriedTxPacketsRate(0.2 + i);
+            mWifiInfo.setLostTxPacketsPerSecond(0.01 * i);
+            mWifiInfo.setSuccessfulRxPacketsPerSecond(0.3 + i);
+            link1.setRssi(-65 + i);
+            link1.setRetriedTxPacketsRate(0.2 + i);
+            link1.setLostTxPacketsPerSecond(0.01 * i);
+            link1.setSuccessfulRxPacketsPerSecond(0.3 + i);
+            link2.setRssi(-65 + i);
+            link2.setRetriedTxPacketsRate(0.2 + i);
+            link2.setLostTxPacketsPerSecond(0.01 * i);
+            link2.setSuccessfulRxPacketsPerSecond(0.3 + i);
+            mWifiScoreReport.calculateAndReportScore();
+        }
+        setupToGenerateAReportWhenPrintlnIsCalled();
+        mWifiScoreReport.dump(null, mPrintWriter, null);
+        verify(mPrintWriter, times(13)).println(anyString());
+    }
+
     /**
      *  Test data logging limit
      *  <p>
@@ -920,7 +983,8 @@
     @Test
     public void testClientNotification() throws RemoteException {
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         // Client should get ScoreChangeCallback.
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
     }
@@ -931,7 +995,8 @@
     @Test
     public void testClearClient() throws RemoteException {
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         mWifiScoreReport.clearWifiConnectedNetworkScorer();
         verify(mAppBinder).unlinkToDeath(any(), anyInt());
@@ -946,7 +1011,8 @@
      */
     @Test
     public void testAddsForBinderDeathOnSetClient() throws Exception {
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
     }
@@ -958,7 +1024,8 @@
     public void testAddsScorerFailureOnLinkToDeath() throws Exception {
         doThrow(new RemoteException())
                 .when(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy, never()).registerCallback(any());
         verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
 
@@ -991,7 +1058,8 @@
     @Test
     public void testClientGetSessionIdOnStart() throws Exception {
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
         mWifiScoreReport.startConnectedNetworkScorer(TEST_NETWORK_ID, TEST_USER_SELECTED);
@@ -1007,7 +1075,8 @@
     public void testClientStartOnRegWhileActive() throws Exception {
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
         mWifiScoreReport.startConnectedNetworkScorer(TEST_NETWORK_ID, TEST_USER_SELECTED);
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         verify(mWifiConnectedNetworkScorer).onStart(
                 argThat(sessionInfo -> sessionInfo.getSessionId() == TEST_SESSION_ID
@@ -1020,7 +1089,8 @@
     @Test
     public void testClientGetSessionIdOnStop() throws Exception {
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
         mWifiScoreReport.startConnectedNetworkScorer(TEST_NETWORK_ID, TEST_USER_SELECTED);
@@ -1043,10 +1113,10 @@
     public void verifyOnlyASingleScorerCanBeRegisteredSuccessively() throws Exception {
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         assertEquals(true, mWifiScoreReport.setWifiConnectedNetworkScorer(
-                mAppBinder, scorerImpl));
+                mAppBinder, scorerImpl, TEST_UID));
         verify(mExternalScoreUpdateObserverProxy).registerCallback(any());
         assertEquals(false, mWifiScoreReport.setWifiConnectedNetworkScorer(
-                mAppBinder, scorerImpl));
+                mAppBinder, scorerImpl, TEST_UID));
     }
 
     /**
@@ -1059,7 +1129,7 @@
         verifySentNetworkScore(60);
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1124,7 +1194,7 @@
     public void testFrameworkTriggersUpdateOfWifiUsabilityStats() throws Exception {
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1145,7 +1215,7 @@
         assumeFalse(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1182,7 +1252,7 @@
     public void bssidBlockListDoesnotHappenWhenExitingIsLessThanMinDuration() throws Exception {
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1212,7 +1282,7 @@
         assumeFalse(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1240,7 +1310,7 @@
     public void bssidBlockListDoesnotHappenWhenExitingIsReset() throws Exception {
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1271,7 +1341,8 @@
     @Test
     public void testOnStartInitialScoreInWifiInfoIsMaxScore() throws Exception {
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         mWifiScoreReport.startConnectedNetworkScorer(TEST_NETWORK_ID, TEST_USER_SELECTED);
         assertEquals(getMaxScore(), mWifiInfo.getScore());
     }
@@ -1285,7 +1356,7 @@
         verifySentAnyNetworkScore();
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1310,7 +1381,7 @@
         verifySentNetworkScore(60);
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1349,7 +1420,7 @@
         verifySentAnyNetworkScore();
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1397,7 +1468,7 @@
         verifySentAnyNetworkScore();
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1436,7 +1507,7 @@
         verifySentAnyNetworkScore();
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1535,7 +1606,7 @@
         verifySentAnyNetworkScore();
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1579,7 +1650,7 @@
         assumeFalse(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1601,7 +1672,7 @@
         assumeTrue(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1624,7 +1695,7 @@
         assertTrue(mWifiInfo.isUsable());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1675,7 +1746,7 @@
         assumeTrue(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1705,7 +1776,7 @@
         assumeTrue(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1727,7 +1798,8 @@
                         .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                         .build());
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1746,7 +1818,8 @@
                         .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                         .build());
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1765,7 +1838,8 @@
                         .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                         .build());
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer,
+                TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
@@ -1782,7 +1856,7 @@
         assumeFalse(SdkLevel.isAtLeastS());
         WifiConnectedNetworkScorerImpl scorerImpl = new WifiConnectedNetworkScorerImpl();
         // Register Client for verification.
-        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl);
+        mWifiScoreReport.setWifiConnectedNetworkScorer(mAppBinder, scorerImpl, TEST_UID);
         verify(mExternalScoreUpdateObserverProxy).registerCallback(
                 mExternalScoreUpdateObserverCbCaptor.capture());
         when(mNetwork.getNetId()).thenReturn(TEST_NETWORK_ID);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index db9a7cf..1500191 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.MANAGE_WIFI_COUNTRY_CODE;
 import static android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS;
 import static android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS;
+import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.net.wifi.ScanResult.WIFI_BAND_60_GHZ;
 import static android.net.wifi.ScanResult.WIFI_BAND_6_GHZ;
 import static android.net.wifi.WifiAvailableChannel.FILTER_REGULATORY;
@@ -51,10 +52,12 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
 import static android.net.wifi.WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_60_GHZ;
 import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
 import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ;
 import static android.os.Process.WIFI_UID;
+import static android.os.Process.myUid;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY;
@@ -66,7 +69,9 @@
 import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE;
 import static com.android.server.wifi.WifiSettingsConfigStore.SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI;
 import static com.android.server.wifi.WifiSettingsConfigStore.SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STA_FACTORY_MAC_ADDRESS;
 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_WEP_ALLOWED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -132,6 +137,8 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.wifi.WifiStatusCode;
+import android.location.Location;
+import android.location.LocationManager;
 import android.net.DhcpInfo;
 import android.net.DhcpOption;
 import android.net.DhcpResultsParcelable;
@@ -227,6 +234,7 @@
 import com.android.server.wifi.coex.CoexManager;
 import com.android.server.wifi.entitlement.PseudonymInfo;
 import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointNetworkNominateHelper;
 import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
 import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
@@ -235,6 +243,7 @@
 import com.android.server.wifi.util.LastCallerInfoManager;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 import com.android.server.wifi.util.WifiPermissionsWrapper;
+import com.android.wifi.flags.FeatureFlags;
 import com.android.wifi.resources.R;
 
 import com.google.common.base.Strings;
@@ -280,6 +289,7 @@
     private static final String TEST_PACKAGE_NAME_OTHER = "TestPackageOther";
     private static final String TEST_FEATURE_ID = "TestFeature";
     private static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
+    private static final String CERT_INSTALLER_PACKAGE_NAME = "com.android.certinstaller";
     private static final int TEST_PID = 6789;
     private static final int TEST_PID2 = 9876;
     private static final int TEST_UID = 1200000;
@@ -409,6 +419,7 @@
     @Mock WifiHealthMonitor mWifiHealthMonitor;
     @Mock PasspointManager mPasspointManager;
     @Mock DeviceConfigFacade mDeviceConfigFacade;
+    @Mock FeatureFlags mFeatureFlags;
     @Mock IDppCallback mDppCallback;
     @Mock ILocalOnlyHotspotCallback mLohsCallback;
     @Mock ICoexCallback mCoexCallback;
@@ -450,6 +461,8 @@
     @Mock LinkProbeManager mLinkProbeManager;
     @Mock IOnWifiDriverCountryCodeChangedListener mIOnWifiDriverCountryCodeChangedListener;
     @Mock WifiShellCommand mWifiShellCommand;
+    @Mock AfcManager mAfcManager;
+    @Mock LocationManager mLocationManager;
     @Mock DevicePolicyManager mDevicePolicyManager;
     @Mock HalDeviceManager mHalDeviceManager;
     @Mock WifiDialogManager mWifiDialogManager;
@@ -459,7 +472,9 @@
     @Mock WifiPulledAtomLogger mWifiPulledAtomLogger;
     @Mock ScoringParams mScoringParams;
     @Mock ApplicationQosPolicyRequestHandler mApplicationQosPolicyRequestHandler;
-
+    @Mock Location mLocation;
+    @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager;
+    @Mock PasspointNetworkNominateHelper mPasspointNetworkNominateHelper;
     @Captor ArgumentCaptor<Intent> mIntentCaptor;
     @Captor ArgumentCaptor<List> mListCaptor;
 
@@ -510,9 +525,14 @@
         when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
         when(mWifiInjector.getLinkProbeManager()).thenReturn(mLinkProbeManager);
         when(mWifiInjector.makeWifiShellCommand(any())).thenReturn(mWifiShellCommand);
+        when(mWifiInjector.getAfcManager()).thenReturn(mAfcManager);
+        when(mWifiInjector.getPasspointNetworkNominateHelper())
+                .thenReturn(mPasspointNetworkNominateHelper);
         // needed to mock this to call "handleBootCompleted"
         when(mWifiInjector.getPasspointProvisionerHandlerThread())
                 .thenReturn(mock(HandlerThread.class));
+        when(mWifiInjector.getWifiDeviceStateChangeManager())
+                .thenReturn(mWifiDeviceStateChangeManager);
         when(mHandlerThread.getThreadHandler()).thenReturn(new Handler(mLooper.getLooper()));
         when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
         when(mContext.getResources()).thenReturn(mResources);
@@ -527,6 +547,7 @@
         when(mFrameworkFacade.getSettingsWorkSource(any())).thenReturn(TEST_SETTINGS_WORKSOURCE);
         when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
         when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+        when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
         IPowerManager powerManagerService = mock(IPowerManager.class);
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager =
@@ -559,6 +580,8 @@
         when(mWifiInjector.getWifiBlocklistMonitor()).thenReturn(mWifiBlocklistMonitor);
         when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager);
         when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
+        when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags);
+        when(mFeatureFlags.delaySaveToStore()).thenReturn(true);
         when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager);
         when(mClientModeManager.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         when(mWifiInjector.getWifiScoreCard()).thenReturn(mWifiScoreCard);
@@ -597,6 +620,9 @@
         when(mScanRequestProxy.startScan(anyInt(), anyString())).thenReturn(true);
         when(mLohsCallback.asBinder()).thenReturn(mock(IBinder.class));
         when(mWifiSettingsConfigStore.get(eq(WIFI_VERBOSE_LOGGING_ENABLED))).thenReturn(true);
+        when(mWifiSettingsConfigStore.get(
+                eq(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)))
+                .thenReturn(false);
         when(mWifiPermissionsUtil.isSystem(anyString(), anyInt())).thenReturn(false);
         when(mActiveModeWarden.getClientModeManagersInRoles(
                 ROLE_CLIENT_LOCAL_ONLY, ROLE_CLIENT_SECONDARY_LONG_LIVED))
@@ -631,6 +657,9 @@
         when(mWifiInjector.getScoringParams()).thenReturn(mScoringParams);
         when(mWifiInjector.getApplicationQosPolicyRequestHandler())
                 .thenReturn(mApplicationQosPolicyRequestHandler);
+        when(mLocationManager.getProviders(anyBoolean())).thenReturn(List.of(
+                LocationManager.FUSED_PROVIDER, LocationManager.PASSIVE_PROVIDER,
+                LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER));
 
         doAnswer(new AnswerWithArguments() {
             public void answer(Runnable onStoppedListener) throws Throwable {
@@ -638,6 +667,7 @@
             }
         }).when(mMakeBeforeBreakManager).stopAllSecondaryTransientClientModeManagers(any());
 
+        when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(true);
         mWifiServiceImpl = makeWifiServiceImpl();
         mDppCallback = new IDppCallback() {
             @Override
@@ -693,7 +723,6 @@
         mWifiConfig.SSID = TEST_SSID;
         mWifiConfig.networkId = TEST_NETWORK_ID;
 
-        mWifiThreadRunner.prepareForAutoDispatch();
         setup24GhzSupported();
     }
 
@@ -771,10 +800,10 @@
     public void testWifiMetricsDump() {
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
-        verify(mWifiMetrics).start();
+        mLooper.startAutoDispatch();
         mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()),
                 new String[]{mWifiMetrics.PROTO_DUMP_ARG});
-        mLooper.dispatchAll();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mWifiMetrics).setNonPersistentMacRandomizationForceEnabled(anyBoolean());
         verify(mWifiMetrics).setIsScanningAlwaysEnabled(anyBoolean());
         verify(mWifiMetrics).setVerboseLoggingEnabled(anyBoolean());
@@ -791,11 +820,13 @@
     public void testDumpNullArgs() {
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
+        mLooper.startAutoDispatch();
         mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
-        mLooper.dispatchAll();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mWifiDiagnostics).captureBugReportData(
                 WifiDiagnostics.REPORT_REASON_USER_ACTION);
         verify(mWifiDiagnostics).dump(any(), any(), any());
+        verify(mPasspointNetworkNominateHelper).dump(any());
     }
 
     @Test
@@ -846,9 +877,6 @@
      */
     @Test
     public void testSetWifiEnabledMetricsNormalAppBelowQSdk() throws Exception {
-        when(mWifiSettingsConfigStore.get(
-                eq(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)))
-                .thenReturn(false);
         doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
                 .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
         when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
@@ -958,9 +986,6 @@
      */
     @Test
     public void testSetWifiEnabledSuccessForAppsTargetingBelowQSdk() throws Exception {
-        when(mWifiSettingsConfigStore.get(
-                eq(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)))
-                .thenReturn(false);
         doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
                 .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
         when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
@@ -1131,9 +1156,6 @@
      */
     @Test
     public void testSetWifiEnabledDialogForThirdPartyAppsTargetingBelowQSdk() throws Exception {
-        when(mWifiSettingsConfigStore.get(
-                eq(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)))
-                .thenReturn(false);
         when(mResources.getBoolean(
                 R.bool.config_showConfirmationDialogForThirdPartyAppsEnablingWifi))
                 .thenReturn(true);
@@ -1159,7 +1181,7 @@
         when(mResources.getString(R.string.wifi_enable_request_dialog_negative_button))
                 .thenReturn(negativeButtonText);
 
-        // Verify the negative reply does not enable wifi
+        // Launch dialog
         WifiDialogManager.DialogHandle dialogHandle = mock(WifiDialogManager.DialogHandle.class);
         when(mWifiDialogManager.createSimpleDialog(any(), any(), any(), any(), any(), any(), any()))
                 .thenReturn(dialogHandle);
@@ -1178,6 +1200,22 @@
                 callbackCaptor.capture(),
                 any());
         verify(dialogHandle).launchDialog();
+
+        // Verify extra call to setWifiEnabled won't launch a new dialog
+        assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+        verify(mActiveModeWarden, times(0)).wifiToggled(
+                eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
+        mLooper.dispatchAll();
+        verify(mWifiDialogManager, times(1)).createSimpleDialog(
+                eq(title),
+                eq(message),
+                eq(positiveButtonText),
+                eq(negativeButtonText),
+                eq(null),
+                callbackCaptor.capture(),
+                any());
+
+        // Verify the negative reply does not enable wifi
         callbackCaptor.getValue().onNegativeButtonClicked();
         verify(mActiveModeWarden, times(0)).wifiToggled(
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
@@ -1284,9 +1322,6 @@
      */
     @Test
     public void testSetWifiEnabledNoDialogForNonThirdPartyAppsTargetingBelowQSdk() {
-        when(mWifiSettingsConfigStore.get(
-                eq(SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI_SET_BY_API)))
-                .thenReturn(false);
         when(mResources.getBoolean(
                 R.bool.config_showConfirmationDialogForThirdPartyAppsEnablingWifi))
                 .thenReturn(true);
@@ -1345,10 +1380,10 @@
     }
 
     /**
-     * Verify that a call from an app cannot enable wifi if we are in softap mode.
+     * Verify that a call from an app cannot enable wifi if we are in softap mode for Pre-S
      */
     @Test
-    public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
+    public void testSetWifiEnabledFromAppFailsWhenApEnabledForPreS() throws Exception {
         doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
                 .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE, Process.myUid(), TEST_PACKAGE_NAME);
         when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
@@ -1364,9 +1399,16 @@
                 eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt()))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
         when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
-        assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
-        verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
-        verify(mActiveModeWarden, never()).wifiToggled(any());
+        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+        if (!SdkLevel.isAtLeastS()) {
+            assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+            verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
+            verify(mActiveModeWarden, never()).wifiToggled(any());
+        } else {
+            assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+            verify(mActiveModeWarden).wifiToggled(
+                    eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
+        }
     }
 
 
@@ -1861,6 +1903,7 @@
         verify(mWifiConfigManager).loadFromStore();
         verify(mActiveModeWarden).start();
         verify(mActiveModeWarden, never()).wifiToggled(any());
+        verify(mWifiDeviceStateChangeManager).handleBootCompleted();
     }
 
     @Test
@@ -1872,8 +1915,9 @@
         verify(mWifiConfigManager).loadFromStore();
         verify(mActiveModeWarden).enableVerboseLogging(true);
         // show key mode is always disabled at the beginning.
-        verify(mWifiGlobals).setShowKeyVerboseLoggingModeEnabled(eq(false));
+        verify(mWifiGlobals).setVerboseLoggingLevel(eq(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED));
         verify(mActiveModeWarden).start();
+        assertTrue(mWifiThreadRunner.mVerboseLoggingEnabled);
     }
 
     /**
@@ -1889,9 +1933,27 @@
                 anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
-        verify(mWifiMetrics).start();
         verify(mWifiConfigManager).loadFromStore();
         verify(mActiveModeWarden).start();
+        verify(mWifiDeviceStateChangeManager).handleBootCompleted();
+    }
+
+    @Test
+    public void testSetPnoScanEnabled() {
+        assertThrows(SecurityException.class,
+                () -> mWifiServiceImpl.setPnoScanEnabled(false, false, TEST_PACKAGE_NAME));
+
+        if (SdkLevel.isAtLeastT()) {
+            when(mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(anyInt()))
+                    .thenReturn(true);
+        } else {
+            when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        }
+        mWifiServiceImpl.setPnoScanEnabled(false, false, TEST_PACKAGE_NAME);
+        verify(mLastCallerInfoManager).put(eq(WifiManager.API_SET_PNO_SCAN_ENABLED),
+                anyInt(), anyInt(), anyInt(), eq(TEST_PACKAGE_NAME), eq(false));
+        mLooper.dispatchAll();
+        verify(mWifiConnectivityManager).setPnoScanEnabledByFramework(false, false);
     }
 
     @Test
@@ -1899,6 +1961,10 @@
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
         verify(mWifiPulledAtomLogger).setPullAtomCallback(WifiStatsLog.WIFI_MODULE_INFO);
+        verify(mWifiPulledAtomLogger).setPullAtomCallback(WifiStatsLog.WIFI_SETTING_INFO);
+        verify(mWifiPulledAtomLogger).setPullAtomCallback(WifiStatsLog.WIFI_COMPLEX_SETTING_INFO);
+        verify(mWifiPulledAtomLogger).setPullAtomCallback(
+                WifiStatsLog.WIFI_CONFIGURED_NETWORK_INFO);
     }
 
     /**
@@ -2127,9 +2193,7 @@
      */
     @Test
     public void testStartTetheredHotspotWithPermissionsAndNullConfig() {
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(null, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
@@ -2143,10 +2207,8 @@
      */
     @Test
     public void testStartTetheredHotspotWithPermissionsAndInvalidConfig() {
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(
                 new SoftApConfiguration.Builder().build(), TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2157,9 +2219,7 @@
     @Test
     public void testStartTetheredHotspotWithPermissionsAndValidConfig() {
         SoftApConfiguration config = createValidSoftApConfiguration();
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
@@ -2176,9 +2236,7 @@
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
         SoftApConfiguration config = createValidSoftApConfiguration();
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2363,10 +2421,7 @@
                 .setBand(SoftApConfiguration.BAND_2GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
@@ -2386,10 +2441,7 @@
                 .setBand(SoftApConfiguration.BAND_2GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2408,10 +2460,7 @@
                 .setBand(SoftApConfiguration.BAND_2GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2429,9 +2478,7 @@
                 .setBand(SoftApConfiguration.BAND_5GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
@@ -2451,10 +2498,7 @@
                 .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setBand(SoftApConfiguration.BAND_5GHZ)
                 .build();
-
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
@@ -2474,9 +2518,7 @@
                 .setBand(SoftApConfiguration.BAND_5GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
@@ -2495,9 +2537,7 @@
                 .setBand(SoftApConfiguration.BAND_6GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
@@ -2517,10 +2557,7 @@
                 .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)
                 .setBand(SoftApConfiguration.BAND_6GHZ)
                 .build();
-
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
@@ -2540,10 +2577,7 @@
                 .setBand(SoftApConfiguration.BAND_6GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2561,10 +2595,7 @@
                 .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setBand(SoftApConfiguration.BAND_60GHZ)
                 .build();
-
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
@@ -2584,10 +2615,7 @@
                 .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setBand(SoftApConfiguration.BAND_60GHZ)
                 .build();
-
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
@@ -2606,11 +2634,7 @@
                 .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setBand(SoftApConfiguration.BAND_60GHZ)
                 .build();
-
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertFalse(result);
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
     }
@@ -2628,10 +2652,7 @@
                 .setBand(SoftApConfiguration.BAND_5GHZ)
                 .build();
 
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
-
         assertTrue(result);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
@@ -2660,10 +2681,8 @@
         when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
         SoftApConfiguration config = createValidSoftApConfiguration();
-        mLooper.startAutoDispatch();
         boolean result = mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME);
         assertTrue(result);
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
         assertThat(config).isEqualTo(mSoftApModeConfigCaptor.getValue().getSoftApConfiguration());
@@ -2677,16 +2696,11 @@
     @Test
     public void testStartTetheredHotspotWithValidConfigSucceedsAfterFailedCall() {
         // First issue an invalid call
-        mLooper.startAutoDispatch();
         assertFalse(mWifiServiceImpl.startTetheredHotspot(
                 new SoftApConfiguration.Builder().build(), TEST_PACKAGE_NAME));
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mActiveModeWarden, never()).startSoftAp(any(), any());
 
-        // Now attempt a successful call
-        mLooper.startAutoDispatch();
         assertTrue(mWifiServiceImpl.startTetheredHotspot(null, TEST_PACKAGE_NAME));
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
         assertNull(mSoftApModeConfigCaptor.getValue().getSoftApConfiguration());
@@ -2797,8 +2811,8 @@
     @Test
     public void testConnectedIdsAreHiddenFromAppWithoutPermission() throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
-        when(mClientModeManager.getConnectionInfo()).thenReturn(wifiInfo);
-
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
         doThrow(new SecurityException()).when(mWifiPermissionsUtil).enforceCanAccessScanResults(
                 anyString(), nullable(String.class), anyInt(), nullable(String.class));
 
@@ -2827,7 +2841,8 @@
     @Test
     public void testConnectedIdsAreHiddenOnSecurityException() throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
-        when(mClientModeManager.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(wifiInfo);
 
         doThrow(new SecurityException()).when(mWifiPermissionsUtil).enforceCanAccessScanResults(
                 anyString(), nullable(String.class), anyInt(), nullable(String.class));
@@ -2851,7 +2866,8 @@
     @Test
     public void testConnectedIdsAreVisibleFromPermittedApp() throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
-        when(mClientModeManager.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(wifiInfo);
 
         mLooper.startAutoDispatch();
         WifiInfo connectionInfo = parcelingRoundTrip(
@@ -2873,6 +2889,9 @@
     public void testConnectedIdsFromSecondaryCmmAreVisibleFromAppRequestingSecondaryCmm()
             throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+        when(mActiveModeWarden.getSecondaryRequestWs())
+                .thenReturn(Set.of(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE)));
         ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class);
         when(secondaryCmm.getRequestorWs())
                 .thenReturn(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE));
@@ -2902,10 +2921,13 @@
     public void testConnectedIdsAreVisibleFromAppRequestingSecondaryCmmWIthPromotesSettingsWs()
             throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(new WifiInfo());
         ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class);
         WorkSource ws = new WorkSource(Binder.getCallingUid(), TEST_PACKAGE);
         ws.add(SETTINGS_WORKSOURCE);
         when(secondaryCmm.getRequestorWs()).thenReturn(ws);
+        when(mActiveModeWarden.getSecondaryRequestWs()).thenReturn(Set.of(ws));
         when(secondaryCmm.getConnectionInfo()).thenReturn(wifiInfo);
         when(mActiveModeWarden.getClientModeManagersInRoles(
                 ROLE_CLIENT_LOCAL_ONLY, ROLE_CLIENT_SECONDARY_LONG_LIVED))
@@ -2934,8 +2956,7 @@
         mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         assertEquals(WifiManager.UNKNOWN_SSID, connectionInfo.getSSID());
-        verify(mActiveModeWarden).getPrimaryClientModeManager();
-        verify(primaryCmm).getConnectionInfo();
+        verify(mActiveModeWarden).getConnectionInfo();
     }
 
     /**
@@ -2946,13 +2967,11 @@
     public void testConnectedIdsFromPrimaryCmmAreVisibleFromAppNotRequestingSecondaryCmm()
             throws Exception {
         WifiInfo wifiInfo = setupForGetConnectionInfo();
-        when(mClientModeManager.getConnectionInfo()).thenReturn(wifiInfo);
-        ConcreteClientModeManager secondaryCmm = mock(ConcreteClientModeManager.class);
-        when(secondaryCmm.getRequestorWs())
-                .thenReturn(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME_OTHER));
-        when(mActiveModeWarden.getClientModeManagersInRoles(
-                ROLE_CLIENT_LOCAL_ONLY, ROLE_CLIENT_SECONDARY_LONG_LIVED))
-                .thenReturn(Arrays.asList(secondaryCmm));
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED);
+        when(mActiveModeWarden.getSecondaryRequestWs())
+                .thenReturn(
+                        Set.of(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME_OTHER)));
 
         mLooper.startAutoDispatch();
         WifiInfo connectionInfo = parcelingRoundTrip(
@@ -3323,11 +3342,14 @@
     }
 
     private void registerLOHSRequestFull() {
+        mLooper.startAutoDispatch();
         setupLohsPermissions();
         int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME,
                 TEST_FEATURE_ID, null, mExtras);
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
         verifyCheckChangePermission(TEST_PACKAGE_NAME);
+        mLooper.dispatchAll();
     }
 
     /**
@@ -3336,9 +3358,7 @@
      */
     @Test
     public void testStartLocalOnlyHotspotSingleRegistrationReturnsRequestRegistered() {
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(), any());
     }
 
@@ -3460,9 +3480,7 @@
     @Test
     public void testStartTetheredHotspotDoesNotStartWhenAlreadyTetheringActive() throws Exception {
         SoftApConfiguration config = createValidSoftApConfiguration();
-        mLooper.startAutoDispatch();
         assertTrue(mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME));
-        mLooper.stopAutoDispatch();
         verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(),
                 eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME)));
         assertThat(config).isEqualTo(mSoftApModeConfigCaptor.getValue().getSoftApConfiguration());
@@ -3473,9 +3491,7 @@
         reset(mActiveModeWarden);
 
         // Start another session without a stop, that should fail.
-        mLooper.startAutoDispatch();
         assertFalse(mWifiServiceImpl.startTetheredHotspot(config, TEST_PACKAGE_NAME));
-        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         verifyNoMoreInteractions(mActiveModeWarden);
     }
@@ -3520,9 +3536,7 @@
      */
     @Test(expected = IllegalStateException.class)
     public void testStartLocalOnlyHotspotThrowsExceptionWhenCallerAlreadyRegistered() {
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
 
         // now do the second request that will fail
         mWifiServiceImpl.startLocalOnlyHotspot(
@@ -3548,12 +3562,11 @@
      */
     @Test
     public void testStopLocalOnlyHotspotDoesNothingWithRemainingRequest() throws Exception {
-        mLooper.startAutoDispatch();
+
         // register a request that will remain after the stopLOHS call
         mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
-
+        mLooper.dispatchAll();
         setupLocalOnlyHotspot();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
         // Since we are calling with the same pid, the second register call will be removed
         mWifiServiceImpl.stopLocalOnlyHotspot();
         mLooper.dispatchAll();
@@ -3590,9 +3603,7 @@
         SoftApConfiguration lohsConfig = createValidSoftApConfiguration();
         when(mWifiApConfigStore.generateLocalOnlyHotspotConfig(
                 eq(mContext), eq(null), any())).thenReturn(lohsConfig);
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
         verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig(
                 eq(mContext), eq(null), any());
         verifyLohsBand(SoftApConfiguration.BAND_2GHZ);
@@ -3832,17 +3843,18 @@
      */
     @Test
     public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRequests() throws Exception {
-        mLooper.startAutoDispatch();
         LocalOnlyRequestorCallback binderDeathCallback =
                 mWifiServiceImpl.new LocalOnlyRequestorCallback();
 
         // registering a request directly from the test will not trigger a message to start
         // softap mode
         mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
+        mLooper.dispatchAll();
 
         setupLocalOnlyHotspot();
 
         binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo);
+        mLooper.dispatchAll();
         verify(mActiveModeWarden, never()).stopSoftAp(anyInt());
 
         reset(mActiveModeWarden);
@@ -3850,8 +3862,8 @@
         // now stop as the second request and confirm CMD_SET_AP will be sent to make sure binder
         // death requestor was removed
         mWifiServiceImpl.stopLocalOnlyHotspot();
+        mLooper.dispatchAll();
         verify(mActiveModeWarden).stopSoftAp(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
     }
 
     /**
@@ -4335,9 +4347,7 @@
         SoftApConfiguration lohsConfig = createValidSoftApConfiguration();
         when(mWifiApConfigStore.generateLocalOnlyHotspotConfig(
                 eq(mContext), eq(null), any())).thenReturn(lohsConfig);
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
         verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig(
                 eq(mContext), eq(null), any());
         mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
@@ -4357,14 +4367,13 @@
     @Test
     public void testRegisterLocalOnlyHotspotRequestAfterAlreadyStartedGetsOnStartedCallback()
             throws Exception {
-        mLooper.startAutoDispatch();
         mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+        mLooper.dispatchAll();
 
         changeLohsState(WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR);
         mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
 
         registerLOHSRequestFull();
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
         verify(mLohsCallback).onHotspotStarted(any());
     }
 
@@ -4412,7 +4421,6 @@
      */
     @Test
     public void testCallOnFailedLocalOnlyHotspotRequestWhenTetheringStarts() throws Exception {
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
 
         mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
@@ -4430,7 +4438,6 @@
 
         mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
         verifyZeroInteractions(ignoreStubs(mLohsCallback));
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
     }
 
     /**
@@ -4440,10 +4447,8 @@
     @Test
     public void testRegisterLocalOnlyHotspotRequestWhenStoppedDoesNotGetOnStoppedCallback()
             throws Exception {
-        mLooper.startAutoDispatch();
         registerLOHSRequestFull();
         verifyZeroInteractions(ignoreStubs(mLohsCallback));
-        stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper);
     }
 
     /**
@@ -4985,13 +4990,14 @@
         doNothing().when(mContext)
                 .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
                         eq("WifiService"));
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
         // Verbose logging is enabled first in the constructor for WifiServiceImpl, so reset
         // before invocation.
         reset(mClientModeManager);
         TestWifiVerboseLoggingStatusChangedListener listener =
                 new TestWifiVerboseLoggingStatusChangedListener();
         mWifiServiceImpl.addWifiVerboseLoggingStatusChangedListener(listener);
-        mLooper.dispatchAll();
         mWifiServiceImpl.enableVerboseLogging(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED);
         verify(mWifiSettingsConfigStore).put(WIFI_VERBOSE_LOGGING_ENABLED, true);
         verify(mActiveModeWarden).enableVerboseLogging(anyBoolean());
@@ -5004,7 +5010,6 @@
 
         // unregister the callback and verify no more updates happen.
         mWifiServiceImpl.removeWifiVerboseLoggingStatusChangedListener(listener);
-        mLooper.dispatchAll();
         mWifiServiceImpl.enableVerboseLogging(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED);
         assertEquals(2, listener.numStatusChangedCounts);
         assertFalse(listener.lastReceivedValue);
@@ -5058,15 +5063,15 @@
      */
     @Test
     public void testEnableVerboseLoggingWithNetworkSettingsPermission() throws Exception {
-        doNothing().when(mContext)
-                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
-                        eq("WifiService"));
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
         // Verbose logging is enabled first in the constructor for WifiServiceImpl, so reset
         // before invocation.
         reset(mClientModeManager);
         mWifiServiceImpl.enableVerboseLogging(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED);
         verify(mWifiSettingsConfigStore).put(WIFI_VERBOSE_LOGGING_ENABLED, true);
         verify(mActiveModeWarden).enableVerboseLogging(anyBoolean());
+        assertTrue(mWifiThreadRunner.mVerboseLoggingEnabled);
     }
 
     /**
@@ -5076,21 +5081,21 @@
      */
     @Test
     public void testEnableShowKeyVerboseLoggingWithNetworkSettingsPermission() throws Exception {
-        doNothing().when(mContext)
-                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
-                        eq("WifiService"));
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
         // Verbose logging is enabled first in the constructor for WifiServiceImpl, so reset
         // before invocation.
         reset(mClientModeManager);
         mWifiServiceImpl.enableVerboseLogging(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY);
         verify(mWifiSettingsConfigStore).put(WIFI_VERBOSE_LOGGING_ENABLED, true);
         verify(mActiveModeWarden).enableVerboseLogging(anyBoolean());
-        verify(mWifiGlobals).setShowKeyVerboseLoggingModeEnabled(eq(true));
+        verify(mWifiGlobals).setVerboseLoggingLevel(
+                eq(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED_SHOW_KEY));
 
         // After auto disable show key mode after the countdown
         mLooper.moveTimeForward(WifiServiceImpl.AUTO_DISABLE_SHOW_KEY_COUNTDOWN_MILLIS + 1);
         mLooper.dispatchAll();
-        verify(mWifiGlobals).setShowKeyVerboseLoggingModeEnabled(eq(false));
+        verify(mWifiGlobals).setVerboseLoggingLevel(eq(WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED));
     }
 
     /**
@@ -5116,9 +5121,10 @@
      */
     @Test(expected = SecurityException.class)
     public void testEnableVerboseLoggingWithNoNetworkSettingsPermission() {
-        doThrow(new SecurityException()).when(mContext)
-                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
-                        eq("WifiService"));
+        when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mContext.checkPermission(eq(android.Manifest.permission.DUMP),
+                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
         // Vebose logging is enabled first in the constructor for WifiServiceImpl, so reset
         // before invocation.
         reset(mClientModeManager);
@@ -5202,6 +5208,46 @@
     }
 
     /**
+     * Verify the secondary internet CMM is stopped when explicit connection is initiated on the
+     * primary.
+     */
+    @Test
+    public void testConnectNetworkStopConnectedSecondaryInternetCmm() throws Exception {
+        // grant permissions to access WifiServiceImpl#connect
+        when(mContext.checkPermission(
+                        eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        when(mWifiConfigManager.addOrUpdateNetwork(any(), anyInt()))
+                .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+        WifiConfiguration config = WifiConfigurationTestUtil.createWpa2Wpa3EnterpriseNetwork();
+        config.SSID = TEST_SSID;
+        WifiConfiguration otherConfig = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
+        when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config);
+
+        // Mock ActiveModeWarden to return a primary CMM and a secondary CMM to be already
+        // connected to the target network.
+        List<ClientModeManager> clientModeManagers = new ArrayList<>();
+        ClientModeManager primaryCmm = mock(ClientModeManager.class);
+        when(primaryCmm.getRole()).thenReturn(ROLE_CLIENT_PRIMARY);
+        ConcreteClientModeManager secondaryInternetCmm = mock(ConcreteClientModeManager.class);
+        when(secondaryInternetCmm.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED);
+        when(secondaryInternetCmm.isSecondaryInternet()).thenReturn(true);
+        when(secondaryInternetCmm.isConnected()).thenReturn(true);
+        when(secondaryInternetCmm.getConnectedWifiConfiguration()).thenReturn(otherConfig);
+        clientModeManagers.add(primaryCmm);
+        clientModeManagers.add(secondaryInternetCmm);
+        when(mActiveModeWarden.getClientModeManagers()).thenReturn(clientModeManagers);
+
+        // Verify that the secondary internet CMM is stopped when manual connection is started
+        mWifiServiceImpl.connect(
+                config, TEST_NETWORK_ID, mock(IActionListener.class), TEST_PACKAGE_NAME);
+        mLooper.dispatchAll();
+        verify(primaryCmm, never()).stop();
+        verify(secondaryInternetCmm).stop();
+    }
+
+    /**
      * Verify that the CONNECT_NETWORK message received from an app with
      * one of the privileged permission will stop secondary CMMs that are alraedy connected to
      * the same network before initiating the connection.
@@ -5224,7 +5270,7 @@
         List<ClientModeManager> clientModeManagers = new ArrayList<>();
         ClientModeManager primaryCmm = mock(ClientModeManager.class);
         when(primaryCmm.getRole()).thenReturn(ROLE_CLIENT_PRIMARY);
-        ClientModeManager localOnlyCmm = mock(ClientModeManager.class);
+        ConcreteClientModeManager localOnlyCmm = mock(ConcreteClientModeManager.class);
         when(localOnlyCmm.getRole()).thenReturn(ROLE_CLIENT_LOCAL_ONLY);
         when(localOnlyCmm.isConnected()).thenReturn(true);
         when(localOnlyCmm.getConnectedWifiConfiguration()).thenReturn(localOnlyConfig);
@@ -5738,19 +5784,21 @@
         TestUtil.sendIdleModeChanged(mBroadcastReceiverCaptor.getValue(), mContext);
 
         // Send a scan request while the device is idle.
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         assertFalse(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME, TEST_FEATURE_ID));
-        mLooper.stopAutoDispatch();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         // No scans must be made yet as the device is idle.
         verify(mScanRequestProxy, never()).startScan(Process.myUid(), SCAN_PACKAGE_NAME);
+        // Verify ActiveModeWarden is notified of the idle mode change
+        verify(mActiveModeWarden).onIdleModeChanged(true);
 
         // Tell the wifi service that idle mode ended.
         when(mPowerManager.isDeviceIdleMode()).thenReturn(false);
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         TestUtil.sendIdleModeChanged(mBroadcastReceiverCaptor.getValue(), mContext);
-        mLooper.stopAutoDispatch();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
+        // Verify ActiveModeWarden is notified of the idle mode change
+        verify(mActiveModeWarden).onIdleModeChanged(false);
 
         // Must scan now.
         verify(mScanRequestProxy).startScan(Process.myUid(), TEST_PACKAGE_NAME);
@@ -5761,10 +5809,9 @@
 
         // Send another scan request. The device is not idle anymore, so it must be executed
         // immediately.
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         assertTrue(mWifiServiceImpl.startScan(SCAN_PACKAGE_NAME, TEST_FEATURE_ID));
-        mLooper.stopAutoDispatch();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mScanRequestProxy).startScan(Process.myUid(), SCAN_PACKAGE_NAME);
     }
 
@@ -5882,6 +5929,36 @@
     }
 
     @Test
+    public void testPackageRemovedWithReplacingBroadcastHandling() throws Exception {
+        mWifiServiceImpl.checkAndStartWifi();
+        mLooper.dispatchAll();
+        verify(mContext)
+                .registerReceiver(
+                        mBroadcastReceiverCaptor.capture(),
+                        argThat(
+                                (IntentFilter filter) ->
+                                        filter.hasAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
+                                                && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+                                                && filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)),
+                        isNull(),
+                        any(Handler.class));
+        int uid = TEST_UID;
+        String packageName = TEST_PACKAGE_NAME;
+        // Send the broadcast
+        Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        intent.setData(Uri.fromParts("package", packageName, ""));
+        intent.putExtra(Intent.EXTRA_REPLACING, true);
+        mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent);
+        mLooper.dispatchAll();
+        verify(mWifiConfigManager, never()).removeNetworksForApp(any());
+        verify(mScanRequestProxy, never()).clearScanRequestTimestampsForApp(anyString(), anyInt());
+        verify(mWifiNetworkSuggestionsManager, never()).removeApp(anyString());
+        verify(mWifiNetworkFactory, never()).removeApp(anyString());
+        verify(mPasspointManager, never()).removePasspointProviderWithPackage(anyString());
+    }
+
+    @Test
     public void testPackageDisableBroadcastHandling() throws Exception {
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
@@ -6418,6 +6495,7 @@
         verify(mWifiScoreCard).clear();
         verify(mWifiHealthMonitor).clear();
         verify(mPasspointManager).getProviderConfigs(anyInt(), anyBoolean());
+        verify(mContext).resetResourceCache();
     }
 
     /**
@@ -7243,6 +7321,8 @@
      */
     @Test
     public void testGetFactoryMacAddresses() throws Exception {
+        when(mWifiGlobals.isSaveFactoryMacToConfigStoreEnabled()).thenReturn(true);
+        when(mWifiSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS)).thenReturn(null);
         when(mClientModeManager.getFactoryMacAddress()).thenReturn(TEST_FACTORY_MAC);
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
         mLooper.startAutoDispatch();
@@ -7251,6 +7331,7 @@
         assertEquals(1, factoryMacs.length);
         assertEquals(TEST_FACTORY_MAC, factoryMacs[0]);
         verify(mClientModeManager).getFactoryMacAddress();
+        verify(mWifiSettingsConfigStore).get(WIFI_STA_FACTORY_MAC_ADDRESS);
     }
 
     /**
@@ -7300,6 +7381,20 @@
         }
     }
 
+    @Test
+    public void testGetFactoryMacAddressesSuccessFromSettingStore() throws Exception {
+        when(mWifiGlobals.isSaveFactoryMacToConfigStoreEnabled()).thenReturn(true);
+        when(mWifiSettingsConfigStore.get(WIFI_STA_FACTORY_MAC_ADDRESS))
+                .thenReturn(TEST_FACTORY_MAC);
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        when(mClientModeManager.getFactoryMacAddress()).thenReturn(TEST_FACTORY_MAC);
+        final String[] factoryMacs = mWifiServiceImpl.getFactoryMacAddresses();
+        assertEquals(1, factoryMacs.length);
+        assertEquals(TEST_FACTORY_MAC, factoryMacs[0]);
+        mLooper.dispatchAll();
+        verify(mClientModeManager, never()).getFactoryMacAddress();
+    }
+
     /**
      * Verify that a call to setDeviceMobilityState throws a SecurityException if the
      * caller does not have WIFI_SET_DEVICE_MOBILITY_STATE permission.
@@ -7626,7 +7721,6 @@
         verify(mPasspointManager, never())
                 .addOrUpdateProvider(any(), anyInt(), anyString(), anyBoolean(), anyBoolean(),
                         eq(false));
-
     }
 
     /**
@@ -7735,6 +7829,42 @@
     }
 
     /**
+     * Verify that addOrUpdatePasspointConfiguration disconnects from current captive portal network
+     * when provisioned via WifiInstaller.
+     */
+    @Test
+    public void addOrUpdatePasspointConfigDisconnectsCaptivePortal() throws Exception {
+        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+                .noteOp(AppOpsManager.OPSTR_CHANGE_WIFI_STATE,
+                        Process.myUid(), CERT_INSTALLER_PACKAGE_NAME);
+        when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(),
+                eq(Build.VERSION_CODES.R), anyInt())).thenReturn(false);
+        when(mWifiPermissionsUtil.isSystem(anyString(), anyInt())).thenReturn(true);
+        PasspointConfiguration config = new PasspointConfiguration();
+        HomeSp homeSp = new HomeSp();
+        homeSp.setFqdn("test.com");
+        config.setHomeSp(homeSp);
+        WifiConfiguration captivePortalConfig =
+                WifiConfigurationTestUtil.createCaptivePortalNetwork();
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(captivePortalConfig.networkId);
+        when(mActiveModeWarden.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mWifiConfigManager.getConfiguredNetworkWithPassword(captivePortalConfig.networkId))
+                .thenReturn(captivePortalConfig);
+
+        when(mPasspointManager.addOrUpdateProvider(
+                config, Binder.getCallingUid(), CERT_INSTALLER_PACKAGE_NAME, false, true, false))
+                .thenReturn(true);
+        mLooper.startAutoDispatch();
+        assertTrue(mWifiServiceImpl.addOrUpdatePasspointConfiguration(config,
+                CERT_INSTALLER_PACKAGE_NAME));
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
+        verify(mPasspointManager).addOrUpdateProvider(any(), anyInt(), anyString(), anyBoolean(),
+                anyBoolean(), eq(false));
+        verify(mClientModeManager).disconnect();
+    }
+
+    /**
      * Verify that addOrUpdatePasspointConfiguration is not allowed for apps targeting below R SDK
      * when the DISALLOW_ADD_WIFI_CONFIG user restriction is set.
      */
@@ -8469,18 +8599,25 @@
                 eq(mSuggestionConnectionStatusListener), eq(TEST_PACKAGE_NAME), anyInt());
     }
 
-
     /**
-     * Test to verify that the lock mode is verified before dispatching the operation
+     * Test to verify that the arguments are verified before dispatching the operation
      *
-     * Steps: call acquireWifiLock with an invalid lock mode.
-     * Expected: the call should throw an IllegalArgumentException.
+     * Steps: call acquireWifiLock with invalid arguments.
+     * Expected: the call should throw proper Exception.
      */
-    @Test(expected = IllegalArgumentException.class)
-    public void acquireWifiLockShouldThrowExceptionOnInvalidLockMode() throws Exception {
+    @Test
+    public void acquireWifiLockShouldThrowExceptionOnInvalidArgs() {
         final int wifiLockModeInvalid = -1;
 
-        mWifiServiceImpl.acquireWifiLock(mAppBinder, wifiLockModeInvalid, "", null);
+        // Package name is null.
+        assertThrows(NullPointerException.class,
+                () -> mWifiServiceImpl.acquireWifiLock(mAppBinder,
+                        WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", null, null, null));
+
+        // Invalid Lock mode.
+        assertThrows(IllegalArgumentException.class,
+                () -> mWifiServiceImpl.acquireWifiLock(mAppBinder, wifiLockModeInvalid, "",
+                        new WorkSource(TEST_UID, TEST_PACKAGE_NAME), TEST_PACKAGE_NAME, null));
     }
 
     private void setupReportActivityInfo() {
@@ -8530,9 +8667,8 @@
     @Test
     public void getWifiActivityEnergyInfoAsyncFeatureUnsupported() throws Exception {
         when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0L);
-        mLooper.startAutoDispatch();
+
         mWifiServiceImpl.getWifiActivityEnergyInfoAsync(mOnWifiActivityEnergyInfoListener);
-        mLooper.stopAutoDispatch();
         verify(mOnWifiActivityEnergyInfoListener).onWifiActivityEnergyInfo(null);
     }
 
@@ -8544,9 +8680,8 @@
     public void getWifiActivityEnergyInfoAsyncSuccess() throws Exception {
         when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(Long.MAX_VALUE);
         setupReportActivityInfo();
-        mLooper.startAutoDispatch();
         mWifiServiceImpl.getWifiActivityEnergyInfoAsync(mOnWifiActivityEnergyInfoListener);
-        mLooper.stopAutoDispatch();
+        mLooper.dispatchAll();
         ArgumentCaptor<WifiActivityEnergyInfo> infoCaptor =
                 ArgumentCaptor.forClass(WifiActivityEnergyInfo.class);
         verify(mOnWifiActivityEnergyInfoListener).onWifiActivityEnergyInfo(infoCaptor.capture());
@@ -8707,7 +8842,7 @@
         mWifiServiceImpl.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer);
         mLooper.stopAutoDispatch();
         verify(mActiveModeWarden).setWifiConnectedNetworkScorer(
-                mAppBinder, mWifiConnectedNetworkScorer);
+                mAppBinder, mWifiConnectedNetworkScorer, myUid());
     }
 
     /**
@@ -9023,9 +9158,11 @@
     public void testDumpShouldDumpWakeupController() {
         mWifiServiceImpl.checkAndStartWifi();
         mLooper.dispatchAll();
+        mLooper.startAutoDispatch();
         mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
-        mLooper.dispatchAll();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
         verify(mWakeupController).dump(any(), any(), any());
+        verify(mPasspointNetworkNominateHelper).dump(any());
     }
 
     /**
@@ -9261,9 +9398,8 @@
      */
     @Test
     public void testSetWifiScoringEnabledGoesToSettingsStore() {
-        mLooper.startAutoDispatch();
-        mWifiServiceImpl.setWifiScoringEnabled(true);
-        mLooper.stopAutoDispatch();
+        when(mSettingsStore.handleWifiScoringEnabled(anyBoolean())).thenReturn(true);
+        assertTrue(mWifiServiceImpl.setWifiScoringEnabled(true));
         verify(mSettingsStore).handleWifiScoringEnabled(true);
     }
 
@@ -9645,8 +9781,10 @@
         when(mWifiCountryCode.getCountryCode()).thenReturn(TEST_COUNTRY_CODE);
 
         // No values stored
+        mLooper.startAutoDispatch();
         assertThat(mWifiServiceImpl.getUsableChannels(WIFI_BAND_24_GHZ, OP_MODE_SAP,
                 FILTER_REGULATORY, TEST_PACKAGE_NAME, mExtras)).isEmpty();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         // Country code doesn't match
         when(mWifiSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE))
@@ -9654,17 +9792,22 @@
         when(mWifiSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ))
                 .thenReturn("[2452,5180,5955,58320]");
         when(mWifiCountryCode.getCountryCode()).thenReturn(TEST_NEW_COUNTRY_CODE);
+        mLooper.startAutoDispatch();
         assertThat(mWifiServiceImpl.getUsableChannels(WIFI_BAND_24_GHZ, OP_MODE_SAP,
                 FILTER_REGULATORY, TEST_PACKAGE_NAME, mExtras)).isEmpty();
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
 
         // Matching country code
         when(mWifiCountryCode.getCountryCode()).thenReturn(TEST_COUNTRY_CODE);
+
+        mLooper.startAutoDispatch();
         assertThat(mWifiServiceImpl.getUsableChannels(WIFI_BAND_24_5_WITH_DFS_6_60_GHZ, OP_MODE_SAP,
                 FILTER_REGULATORY, TEST_PACKAGE_NAME, mExtras)).containsExactly(
                 new WifiAvailableChannel(2452, WifiAvailableChannel.OP_MODE_SAP),
                 new WifiAvailableChannel(5180, WifiAvailableChannel.OP_MODE_SAP),
                 new WifiAvailableChannel(5955, WifiAvailableChannel.OP_MODE_SAP),
                 new WifiAvailableChannel(58320, WifiAvailableChannel.OP_MODE_SAP));
+        mLooper.stopAutoDispatchAndIgnoreExceptions();
     }
 
     /**
@@ -10609,7 +10752,7 @@
         when(attributionSource.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
         WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
         config.networkId = 1;
-        mWifiThreadRunner.prepareForAutoDispatch();
+
         mLooper.startAutoDispatch();
         mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, mAttribution);
         mLooper.stopAutoDispatch();
@@ -10633,7 +10776,6 @@
                 .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
         WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
 
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, null);
         mLooper.stopAutoDispatch();
@@ -10667,7 +10809,6 @@
 
         WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
 
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, mAttribution);
         mLooper.stopAutoDispatch();
@@ -10705,7 +10846,6 @@
         WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
         config.networkId = TEST_NETWORK_ID;
 
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, mAttribution);
         mLooper.stopAutoDispatch();
@@ -10742,7 +10882,6 @@
 
         WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(TEST_SSID);
 
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         mWifiServiceImpl.addOrUpdateNetwork(config, TEST_PACKAGE_NAME, mAttribution);
         mLooper.stopAutoDispatch();
@@ -11458,7 +11597,6 @@
                 true);
 
         // Verify setMloMode() success.
-        mWifiThreadRunner.prepareForAutoDispatch();
         when(mWifiNative.setMloMode(eq(WifiManager.MLO_MODE_LOW_POWER))).thenReturn(
                 WifiStatusCode.SUCCESS);
         mWifiServiceImpl.setMloMode(WifiManager.MLO_MODE_LOW_POWER, listener);
@@ -11466,7 +11604,6 @@
         inOrder.verify(listener).onResult(true);
 
         // Verify setMloMode() failure case.
-        mWifiThreadRunner.prepareForAutoDispatch();
         mLooper.startAutoDispatch();
         when(mWifiNative.setMloMode(eq(WifiManager.MLO_MODE_HIGH_THROUGHPUT))).thenReturn(
                 WifiStatusCode.ERROR_INVALID_ARGS);
@@ -11664,4 +11801,94 @@
         when(mWifiGlobals.isBackgroundScanSupported()).thenReturn(true);
         assertTrue(mWifiServiceImpl.isPnoSupported());
     }
+
+    /**
+     * Verify that on a country code change, we inform the AFC Manager.
+     */
+    @Test
+    public void testInformAfcManagerOnCountryCodeChange() {
+        final String newCountryCode = "US";
+        mWifiServiceImpl.mCountryCodeTracker.onDriverCountryCodeChanged(newCountryCode);
+        mLooper.dispatchAll();
+
+        // check that we let the AFC Manager know that the country code has changed
+        verify(mAfcManager).onCountryCodeChange(newCountryCode);
+    }
+
+    @Test
+    public void testShutDownHandling() {
+        mWifiServiceImpl.checkAndStartWifi();
+        mWifiServiceImpl.handleBootCompleted();
+        mLooper.dispatchAll();
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                argThat((IntentFilter filter) ->
+                        filter.hasAction(ACTION_SHUTDOWN)),
+                isNull(),
+                any(Handler.class));
+        Intent intent = new Intent(ACTION_SHUTDOWN);
+        mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent);
+        verify(mActiveModeWarden).notifyShuttingDown();
+        verify(mWifiScoreCard).resetAllConnectionStates();
+        verify(mWifiConfigManager).handleShutDown();
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSetWepAllowedWithoutPermission() {
+        // by default no permissions are given so the call should fail.
+        mWifiServiceImpl.setWepAllowed(true);
+    }
+
+    @Test
+    public void testSetWepAllowedWithPermission() {
+        when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(false);
+        mWifiServiceImpl.checkAndStartWifi();
+        mLooper.dispatchAll();
+        verify(mWifiSettingsConfigStore).get(eq(WIFI_WEP_ALLOWED));
+        verify(mWifiGlobals).setWepAllowed(eq(false));
+        // verify setWepAllowed with MANAGE_WIFI_NETWORK_SELECTION
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        mWifiServiceImpl.setWepAllowed(true);
+        mLooper.dispatchAll();
+        verify(mWifiGlobals).setWepAllowed(true);
+        verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(true));
+
+        mWifiServiceImpl.setWepAllowed(false);
+        mLooper.dispatchAll();
+        verify(mWifiGlobals, times(2)).setWepAllowed(false);
+        verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(false));
+    }
+
+    @Test
+    public void testQueryWepAllowedExceptionCases() {
+        // null listener ==> IllegalArgumentException
+        assertThrows(IllegalArgumentException.class,
+                () -> mWifiServiceImpl.queryWepAllowed(null));
+        // by default no permissions are given so the call should fail.
+        assertThrows(SecurityException.class,
+                () -> mWifiServiceImpl.queryWepAllowed(mock(IBooleanListener.class)));
+    }
+
+    @Test
+    public void testQueryWepAllowedNormalCase() throws Exception {
+        when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(false);
+        mWifiServiceImpl.checkAndStartWifi();
+        mLooper.dispatchAll();
+        verify(mWifiSettingsConfigStore).get(eq(WIFI_WEP_ALLOWED));
+        verify(mWifiGlobals).setWepAllowed(eq(false));
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        IBooleanListener listener = mock(IBooleanListener.class);
+
+        InOrder inOrder = inOrder(listener);
+        when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(true);
+        mWifiServiceImpl.queryWepAllowed(listener);
+        mLooper.dispatchAll();
+        verify(mWifiSettingsConfigStore, times(2)).get(eq(WIFI_WEP_ALLOWED));
+        inOrder.verify(listener).onResult(true);
+
+        when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(false);
+        mWifiServiceImpl.queryWepAllowed(listener);
+        mLooper.dispatchAll();
+        verify(mWifiSettingsConfigStore, times(3)).get(eq(WIFI_WEP_ALLOWED));
+        inOrder.verify(listener).onResult(false);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java
index f0767e2..07de187 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java
@@ -36,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -49,11 +50,13 @@
 import android.net.NetworkRequest;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiAvailableChannel;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiContext;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiNetworkSpecifier;
 import android.net.wifi.WifiNetworkSuggestion;
+import android.net.wifi.WifiScanner;
 import android.os.Binder;
 import android.os.PatternMatcher;
 import android.os.Process;
@@ -101,6 +104,7 @@
     @Mock ScanRequestProxy mScanRequestProxy;
     @Mock WifiDiagnostics mWifiDiagnostics;
     @Mock DeviceConfigFacade mDeviceConfig;
+    @Mock WifiScanner mWifiScanner;
 
     WifiShellCommand mWifiShellCommand;
 
@@ -127,6 +131,7 @@
         when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
         when(mWifiInjector.getWifiDiagnostics()).thenReturn(mWifiDiagnostics);
         when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfig);
+        when(mContext.getSystemService(WifiScanner.class)).thenReturn(mWifiScanner);
 
         mWifiShellCommand = new WifiShellCommand(mWifiInjector, mWifiService, mContext,
                 mWifiGlobals, mWifiThreadRunner);
@@ -1003,4 +1008,37 @@
                 new FileDescriptor(), new String[]{"take-bugreport"});
         verify(mWifiDiagnostics).takeBugReport("Wifi bugreport test", "");
     }
+
+    @Test
+    public void testGetAllowedChannel() {
+        assumeTrue(SdkLevel.isAtLeastS());
+        BinderUtil.setUid(Process.ROOT_UID);
+        doThrow(UnsupportedOperationException.class).when(mWifiService).getUsableChannels(
+                eq(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ), eq(WifiAvailableChannel.OP_MODE_STA),
+                eq(WifiAvailableChannel.FILTER_REGULATORY), eq(SHELL_PACKAGE_NAME), any());
+        mWifiShellCommand.exec(new Binder(), new FileDescriptor(), new FileDescriptor(),
+                new FileDescriptor(), new String[]{"get-allowed-channel", "-b",
+                        String.valueOf(WifiScanner.WIFI_BAND_24_GHZ)});
+        verify(mWifiScanner).getAvailableChannels(eq(WifiScanner.WIFI_BAND_24_GHZ));
+
+        when(mWifiService.getUsableChannels(eq(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_GHZ),
+                eq(WifiAvailableChannel.OP_MODE_STA), eq(WifiAvailableChannel.FILTER_REGULATORY),
+                eq(SHELL_PACKAGE_NAME), any())).thenReturn(null);
+        doThrow(IllegalArgumentException.class).when(mWifiService).getUsableChannels(
+                eq(WifiScanner.WIFI_BAND_BOTH), anyInt(),
+                eq(WifiAvailableChannel.FILTER_REGULATORY), eq(SHELL_PACKAGE_NAME), any());
+        mWifiShellCommand.exec(new Binder(), new FileDescriptor(), new FileDescriptor(),
+                new FileDescriptor(), new String[]{"get-allowed-channel", "-b",
+                        String.valueOf(WifiScanner.WIFI_BAND_BOTH)});
+        verify(mWifiService, never()).getUsableChannels(eq(WifiScanner.WIFI_BAND_BOTH),
+                eq(WifiAvailableChannel.OP_MODE_SAP), eq(WifiAvailableChannel.FILTER_REGULATORY),
+                eq(SHELL_PACKAGE_NAME), any());
+
+        mWifiShellCommand.exec(new Binder(), new FileDescriptor(), new FileDescriptor(),
+                new FileDescriptor(), new String[]{"get-allowed-channel", "-b",
+                        String.valueOf(WifiScanner.WIFI_BAND_BOTH_WITH_DFS)});
+        verify(mWifiService, times(6)).getUsableChannels(eq(WifiScanner.WIFI_BAND_BOTH_WITH_DFS),
+                anyInt(), eq(WifiAvailableChannel.FILTER_REGULATORY), eq(SHELL_PACKAGE_NAME),
+                any());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 106cb03..80ab460 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
@@ -134,6 +135,8 @@
     private SoftApManager mSoftApManager;
     @Mock
     private SsidTranslator mSsidTranslator;
+    @Mock
+    WifiChip.AfcChannelAllowance mAfcChannelAllowance;
 
     private ArgumentCaptor<List> mListCaptor = ArgumentCaptor.forClass(List.class);
 
@@ -171,7 +174,7 @@
         when(mHalDeviceManager.createStaIface(any(), any(), any(), eq(mConcreteClientModeManager)))
                 .thenReturn(mWifiStaIface);
         when(mHalDeviceManager.createApIface(anyLong(), any(), any(), any(), anyBoolean(),
-                eq(mSoftApManager)))
+                eq(mSoftApManager), anyList()))
                 .thenReturn(mWifiApIface);
         when(mHalDeviceManager.removeIface(any())).thenReturn(true);
         when(mHalDeviceManager.getChip(any(WifiHal.WifiInterface.class)))
@@ -251,7 +254,7 @@
         verify(mWifiChip).registerCallback(any(WifiChip.Callback.class));
 
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
     }
 
     /**
@@ -275,7 +278,7 @@
         verify(mHalDeviceManager, never()).createStaIface(any(), any(), any(),
                 eq(mConcreteClientModeManager));
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
         verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class));
         verify(mWifiStaIface, never())
                 .registerFrameworkCallback(any(WifiStaIface.Callback.class));
@@ -298,7 +301,7 @@
         verify(mHalDeviceManager).stop();
 
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
         verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class));
         verify(mWifiStaIface, never())
                 .registerFrameworkCallback(any(WifiStaIface.Callback.class));
@@ -322,7 +325,7 @@
         verify(mWifiStaIface).registerFrameworkCallback(any(WifiStaIface.Callback.class));
 
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
     }
 
     /**
@@ -344,7 +347,7 @@
 
         verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class));
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
     }
 
     /**
@@ -367,7 +370,7 @@
         verify(mWifiChip).registerCallback(any(WifiChip.Callback.class));
 
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
     }
 
     /**
@@ -391,7 +394,7 @@
         verify(mHalDeviceManager, times(2)).isStarted();
 
         verify(mHalDeviceManager, never()).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
     }
 
     /**
@@ -402,7 +405,7 @@
     public void testStopHalInApMode() {
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(null, null,
-                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
 
         assertTrue(mWifiVendorHal.isHalStarted());
 
@@ -412,7 +415,7 @@
         verify(mHalDeviceManager).start();
         verify(mHalDeviceManager).stop();
         verify(mHalDeviceManager).createApIface(
-                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager));
+                anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList());
         verify(mHalDeviceManager).getChip(eq(mWifiApIface));
         verify(mHalDeviceManager, times(2)).isReady();
         verify(mHalDeviceManager, times(2)).isStarted();
@@ -466,13 +469,13 @@
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(
                 externalLister, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false,
-                mSoftApManager));
+                mSoftApManager, new ArrayList<>()));
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
         verify(mHalDeviceManager).createApIface(anyLong(),
                 internalListenerCaptor.capture(), any(), eq(TEST_WORKSOURCE), eq(false),
-                eq(mSoftApManager));
+                eq(mSoftApManager), anyList());
         verify(mHalDeviceManager).getChip(eq(mWifiApIface));
         verify(mHalDeviceManager).isReady();
         verify(mHalDeviceManager).isStarted();
@@ -624,7 +627,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(null, null,
-                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
         assertTrue(mWifiVendorHal.isHalStarted());
         assertNull(mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME));
 
@@ -821,7 +824,7 @@
         assertFalse(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 0, 0, "One"));
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(null, null,
-                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
         assertTrue(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 11, 3000, "One"));
 
         verify(mWifiChip).startLoggingToDebugRingBuffer("One", 1, 11, 3000);
@@ -1133,7 +1136,7 @@
         // This should work in both AP & STA mode.
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(null, null,
-                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
 
         assertNull(mWifiVendorHal.getWlanWakeReasonCount());
         verify(mWifiChip).getDebugHostWakeReasonStats();
@@ -1163,7 +1166,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNotNull(mWifiVendorHal.createApIface(null, null,
-                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
         assertArrayEquals(sample, mWifiVendorHal.getDriverStateDump());
     }
 
@@ -1460,9 +1463,11 @@
         when(mWifiApIface.getName()).thenReturn(null);
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNull(mWifiVendorHal.createApIface(
-                null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager));
+                null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager,
+                new ArrayList<>()));
         verify(mHalDeviceManager).createApIface(
-                anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager));
+                anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager),
+                anyList());
     }
 
     /**
@@ -1487,9 +1492,11 @@
     public void testCreateRemoveApIface() throws RemoteException {
         assertTrue(mWifiVendorHal.startVendorHal());
         String ifaceName = mWifiVendorHal.createApIface(
-                null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager);
+                null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager,
+                new ArrayList<>());
         verify(mHalDeviceManager).createApIface(
-                anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager));
+                anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager),
+                anyList());
         assertEquals(TEST_IFACE_NAME, ifaceName);
         assertTrue(mWifiVendorHal.removeApIface(ifaceName));
         verify(mHalDeviceManager).removeIface(eq(mWifiApIface));
@@ -1793,4 +1800,17 @@
         assertEquals(-1, mWifiVendorHal.getMaxMloAssociationLinkCount(TEST_IFACE_NAME));
         assertEquals(-1, mWifiVendorHal.getMaxSupportedConcurrentTdlsSessions(TEST_IFACE_NAME));
     }
+
+    /**
+     * Verifies that setAfcChannelAllowance() calls underlying WifiChip.
+     */
+    @Test
+    public void testSetAfcChannelAllowance() {
+        assertTrue(mWifiVendorHal.startVendorHal());
+        assertNotNull(mWifiVendorHal.createApIface(null, null,
+                SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()));
+
+        mWifiVendorHal.setAfcChannelAllowance(mAfcChannelAllowance);
+        verify(mWifiChip).setAfcChannelAllowance(mAfcChannelAllowance);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index 384bf08..b5db379 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.app.StatsManager;
 import android.app.test.TestAlarmManager;
 import android.content.Context;
 import android.content.Intent;
@@ -84,7 +85,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
-import android.util.LocalLog;
 
 import androidx.test.filters.SmallTest;
 
@@ -158,12 +158,12 @@
     @Mock private PowerManager mMockPowerManager;
     @Mock private WifiInjector mWifiInjector;
     @Mock private PairingConfigManager mPairingConfigManager;
+    @Mock private StatsManager mStatsManager;
 
     @Rule
     public ErrorCollector collector = new ErrorCollector();
     private MockResources mResources;
     private Bundle mExtras = new Bundle();
-    private LocalLog mLocalLog = new LocalLog(512);
 
     /**
      * Initialize mocks.
@@ -187,6 +187,7 @@
         when(mMockContext.getSystemServiceName(PowerManager.class)).thenReturn(
                 Context.POWER_SERVICE);
         when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
+        when(mMockContext.getSystemService(StatsManager.class)).thenReturn(mStatsManager);
 
         when(mInterfaceConflictManager.manageInterfaceConflictForStateMachine(any(), any(), any(),
                 any(), any(), eq(HalDeviceManager.HDM_CREATE_IFACE_NAN), any(), anyBoolean()))
@@ -201,7 +202,6 @@
         if (SdkLevel.isAtLeastS()) {
             when(mWifiPermissionsUtil.getWifiCallerType(any())).thenReturn(6);
         }
-        when(mWifiInjector.getWifiAwareLocalLog()).thenReturn(mLocalLog);
 
         mDut = new WifiAwareStateManager(mWifiInjector, mPairingConfigManager);
         mDut.setNative(mMockNativeManager, mMockNative);
@@ -209,7 +209,7 @@
                 mWifiPermissionsUtil, mPermissionsWrapperMock, mClock, mMockNetdWrapper,
                 mInterfaceConflictManager);
         mDut.startLate();
-        mDut.enableVerboseLogging(true, true);
+        mDut.enableVerboseLogging(true, true , true);
         mMockLooper.dispatchAll();
 
         when(mMockNetworkInterface.configureAgentProperties(any(), any(), any())).thenReturn(true);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java
index 5dce2e8..e7cb58e 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareMetricsTest.java
@@ -265,28 +265,28 @@
         // uid1: session 1
         clients.put(10,
                 new WifiAwareClientState(mMockContext, 10, uid1, 0, null, null, null, null, false,
-                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null, mLocalLog,
+                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null,
                         false, 6));
         mDut.recordAttachSession(uid1, false, clients, 6, tag1);
 
         // uid1: session 2
         clients.put(11,
                 new WifiAwareClientState(mMockContext, 11, uid1, 0, null, null, null, null, false,
-                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null, mLocalLog,
+                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null,
                         false, 6));
         mDut.recordAttachSession(uid1, false, clients, 6, tag1);
 
         // uid2: session 1
         clients.put(12,
                 new WifiAwareClientState(mMockContext, 12, uid2, 0, null, null, null, null, false,
-                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null, mLocalLog,
+                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null,
                         false, 6));
         mDut.recordAttachSession(uid2, false, clients, 6, tag2);
 
         // uid2: session 2
         clients.put(13,
                 new WifiAwareClientState(mMockContext, 13, uid2, 0, null, null, null, null, true,
-                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null, mLocalLog,
+                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null,
                         false, 6));
         mDut.recordAttachSession(uid2, true, clients, 6, tag2);
 
@@ -303,7 +303,7 @@
         // uid2: session 3
         clients.put(14,
                 new WifiAwareClientState(mMockContext, 14, uid2, 0, null, null, null, null, false,
-                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null, mLocalLog,
+                        mClock.getElapsedSinceBootMillis(), mWifiPermissionsUtil, null,
                         false, 6));
         mDut.recordAttachSession(uid2, false, clients, 6, tag2);
 
@@ -376,11 +376,11 @@
 
         setTime(5);
         WifiAwareClientState client1 = new WifiAwareClientState(mMockContext, 10, uid1, 0, null,
-                null, null, null, false, 0, mWifiPermissionsUtil, null, mLocalLog, false, 6);
+                null, null, null, false, 0, mWifiPermissionsUtil, null, false, 6);
         WifiAwareClientState client2 = new WifiAwareClientState(mMockContext, 11, uid2, 0, null,
-                null, null, null, false, 0, mWifiPermissionsUtil, null, mLocalLog, false, 6);
+                null, null, null, false, 0, mWifiPermissionsUtil, null, false, 6);
         WifiAwareClientState client3 = new WifiAwareClientState(mMockContext, 12, uid3, 0, null,
-                null, null, null, false, 0, mWifiPermissionsUtil, null, mLocalLog, false, 6);
+                null, null, null, false, 0, mWifiPermissionsUtil, null, false, 6);
         clients.put(10, client1);
         clients.put(11, client2);
         clients.put(12, client3);
@@ -398,7 +398,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySession(uid1, clients);
         mDut.recordDiscoveryStatus(uid1, NanStatusCode.SUCCESS, true, 100, 6, tag1);
@@ -416,7 +415,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySession(uid1, clients);
         mDut.recordDiscoveryStatus(uid1, NanStatusCode.SUCCESS, true, 101, 6, tag1);
@@ -434,7 +432,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySessionWithRanging(uid3, false, -1, -1, clients);
         mDut.recordDiscoveryStatus(uid3, NanStatusCode.SUCCESS, true, 111, 6, tag3);
@@ -452,7 +449,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySession(uid2, clients);
         mDut.recordDiscoveryStatus(uid2, NanStatusCode.SUCCESS, false, 102, 6, tag2);
@@ -470,7 +466,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySession(uid2, clients);
         mDut.recordDiscoveryStatus(uid2, NanStatusCode.SUCCESS, false, 103, 6, tag2);
@@ -488,7 +483,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySessionWithRanging(uid3, true, 10, -1, clients);
         mDut.recordDiscoveryStatus(uid3, NanStatusCode.SUCCESS, false, 112, 6, tag3);
@@ -506,7 +500,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySessionWithRanging(uid3, true, -1, 50, clients);
         mDut.recordDiscoveryStatus(uid3, NanStatusCode.SUCCESS, false, 113, 6, tag3);
@@ -524,7 +517,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
         mDut.recordDiscoverySessionWithRanging(uid3, true, 0, 110, clients);
         mDut.recordDiscoveryStatus(uid3, NanStatusCode.SUCCESS, false, 114, 6, tag3);
@@ -555,7 +547,6 @@
                         /* instantModeEnabled= */ false,
                         /* instantModeBand= */ 0,
                         /* isSuspendable= */ false,
-                        mLocalLog,
                         /* pairingConfig= */ null));
 
         // a few failures
diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
index a001897..2dc4e26 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
@@ -18,7 +18,7 @@
 
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128;
 
-import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED;
+import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_AWARE_VERBOSE_LOGGING_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
@@ -159,7 +159,8 @@
         when(mDeviceConfigFacade.isAwareSuspensionEnabled()).thenReturn(true);
         Characteristics characteristics = getCharacteristics(mDeviceConfigFacade);
         when(mAwareStateManagerMock.getCharacteristics()).thenReturn(characteristics);
-        when(mWifiSettingsConfigStore.get(eq(WIFI_VERBOSE_LOGGING_ENABLED))).thenReturn(true);
+        when(mWifiSettingsConfigStore.get(WIFI_AWARE_VERBOSE_LOGGING_ENABLED))
+                .thenReturn(true);
         // mock target SDK version to be pre-T by default to keep existing tests working.
         when(mWifiPermissionsUtil.isTargetSdkLessThan(any(), eq(Build.VERSION_CODES.TIRAMISU),
                 anyInt())).thenReturn(true);
@@ -767,7 +768,7 @@
 
         Characteristics characteristics = cap.toPublicCharacteristics(mDeviceConfigFacade);
         assertEquals(characteristics.getMaxServiceNameLength(), maxServiceName);
-        assertEquals(characteristics.getMaxServiceSpecificInfoLength(), maxServiceSpecificInfo);
+        assertEquals(characteristics.getMaxServiceSpecificInfoLength(), MAX_LENGTH);
         assertEquals(characteristics.getMaxMatchFilterLength(), maxMatchFilter);
         assertEquals(characteristics.getSupportedCipherSuites(),
                 Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_256);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
index cb17eb4..130fbf1 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
@@ -20,6 +20,8 @@
 import static android.net.wifi.WifiAvailableChannel.OP_MODE_WIFI_AWARE;
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_PK_PASN_128;
 
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_AWARE_CAPABILITIES;
+
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsNull.notNullValue;
 import static org.hamcrest.core.IsNull.nullValue;
@@ -51,6 +53,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.app.StatsManager;
 import android.app.test.MockAnswerUtil;
 import android.app.test.TestAlarmManager;
 import android.content.AttributionSource;
@@ -89,12 +92,13 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.test.TestLooper;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.modules.utils.build.SdkLevel;
@@ -108,6 +112,7 @@
 import com.android.server.wifi.WifiThreadRunner;
 import com.android.server.wifi.hal.WifiNanIface.NanRangingIndication;
 import com.android.server.wifi.hal.WifiNanIface.NanStatusCode;
+import com.android.server.wifi.proto.WifiStatsLog;
 import com.android.server.wifi.util.NetdWrapper;
 import com.android.server.wifi.util.WaitingState;
 import com.android.server.wifi.util.WifiPermissionsUtil;
@@ -120,14 +125,17 @@
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.quality.Strictness;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -164,6 +172,7 @@
     @Mock private WifiAwareDataPathStateManager mMockAwareDataPathStatemanager;
     @Mock private WifiInjector mWifiInjector;
     @Mock private PairingConfigManager mPairingConfigManager;
+    @Mock private StatsManager mStatsManager;
 
     @Rule
     public ErrorCollector collector = new ErrorCollector();
@@ -173,20 +182,29 @@
     private Bundle mExtras = new Bundle();
     private WifiManager.ActiveCountryCodeChangedCallback mActiveCountryCodeChangedCallback;
     private HandlerThread mWifiHandlerThread;
-    private LocalLog mLocalLog = new LocalLog(512);
     private byte[] mNik = "0123456789012345".getBytes();
     private byte[] mPeerNik = "6789012345678901".getBytes();
     private byte[] mPmk = "01234567890123456789012345678901".getBytes();
+    private StaticMockitoSession mSession;
+
+    @Captor
+    ArgumentCaptor<StatsManager.StatsPullAtomCallback> mPullAtomCallbackArgumentCaptor;
+
     /**
      * Pre-test configuration. Initialize and install mocks.
      */
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mSession = ExtendedMockito.mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(WifiStatsLog.class)
+                .startMocking();
 
         mAlarmManager = new TestAlarmManager();
         when(mMockContext.getSystemService(Context.ALARM_SERVICE))
                 .thenReturn(mAlarmManager.getAlarmManager());
+        when(mMockContext.getSystemService(StatsManager.class)).thenReturn(mStatsManager);
 
         when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
         when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED);
@@ -230,14 +248,13 @@
         WifiThreadRunner wifiThreadRunner = new WifiThreadRunner(wifiHandler);
         when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
         when(mWifiInjector.getWifiThreadRunner()).thenReturn(wifiThreadRunner);
-        when(mWifiInjector.getWifiAwareLocalLog()).thenReturn(mLocalLog);
         mDut = new WifiAwareStateManager(mWifiInjector, mPairingConfigManager);
         mDut.setNative(mMockNativeManager, mMockNative);
         mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
                 mWifiPermissionsUtil, mPermissionsWrapperMock, new Clock(),
                 mock(NetdWrapper.class), mInterfaceConflictManager);
         mDut.startLate();
-        mDut.enableVerboseLogging(true, true);
+        mDut.enableVerboseLogging(true, true, true);
         mMockLooper.dispatchAll();
         ArgumentCaptor<WifiManager.ActiveCountryCodeChangedCallback> callbackArgumentCaptor =
                 ArgumentCaptor.forClass(WifiManager.ActiveCountryCodeChangedCallback.class);
@@ -246,8 +263,12 @@
                     callbackArgumentCaptor.capture());
             mActiveCountryCodeChangedCallback = callbackArgumentCaptor.getValue();
         }
-        verify(mMockContext, times(3)).registerReceiver(bcastRxCaptor.capture(),
-                any(IntentFilter.class));
+        verify(mMockContext, times(3))
+                .registerReceiver(
+                        bcastRxCaptor.capture(),
+                        any(IntentFilter.class),
+                        isNull(),
+                        any(Handler.class));
         mPowerBcastReceiver = bcastRxCaptor.getAllValues().get(0);
         mLocationModeReceiver = bcastRxCaptor.getAllValues().get(1);
         mWifiStateChangedReceiver = bcastRxCaptor.getAllValues().get(2);
@@ -260,6 +281,12 @@
         mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
         mMockLooper.dispatchAll();
         when(mMockAwareDataPathStatemanager.getNumOfNdps()).thenReturn(1);
+        verify(mStatsManager).setPullAtomCallback(eq(WIFI_AWARE_CAPABILITIES), isNull(), any(),
+                mPullAtomCallbackArgumentCaptor.capture());
+        assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue()
+                .onPullAtom(WIFI_AWARE_CAPABILITIES, new ArrayList<>()));
+        ExtendedMockito.verify(() -> WifiStatsLog.buildStatsEvent(
+                WIFI_AWARE_CAPABILITIES, true, true, true, 0, 1, 1, 2));
     }
 
     /**
@@ -267,6 +294,7 @@
      */
     @After
     public void tearDown() throws Exception {
+        mSession.finishMocking();
         mMockNative.validateUniqueTransactionIds();
         mWifiHandlerThread.quit();
     }
@@ -4466,7 +4494,9 @@
      * Validate the connection operation when user approval is required and the user accepts or
      * rejects the request.
      */
-    private void runTestConnectUserApproval(boolean userAcceptsRequest) throws Exception {
+    private void runTestConnectUserApproval(
+            boolean userAcceptsRequest,
+            boolean changedToOpportunistic) throws Exception {
         final int clientId = 1005;
         final int uid = 1000;
         final int pid = 2000;
@@ -4503,6 +4533,11 @@
                 any(), any(), mWaitingStateCaptor.capture(), mTargetStateCaptor.capture(),
                 eq(HalDeviceManager.HDM_CREATE_IFACE_NAN), any(), anyBoolean());
 
+        if (changedToOpportunistic) {
+            // calling package changed to opportunistic before the response was received.
+            mDut.setOpportunisticPackage(callingPackage, true);
+        }
+
         // simulate user approval triggered and granted/rejected (userAcceptsRequest)
         when(mInterfaceConflictManager.manageInterfaceConflictForStateMachine(any(), any(), any(),
                 any(), any(), eq(HalDeviceManager.HDM_CREATE_IFACE_NAN), any(), anyBoolean()))
@@ -4512,7 +4547,7 @@
         mMockLooper.dispatchAll();
         inOrder.verify(mInterfaceConflictManager).manageInterfaceConflictForStateMachine(any(),
                 any(), any(), any(), any(), eq(HalDeviceManager.HDM_CREATE_IFACE_NAN), any(),
-                anyBoolean());
+                eq(changedToOpportunistic));
 
         if (userAcceptsRequest) {
             verify(mMockNative).enableAndConfigure(anyShort(), eq(configRequest), eq(false),
@@ -5377,7 +5412,7 @@
      */
     @Test
     public void testConnectUserApprovalAccept() throws Exception {
-        runTestConnectUserApproval(true);
+        runTestConnectUserApproval(true, false);
     }
 
     /**
@@ -5386,7 +5421,25 @@
      */
     @Test
     public void testConnectUserApprovalReject() throws Exception {
-        runTestConnectUserApproval(false);
+        runTestConnectUserApproval(false, false);
+    }
+
+    /**
+     * Validate the connection operation when user approval is required and the user accepts the
+     * request after the request changes to opportunistic.
+     */
+    @Test
+    public void testConnectUserApprovalAcceptAfterChangingToOpportunistic() throws Exception {
+        runTestConnectUserApproval(true, true);
+    }
+
+    /**
+     * Validate the connection operation when user approval is required and the user rejects the
+     * request after the request changes to opportunistic.
+     */
+    @Test
+    public void testConnectUserApprovalRejectAfterChangingToOpportunistic() throws Exception {
+        runTestConnectUserApproval(false, true);
     }
 
     /*
@@ -5623,6 +5676,10 @@
         cap.maxQueuedTransmitMessages = 6;
         cap.isInstantCommunicationModeSupported = true;
         cap.isSuspensionSupported = true;
+        cap.isNanPairingSupported = true;
+        cap.isSetClusterIdSupported = true;
+        cap.isHeSupported = true;
+        cap.is6gSupported = true;
         return cap;
     }
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/entitlement/PseudonymInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/entitlement/PseudonymInfoTest.java
index d9166e6..a608780 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/entitlement/PseudonymInfoTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/entitlement/PseudonymInfoTest.java
@@ -53,10 +53,8 @@
         PseudonymInfo pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI, TTL_IN_MILLIS);
         assertEquals(PSEUDONYM, pseudonymInfo.getPseudonym());
         assertEquals(TTL_IN_MILLIS, pseudonymInfo.getTtlInMillis());
-        assertEquals(TTL_IN_MILLIS - REFRESH_AHEAD_TIME_IN_MILLIS,
-                pseudonymInfo.getAtrInMillis());
+        assertEquals(TTL_IN_MILLIS - REFRESH_AHEAD_TIME_IN_MILLIS, pseudonymInfo.getLttrInMillis());
         assertFalse(pseudonymInfo.hasExpired());
-        assertFalse(pseudonymInfo.shouldBeRefreshed());
         assertFalse(pseudonymInfo.isOldEnoughToRefresh());
     }
 
@@ -65,19 +63,17 @@
         PseudonymInfo pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI, TTL_IN_MILLIS,
                 Instant.now().toEpochMilli() - MINIMUM_REFRESH_INTERVAL_IN_MILLIS);
         assertFalse(pseudonymInfo.hasExpired());
-        assertFalse(pseudonymInfo.shouldBeRefreshed());
         assertTrue(pseudonymInfo.isOldEnoughToRefresh());
 
         pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI, TTL_IN_MILLIS,
                 Instant.now().toEpochMilli() - TTL_IN_MILLIS + REFRESH_AHEAD_TIME_IN_MILLIS);
         assertFalse(pseudonymInfo.hasExpired());
-        assertTrue(pseudonymInfo.shouldBeRefreshed());
         assertTrue(pseudonymInfo.isOldEnoughToRefresh());
 
         pseudonymInfo = new PseudonymInfo(PSEUDONYM, IMSI, TTL_IN_MILLIS,
                 Instant.now().toEpochMilli() - TTL_IN_MILLIS);
         assertTrue(pseudonymInfo.hasExpired());
-        assertTrue(pseudonymInfo.shouldBeRefreshed());
+        assertEquals(0, pseudonymInfo.getLttrInMillis());
         assertTrue(pseudonymInfo.isOldEnoughToRefresh());
     }
 
@@ -85,6 +81,6 @@
     public void pseudonymInfoWithSmallTtl() {
         PseudonymInfo pseudonymInfo =
                 new PseudonymInfo(PSEUDONYM, IMSI, TWENTY_MINUTES_IN_MILLIS);
-        assertTrue(pseudonymInfo.getAtrInMillis() >= TWENTY_MINUTES_IN_MILLIS / 2);
+        assertTrue(pseudonymInfo.getLttrInMillis() == TWENTY_MINUTES_IN_MILLIS / 2);
     }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java
index 543d2c0..ff62b05 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.wifi.AfcChannelAllowance;
 import android.hardware.wifi.IWifiChip;
 import android.hardware.wifi.WifiDebugHostWakeReasonRxIcmpPacketDetails;
 import android.hardware.wifi.WifiDebugHostWakeReasonRxMulticastPacketDetails;
@@ -50,10 +51,12 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
@@ -351,4 +354,86 @@
                 | IWifiChip.ChannelCategoryMask.DFS_CHANNEL;
         verify(mIWifiChipMock).enableStaChannelForPeerNetwork(eq(channelCategoryEnableFlag));
     }
+
+    /**
+     * Verifies that setAfcChannelAllowance() calls underlying WifiVendorHAL, and that the
+     * AfcChannelAllowance is correctly converted from the framework version to the HAL version.
+     */
+    @Test
+    public void testSetAfcChannelAllowance() throws RemoteException {
+        WifiChip.AfcChannelAllowance afcChannelAllowance = makeAfcChannelAllowance();
+        mDut.setAfcChannelAllowance(afcChannelAllowance);
+
+        ArgumentCaptor<AfcChannelAllowance> afcChannelAllowanceCaptor = ArgumentCaptor.forClass(
+                AfcChannelAllowance.class);
+        verify(mIWifiChipMock).setAfcChannelAllowance(afcChannelAllowanceCaptor.capture());
+        AfcChannelAllowance halAfcChannelAllowance = afcChannelAllowanceCaptor.getValue();
+
+        // Verify that the AFC allowance object was correctly converted from the framework version
+        // to the HAL version.
+        assertEquals(halAfcChannelAllowance.availableAfcFrequencyInfos.length,
+                afcChannelAllowance.availableAfcFrequencyInfos.size());
+        assertEquals(halAfcChannelAllowance.availableAfcChannelInfos.length,
+                afcChannelAllowance.availableAfcChannelInfos.size());
+        assertEquals(halAfcChannelAllowance.availabilityExpireTimeMs,
+                afcChannelAllowance.availabilityExpireTimeMs);
+
+        for (int i = 0; i < halAfcChannelAllowance.availableAfcFrequencyInfos.length; ++i) {
+            assertEquals(halAfcChannelAllowance.availableAfcFrequencyInfos[i].startFrequencyMhz,
+                    afcChannelAllowance.availableAfcFrequencyInfos.get(i).startFrequencyMhz);
+            assertEquals(halAfcChannelAllowance.availableAfcFrequencyInfos[i].endFrequencyMhz,
+                    afcChannelAllowance.availableAfcFrequencyInfos.get(i).endFrequencyMhz);
+            assertEquals(halAfcChannelAllowance.availableAfcFrequencyInfos[i].maxPsd,
+                    afcChannelAllowance.availableAfcFrequencyInfos.get(i).maxPsdDbmPerMhz);
+        }
+
+        for (int i = 0; i < halAfcChannelAllowance.availableAfcChannelInfos.length; ++i) {
+            assertEquals(halAfcChannelAllowance.availableAfcChannelInfos[i].globalOperatingClass,
+                    afcChannelAllowance.availableAfcChannelInfos.get(i).globalOperatingClass);
+            assertEquals(halAfcChannelAllowance.availableAfcChannelInfos[i].channelCfi,
+                    afcChannelAllowance.availableAfcChannelInfos.get(i).channelCfi);
+            assertEquals(halAfcChannelAllowance.availableAfcChannelInfos[i].maxEirpDbm,
+                    afcChannelAllowance.availableAfcChannelInfos.get(i).maxEirpDbm);
+        }
+    }
+
+    /**
+     * Creates and returns an object used to set the allowed AFC channels and frequencies.
+     */
+    WifiChip.AfcChannelAllowance makeAfcChannelAllowance() {
+        WifiChip.AfcChannelAllowance afcChannelAllowance = new WifiChip.AfcChannelAllowance();
+
+        int[] startFrequencies = {5995, 6150, 6350};
+        int[] endFrequencies = {6110, 6300, 6590};
+        int[] maxPsds = {13, 27, 14};
+
+        afcChannelAllowance.availableAfcFrequencyInfos = new ArrayList<>();
+        afcChannelAllowance.availableAfcChannelInfos = new ArrayList<>();
+        afcChannelAllowance.availabilityExpireTimeMs = 1000L;
+
+        for (int i = 0; i < startFrequencies.length; ++i) {
+            afcChannelAllowance.availableAfcFrequencyInfos.add(
+                    new WifiChip.AvailableAfcFrequencyInfo());
+            afcChannelAllowance.availableAfcFrequencyInfos.get(i).startFrequencyMhz =
+                    startFrequencies[i];
+            afcChannelAllowance.availableAfcFrequencyInfos.get(i).endFrequencyMhz =
+                    endFrequencies[i];
+            afcChannelAllowance.availableAfcFrequencyInfos.get(i).maxPsdDbmPerMhz = maxPsds[i];
+        }
+
+        int[] globalOperatingClasses = {3, 5, 17};
+        int[] channelCfis = {20, 25, 35};
+        int[] maxEirpDbms = {100, 200, 950};
+
+        for (int i = 0; i < globalOperatingClasses.length; ++i) {
+            afcChannelAllowance.availableAfcChannelInfos.add(
+                    new WifiChip.AvailableAfcChannelInfo());
+            afcChannelAllowance.availableAfcChannelInfos.get(i).globalOperatingClass =
+                    globalOperatingClasses[i];
+            afcChannelAllowance.availableAfcChannelInfos.get(i).channelCfi = channelCfis[i];
+            afcChannelAllowance.availableAfcChannelInfos.get(i).maxEirpDbm = maxEirpDbms[i];
+        }
+
+        return afcChannelAllowance;
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiNanIfaceAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiNanIfaceAidlImplTest.java
index 12c3e2a..0786e60 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiNanIfaceAidlImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiNanIfaceAidlImplTest.java
@@ -22,18 +22,27 @@
 import static android.net.wifi.aware.Characteristics.WIFI_AWARE_CIPHER_SUITE_NCS_SK_256;
 
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.hardware.wifi.IWifiNanIface;
 import android.hardware.wifi.NanBandIndex;
+import android.hardware.wifi.NanBootstrappingRequest;
+import android.hardware.wifi.NanBootstrappingResponse;
 import android.hardware.wifi.NanConfigRequest;
 import android.hardware.wifi.NanConfigRequestSupplemental;
 import android.hardware.wifi.NanDataPathSecurityType;
 import android.hardware.wifi.NanEnableRequest;
+import android.hardware.wifi.NanPairingRequest;
+import android.hardware.wifi.NanPairingRequestType;
+import android.hardware.wifi.NanPairingSecurityType;
 import android.hardware.wifi.NanPublishRequest;
 import android.hardware.wifi.NanRangingIndication;
+import android.hardware.wifi.NanRespondToPairingIndicationRequest;
 import android.hardware.wifi.NanSubscribeRequest;
 import android.net.MacAddress;
 import android.net.wifi.aware.ConfigRequest;
@@ -95,12 +104,12 @@
         SubscribeConfig subWithMinMax = new SubscribeConfig.Builder().setServiceName(
                 "XXX").setMinDistanceMm(minDistanceMm).setMaxDistanceMm(maxDistanceMm).build();
 
-        mDut.publish(tid, pid, pubDefault, null);
-        mDut.publish(tid, pid, pubWithRanging, null);
-        mDut.subscribe(tid, pid, subDefault, null);
-        mDut.subscribe(tid, pid, subWithMin, null);
-        mDut.subscribe(tid, pid, subWithMax, null);
-        mDut.subscribe(tid, pid, subWithMinMax, null);
+        assertTrue(mDut.publish(tid, pid, pubDefault, null));
+        assertTrue(mDut.publish(tid, pid, pubWithRanging, null));
+        assertTrue(mDut.subscribe(tid, pid, subDefault, null));
+        assertTrue(mDut.subscribe(tid, pid, subWithMin, null));
+        assertTrue(mDut.subscribe(tid, pid, subWithMax, null));
+        assertTrue(mDut.subscribe(tid, pid, subWithMinMax, null));
 
         verify(mIWifiNanIfaceMock, times(2))
                 .startPublishRequest(eq((char) tid), pubCaptor.capture());
@@ -398,7 +407,7 @@
     public void testSuspendRequest() throws Exception {
         short tid = 250;
         byte pid = 34;
-        mDut.suspend(tid, pid);
+        assertTrue(mDut.suspend(tid, pid));
         verify(mIWifiNanIfaceMock).suspendRequest(eq((char) tid), eq(pid));
     }
 
@@ -406,20 +415,103 @@
     public void testResumeRequest() throws Exception {
         short tid = 251;
         byte pid = 35;
-        mDut.resume(tid, pid);
+        assertTrue(mDut.resume(tid, pid));
         verify(mIWifiNanIfaceMock).resumeRequest(eq((char) tid), eq(pid));
     }
 
+    @Test
+    public void testEndDataPath() throws Exception {
+        short tid = 251;
+        int ndpId = 35;
+        assertTrue(mDut.endDataPath(tid, ndpId));
+        verify(mIWifiNanIfaceMock).terminateDataPathRequest(eq((char) tid), eq(ndpId));
+    }
+    @Test
+    public void testRespondToPairingRequest() throws Exception {
+        short tid = 251;
+        ArgumentCaptor<NanRespondToPairingIndicationRequest> reqCaptor = ArgumentCaptor.forClass(
+                NanRespondToPairingIndicationRequest.class);
+        assertTrue(mDut.respondToPairingRequest(tid, 1, true, null, true,
+                NanPairingRequestType.NAN_PAIRING_SETUP, null, null , 0, 0));
+        verify(mIWifiNanIfaceMock).respondToPairingIndicationRequest(eq((char) tid),
+                reqCaptor.capture());
+        NanRespondToPairingIndicationRequest request = reqCaptor.getValue();
+        assertEquals(NanPairingRequestType.NAN_PAIRING_SETUP, request.requestType);
+        assertTrue(request.acceptRequest);
+        assertEquals(1, request.pairingInstanceId);
+        assertEquals(NanPairingSecurityType.OPPORTUNISTIC, request.securityConfig.securityType);
+        assertArrayEquals(new byte[32], request.securityConfig.pmk);
+        assertArrayEquals(new byte[0], request.securityConfig.passphrase);
+        assertTrue(request.enablePairingCache);
+        assertArrayEquals(new byte[16], request.pairingIdentityKey);
+    }
+
+    @Test
+    public void testInitiateNanPairingRequest() throws Exception {
+        short tid = 251;
+        MacAddress peer = MacAddress.fromString("00:01:02:03:04:05");
+        ArgumentCaptor<NanPairingRequest> reqCaptor = ArgumentCaptor.forClass(
+                NanPairingRequest.class);
+        assertTrue(mDut.initiateNanPairingRequest(tid, 1, peer, null, true,
+                NanPairingRequestType.NAN_PAIRING_SETUP, null, null , 0, 0));
+        verify(mIWifiNanIfaceMock).initiatePairingRequest(eq((char) tid),
+                reqCaptor.capture());
+        NanPairingRequest request = reqCaptor.getValue();
+        assertEquals(NanPairingRequestType.NAN_PAIRING_SETUP, request.requestType);
+        assertEquals(1, request.peerId);
+        assertEquals(NanPairingSecurityType.OPPORTUNISTIC, request.securityConfig.securityType);
+        assertArrayEquals(new byte[32], request.securityConfig.pmk);
+        assertArrayEquals(new byte[0], request.securityConfig.passphrase);
+        assertTrue(request.enablePairingCache);
+        assertArrayEquals(new byte[16], request.pairingIdentityKey);
+    }
+
+    @Test
+    public void testEndPairing() throws Exception {
+        short tid = 251;
+        assertTrue(mDut.endPairing(tid, 1));
+        verify(mIWifiNanIfaceMock).terminatePairingRequest(eq((char) tid), eq(1));
+    }
+
+    @Test
+    public void testInitiateNanBootstrappingRequest() throws Exception {
+        short tid = 251;
+        MacAddress peer = MacAddress.fromString("00:01:02:03:04:05");
+        ArgumentCaptor<NanBootstrappingRequest> reqCaptor = ArgumentCaptor.forClass(
+                NanBootstrappingRequest.class);
+        assertTrue(mDut.initiateNanBootstrappingRequest(tid, 1, peer, 2, null));
+        verify(mIWifiNanIfaceMock).initiateBootstrappingRequest(eq((char) tid),
+                reqCaptor.capture());
+        NanBootstrappingRequest request = reqCaptor.getValue();
+        assertEquals(1, request.peerId);
+        assertEquals(2, request.requestBootstrappingMethod);
+        assertArrayEquals(peer.toByteArray(), request.peerDiscMacAddr);
+        assertArrayEquals(new byte[0], request.cookie);
+    }
+
+    @Test
+    public void testRespondToNanBootstrappingRequest() throws Exception {
+        short tid = 251;
+        ArgumentCaptor<NanBootstrappingResponse> reqCaptor = ArgumentCaptor.forClass(
+                NanBootstrappingResponse.class);
+        assertTrue(mDut.respondToNanBootstrappingRequest(tid, 1, true));
+        verify(mIWifiNanIfaceMock).respondToBootstrappingIndicationRequest(eq((char) tid),
+                reqCaptor.capture());
+        NanBootstrappingResponse request = reqCaptor.getValue();
+        assertEquals(1, request.bootstrappingInstanceId);
+        assertTrue(request.acceptRequest);
+    }
+
     // utilities
 
     private Pair<NanConfigRequest, NanConfigRequestSupplemental> validateEnableAndConfigure(
             short transactionId, ConfigRequest configRequest, boolean notifyIdentityChange,
             boolean initialConfiguration, boolean isInteractive, boolean isIdle,
             int discoveryWindow24Ghz, int discoveryWindow5Ghz) throws RemoteException {
-        mDut.enableAndConfigure(transactionId, configRequest, notifyIdentityChange,
+        assertTrue(mDut.enableAndConfigure(transactionId, configRequest, notifyIdentityChange,
                 initialConfiguration, false, false, 2437, -1 /* clusterId */,
                 1800 /* PARAM_MAC_RANDOM_INTERVAL_SEC_DEFAULT */,
-                getPowerParams(isInteractive, isIdle, discoveryWindow24Ghz, discoveryWindow5Ghz));
+                getPowerParams(isInteractive, isIdle, discoveryWindow24Ghz, discoveryWindow5Ghz)));
 
         ArgumentCaptor<NanEnableRequest> enableReqCaptor = ArgumentCaptor.forClass(
                 NanEnableRequest.class);
@@ -501,8 +593,8 @@
                     .build();
         }
 
-        mDut.initiateDataPath(tid, peerId, channelRequestType, channel, peer, interfaceName,
-                isOutOfBand, appInfo, TEST_CAPABILITIES, securityConfig, pubSubId);
+        assertTrue(mDut.initiateDataPath(tid, peerId, channelRequestType, channel, peer,
+                interfaceName, isOutOfBand, appInfo, TEST_CAPABILITIES, securityConfig, pubSubId));
 
         verify(mIWifiNanIfaceMock).initiateDataPathRequest(eq((char) tid), captor.capture());
 
@@ -576,8 +668,8 @@
                     .build();
         }
 
-        mDut.respondToDataPathRequest(tid, accept, ndpId, interfaceName,
-                appInfo, isOutOfBand, TEST_CAPABILITIES, securityConfig, pubSubId);
+        assertTrue(mDut.respondToDataPathRequest(tid, accept, ndpId, interfaceName,
+                appInfo, isOutOfBand, TEST_CAPABILITIES, securityConfig, pubSubId));
 
         verify(mIWifiNanIfaceMock)
                 .respondToDataPathIndicationRequest(eq((char) tid), captor.capture());
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java
index e13920d..731df4d 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java
@@ -16,22 +16,33 @@
 
 package com.android.server.wifi.hotspot2;
 
+import static com.android.server.wifi.hotspot2.ANQPRequestManager.ANQP_REQUEST_ALARM_TAG;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.anyObject;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.app.test.TestAlarmManager;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wifi.Clock;
+import com.android.server.wifi.DeviceConfigFacade;
 import com.android.server.wifi.WifiBaseTest;
+import com.android.server.wifi.WifiInjector;
 import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.wifi.flags.FeatureFlags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -98,6 +109,15 @@
     @Mock
     Clock mClock;
     ANQPRequestManager mManager;
+    private TestAlarmManager mAlarmManager;
+    private TestLooper mLooper = new TestLooper();
+    @Mock
+    WifiInjector mWifiInjector;
+    @Mock
+    FeatureFlags mFeatureFlags;
+    @Mock
+    DeviceConfigFacade mDeviceConfigFacade;
+
 
     /**
      * Test setup.
@@ -105,7 +125,13 @@
     @Before
     public void setUp() throws Exception {
         initMocks(this);
-        mManager = new ANQPRequestManager(mHandler, mClock);
+        mAlarmManager = new TestAlarmManager();
+        when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
+        when(mWifiInjector.getAlarmManager()).thenReturn(mAlarmManager.getAlarmManager());
+        when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags);
+        when(mFeatureFlags.anqpRequestWaitForResponse()).thenReturn(false);
+        mManager = new ANQPRequestManager(mHandler, mClock, mWifiInjector,
+                new Handler(mLooper.getLooper()));
     }
 
     /**
@@ -132,8 +158,8 @@
     @Test
     public void requestR1ANQPElementsWithRC() throws Exception {
         when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITH_RC)).thenReturn(true);
-        assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, true,
-                NetworkDetail.HSRelease.R1));
+        mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, true,
+                NetworkDetail.HSRelease.R1);
     }
 
     /**
@@ -372,4 +398,33 @@
         when(mHandler.requestVenueUrlAnqp(TEST_BSSID)).thenReturn(true);
         assertTrue(mManager.requestVenueUrlAnqpElement(TEST_BSSID, TEST_ANQP_KEY));
     }
+
+    @Test
+    public void testWaitResponseEnabled() {
+        when(mFeatureFlags.anqpRequestWaitForResponse()).thenReturn(true);
+        when(mHandler.requestANQP(anyLong(), any())).thenReturn(true);
+        mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, true,
+                NetworkDetail.HSRelease.R3);
+        mManager.requestANQPElements(TEST_BSSID + 1, TEST_ANQP_KEY, true,
+                NetworkDetail.HSRelease.R3);
+        mManager.requestANQPElements(TEST_BSSID + 2, TEST_ANQP_KEY, true,
+                NetworkDetail.HSRelease.R3);
+        verify(mHandler).requestANQP(TEST_BSSID, R1R2_ANQP_WITH_RC);
+        verify(mHandler).requestANQP(anyLong(), any());
+        // Request completed, should process next one
+        mManager.onRequestCompleted(TEST_BSSID, true);
+        verify(mHandler).requestANQP(TEST_BSSID + 1, R1R2_ANQP_WITH_RC);
+        verify(mHandler, times(2)).requestANQP(anyLong(), any());
+        assertTrue(mAlarmManager.isPending(ANQP_REQUEST_ALARM_TAG));
+        // If alarm has been triggered(request time out), should process next request
+        mAlarmManager.dispatch(ANQP_REQUEST_ALARM_TAG);
+        mLooper.dispatchAll();
+        verify(mHandler).requestANQP(TEST_BSSID + 2, R1R2_ANQP_WITH_RC);
+        verify(mHandler, times(3)).requestANQP(anyLong(), any());
+        mManager.onRequestCompleted(TEST_BSSID + 2, true);
+        // No more request in the queue, should process new request immediately
+        mManager.requestANQPElements(TEST_BSSID + 3, TEST_ANQP_KEY, true,
+                NetworkDetail.HSRelease.R3);
+        verify(mHandler).requestANQP(TEST_BSSID + 3, R1R2_ANQP_WITH_RC);
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/NetworkDetailTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/NetworkDetailTest.java
index 09a2ae1..707f2e8 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/NetworkDetailTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/NetworkDetailTest.java
@@ -54,7 +54,7 @@
                 Collections.emptyList(), 5745);
 
         assertTrue(networkDetail.toKeyString() + " not expected!",
-                TextUtils.equals("'null':020304050607", networkDetail.toKeyString()));
+                TextUtils.equals("'null':02:03:04:05:06:07", networkDetail.toKeyString()));
         assertNull(networkDetail.getMldMacAddress());
         assertEquals(MloLink.INVALID_MLO_LINK_ID, networkDetail.getMloLinkId());
         assertTrue(networkDetail.getAffiliatedMloLinks().isEmpty());
@@ -182,17 +182,13 @@
     }
 
     @Test
-    public void  verifySameTextFormat() throws Exception {
+    public void  verifySameTextFormat() {
         long[] testBssids = {0x11ab0d0f7890L, 0x1, 0x3300, 0x0, 0xffffffffffffL};
         String[] testBssidMacStrs = {"11:ab:0d:0f:78:90", "00:00:00:00:00:01", "00:00:00:00:33:00",
                 "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff"};
         for (int i = 0; i < testBssids.length; i++) {
-            String bssidStr1 = String.format("%012x", testBssids[i]);
-            String bssidStr2 = Utils.macToSimpleString(testBssids[i]);
-            assertTrue(bssidStr1 + " not equals " + bssidStr2,
-                    TextUtils.equals(bssidStr1, bssidStr2));
-            bssidStr1 = testBssidMacStrs[i];
-            bssidStr2 = Utils.macToString(testBssids[i]);
+            String bssidStr1 = testBssidMacStrs[i];
+            String bssidStr2 = Utils.macToString(testBssids[i]);
             assertTrue(bssidStr1 + " not equals " + bssidStr2,
                     TextUtils.equals(bssidStr1, bssidStr2));
         }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index 690660b..433dc21 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -252,7 +252,7 @@
         initMocks(this);
         when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
         when(mObjectFactory.makeAnqpCache(mClock)).thenReturn(mAnqpCache);
-        when(mObjectFactory.makeANQPRequestManager(any(), eq(mClock)))
+        when(mObjectFactory.makeANQPRequestManager(any(), eq(mClock), any(), any()))
                 .thenReturn(mAnqpRequestManager);
         when(mObjectFactory.makeOsuNetworkConnection(any(Context.class)))
                 .thenReturn(mOsuNetworkConnection);
@@ -279,7 +279,7 @@
         when(mWifiSettingsStore.isWifiPasspointEnabled())
                 .thenReturn(mConfigSettingsPasspointEnabled);
         mLooper = new TestLooper();
-        mHandler = new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128), mWifiMetrics);
+        mHandler = new RunnerHandler(mLooper.getLooper(), 100, new LocalLog(128));
         mWifiCarrierInfoManager = new WifiCarrierInfoManager(mTelephonyManager,
                 mSubscriptionManager, mWifiInjector, mock(FrameworkFacade.class),
                 mock(WifiContext.class), mWifiConfigStore, mHandler, mWifiMetrics, mClock,
@@ -1304,11 +1304,13 @@
         WifiConfiguration config4 = provider4.getWifiConfig();
         when(mWifiConfigManager.getConfiguredNetwork(provider4.getConfig().getUniqueId()))
                 .thenReturn(config4);
-
+        verify(mPasspointNetworkNominateHelper, times(4)).refreshWifiConfigsForProviders();
+        reset(mPasspointNetworkNominateHelper);
         List<WifiConfiguration> wifiConfigurationList = mManager.getWifiConfigsForPasspointProfiles(
                 List.of(provider1.getConfig().getUniqueId(), provider2.getConfig().getUniqueId(),
                         provider3.getConfig().getUniqueId(), provider4.getConfig().getUniqueId(),
                         TEST_FQDN + "_353ab8c93", TEST_FQDN + "_83765319aca"));
+        verify(mPasspointNetworkNominateHelper).refreshWifiConfigsForProviders();
         assertEquals(2, wifiConfigurationList.size());
         Set<String> uniqueIdSet = wifiConfigurationList
                 .stream()
@@ -1363,8 +1365,8 @@
     }
 
     /**
-     * Verify that {@link PasspointManager#getWifiConfigsForPasspointProfilesWithSsids()}
-     * only returns configs for providers that have been assigned a recent SSID.
+     * Verify that {@link PasspointManager#getWifiConfigsForPasspointProfiles(boolean)} returns
+     * configs for the expected providers.
      */
     @Test
     public void testGetWifiConfigsForPasspointProfilesWithSsids() {
@@ -1374,8 +1376,12 @@
                 TEST_PACKAGE, false, null, false);
         when(provider2.getMostRecentSsid()).thenReturn(TEST_SSID); // assign a recent SSID
 
-        // Only entry should be for the provider that was assigned a recent SSID.
-        List<WifiConfiguration> configs = mManager.getWifiConfigsForPasspointProfilesWithSsids();
+        // If SSIDs are not required, both providers should appear in the results list.
+        List<WifiConfiguration> configs = mManager.getWifiConfigsForPasspointProfiles(false);
+        assertEquals(2, configs.size());
+
+        // If SSIDs are required, only the provider with an SSID should appear in the results.
+        configs = mManager.getWifiConfigsForPasspointProfiles(true);
         assertEquals(1, configs.size());
         assertEquals(provider2.getConfig().getUniqueId(), configs.get(0).getPasspointUniqueId());
         assertEquals(TEST_SSID, configs.get(0).SSID);
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelperTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelperTest.java
index 9fb860e..4e59dda 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelperTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkNominateHelperTest.java
@@ -200,7 +200,7 @@
                 generateScanDetail(TEST_SSID2, TEST_BSSID2));
         when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(null);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertTrue(candidates.isEmpty());
     }
 
@@ -219,7 +219,7 @@
 
         List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {scanDetail});
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertTrue(candidates.isEmpty());
         // Verify that no provider matching is performed.
         verify(mPasspointManager, never()).matchProvider(any(ScanResult.class));
@@ -250,7 +250,7 @@
                 any(), eq(false))).thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
 
         // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
@@ -274,7 +274,7 @@
         // When Scan results time out, should be not candidate return.
         when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider);
         candidates = mNominateHelper
-                .getPasspointNetworkCandidates(Collections.emptyList(), false);
+                .getPasspointNetworkCandidates(Collections.emptyList());
         assertTrue(candidates.isEmpty());
     }
 
@@ -305,7 +305,7 @@
                 any(), eq(false))).thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
 
         // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
@@ -348,7 +348,7 @@
                 .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
 
         // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
@@ -397,7 +397,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID + 1))
                 .thenReturn(TEST_CONFIG2);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(2, candidates.size());
 
         verify(mWifiConfigManager, times(2))
@@ -431,7 +431,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(config);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
 
         assertEquals("", candidates.get(0).second.enterpriseConfig.getAnonymousIdentity());
@@ -462,7 +462,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(currentNetwork);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
 
         assertEquals(1, candidates.size());
 
@@ -510,7 +510,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(anyString())).thenReturn(disableConfig);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(WifiConfiguration.class),
                 anyInt(), any(), eq(false));
         assertTrue(candidates.isEmpty());
@@ -536,7 +536,7 @@
         when(mWifiConfigManager.isNetworkTemporarilyDisabledByUser(TEST_FQDN1))
                 .thenReturn(true);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertTrue(candidates.isEmpty());
     }
 
@@ -569,7 +569,7 @@
         when(wm.isElementInitialized()).thenReturn(true);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
     }
 
@@ -603,7 +603,7 @@
         when(wm.isElementInitialized()).thenReturn(true);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(2, candidates.size());
     }
 
@@ -641,7 +641,7 @@
         when(wm.isElementInitialized()).thenReturn(true);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
     }
 
@@ -669,7 +669,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         // verify Only home provider matched candidate will by chosen
         assertEquals(1, candidates.size());
         assertTrue(candidates.get(0).second.isHomeProviderNetwork);
@@ -703,7 +703,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID2)).thenReturn(TEST_CONFIG2);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         // Nominate matched home provider for first ScanDetail (TEST_SSID1) and roaming provider for
         // the second (TEST_SSID2).
         assertEquals(2, candidates.size());
@@ -740,16 +740,10 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID2))
                 .thenReturn(suggestionConfig);
-        //Get saved passpoint network candidate
+        //Get both passpoint network candidate
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
-        assertEquals(1, candidates.size());
-        assertEquals(TEST_FQDN1, candidates.get(0).second.FQDN);
-        //Get suggestion passpoint network candidate
-        candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, true);
-        assertEquals(1, candidates.size());
-        assertEquals(TEST_FQDN2, candidates.get(0).second.FQDN);
+                .getPasspointNetworkCandidates(scanDetails);
+        assertEquals(2, candidates.size());
     }
 
     /**
@@ -771,7 +765,7 @@
 
         List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {scanDetail});
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertTrue(candidates.isEmpty());
         // Verify that no provider matching is performed.
         verify(mPasspointManager, never()).matchProvider(any(ScanResult.class));
@@ -799,7 +793,7 @@
                 any(), eq(false))).thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
         assertTrue(WifiConfiguration.isMetered(candidates.get(0).second, null));
     }
@@ -832,7 +826,7 @@
                 .thenReturn(anqpElements);
 
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
     }
 
@@ -846,7 +840,7 @@
 
         // No profiles have been added, so expect the first candidate matching to return nothing.
         assertEquals(mNominateHelper.getPasspointNetworkCandidates(
-                scanDetails, false).size(), 0);
+                scanDetails).size(), 0);
         verify(mPasspointManager, times(1)).matchProvider(any());
 
         // Add a homeProvider for the scan detail passed in earlier
@@ -968,7 +962,7 @@
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
         when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID2)).thenReturn(TEST_CONFIG2);
         List<Pair<ScanDetail, WifiConfiguration>> candidates = mNominateHelper
-                .getPasspointNetworkCandidates(scanDetails, false);
+                .getPasspointNetworkCandidates(scanDetails);
         assertEquals(1, candidates.size());
         verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt(),
                 any(), eq(false));
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
index db50d94..8f73f15 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
@@ -74,8 +74,10 @@
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -2028,4 +2030,40 @@
         // aged out
         assertEquals(0, mProvider.getAndRemoveMatchedRcoi(TEST_SSID_QUOTED));
     }
+
+    /**
+     * Verify that the {@link PasspointProvider.ConnectionTimeComparator} can be used to sort
+     * providers in descending order of connection time.
+     */
+    @Test
+    public void testConnectionTimeComparator() throws Exception {
+        PasspointConfiguration config1 =
+                generateTestPasspointConfiguration(CredentialType.SIM, false);
+        PasspointConfiguration config2 =
+                generateTestPasspointConfiguration(CredentialType.SIM, false);
+        PasspointConfiguration config3 =
+                generateTestPasspointConfiguration(CredentialType.SIM, false);
+        PasspointProvider leastRecentProvider = createProvider(config1);
+        PasspointProvider recentProvider = createProvider(config2);
+        PasspointProvider mostRecentProvider = createProvider(config3);
+
+        when(mClock.getWallClockMillis())
+                .thenReturn((long) 10)
+                .thenReturn((long) 20)
+                .thenReturn((long) 30);
+        leastRecentProvider.updateMostRecentConnectionTime();
+        recentProvider.updateMostRecentConnectionTime();
+        mostRecentProvider.updateMostRecentConnectionTime();
+
+        // Initialize the list with providers in ascending order by connection time.
+        List<PasspointProvider> providerList =
+                Arrays.asList(leastRecentProvider, recentProvider, mostRecentProvider);
+
+        // Sort using the comparator. Expect the providers to be in descending order,
+        // with the most recent provider at the beginning of the list.
+        Collections.sort(providerList, new PasspointProvider.ConnectionTimeComparator());
+        assertTrue(mostRecentProvider.equals(providerList.get(0)));
+        assertTrue(recentProvider.equals(providerList.get(1)));
+        assertTrue(leastRecentProvider.equals(providerList.get(2)));
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java
index b3e17c6..7b579b2 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/hotspot2/UtilsTest.java
@@ -16,9 +16,23 @@
 
 package com.android.server.wifi.hotspot2;
 
-import static org.junit.Assert.*;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED;
+import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.wifi.WifiConfiguration;
 
 import com.android.server.wifi.WifiBaseTest;
+import com.android.server.wifi.WifiConfigurationTestUtil;
 
 import org.junit.Test;
 
@@ -173,6 +187,88 @@
         assertEquals(unclosedQuoted, Utils.unquote(unclosedQuoted));
         assertEquals(unclosedQuoted, Utils.unquote(quotedUnclosedQuoted));
     }
+
+    @Test
+    public void testIsFreeOpenRoaming() {
+        // 3 octets
+        assertFalse(Utils.isFreeOpenRoaming(0xABCDEFL));
+        assertTrue(Utils.isFreeOpenRoaming(0x5A03BAL));
+
+        // 4 octets
+        assertFalse(Utils.isFreeOpenRoaming(0xFF5A03BAL));
+        assertFalse(Utils.isFreeOpenRoaming(0x5A03BAFFL));
+
+        // 5 octets
+        assertFalse(Utils.isFreeOpenRoaming(0xEEFF5A03BAL));
+        assertTrue(Utils.isFreeOpenRoaming(0x5A03BA0000L));
+        assertTrue(Utils.isFreeOpenRoaming(0x5A03BAEEFFL));
+
+        // 6 octets
+        assertFalse(Utils.isFreeOpenRoaming(0x5A03BAEEFFFFL));
+        assertFalse(Utils.isFreeOpenRoaming(0xFF5A03BAEEFFL));
+    }
+
+    @Test
+    public void testContainsFreeOpenRoaming() {
+        assertFalse(Utils.containsFreeOpenRoaming(new long[]{0xABCDEFL}));
+        assertTrue(Utils.containsFreeOpenRoaming(new long[]{0x5A03BAL}));
+        assertTrue(Utils.containsFreeOpenRoaming(new long[]{0xABCDEFL, 0x5A03BAFFFFL, 0xABCDEFL}));
+    }
+
+    @Test
+    public void testIsSettledOpenRoaming() {
+        // 3 octets
+        assertFalse(Utils.isSettledOpenRoaming(0xABCDEFL));
+        assertTrue(Utils.isSettledOpenRoaming(0xBAA2D0L));
+
+        // 4 octets
+        assertFalse(Utils.isSettledOpenRoaming(0xBAA2D0FFL));
+        assertFalse(Utils.isSettledOpenRoaming(0xFFBAA2D0L));
+
+        // 5 octets
+        assertFalse(Utils.isSettledOpenRoaming(0xEEFFBAA2D0L));
+        assertTrue(Utils.isSettledOpenRoaming(0xBAA2D00000L));
+        assertTrue(Utils.isSettledOpenRoaming(0xBAA2D0EEFFL));
+
+        // 6 octets
+        assertFalse(Utils.isSettledOpenRoaming(0xBAA2D0EEFFFFL));
+        assertFalse(Utils.isSettledOpenRoaming(0xFFBAA2D0EEFFL));
+    }
+
+    @Test
+    public void testContainsSettledOpenRoaming() {
+        assertFalse(Utils.containsSettledOpenRoaming(new long[]{0xABCDEFL}));
+        assertTrue(Utils.containsSettledOpenRoaming(new long[]{0xBAA2D0L}));
+        assertTrue(
+                Utils.containsSettledOpenRoaming(new long[]{0xABCDEFL, 0xBAA2D0EEFFL, 0xABCDEFL}));
+    }
+
+    @Test
+    public void testGetRoamingType() {
+        WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_PASSPOINT);
+
+        config = WifiConfigurationTestUtil.createPasspointNetwork();
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NOT_RCOI);
+
+        config.enterpriseConfig.setSelectedRcoi(0xABCDEFL);
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OTHERS);
+
+        config.enterpriseConfig.setSelectedRcoi(0x5A03BAFFFFL);
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_FREE);
+
+        config.enterpriseConfig.setSelectedRcoi(0xBAA2D0FFFFL);
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_RCOI_OPENROAMING_SETTLED);
+
+        config.isHomeProviderNetwork = true;
+        assertEquals(Utils.getRoamingType(config),
+                WIFI_CONNECTION_RESULT_REPORTED__PASSPOINT_ROAMING_TYPE__ROAMING_NONE);
+    }
 }
 
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java
index 1520e2c..b4a3cb0 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java
@@ -31,8 +31,10 @@
 
 import android.app.test.MockAnswerUtil.AnswerWithArguments;
 import android.hardware.wifi.supplicant.P2pClientEapolIpAddressInfo;
+import android.hardware.wifi.supplicant.P2pDeviceFoundEventParams;
 import android.hardware.wifi.supplicant.P2pGroupStartedEventParams;
 import android.hardware.wifi.supplicant.P2pProvDiscStatusCode;
+import android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams;
 import android.hardware.wifi.supplicant.P2pStatusCode;
 import android.hardware.wifi.supplicant.WpsConfigMethods;
 import android.hardware.wifi.supplicant.WpsDevPasswordId;
@@ -581,6 +583,63 @@
         assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event);
     }
 
+    /**
+     * Test provision discovery completed callback.
+     */
+    @Test
+    public void testOnProvisionDiscoveryCompletedEvent() throws Exception {
+        P2pProvisionDiscoveryCompletedEventParams params =
+                new P2pProvisionDiscoveryCompletedEventParams();
+        params.p2pDeviceAddress = DEVICE_ADDRESS;
+        params.isRequest = false;
+        params.status = P2pProvDiscStatusCode.SUCCESS;
+        params.configMethods = WpsConfigMethods.DISPLAY;
+        params.generatedPin = "12345678";
+        params.groupInterfaceName = null;
+
+        ArgumentCaptor<WifiP2pProvDiscEvent> discEventCaptor =
+                ArgumentCaptor.forClass(WifiP2pProvDiscEvent.class);
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event);
+
+        params.configMethods = WpsConfigMethods.KEYPAD;
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event);
+        assertEquals("12345678", discEventCaptor.getValue().pin);
+
+        params.isRequest = true;
+        params.configMethods = WpsConfigMethods.KEYPAD;
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event);
+
+        params.configMethods = WpsConfigMethods.DISPLAY;
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event);
+        assertEquals("12345678", discEventCaptor.getValue().pin);
+
+        params.isRequest = false;
+        params.configMethods = WpsConfigMethods.PUSHBUTTON;
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event);
+
+        params.isRequest = true;
+        params.groupInterfaceName = "group name";
+        mDut.onProvisionDiscoveryCompletedEvent(params);
+        verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest(
+                anyString(), discEventCaptor.capture());
+        assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event);
+    }
+
     private void verifyProvisionDiscoveryFailureEvent(
             int halStatus, int expectedStatus) throws Exception {
         byte[] p2pDeviceAddr = DEVICE_ADDRESS;
@@ -902,6 +961,42 @@
     }
 
     /**
+     * Test a successful call to testOnDeviceFoundWithParams.
+     */
+    @Test
+    public void testOnDeviceFoundWithParams() throws Exception {
+        byte[] testVsieBytes = {
+                (byte) ScanResult.InformationElement.EID_VSA, 4, 0x1, 0x2, 0x3, 0x0,
+                (byte) ScanResult.InformationElement.EID_VSA, 4, 0x1, 0x2, 0x3, 0x1};
+        ArrayList<ScanResult.InformationElement> expectedVsieList = new ArrayList<>();
+        expectedVsieList.add(new ScanResult.InformationElement(
+                ScanResult.InformationElement.EID_VSA, 0, new byte[]{0x1, 0x2, 0x3, 0x0}));
+        expectedVsieList.add(new ScanResult.InformationElement(
+                ScanResult.InformationElement.EID_VSA, 0, new byte[]{0x1, 0x2, 0x3, 0x1}));
+
+        P2pDeviceFoundEventParams params = new P2pDeviceFoundEventParams();
+        params.srcAddress = mDeviceAddress1Bytes;
+        params.p2pDeviceAddress = mDeviceAddress2Bytes;
+        params.primaryDeviceType = mTestPrimaryDeviceTypeBytes;
+        params.deviceName = mTestDeviceName;
+        params.configMethods = mTestConfigMethods;
+        params.deviceCapabilities = mTestCapabilities;
+        params.groupCapabilities = mTestGroupCapabilities;
+        params.wfdDeviceInfo = mDeviceInfoBytes;
+        params.wfdR2DeviceInfo = null;
+        params.vendorElemBytes = testVsieBytes;
+        params.vendorData = null;
+
+        mDut.onDeviceFoundWithParams(params);
+        ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor =
+                ArgumentCaptor.forClass(WifiP2pDevice.class);
+        verify(mMonitor).broadcastP2pDeviceFound(eq(mIface), p2pDeviceCaptor.capture());
+
+        assertInformationElementListEquals(
+                expectedVsieList, p2pDeviceCaptor.getValue().getVendorElements());
+    }
+
+    /**
      * Helper function for comparing InformationElement lists.
      *
      * InformationElement equals() is implemented in S. List equals() cannot
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 de2f9f8..546da0c 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
@@ -17,6 +17,7 @@
 package com.android.server.wifi.p2p;
 
 import static android.net.NetworkInfo.DetailedState.CONNECTING;
+import static android.net.NetworkInfo.DetailedState.DISCONNECTED;
 import static android.net.NetworkInfo.DetailedState.FAILED;
 import static android.net.NetworkInfo.DetailedState.IDLE;
 import static android.net.wifi.WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE;
@@ -79,6 +80,7 @@
 import android.net.InetAddresses;
 import android.net.MacAddress;
 import android.net.NetworkInfo;
+import android.net.NetworkStack;
 import android.net.TetheringInterface;
 import android.net.TetheringManager;
 import android.net.wifi.CoexUnsafeChannel;
@@ -138,6 +140,7 @@
 import com.android.server.wifi.WifiDialogManager;
 import com.android.server.wifi.WifiGlobals;
 import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiP2pConnection;
 import com.android.server.wifi.WifiSettingsConfigStore;
 import com.android.server.wifi.coex.CoexManager;
 import com.android.server.wifi.proto.nano.WifiMetricsProto;
@@ -273,6 +276,7 @@
     @Mock DeviceConfigFacade mDeviceConfigFacade;
     @Mock TetheringManager mTetheringManager;
     @Mock WifiDiagnostics mWifiDiagnostics;
+    @Mock WifiP2pConnection mWifiP2pConnection;
 
     private void generatorTestData() {
         mTestWifiP2pGroup = new WifiP2pGroup();
@@ -648,6 +652,19 @@
     }
 
     /**
+     * Send WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT.
+     *
+     * @param group Peer group information
+     */
+    private void sendInvitationReceivedMsg(WifiP2pGroup group) throws Exception {
+        Message msg = Message.obtain();
+        msg.what = WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT;
+        msg.obj = group;
+        mP2pStateMachineMessenger.send(Message.obtain(msg));
+        mLooper.dispatchAll();
+    }
+
+    /**
      * Mock send WifiP2pManager.SET_CHANNEL
      *
      * @param replyMessenger for checking replied message.
@@ -1076,14 +1093,21 @@
         verify(mTetheringManager).registerTetheringEventCallback(any(Executor.class),
                 mTetheringEventCallbackCaptor.capture());
         mTetheringEventCallback = mTetheringEventCallbackCaptor.getValue();
+        if (SdkLevel.isAtLeastU()) {
+            ArgumentCaptor<String[]> permissionCaptor = ArgumentCaptor.forClass(String[].class);
+            verify(mContext).sendBroadcastWithMultiplePermissions(any(),
+                    permissionCaptor.capture());
+            String[] permission = permissionCaptor.getValue();
+            assertEquals(new String[]{NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}, permission);
+        }
         verify(mContext).sendBroadcastWithMultiplePermissions(
                 argThat(new WifiP2pServiceImplTest
-                       .P2pConnectionChangedIntentMatcherForNetworkState(IDLE)), any());
+                        .P2pConnectionChangedIntentMatcherForNetworkState(IDLE)), any());
         verify(mContext, never()).sendBroadcastWithMultiplePermissions(
                 argThat(new WifiP2pServiceImplTest
                         .P2pConnectionChangedIntentMatcherForNetworkState(FAILED)), any());
         if (SdkLevel.isAtLeastT()) {
-            verify(mContext).sendBroadcast(
+            verify(mContext, atLeastOnce()).sendBroadcast(
                     argThat(new WifiP2pServiceImplTest
                             .P2pConnectionChangedIntentMatcherForNetworkState(IDLE)), any(), any());
             verify(mBroadcastOptions, atLeastOnce())
@@ -1207,9 +1231,17 @@
                     android.Manifest.permission.NETWORK_SETTINGS};
         }
         ArgumentCaptor<String []> permissionCaptor = ArgumentCaptor.forClass(String[].class);
-        verify(mContext, atLeastOnce()).sendBroadcastWithMultiplePermissions(
-                intentCaptor.capture(), permissionCaptor.capture());
-        String [] permission = permissionCaptor.getValue();
+        String [] permission;
+        if (SdkLevel.isAtLeastU()) {
+            verify(mContext, atLeastOnce()).sendBroadcast(intentCaptor.capture(), any(), any());
+            verify(mBroadcastOptions, atLeastOnce()).setRequireAllOfPermissions(
+                    permissionCaptor.capture());
+            permission = permissionCaptor.getAllValues().get(0);
+        } else {
+            verify(mContext, atLeastOnce()).sendBroadcastWithMultiplePermissions(
+                    intentCaptor.capture(), permissionCaptor.capture());
+            permission = permissionCaptor.getValue();
+        }
         Arrays.sort(permission);
         Arrays.sort(permission_gold);
         assertEquals(permission_gold, permission);
@@ -1344,6 +1376,8 @@
         when(mWifiInjector.makeBroadcastOptions()).thenReturn(mBroadcastOptions);
         when(mWifiInjector.getWifiDialogManager()).thenReturn(mWifiDialogManager);
         when(mWifiInjector.getLastCallerInfoManager()).thenReturn(mLastCallerInfoManager);
+        when(mWifiInjector.getWifiP2pConnection()).thenReturn(mWifiP2pConnection);
+
         when(mWifiDialogManager.createP2pInvitationReceivedDialog(any(), anyBoolean(), any(),
                 anyInt(), any(), any())).thenReturn(mDialogHandle);
         when(mWifiDialogManager.createP2pInvitationSentDialog(any(), any(), anyInt()))
@@ -1544,6 +1578,12 @@
         sendNegotiationRequestEvent(config);
     }
 
+    /** Mock enter the user authorizing invite request state. */
+    private void mockEnterUserAuthorizingInviteRequestState() throws Exception {
+        mockPeersList();
+        // Enter UserAuthorizingNegotiationRequestState
+        sendInvitationReceivedMsg(mTestWifiP2pGroup);
+    }
 
     /**
      * Mock WifiP2pServiceImpl.mPeers.
@@ -2722,7 +2762,7 @@
         sendSimpleMsg(mClientMessenger, WifiP2pManager.START_LISTEN);
         // p2pFlush should be invoked once in forceP2pEnabled.
         verify(mWifiNative).p2pFlush();
-        verify(mWifiNative).p2pStopFind();
+        verify(mWifiNative, times(2)).p2pStopFind();
         verify(mWifiNative).p2pExtListen(eq(true), eq(P2P_EXT_LISTEN_PERIOD_MS),
                 eq(P2P_EXT_LISTEN_INTERVAL_MS));
         assertTrue(mClientHandler.hasMessages(WifiP2pManager.START_LISTEN_SUCCEEDED));
@@ -3189,6 +3229,39 @@
                 configCaptor.getValue().toString());
     }
 
+    @Test
+    public void testStartFastConnectionEventWhenSendConnectWithConfigAndP2pClose()
+            throws Exception {
+        setTargetSdkGreaterThanT();
+        forceP2pEnabled(mClient1);
+        when(mWifiNative.p2pGroupAdd(any(), eq(true))).thenReturn(true);
+
+        sendConnectMsg(mClientMessenger, mTestWifiP2pFastConnectionConfig);
+        if (SdkLevel.isAtLeastT()) {
+            verify(mWifiPermissionsUtil, atLeastOnce()).checkNearbyDevicesPermission(
+                    any(), eq(true), any());
+            verify(mWifiPermissionsUtil, never()).checkCanAccessWifiDirect(
+                    any(), any(), anyInt(), anyBoolean());
+        } else {
+            verify(mWifiPermissionsUtil).checkCanAccessWifiDirect(eq(TEST_PACKAGE_NAME),
+                    eq("testFeature"), anyInt(), eq(false));
+        }
+
+        ArgumentCaptor<WifiP2pConfig> configCaptor =
+                ArgumentCaptor.forClass(WifiP2pConfig.class);
+        verify(mWifiP2pMetrics).startConnectionEvent(
+                eq(P2pConnectionEvent.CONNECTION_FAST),
+                configCaptor.capture(),
+                eq(WifiMetricsProto.GroupEvent.GROUP_CLIENT),
+                eq(mClient1.getCallingUid()));
+        assertEquals(mTestWifiP2pFastConnectionConfig.toString(),
+                configCaptor.getValue().toString());
+
+        mWifiP2pServiceImpl.close(mClient1);
+        mLooper.dispatchAll();
+        verify(mWifiP2pMetrics).endConnectionEvent(eq(P2pConnectionEvent.CLF_GROUP_REMOVED));
+    }
+
     /**
      * Verify the connection event for a fast connection via
      * createGroup API with config.
@@ -3323,6 +3396,8 @@
 
         reset(mAsyncChannel);
         sendSimpleMsg(mClientMessenger, WifiP2pManager.CANCEL_CONNECT);
+        mLooper.moveTimeForward(100);
+        mLooper.dispatchAll();
         verify(mAsyncChannel).sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
         verify(mLastCallerInfoManager).put(eq(WifiManager.API_P2P_CANCEL_CONNECT), anyInt(),
                 anyInt(), anyInt(), anyString(), eq(true));
@@ -3501,7 +3576,7 @@
                 argThat(new WifiP2pServiceImplTest
                         .P2pConnectionChangedIntentMatcherForNetworkState(FAILED)), any());
         if (SdkLevel.isAtLeastT()) {
-            verify(mContext).sendBroadcast(
+            verify(mContext, atLeastOnce()).sendBroadcast(
                     argThat(new WifiP2pServiceImplTest
                             .P2pConnectionChangedIntentMatcherForNetworkState(FAILED)), any(),
                     any());
@@ -3568,6 +3643,27 @@
     }
 
     /**
+     * Verify the connection event ends when peer reject the connect request in negotiation flow.
+     */
+    @Test
+    public void testEndConnectionEventWhenPeerRejectTheConnectRequestInNegotiationFlow()
+            throws Exception {
+        forceP2pEnabled(mClient1);
+        when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true);
+        mockEnterGroupNegotiationState();
+
+        WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent();
+        pdEvent.device = mTestWifiP2pDevice;
+        sendSimpleMsg(null,
+                WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT,
+                WifiP2pMonitor.PROV_DISC_STATUS_REJECTED,
+                pdEvent);
+
+        verify(mWifiP2pMetrics).endConnectionEvent(
+                eq(P2pConnectionEvent.CLF_GROUP_REMOVED));
+    }
+
+    /**
      * Verify WifiP2pManager.RESPONSE_DEVICE_INFO is returned with null object when a caller
      * without proper permission attempts.
      */
@@ -4008,6 +4104,12 @@
         sendSimpleMsg(mClientMessenger, WifiP2pManager.CANCEL_CONNECT);
         verify(mClientHandler, atLeastOnce()).sendMessage(mMessageCaptor.capture());
         Message message = mMessageCaptor.getValue();
+        assertNotEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what);
+
+        mLooper.moveTimeForward(100);
+        mLooper.dispatchAll();
+        verify(mClientHandler, atLeastOnce()).sendMessage(mMessageCaptor.capture());
+        message = mMessageCaptor.getValue();
         assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what);
     }
 
@@ -7104,6 +7206,24 @@
         verify(mWifiNative).p2pReject(eq(mTestWifiP2pDevice.deviceAddress));
     }
 
+    /**
+     * Verifies that we broadcast WIFI_P2P_CONNECTION_CHANGED_ACTION with DISCONNECTED state upon
+     * user reject for negotiation request.
+     * @throws Exception
+     */
+    @Test
+    public void testBroadcastDisconnectedStateOnRejectRequest() throws Exception {
+        when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true);
+        forceP2pEnabled(mClient1);
+
+        mockEnterUserAuthorizingNegotiationRequestState(WpsInfo.PBC);
+
+        sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_REJECT);
+        verify(mContext).sendBroadcastWithMultiplePermissions(
+                argThat(new WifiP2pServiceImplTest
+                        .P2pConnectionChangedIntentMatcherForNetworkState(DISCONNECTED)), any());
+    }
+
     @Test
     public void testDismissDialogOnReceiveProvDiscFailureEvent() throws Exception {
         assumeTrue(SdkLevel.isAtLeastT());
@@ -7143,9 +7263,11 @@
     public void testTetheringRequestWithTetherPrivilegedPermission() throws Exception {
         mockEnterGroupCreatedState();
 
-        String[] permission_gold = new String[] {
-                android.Manifest.permission.TETHER_PRIVILEGED};
-        ArgumentCaptor<String []> permissionCaptor = ArgumentCaptor.forClass(String[].class);
+        String[] permission_gold = new String[]{android.Manifest.permission.TETHER_PRIVILEGED};
+        if (SdkLevel.isAtLeastU()) {
+            permission_gold = new String[]{NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK};
+        }
+        ArgumentCaptor<String[]> permissionCaptor = ArgumentCaptor.forClass(String[].class);
         String[] permission;
         // 3 connection changed event:
         // * Enter Enabled state
@@ -7336,6 +7458,7 @@
 
     @Test
     public void testGroupStartedTetheringDirectCallback() throws Exception {
+        when(mWifiNative.p2pExtListen(anyBoolean(), anyInt(), anyInt())).thenReturn(true);
         assumeTrue(SdkLevel.isAtLeastS());
         forceP2pEnabled(mClient1);
         verify(mTetheringManager).registerTetheringEventCallback(any(), any());
@@ -7352,6 +7475,8 @@
         sendGroupStartedMsg(group);
         simulateTetherReady();
         verify(mWifiP2pMetrics).startGroupEvent(group);
+        verify(mWifiNative).p2pStopFind();
+        verify(mWifiNative).p2pExtListen(eq(false), anyInt(), anyInt());
         sendGroupRemovedMsg();
 
         //force to back disabled state
@@ -7376,6 +7501,8 @@
 
         /* Trigger a group failure */
         sendSimpleMsg(null, WifiP2pManager.CANCEL_CONNECT);
+        mLooper.moveTimeForward(100);
+        mLooper.dispatchAll();
 
         /* Verify that p2p discover is triggered */
         verify(mWifiNative).p2pFind(anyInt());
@@ -7401,6 +7528,53 @@
         verify(mWifiP2pMetrics).setFallbackToNegotiationOnInviteStatusInfoUnavailable();
     }
 
+    @Test
+    public void testFallbackToP2pConnectWithWpsProvisioningOnInviteRequestFromAnUnknownNetwork()
+            throws Exception {
+        forceP2pEnabled(mClient1);
+        mockEnterUserAuthorizingInviteRequestState();
+        // Return an unknown network name
+        when(mWifiNative.p2pGetSsid(eq(mTestWifiP2pDevice.deviceAddress)))
+                .thenReturn(mTestWifiP2pGroup.getNetworkName() + "unknown");
+        sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_ACCEPT);
+        verify(mWifiNative, never()).p2pGroupAdd(anyInt());
+        verify(mWifiNative).p2pConnect(any(), anyBoolean());
+    }
+
+    @Test
+    public void testP2pSetupPersistentGroupOnInviteRequestFromAKnownNetwork() throws Exception {
+        forceP2pEnabled(mClient1);
+        mockEnterUserAuthorizingInviteRequestState();
+        when(mWifiNative.p2pGetSsid(eq(mTestWifiP2pDevice.deviceAddress)))
+                .thenReturn(mTestWifiP2pGroup.getNetworkName());
+        sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_ACCEPT);
+        verify(mWifiNative).p2pGroupAdd(anyInt());
+    }
+
+    @Test
+    public void testSetListenOnInviteStatusCodeInfoUnavailable() throws Exception {
+        forceP2pEnabled(mClient1);
+        when(mWifiNative.p2pExtListen(anyBoolean(), anyInt(), anyInt())).thenReturn(true);
+        when(mWifiNative.getGroupCapability(any())).thenReturn(0);
+        when(mWifiNative.p2pReinvoke(anyInt(), any())).thenReturn(true);
+        when(mWifiNative.p2pGetSsid(any())).thenReturn(null);
+        when(mTestWifiP2pDevice.isGroupOwner()).thenReturn(false);
+        when(mTestWifiP2pDevice.isInvitationCapable()).thenReturn(true);
+        when(mResources.getBoolean(R.bool
+                .config_p2pWaitForPeerInviteOnInviteStatusInfoUnavailable)).thenReturn(true);
+
+        mockPeersList();
+        // Trigger reinvoke to enter Group Negotiation state
+        mTestWifiP2pPeerConfig.wps.setup = WpsInfo.KEYPAD;
+        sendConnectMsg(mClientMessenger, mTestWifiP2pPeerConfig);
+        // Got Fail: information is currently unavailable
+        sendInvitationResultMsg(WifiP2pServiceImpl.P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE);
+
+        verify(mWifiNative, times(2)).p2pStopFind();
+        verify(mWifiNative).p2pExtListen(eq(true), eq(P2P_EXT_LISTEN_PERIOD_MS),
+                eq(P2P_EXT_LISTEN_INTERVAL_MS));
+    }
+
     /**
      * Verify that p2p connect with Join existing group is set
      */
diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pShellCommandTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pShellCommandTest.java
index 339d0d9..b329c3e 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pShellCommandTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pShellCommandTest.java
@@ -314,11 +314,19 @@
     public void testP2pConnect() {
         String deviceAddress = "aa:bb:cc:11:22:33";
         runP2pCommandAsRoot("init");
-        runP2pCommandAsRoot("connect", deviceAddress);
+        if (SdkLevel.isAtLeastT()) {
+            runP2pCommandAsRoot("connect", deviceAddress, "-i", "10", "-m", "1");
+        } else {
+            runP2pCommandAsRoot("connect", deviceAddress, "-i", "10");
+        }
         ArgumentCaptor<WifiP2pConfig> captor = ArgumentCaptor.forClass(WifiP2pConfig.class);
         verify(mWifiP2pManager).connect(eq(mWifiP2pChannel), captor.capture(), any());
         WifiP2pConfig config = captor.getValue();
         assertEquals(deviceAddress, config.deviceAddress);
+        assertEquals(10, config.groupOwnerIntent);
+        if (SdkLevel.isAtLeastT()) {
+            assertEquals(1, config.getGroupClientIpProvisioningMode());
+        }
     }
 
     @Test
diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
index d70421e..7c779dd 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
@@ -424,8 +424,20 @@
         results.first.remove(results.first.size() - 1);
         RangingResult removed = results.second.remove(results.second.size() - 1);
         results.second.add(
-                new RangingResult(RangingResult.STATUS_FAIL, removed.getPeerHandle(), 0, 0, 0, 0, 0,
-                        null, null, null, 0));
+                new RangingResult(
+                        RangingResult.STATUS_FAIL,
+                        removed.getPeerHandle(),
+                        0,
+                        0,
+                        0,
+                        0,
+                        0,
+                        null,
+                        null,
+                        null,
+                        0,
+                        RangingResult.UNSPECIFIED,
+                        RangingResult.UNSPECIFIED));
         clock.time += MEASUREMENT_DURATION;
         mRangingResultsCbCaptor.getValue()
                 .onRangingResults(mIntCaptor.getValue(), results.first);
@@ -462,14 +474,18 @@
      */
     @Test
     public void testRangingOnlyAwareAps() throws Exception {
+        final int burstSize = 31;
         assumeTrue(SdkLevel.isAtLeastT());
         mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, mock(
                 AttributionSource.class));
         when(mockPermissionUtil.checkNearbyDevicesPermission(any(), eq(true), any()))
                 .thenReturn(true);
-        RangingRequest request = new RangingRequest.Builder()
-                .addWifiAwarePeer(MacAddress.fromString("08:09:08:07:06:01"))
-                .addWifiAwarePeer(MacAddress.fromString("08:09:08:07:06:02")).build();
+        RangingRequest request =
+                new RangingRequest.Builder()
+                        .addWifiAwarePeer(MacAddress.fromString("08:09:08:07:06:01"))
+                        .addWifiAwarePeer(MacAddress.fromString("08:09:08:07:06:02"))
+                        .setRttBurstSize(burstSize)
+                        .build();
 
         // issue request
         ClockAnswer clock = new ClockAnswer();
@@ -483,6 +499,8 @@
         verify(mockRttControllerHal).rangeRequest(mIntCaptor.capture(), mRequestCaptor.capture());
         verifyWakeupSet(true, 0);
 
+        assertEquals(burstSize, mRequestCaptor.getValue().getRttBurstSize());
+
         // issue results
         Pair<List<RangingResult>, List<RangingResult>> results =
                 RttTestUtils.getDummyRangingResults(mRequestCaptor.getValue());
diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
index 75e60de..3af1324 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
@@ -138,9 +138,21 @@
                             8, 5, null, null, null, rangeTimestampBase++, true, 5180,
                             ScanResult.CHANNEL_WIDTH_40MHZ);
                 } else {
-                    rangingResult = new RangingResult(RangingResult.STATUS_SUCCESS,
-                            peer.peerHandle, rangeCmBase++, rangeStdDevCmBase++, rssiBase++,
-                            8, 5, null, null, null, rangeTimestampBase++);
+                    rangingResult =
+                            new RangingResult(
+                                    RangingResult.STATUS_SUCCESS,
+                                    peer.peerHandle,
+                                    rangeCmBase++,
+                                    rangeStdDevCmBase++,
+                                    rssiBase++,
+                                    8,
+                                    5,
+                                    null,
+                                    null,
+                                    null,
+                                    rangeTimestampBase++,
+                                    5180,
+                                    ScanResult.CHANNEL_WIDTH_40MHZ);
                 }
                 results.add(rangingResult);
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
index 54bf94b..456d2fe 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -284,6 +284,11 @@
                     anyInt());
         }
 
+        private void verifyUnlinkedToDeath() throws Exception {
+            verify(listener, atLeastOnce()).asBinder();
+            verify(mIBinder, atLeastOnce()).unlinkToDeath(any(), anyInt());
+        }
+
         private void registerScanListener() throws Exception {
             mWifiScanningServiceImpl.registerScanListener(listener, TEST_PACKAGE_NAME,
                     TEST_FEATURE_ID);
@@ -1206,6 +1211,7 @@
         mLooper.dispatchAll();
         client.verifyFailedResponse(WifiScanner.REASON_UNSPECIFIED,
                 "Scan was interrupted");
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
     }
 
@@ -1244,6 +1250,7 @@
         mLooper.dispatchAll();
         client.verifyFailedResponse(WifiScanner.REASON_UNSPECIFIED,
                 "Scan was interrupted");
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
     }
 
@@ -2164,6 +2171,7 @@
         client1.verifyScanResultsReceived(results.getScanData());
         client1.verifySingleScanCompletedReceived();
         client.verifyScanResultsReceived(results.getScanData());
+        client1.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
         verifyNoMoreInteractions(client1.listener);
         assertDumpContainsRequestLog("registerScanListener");
@@ -2211,6 +2219,8 @@
         mLooper.dispatchAll();
         client1.verifyScanResultsReceived(results.getScanData());
         client1.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
+        client1.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
         verifyNoMoreInteractions(client1.listener);
 
@@ -2778,6 +2788,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
 
         mLooper.dispatchAll();
@@ -2792,6 +2803,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
     }
 
@@ -2866,6 +2878,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
         verify(mBatteryStats).reportWifiScanStoppedFromSource(eq(workSource));
         assertDumpContainsRequestLog("addSingleScanRequest");
@@ -3399,6 +3412,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
         verify(mBatteryStats).reportWifiScanStoppedFromSource(eq(workSource));
         assertDumpContainsRequestLog("addSingleScanRequest");
@@ -3504,6 +3518,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
         verify(mBatteryStats).reportWifiScanStoppedFromSource(eq(workSource));
         assertDumpContainsRequestLog("addSingleScanRequest");
@@ -3758,6 +3773,7 @@
         mLooper.dispatchAll();
         client.verifyScanResultsReceived(results.getScanData());
         client.verifySingleScanCompletedReceived();
+        client.verifyUnlinkedToDeath();
         verifyNoMoreInteractions(client.listener);
 
         // Verify that now isScanning = false
diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java
index 0acedc3..40f1c53 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wifi.util;
 
+import static android.net.wifi.SoftApCapability.SOFTAP_FEATURE_IEEE80211_BE;
+
 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE;
 import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA;
 
@@ -51,8 +53,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.modules.utils.build.SdkLevel;
+import com.android.server.wifi.SoftApManager;
 import com.android.server.wifi.WifiBaseTest;
 import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.WifiSettingsConfigStore;
 import com.android.server.wifi.coex.CoexManager;
 import com.android.wifi.resources.R;
 
@@ -159,6 +163,7 @@
     @Mock Resources mResources;
     @Mock WifiNative mWifiNative;
     @Mock CoexManager mCoexManager;
+    @Mock WifiSettingsConfigStore mConfigStore;
     private SoftApCapability mCapability;
     private boolean mApBridgeIfaceCobinationSupported = false;
     private boolean mApBridgeWithStaIfaceCobinationSupported = false;
@@ -184,6 +189,8 @@
         when(mResources.getBoolean(
                 R.bool.config_wifiStaWithBridgedSoftApConcurrencySupported)).thenReturn(true);
         when(mWifiNative.getUsableChannels(anyInt(), anyInt(), anyInt())).thenReturn(null);
+        when(mConfigStore.get(
+                WifiSettingsConfigStore.WIFI_WIPHY_11BE_SUPPORTED)).thenReturn(false);
         when(mWifiNative.canDeviceSupportCreateTypeCombo(any()))
                 .thenAnswer(answer -> {
                     SparseArray<Integer> combo = answer.getArgument(0);
@@ -603,21 +610,24 @@
                 .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN)
                 .build();
         assertEquals(SoftApConfiguration.BAND_5GHZ,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBand());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                        .getBand());
 
         config = new SoftApConfiguration.Builder()
                 .setBand(SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ)
                 .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .build();
         assertEquals(SoftApConfiguration.BAND_5GHZ,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBand());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                        .getBand());
 
         config = new SoftApConfiguration.Builder()
                 .setBand(SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ)
                 .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)
                 .build();
         assertEquals(SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBand());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                        .getBand());
 
         config = new SoftApConfiguration.Builder()
                 .setBand(SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ)
@@ -625,7 +635,8 @@
                         SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
                 .build();
         assertEquals(SoftApConfiguration.BAND_5GHZ,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBand());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                        .getBand());
 
         if (SdkLevel.isAtLeastT()) {
             config = new SoftApConfiguration.Builder()
@@ -633,13 +644,14 @@
                     .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION)
                     .build();
             assertEquals(SoftApConfiguration.BAND_5GHZ,
-                    ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBand());
+                    ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                            .getBand());
         }
         config = new SoftApConfiguration.Builder()
                 .setBand(SoftApConfiguration.BAND_6GHZ)
                 .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .build();
-        assertNull(ApConfigUtil.remove6gBandForUnsupportedSecurity(config));
+        assertNull(ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false));
     }
 
     /**
@@ -660,21 +672,24 @@
                 .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN)
                 .build();
         assertArrayEquals(bands_no6g,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBands());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                        .getBands());
 
         config = new SoftApConfiguration.Builder()
                 .setBands(bands)
                 .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .build();
         assertArrayEquals(bands_no6g,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBands());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                        .getBands());
 
         config = new SoftApConfiguration.Builder()
                 .setBands(bands)
                 .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)
                 .build();
         assertArrayEquals(bands,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBands());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                        .getBands());
 
         config = new SoftApConfiguration.Builder()
                 .setBands(bands)
@@ -682,7 +697,8 @@
                         SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
                 .build();
         assertArrayEquals(bands_no6g,
-                ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBands());
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                        .getBands());
 
         if (SdkLevel.isAtLeastT()) {
             config = new SoftApConfiguration.Builder()
@@ -690,11 +706,53 @@
                     .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION)
                     .build();
             assertArrayEquals(bands_no6g,
-                    ApConfigUtil.remove6gBandForUnsupportedSecurity(config).getBands());
+                    ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                            .getBands());
         }
     }
 
     /**
+     * Verify that 6GHz band is not removed when HAL is capable of converting a restricted security
+     * type (SECURITY_TYPE_WPA3_SAE_TRANSITION) to an allowed security type
+     * (SECURITY_TYPE_WPA3_SAE) in bridged mode.
+     */
+    @Test
+    public void verifyThatBand6GIsUpdatedWhenHalCanConvertRestrictedSecurityType()
+            throws Exception {
+        when(mResources.getBoolean(
+                R.bool.config_wifiSofapHalMapWpa3TransitionModeToWpa3OnlyIn6GHzBand))
+                .thenReturn(true);
+        SoftApConfiguration config;
+
+        // Single AP configuration
+        config = new SoftApConfiguration.Builder()
+                .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_6GHZ)
+                .setPassphrase("somepassword",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+                .build();
+        // Expected to remove 6G band
+        assertEquals(SoftApConfiguration.BAND_2GHZ,
+                ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, false)
+                        .getBand());
+
+        if (SdkLevel.isAtLeastS()) {
+            // Bridged mode AP configuration
+            int[] bands = {SoftApConfiguration.BAND_2GHZ,
+                    SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ};
+            config = new SoftApConfiguration.Builder()
+                    .setBands(bands)
+                    .setPassphrase("somepassword",
+                            SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+                    .build();
+            // Expected to Keep 6G band
+            assertArrayEquals(bands,
+                    ApConfigUtil.remove6gBandForUnsupportedSecurity(mResources, config, true)
+                            .getBands());
+        }
+
+    }
+
+    /**
      * Verify default band and channel is used when HAL support is
      * not available.
      */
@@ -704,7 +762,7 @@
         configBuilder.setChannel(36, SoftApConfiguration.BAND_5GHZ);
 
         when(mWifiNative.isHalStarted()).thenReturn(false);
-        assertEquals(ApConfigUtil.SUCCESS,
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources,
                         TEST_COUNTRY_CODE, configBuilder, configBuilder.build(), mCapability));
         /* Verify default band and channel is used. */
@@ -721,7 +779,7 @@
         Builder configBuilder = new SoftApConfiguration.Builder();
         configBuilder.setBand(SoftApConfiguration.BAND_5GHZ);
         when(mWifiNative.isHalStarted()).thenReturn(true);
-        assertEquals(ApConfigUtil.ERROR_GENERIC,
+        assertEquals(SoftApManager.START_RESULT_FAILURE_GENERAL,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources, null,
                         configBuilder, configBuilder.build(), mCapability));
     }
@@ -734,7 +792,7 @@
         Builder configBuilder = new SoftApConfiguration.Builder();
         configBuilder.setChannel(36, SoftApConfiguration.BAND_5GHZ);
         when(mWifiNative.isHalStarted()).thenReturn(true);
-        assertEquals(ApConfigUtil.SUCCESS,
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources,
                         TEST_COUNTRY_CODE, configBuilder, configBuilder.build(), mCapability));
         assertEquals(SoftApConfiguration.BAND_5GHZ, configBuilder.build().getBand());
@@ -753,7 +811,7 @@
         when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ))
                 .thenReturn(EMPTY_CHANNEL_LIST);
         mCapability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, new int[0]);
-        assertEquals(ApConfigUtil.ERROR_NO_CHANNEL,
+        assertEquals(SoftApManager.START_RESULT_FAILURE_NO_CHANNEL,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources,
                         TEST_COUNTRY_CODE, configBuilder, configBuilder.build(), mCapability));
     }
@@ -782,7 +840,7 @@
                 .thenReturn(ALLOWED_5G_FREQS); // ch# 149, 153
         mCapability = ApConfigUtil.updateSoftApCapabilityWithAvailableChannelList(mCapability,
                 mContext, mWifiNative);
-        assertEquals(ApConfigUtil.SUCCESS,
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources,
                         TEST_COUNTRY_CODE, configBuilder, configBuilder.build(),
                         mCapability));
@@ -806,7 +864,7 @@
         when(mWifiNative.isHalStarted()).thenReturn(true);
         when(mWifiNative.getUsableChannels(anyInt(), anyInt(), anyInt()))
                 .thenReturn(new ArrayList<>());
-        assertEquals(ApConfigUtil.SUCCESS,
+        assertEquals(SoftApManager.START_RESULT_SUCCESS,
                 ApConfigUtil.updateApChannelConfig(mWifiNative, mCoexManager, mResources,
                         TEST_COUNTRY_CODE, configBuilder, configBuilder.build(),
                         mCapability));
@@ -845,6 +903,23 @@
                 capability);
     }
 
+
+    /**
+     * Verify updating capability from config store.
+     * Force 11BE capa to be true and then try to set it to false
+     * using updateCapabilityFromConfigStore
+     * assert if capability still has 11BE enabled.
+     */
+    @Test
+    public void testSoftApCapabilityInitWithWifiConfiguration() throws Exception {
+        long features = 0;
+        // Forcefully make 11BE as true in capability
+        features |= SOFTAP_FEATURE_IEEE80211_BE;
+        SoftApCapability capability = new SoftApCapability(features);
+        ApConfigUtil.updateCapabilityFromConfigStore(capability, mConfigStore);
+        assertFalse(capability.areFeaturesSupported(SOFTAP_FEATURE_IEEE80211_BE));
+    }
+
     @Test
     public void testConvertInvalidWifiConfigurationToSoftApConfiguration() throws Exception {
         WifiConfiguration wifiConfig = new WifiConfiguration();
diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/HalAidlUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/HalAidlUtilTest.java
new file mode 100644
index 0000000..7703e0a
--- /dev/null
+++ b/service/tests/wifitests/src/com/android/server/wifi/util/HalAidlUtilTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.util;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.net.wifi.util.PersistableBundleUtils;
+import android.os.PersistableBundle;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link HalAidlUtil}.
+ */
+public class HalAidlUtilTest {
+    private static final int TEST_VENDOR_DATA_LIST_SIZE = 10;
+
+    @Before
+    public void setUp() throws Exception {
+        assumeTrue(SdkLevel.isAtLeastV());
+    }
+
+    private static PersistableBundle createTestPersistableBundle() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString("stringKey", "someStringData");
+        bundle.putInt("intKey", 55);
+        bundle.putIntArray("arrayKey", new int[]{1, 2, 3});
+        return bundle;
+    }
+
+    private static android.net.wifi.OuiKeyedData createFrameworkOuiKeyedData(int oui) {
+        return new android.net.wifi.OuiKeyedData.Builder(
+                oui, createTestPersistableBundle()).build();
+    }
+
+    private static boolean frameworkAndHalOuiKeyedDataEqual(
+            android.net.wifi.OuiKeyedData frameworkData,
+            android.hardware.wifi.common.OuiKeyedData halData) {
+        return (frameworkData.getOui() == halData.oui)
+                && PersistableBundleUtils.isEqual(frameworkData.getData(), halData.vendorData);
+    }
+
+    /**
+     * Test the conversion of a valid framework OuiKeyedData list to its HAL equivalent.
+     */
+    @Test
+    public void testConvertOuiKeyedDataToHal() {
+        List<android.net.wifi.OuiKeyedData> frameworkList = new ArrayList<>();
+        for (int i = 0; i < TEST_VENDOR_DATA_LIST_SIZE; i++) {
+            frameworkList.add(createFrameworkOuiKeyedData(i + 1));
+        }
+
+        android.hardware.wifi.common.OuiKeyedData[] halList =
+                HalAidlUtil.frameworkToHalOuiKeyedDataList(frameworkList);
+        assertEquals(frameworkList.size(), halList.length);
+        for (int i = 0; i < TEST_VENDOR_DATA_LIST_SIZE; i++) {
+            assertTrue(frameworkAndHalOuiKeyedDataEqual(frameworkList.get(i), halList[i]));
+        }
+    }
+
+    /**
+     * Test the conversion of an invalid OuiKeyedData list. Invalid entries should be ignored.
+     */
+    @Test
+    public void testConvertOuiKeyedDataToHal_invalid() {
+        List<android.net.wifi.OuiKeyedData> frameworkList = new ArrayList<>();
+        for (int i = 0; i < TEST_VENDOR_DATA_LIST_SIZE; i++) {
+            // Fill framework list with null entries.
+            frameworkList.add(null);
+        }
+
+        // No entries should appear in the converted list.
+        android.hardware.wifi.common.OuiKeyedData[] halList =
+                HalAidlUtil.frameworkToHalOuiKeyedDataList(frameworkList);
+        assertEquals(0, halList.length);
+    }
+}
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 f4d69dc..f595de3 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
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.ScanResult.InformationElement;
 
@@ -29,6 +30,8 @@
 import com.android.server.wifi.MboOceConstants;
 import com.android.server.wifi.WifiBaseTest;
 import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.util.InformationElementUtil.ApType6GHz;
+import com.android.server.wifi.util.InformationElementUtil.EhtOperation;
 import com.android.server.wifi.util.InformationElementUtil.HeOperation;
 import com.android.server.wifi.util.InformationElementUtil.HtOperation;
 import com.android.server.wifi.util.InformationElementUtil.VhtOperation;
@@ -295,6 +298,169 @@
                 testByteArray[3], results[0].bytes[0]);
     }
 
+    /** Verify subelement fragmentation within a fragmented element. */
+    @Test
+    public void parseInformationElementWithTwoLevelFragmentation() throws IOException {
+
+        /**
+         *   Format of the Multi link element
+         *
+         *   Ext Tag: Multi-Link
+         *     Ext Tag length: 578 (Tag len: 579)   [fragmented]
+         *     Ext Tag Number: Multi-Link
+         *      Multi-Link Control: 0x01b0
+         *      Common Info
+         *      Subelement ID: Per-STA Profile
+         *      Subelement Length: 309              [fragmented]
+         *      Per-STA Profile 1
+         *        Per-STA Profile, Link-ID = 0
+         *      Subelement ID: Per-STA Profile
+         *      Subelement Length: 248
+         *      Per-STA Profile 2
+         *        Per-STA Profile, Link-ID = 1
+         *
+         *      Basic STA Profile Count: 2
+         *      STA Profiles LinkIds: 0_1
+         */
+        byte[] testByteArray =
+                new byte[] {
+                    (byte) 0xff, (byte) 0xff, (byte) 0x6b, (byte) 0xb0, (byte) 0x01, (byte) 0x0d,
+                    (byte) 0x40, (byte) 0xed, (byte) 0x00, (byte) 0x14, (byte) 0xf9, (byte) 0xf1,
+                    (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x42, (byte) 0x00,
+                    (byte) 0x00, (byte) 0xff, (byte) 0xf0, (byte) 0x01, (byte) 0x13, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x64,
+                    (byte) 0x00, (byte) 0x22, (byte) 0xa8, (byte) 0x31, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x31,
+                    (byte) 0x04, (byte) 0x01, (byte) 0x08, (byte) 0x82, (byte) 0x84, (byte) 0x8b,
+                    (byte) 0x96, (byte) 0x0c, (byte) 0x12, (byte) 0x18, (byte) 0x24, (byte) 0x03,
+                    (byte) 0x01, (byte) 0x06, (byte) 0x07, (byte) 0x06, (byte) 0x55, (byte) 0x53,
+                    (byte) 0x20, (byte) 0x01, (byte) 0x0b, (byte) 0x1e, (byte) 0x2a, (byte) 0x01,
+                    (byte) 0x00, (byte) 0x32, (byte) 0x04, (byte) 0x30, (byte) 0x48, (byte) 0x60,
+                    (byte) 0x6c, (byte) 0x2d, (byte) 0x1a, (byte) 0x8d, (byte) 0x09, (byte) 0x03,
+                    (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x3d,
+                    (byte) 0x16, (byte) 0x06, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x4a,
+                    (byte) 0x0e, (byte) 0x14, (byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x2c,
+                    (byte) 0x01, (byte) 0xc8, (byte) 0x00, (byte) 0x14, (byte) 0x00, (byte) 0x05,
+                    (byte) 0x00, (byte) 0x19, (byte) 0x00, (byte) 0x7f, (byte) 0x08, (byte) 0x05,
+                    (byte) 0x00, (byte) 0x0f, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x40, (byte) 0xbf, (byte) 0x0c, (byte) 0x92, (byte) 0x79, (byte) 0x83,
+                    (byte) 0x33, (byte) 0xaa, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0xaa,
+                    (byte) 0xff, (byte) 0x00, (byte) 0x20, (byte) 0xc0, (byte) 0x05, (byte) 0x00,
+                    (byte) 0x06, (byte) 0x00, (byte) 0xfc, (byte) 0xff, (byte) 0xff, (byte) 0x1d,
+                    (byte) 0x23, (byte) 0x09, (byte) 0x01, (byte) 0x08, (byte) 0x1a, (byte) 0x40,
+                    (byte) 0x10, (byte) 0x00, (byte) 0x60, (byte) 0x40, (byte) 0x88, (byte) 0x0f,
+                    (byte) 0x43, (byte) 0x81, (byte) 0x1c, (byte) 0x11, (byte) 0x08, (byte) 0x00,
+                    (byte) 0xaa, (byte) 0xff, (byte) 0xaa, (byte) 0xff, (byte) 0x1b, (byte) 0x1c,
+                    (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0xff,
+                    (byte) 0x07, (byte) 0x24, (byte) 0xf4, (byte) 0x3f, (byte) 0x00, (byte) 0x2e,
+                    (byte) 0xfc, (byte) 0xff, (byte) 0xff, (byte) 0x0f, (byte) 0x6c, (byte) 0x80,
+                    (byte) 0x00, (byte) 0xe0, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x18,
+                    (byte) 0x36, (byte) 0x08, (byte) 0x12, (byte) 0x00, (byte) 0x44, (byte) 0x44,
+                    (byte) 0x44, (byte) 0xff, (byte) 0x06, (byte) 0x6a, (byte) 0x04, (byte) 0x11,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x17, (byte) 0x8c,
+                    (byte) 0xfd, (byte) 0xf0, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x01,
+                    (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x03,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0xf2,
+                    (byte) 0xff, (byte) 0x01, (byte) 0x09, (byte) 0x02, (byte) 0x0f, (byte) 0x00,
+                    (byte) 0xdd, (byte) 0x18, (byte) 0x00, (byte) 0x50, (byte) 0xf2, (byte) 0x02,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x80, (byte) 0x00, (byte) 0x03, (byte) 0xa4,
+                    (byte) 0x00, (byte) 0xfe, (byte) 0x36, (byte) 0x00, (byte) 0x27, (byte) 0xa4,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x42, (byte) 0x43, (byte) 0x5e, (byte) 0x00,
+                    (byte) 0x62, (byte) 0x32, (byte) 0x2f, (byte) 0x00, (byte) 0xdd, (byte) 0x16,
+                    (byte) 0x8c, (byte) 0xfd, (byte) 0xf0, (byte) 0x04, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x49, (byte) 0x4c, (byte) 0x51, (byte) 0x03, (byte) 0x02, (byte) 0x09,
+                    (byte) 0x72, (byte) 0x01, (byte) 0xcb, (byte) 0x17, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x09, (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x07,
+                    (byte) 0x8c, (byte) 0xfd, (byte) 0xf0, (byte) 0x04, (byte) 0x01, (byte) 0x01,
+                    (byte) 0x00, (byte) 0xff, (byte) 0x06, (byte) 0x38, (byte) 0x03, (byte) 0x20,
+                    (byte) 0x23, (byte) 0xc3, (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0xf1,
+                    (byte) 0x01, (byte) 0x13, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x02, (byte) 0x64, (byte) 0x00, (byte) 0x0c, (byte) 0x74,
+                    (byte) 0x19, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x00, (byte) 0x03, (byte) 0x11, (byte) 0x05, (byte) 0x07, (byte) 0x0a,
+                    (byte) 0x55, (byte) 0x53, (byte) 0x04, (byte) 0xc9, (byte) 0x83, (byte) 0x00,
+                    (byte) 0x21, (byte) 0x33, (byte) 0x00, (byte) 0x00, (byte) 0x23, (byte) 0x02,
+                    (byte) 0x13, (byte) 0x00, (byte) 0x7f, (byte) 0x0b, (byte) 0x04, (byte) 0x00,
+                    (byte) 0x4f, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x09, (byte) 0xc3, (byte) 0x05, (byte) 0x53,
+                    (byte) 0x3c, (byte) 0x3c, (byte) 0x3c, (byte) 0x3c, (byte) 0xc3, (byte) 0x05,
+                    (byte) 0x13, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0xff,
+                    (byte) 0x27, (byte) 0x23, (byte) 0x09, (byte) 0x01, (byte) 0x08, (byte) 0x1a,
+                    (byte) 0x40, (byte) 0x10, (byte) 0x0c, (byte) 0x63, (byte) 0x40, (byte) 0x88,
+                    (byte) 0xfd, (byte) 0x5b, (byte) 0x81, (byte) 0x1c, (byte) 0x11, (byte) 0x08,
+                    (byte) 0x00, (byte) 0xaa, (byte) 0xff, (byte) 0xaa, (byte) 0xff, (byte) 0xaa,
+                    (byte) 0xff, (byte) 0xaa, (byte) 0xff, (byte) 0x7b, (byte) 0x1c, (byte) 0xc7,
+                    (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0xc7,
+                    (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0xff, (byte) 0x0c,
+                    (byte) 0x24, (byte) 0xf4, (byte) 0x3f, (byte) 0x02, (byte) 0x13, (byte) 0xfc,
+                    (byte) 0xff, (byte) 0x45, (byte) 0x03, (byte) 0x47, (byte) 0x4f, (byte) 0x01,
+                    (byte) 0xff, (byte) 0x03, (byte) 0x3b, (byte) 0xb8, (byte) 0x36, (byte) 0xff,
+                    (byte) 0x12, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0xe0, (byte) 0x1f,
+                    (byte) 0x1b, (byte) 0x00, (byte) 0x18, (byte) 0x36, (byte) 0xd8, (byte) 0x36,
+                    (byte) 0x00, (byte) 0x44, (byte) 0x44, (byte) 0x44, (byte) 0x44, (byte) 0x44,
+                    (byte) 0x44, (byte) 0xff, (byte) 0x06, (byte) 0x6a, (byte) 0x04, (byte) 0x11,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x17, (byte) 0x8c,
+                    (byte) 0xfd, (byte) 0xf0, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x01,
+                    (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x03,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01,
+                    (byte) 0x09, (byte) 0x02, (byte) 0x0f, (byte) 0x0f, (byte) 0xf2, (byte) 0x45,
+                    (byte) 0xdd, (byte) 0x18, (byte) 0x00, (byte) 0x50, (byte) 0xf2, (byte) 0x02,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x80, (byte) 0x00, (byte) 0x03, (byte) 0xa4,
+                    (byte) 0x00, (byte) 0x00, (byte) 0x27, (byte) 0xa4, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x42, (byte) 0x43, (byte) 0x5e, (byte) 0x00, (byte) 0x62, (byte) 0x32,
+                    (byte) 0x2f, (byte) 0x00, (byte) 0xdd, (byte) 0x16, (byte) 0x8c, (byte) 0xfd,
+                    (byte) 0xf0, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x49, (byte) 0x4c,
+                    (byte) 0x51, (byte) 0x03, (byte) 0x02, (byte) 0x09, (byte) 0x72, (byte) 0x01,
+                    (byte) 0xcb, (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x09, (byte) 0x11,
+                    (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x07, (byte) 0x8c, (byte) 0xfd,
+                    (byte) 0xf0, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xff,
+                    (byte) 0x08, (byte) 0x38, (byte) 0x05, (byte) 0x2d, (byte) 0x3d, (byte) 0xbf,
+                    (byte) 0xc0, (byte) 0xc9, (byte) 0x00
+                };
+
+        final int MAX_NUM_IES = 3;
+        // Generate multiple IE's concatenated
+        ByteArrayOutputStream multiLinkIes = new ByteArrayOutputStream();
+        for (int i = 0; i < MAX_NUM_IES; ++i) {
+            multiLinkIes.write(testByteArray);
+        }
+
+        /* Multi link element fragmentation verification */
+        InformationElement[] results =
+                InformationElementUtil.parseInformationElements(multiLinkIes.toByteArray());
+        assertEquals("Parsed results should have 1 element", MAX_NUM_IES, results.length);
+        assertEquals(
+                "First result should have id = EID_EXTENSION_PRESENT",
+                InformationElement.EID_EXTENSION_PRESENT,
+                results[0].id);
+        assertEquals(
+                "First result should have idExt = " + InformationElement.EID_EXT_MULTI_LINK,
+                InformationElement.EID_EXT_MULTI_LINK,
+                results[0].idExt);
+        assertEquals("First result should have data of 578 bytes", 578, results[0].bytes.length);
+
+        /* Per STA profile sub-element fragmentation verification */
+        InformationElementUtil.MultiLink multiLink = new InformationElementUtil.MultiLink();
+        multiLink.from(results[0]);
+        assertTrue(multiLink.isPresent());
+        assertEquals(2, multiLink.getLinkId());
+        assertEquals(2, multiLink.getAffiliatedLinks().size());
+        assertEquals(0, multiLink.getAffiliatedLinks().get(0).getLinkId());
+        assertEquals(
+                MacAddress.fromString("00:00:00:00:00:01"),
+                multiLink.getAffiliatedLinks().get(0).getApMacAddress());
+        assertEquals(1, multiLink.getAffiliatedLinks().get(1).getLinkId());
+        assertEquals(
+                MacAddress.fromString("00:00:00:00:00:02"),
+                multiLink.getAffiliatedLinks().get(1).getApMacAddress());
+    }
+
     private void verifyCapabilityStringFromIes(
             InformationElement[] ies, int beaconCap, boolean isOweSupported,
             String capsStr) {
@@ -1093,6 +1259,11 @@
         extendedCap.from(ie);
         assertFalse(extendedCap.isStrictUtf8());
         assertFalse(extendedCap.is80211McRTTResponder());
+        assertFalse(extendedCap.isTriggerBasedRangingRespSupported());
+        assertFalse(extendedCap.isNonTriggerBasedRangingRespSupported());
+        assertFalse(extendedCap.isTwtRequesterSupported());
+        assertFalse(extendedCap.isTwtResponderSupported());
+        assertFalse(extendedCap.isFilsCapable());
     }
 
     /**
@@ -1133,6 +1304,28 @@
     }
 
     /**
+     * Verify Extended Capabilities: trigger and non-trigger based ranging support, TWT requester
+     * and responder support and FILS support.
+     */
+    @Test
+    public void testExtendedCapabilitiesMisc() {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENDED_CAPS;
+        ie.bytes = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x0c };
+
+        InformationElementUtil.ExtendedCapabilities extendedCap =
+                new InformationElementUtil.ExtendedCapabilities();
+        extendedCap.from(ie);
+        assertTrue(extendedCap.isTriggerBasedRangingRespSupported());
+        assertTrue(extendedCap.isNonTriggerBasedRangingRespSupported());
+        assertTrue(extendedCap.isTwtRequesterSupported());
+        assertTrue(extendedCap.isTwtResponderSupported());
+        assertTrue(extendedCap.isFilsCapable());
+    }
+
+    /**
      * Test a that a correctly formed TIM Information Element is decoded into a valid TIM element,
      * and the values are captured
      */
@@ -1546,7 +1739,7 @@
          *          3                1            2           0/3           0/1         0/5
          *
          * HE Operation Info:
-         *    |  Misc | VHT Operatoin Info | Misc | 6 GHz Operation Info Present | reserved |
+         *    |  Misc | VHT Operation Info | Misc | 6 GHz Operation Info Present | reserved |
          * bits:  14           1              2                   1                   6
          *
          * 6GHz Info Format:
@@ -1591,7 +1784,7 @@
          *          3                1            2           0/3           0/1         0/5
          *
          * HE Operation Info:
-         *    |  Misc | VHT Operatoin Info | Misc | 6 GHz Operation Info Present | reserved |
+         *    |  Misc | VHT Operation Info | Misc | 6 GHz Operation Info Present | reserved |
          * bits:  14           1              2                   1                   6
          *
          * 6GHz Info Format:
@@ -1636,7 +1829,7 @@
          *          3                1            2           0/3           0/1         0/5
          *
          * HE Operation Info:
-         *    |  Misc | VHT Operatoin Info | Misc | 6 GHz Operation Info Present | reserved |
+         *    |  Misc | VHT Operation Info | Misc | 6 GHz Operation Info Present | reserved |
          * bits:  14           1              2                   1                   6
          *
          */
@@ -1675,7 +1868,7 @@
          *          3                1            2           0/3           0/1         0/5
          *
          * HE Operation Info:
-         *    |  Misc | VHT Operatoin Info | Misc | 6 GHz Operation Info Present | reserved |
+         *    |  Misc | VHT Operation Info | Misc | 6 GHz Operation Info Present | reserved |
          * bits:  14           1              2                   1                   6
          *
          * VHT Operation Info Format:
@@ -1702,6 +1895,45 @@
         assertEquals(5200, vhtOperation.getCenterFreq0());
         assertEquals(0, vhtOperation.getCenterFreq1());
     }
+
+    /**
+     * Verify TWT Info, 6 Ghz Access Point Type
+     */
+    @Test
+    public void testHeOperationMisc() throws Exception {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_HE_OPERATION;
+        /**
+         * HE Operation Format:
+         * | HE Operation Info | BSS Color | Basic HE-MCS | VHT Info  | Cohosted BSS| 6GH Info |
+         *          3                1            2           0/3           0/1         0/5
+         *
+         * HE Operation Info:
+         *    |  Misc | VHT Operation Info | Misc | 6 GHz Operation Info Present | reserved |
+         * bits:  14           1              2                   1                   6
+         *
+         * 6GHz Info Format:
+         * | Primary Channel | Control | Center Freq Seg 0 | Center Freq Seg 1 | Min Rate |
+         *         1             1               1                  1               1
+         *
+         * Control Field:
+         *       | Channel Width | Duplicate Beacon | Reg Info | Reserved |
+         * bits:        2             1                  3          2
+         *
+         */
+        ie.bytes = new byte[]{(byte) 0x08, (byte) 0x00, (byte) 0x02,  //HE Operation Info
+            (byte) 0x00, (byte) 0x00, (byte) 0x00,  // BSS Color and HE-MCS
+            (byte) 0x10, (byte) 0x0B, (byte) 0x14, (byte) 0x1C, (byte) 0x00};
+
+        HeOperation heOperation = new HeOperation();
+        heOperation.from(ie);
+        assertTrue(heOperation.isPresent());
+        assertTrue(heOperation.is6GhzInfoPresent());
+        assertTrue(heOperation.isTwtRequired());
+        assertEquals(ApType6GHz.AP_TYPE_6GHZ_STANDARD_POWER, heOperation.getApType6GHz());
+    }
+
     /**
      * Verify that the expected max number of spatial stream is parsed correctly from
      * HT capabilities IE
@@ -1793,6 +2025,34 @@
         assertEquals(true, heCapabilities.isPresent());
     }
 
+    @Test
+    public void testTwtHeCapabilities() throws Exception {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_HE_CAPABILITIES;
+        /**
+         * HE Capabilities IE Format:
+         * | HE MAC Capabilities Info | HE PHY Capabilities Info | Supported HE-MCS and NSS Set |
+         *           6                              11                     4
+         *
+         * HE MAC Capabilities Info format:
+         *      B1 : TWT Requester Support
+         *      B2 : TWT Responder Support
+         *      B20: Broadcast TWT Support
+         */
+        ie.bytes = new byte[]{(byte) 0x06, (byte) 0x00, (byte) 0x10, (byte) 0x02, (byte) 0x40,
+            (byte) 0x04, (byte) 0x70, (byte) 0x0c, (byte) 0x80, (byte) 0x00, (byte) 0x07,
+            (byte) 0x80, (byte) 0x04, (byte) 0x00, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
+            (byte) 0xaa, (byte) 0x7f, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0x1c,
+            (byte) 0xc7, (byte) 0x71};
+        InformationElementUtil.HeCapabilities heCapabilities =
+                new InformationElementUtil.HeCapabilities();
+        heCapabilities.from(ie);
+        assertEquals(true, heCapabilities.isTwtRequesterSupported());
+        assertEquals(true, heCapabilities.isTwtResponderSupported());
+        assertEquals(true, heCapabilities.isBroadcastTwtSupported());
+    }
+
     /**
      * Verify that the expected MBO-OCE Vendor Specific information
      * element is parsed and MBO AP Capability Indication is
@@ -2040,6 +2300,135 @@
     }
 
     /**
+     * Verify that the expected Multi-Link information element with fragmented link info to be
+     * parsed correctly.
+     */
+    @Test
+    public void parseMultiLinkIeWithFragmentedLinkInfo() throws Exception {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_MULTI_LINK;
+
+        ie.bytes = new byte[] {
+                /* Multi-link Control */
+                (byte) 0xb0, (byte) 0x01,
+                /* Common Info */
+                (byte) 0x0d, (byte) 0x40, (byte) 0xed, (byte) 0x00, (byte) 0x14, (byte) 0xf9,
+                (byte) 0xf1, (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x42,
+                (byte) 0x00,
+                /* Per STA Profile 1: Fragmented */
+                (byte) 0x00, (byte) 0xff, (byte) 0xf0, (byte) 0x01, (byte) 0x13, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x64,
+                (byte) 0x00, (byte) 0x22, (byte) 0xa8, (byte) 0x31, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x31,
+                (byte) 0x04, (byte) 0x01, (byte) 0x08, (byte) 0x82, (byte) 0x84, (byte) 0x8b,
+                (byte) 0x96, (byte) 0x0c, (byte) 0x12, (byte) 0x18, (byte) 0x24, (byte) 0x03,
+                (byte) 0x01, (byte) 0x06, (byte) 0x07, (byte) 0x06, (byte) 0x55, (byte) 0x53,
+                (byte) 0x20, (byte) 0x01, (byte) 0x0b, (byte) 0x1e, (byte) 0x2a, (byte) 0x01,
+                (byte) 0x00, (byte) 0x32, (byte) 0x04, (byte) 0x30, (byte) 0x48, (byte) 0x60,
+                (byte) 0x6c, (byte) 0x2d, (byte) 0x1a, (byte) 0x8d, (byte) 0x09, (byte) 0x03,
+                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x3d,
+                (byte) 0x16, (byte) 0x06, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x4a,
+                (byte) 0x0e, (byte) 0x14, (byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x2c,
+                (byte) 0x01, (byte) 0xc8, (byte) 0x00, (byte) 0x14, (byte) 0x00, (byte) 0x05,
+                (byte) 0x00, (byte) 0x19, (byte) 0x00, (byte) 0x7f, (byte) 0x08, (byte) 0x05,
+                (byte) 0x00, (byte) 0x0f, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x40, (byte) 0xbf, (byte) 0x0c, (byte) 0x92, (byte) 0x79, (byte) 0x83,
+                (byte) 0x33, (byte) 0xaa, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0xaa,
+                (byte) 0xff, (byte) 0x00, (byte) 0x20, (byte) 0xc0, (byte) 0x05, (byte) 0x00,
+                (byte) 0x06, (byte) 0x00, (byte) 0xfc, (byte) 0xff, (byte) 0xff, (byte) 0x1d,
+                (byte) 0x23, (byte) 0x09, (byte) 0x01, (byte) 0x08, (byte) 0x1a, (byte) 0x40,
+                (byte) 0x10, (byte) 0x00, (byte) 0x60, (byte) 0x40, (byte) 0x88, (byte) 0x0f,
+                (byte) 0x43, (byte) 0x81, (byte) 0x1c, (byte) 0x11, (byte) 0x08, (byte) 0x00,
+                (byte) 0xaa, (byte) 0xff, (byte) 0xaa, (byte) 0xff, (byte) 0x1b, (byte) 0x1c,
+                (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0xff,
+                (byte) 0x07, (byte) 0x24, (byte) 0xf4, (byte) 0x3f, (byte) 0x00, (byte) 0x2e,
+                (byte) 0xfc, (byte) 0xff, (byte) 0xff, (byte) 0x0f, (byte) 0x6c, (byte) 0x80,
+                (byte) 0x00, (byte) 0xe0, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x18,
+                (byte) 0x36, (byte) 0x08, (byte) 0x12, (byte) 0x00, (byte) 0x44, (byte) 0x44,
+                (byte) 0x44, (byte) 0xff, (byte) 0x06, (byte) 0x6a, (byte) 0x04, (byte) 0x11,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x17, (byte) 0x8c,
+                (byte) 0xfd, (byte) 0xf0, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x01,
+                (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x03,
+                (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01,
+                (byte) 0x09, (byte) 0x02, (byte) 0x0f, (byte) 0x00, (byte) 0xdd, (byte) 0x18,
+                (byte) 0x00, (byte) 0x50, (byte) 0xf2, (byte) 0x02, (byte) 0x01, (byte) 0x01,
+                (byte) 0x80, (byte) 0x00, (byte) 0x03, (byte) 0xa4, (byte) 0x00, (byte) 0xfe,
+                (byte) 0x36, (byte) 0x00, (byte) 0x27, (byte) 0xa4, (byte) 0x00, (byte) 0x00,
+                (byte) 0x42, (byte) 0x43, (byte) 0x5e, (byte) 0x00, (byte) 0x62, (byte) 0x32,
+                (byte) 0x2f, (byte) 0x00, (byte) 0xdd, (byte) 0x16, (byte) 0x8c, (byte) 0xfd,
+                (byte) 0xf0, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x49, (byte) 0x4c,
+                (byte) 0x51, (byte) 0x03, (byte) 0x02, (byte) 0x09, (byte) 0x72, (byte) 0x01,
+                (byte) 0xcb, (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x09, (byte) 0x11,
+                (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x07, (byte) 0x8c, (byte) 0xfd,
+                (byte) 0xf0, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xff,
+                (byte) 0x06, (byte) 0x38, (byte) 0x03, (byte) 0x20, (byte) 0x23, (byte) 0xc3,
+                (byte) 0x00,
+                /* Per STA Profile 1: Non Fragmented */
+                (byte) 0x00, (byte) 0xf8, (byte) 0xf1, (byte) 0x01, (byte) 0x13, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x64,
+                (byte) 0x00, (byte) 0x0c, (byte) 0x74, (byte) 0x19, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x11,
+                (byte) 0x05, (byte) 0x07, (byte) 0x0a, (byte) 0x55, (byte) 0x53, (byte) 0x04,
+                (byte) 0xc9, (byte) 0x83, (byte) 0x00, (byte) 0x21, (byte) 0x33, (byte) 0x00,
+                (byte) 0x00, (byte) 0x23, (byte) 0x02, (byte) 0x13, (byte) 0x00, (byte) 0x7f,
+                (byte) 0x0b, (byte) 0x04, (byte) 0x00, (byte) 0x4f, (byte) 0x02, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x09,
+                (byte) 0xc3, (byte) 0x05, (byte) 0x53, (byte) 0x3c, (byte) 0x3c, (byte) 0x3c,
+                (byte) 0x3c, (byte) 0xc3, (byte) 0x05, (byte) 0x13, (byte) 0x30, (byte) 0x30,
+                (byte) 0x30, (byte) 0x30, (byte) 0xff, (byte) 0x27, (byte) 0x23, (byte) 0x09,
+                (byte) 0x01, (byte) 0x08, (byte) 0x1a, (byte) 0x40, (byte) 0x10, (byte) 0x0c,
+                (byte) 0x63, (byte) 0x40, (byte) 0x88, (byte) 0xfd, (byte) 0x5b, (byte) 0x81,
+                (byte) 0x1c, (byte) 0x11, (byte) 0x08, (byte) 0x00, (byte) 0xaa, (byte) 0xff,
+                (byte) 0xaa, (byte) 0xff, (byte) 0xaa, (byte) 0xff, (byte) 0xaa, (byte) 0xff,
+                (byte) 0x7b, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0xc7,
+                (byte) 0x71, (byte) 0x1c, (byte) 0xc7, (byte) 0x71, (byte) 0x1c, (byte) 0xc7,
+                (byte) 0x71, (byte) 0xff, (byte) 0x0c, (byte) 0x24, (byte) 0xf4, (byte) 0x3f,
+                (byte) 0x02, (byte) 0x13, (byte) 0xfc, (byte) 0xff, (byte) 0x45, (byte) 0x03,
+                (byte) 0x47, (byte) 0x4f, (byte) 0x01, (byte) 0xff, (byte) 0x03, (byte) 0x3b,
+                (byte) 0xb8, (byte) 0x36, (byte) 0xff, (byte) 0x12, (byte) 0x6c, (byte) 0x00,
+                (byte) 0x00, (byte) 0xe0, (byte) 0x1f, (byte) 0x1b, (byte) 0x00, (byte) 0x18,
+                (byte) 0x36, (byte) 0xd8, (byte) 0x36, (byte) 0x00, (byte) 0x44, (byte) 0x44,
+                (byte) 0x44, (byte) 0x44, (byte) 0x44, (byte) 0x44, (byte) 0xff, (byte) 0x06,
+                (byte) 0x6a, (byte) 0x04, (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0xdd, (byte) 0x17, (byte) 0x8c, (byte) 0xfd, (byte) 0xf0, (byte) 0x01,
+                (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x01,
+                (byte) 0x01, (byte) 0x03, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x00,
+                (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x09, (byte) 0x02, (byte) 0x0f,
+                (byte) 0x0f, (byte) 0xdd, (byte) 0x18, (byte) 0x00, (byte) 0x50, (byte) 0xf2,
+                (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x80, (byte) 0x00, (byte) 0x03,
+                (byte) 0xa4, (byte) 0x00, (byte) 0x00, (byte) 0x27, (byte) 0xa4, (byte) 0x00,
+                (byte) 0x00, (byte) 0x42, (byte) 0x43, (byte) 0x5e, (byte) 0x00, (byte) 0x62,
+                (byte) 0x32, (byte) 0x2f, (byte) 0x00, (byte) 0xdd, (byte) 0x16, (byte) 0x8c,
+                (byte) 0xfd, (byte) 0xf0, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x49,
+                (byte) 0x4c, (byte) 0x51, (byte) 0x03, (byte) 0x02, (byte) 0x09, (byte) 0x72,
+                (byte) 0x01, (byte) 0xcb, (byte) 0x17, (byte) 0x00, (byte) 0x00, (byte) 0x09,
+                (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0xdd, (byte) 0x07, (byte) 0x8c,
+                (byte) 0xfd, (byte) 0xf0, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x00,
+                (byte) 0xff, (byte) 0x08, (byte) 0x38, (byte) 0x05, (byte) 0x2d, (byte) 0x3d,
+                (byte) 0xbf, (byte) 0xc0, (byte) 0xc9, (byte) 0x00
+        };
+        InformationElementUtil.MultiLink multiLink = new InformationElementUtil.MultiLink();
+        multiLink.from(ie);
+        assertTrue(multiLink.isPresent());
+        assertEquals(2, multiLink.getLinkId());
+        assertEquals(2, multiLink.getAffiliatedLinks().size());
+        assertEquals(0, multiLink.getAffiliatedLinks().get(0).getLinkId());
+        assertEquals(
+                MacAddress.fromString("00:00:00:00:00:01"),
+                multiLink.getAffiliatedLinks().get(0).getApMacAddress());
+        assertEquals(1, multiLink.getAffiliatedLinks().get(1).getLinkId());
+        assertEquals(
+                MacAddress.fromString("00:00:00:00:00:02"),
+                multiLink.getAffiliatedLinks().get(1).getApMacAddress());
+    }
+    /**
      * verify determineMode for various combinations.
      */
     @Test
@@ -2094,4 +2483,74 @@
         assertEquals("US", country.getCountryCode());
     }
     // TODO: SAE, OWN, SUITE_B
+
+    /**
+     * Verify EHT capabilities default values.
+     */
+    @Test
+    public void testEhtCapabilitiesNotSet() {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_EHT_CAPABILITIES;
+        ie.bytes = new byte[2];
+
+        InformationElementUtil.EhtCapabilities ehtCapabilities =
+                new InformationElementUtil.EhtCapabilities();
+        ehtCapabilities.from(ie);
+        assertFalse(ehtCapabilities.isRestrictedTwtSupported());
+        assertFalse(ehtCapabilities.isEpcsPriorityAccessSupported());
+    }
+
+    /**
+     * Verify EHT Capabilities, R-TWT support and EPCS priority support.
+     */
+    @Test
+    public void testEhtCapabilities() {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_EHT_CAPABILITIES;
+        ie.bytes = new byte[]{(byte) 0x11, (byte) 0x00};
+
+        InformationElementUtil.EhtCapabilities ehtCapabilities =
+                new InformationElementUtil.EhtCapabilities();
+        ehtCapabilities.from(ie);
+        assertTrue(ehtCapabilities.isRestrictedTwtSupported());
+        assertTrue(ehtCapabilities.isEpcsPriorityAccessSupported());
+    }
+
+    /**
+     * Verify that the expected EHT Operation information element is parsed and
+     * DisabledSubchannelBitmap is present.
+     */
+    @Test
+    public void testEhtOperationElementWithDisabledSubchannelBitmapPresent() throws Exception {
+        InformationElement ie = new InformationElement();
+        ie.id = InformationElement.EID_EXTENSION_PRESENT;
+        ie.idExt = InformationElement.EID_EXT_EHT_OPERATION;
+        /**
+         * EHT Operation Format:
+         * | EHT Operation Param | Basic EHT-MCS | EHT Operation Info |
+         *          1                   1                0/3/5
+         *
+         * EHT Operation Param:
+         * |  EHT Operation Info Present | Disabled Subchannel Bitmap present |
+         * bits:       1                                  1                        ...
+         *
+         * EHT Operation Info:
+         * | Control | CCFS0 | CCFS 1 | Disabled Subchannel Bitmap |
+         *     1         1       1             0/2
+         */
+        ie.bytes = new byte[]{(byte) 0x03, //EHT Operation Param
+                (byte) 0xfc, (byte) 0xff, (byte) 0xfc, (byte) 0xff, //EHT-MCS
+                (byte) 0x03, (byte) 0x32, (byte) 0x32, //EHT Operation Info: Control, CCFS0, CCFS1
+                (byte) 0x03, (byte) 0x00}; //EHT Operation Info: Disabled Subchannel Bitmap
+
+        EhtOperation ehtOperation = new EhtOperation();
+        ehtOperation.from(ie);
+
+        assertTrue(ehtOperation.isPresent());
+        assertTrue(ehtOperation.isDisabledSubchannelBitmapPresent());
+        assertArrayEquals(new byte[]{(byte) 0x3, (byte) 0x0},
+                ehtOperation.getDisabledSubchannelBitmap());
+    }
 }
diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
index f821b4e..79d9cce 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
@@ -543,6 +543,9 @@
 
         // Verify that hasNeverDetectedCaptivePortal returns false.
         assertFalse(retrieved.hasNeverDetectedCaptivePortal());
+
+        // Verify that hasEverValidatedInternetAccess returns true.
+        assertTrue(retrieved.hasEverValidatedInternetAccess());
     }
 
     /**
diff --git a/tests/hostsidetests/multidevices/test/aware/AndroidTest.xml b/tests/hostsidetests/multidevices/test/aware/AndroidTest.xml
index fd98ec2..b2ffdc2 100644
--- a/tests/hostsidetests/multidevices/test/aware/AndroidTest.xml
+++ b/tests/hostsidetests/multidevices/test/aware/AndroidTest.xml
@@ -20,11 +20,15 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.wifi.apex" />
 
     <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
         type="module_controller">
         <option name="required-feature" value="android.hardware.wifi.aware" />
     </object>
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.wifi" />
+    </object>
 
     <device name="device1">
         <!-- For coverage to work, the APK should not be uninstalled until after coverage is pulled.
@@ -33,6 +37,10 @@
         <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
             <option name="test-file-name" value="wifi_aware_snippet.apk" />
         </target_preparer>
+        <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+            <option name="force-skip-system-props" value="true" />
+            <option name="screen-always-on" value="on" />
+        </target_preparer>
         <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
             <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
             <option name="run-command" value="wm dismiss-keyguard" />
@@ -42,6 +50,10 @@
         <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
             <option name="test-file-name" value="wifi_aware_snippet.apk" />
         </target_preparer>
+        <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+            <option name="force-skip-system-props" value="true" />
+            <option name="screen-always-on" value="on" />
+        </target_preparer>
         <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
             <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
             <option name="run-command" value="wm dismiss-keyguard" />
diff --git a/tests/hostsidetests/multidevices/test/aware/wifi_aware_test.py b/tests/hostsidetests/multidevices/test/aware/wifi_aware_test.py
index 7950bb0..eebd6eb 100644
--- a/tests/hostsidetests/multidevices/test/aware/wifi_aware_test.py
+++ b/tests/hostsidetests/multidevices/test/aware/wifi_aware_test.py
@@ -56,6 +56,15 @@
             max_workers=2,
             raise_on_exception=True)
 
+    def on_fail(self, record):
+        logging.info('Collecting bugreports...')
+        android_device.take_bug_reports(
+            ads=[self.publisher, self.subscriber],
+            test_name=record.test_name,
+            begin_time=record.begin_time,
+            destination=self.current_test_info.output_path
+        )
+
     def test_discovery_base_test_case(self):
         is_unsolicited = True
         is_ranging_required = False
diff --git a/wifi-preupload-checks.xml b/wifi-preupload-checks.xml
new file mode 100644
index 0000000..0b02dc6
--- /dev/null
+++ b/wifi-preupload-checks.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+<module name="Checker">
+    <module name="TreeWalker">
+        <module name="RegexpSinglelineJava">
+            <property name="ignoreComments" value="true" />
+            <property name="severity" value="error" />
+            <property name="format" value="String.format(.*)" />
+            <property name="message" value="/String.format/ has performance issue" />
+        </module>
+        <module name="RegexpSinglelineJava">
+            <property name="ignoreComments" value="true" />
+            <property name="severity" value="error" />
+            <property name="format" value="\.stream(.*)" />
+            <property name="message" value="/Java.Stream/ has performance issue" />
+        </module>
+    </module>
+</module>
\ No newline at end of file
diff --git a/overlayable_hook.py b/wifi_upload_hook.py
similarity index 67%
rename from overlayable_hook.py
rename to wifi_upload_hook.py
index 52b33f7..55fa267 100755
--- a/overlayable_hook.py
+++ b/wifi_upload_hook.py
@@ -20,6 +20,14 @@
 import subprocess
 import sys
 
+BASE_DIR = "service/ServiceWifiResources/res/"
+OVERLAY_FILE = BASE_DIR + "values/overlayable.xml"
+CONFIG_FILE = BASE_DIR + "values/config.xml"
+STRING_FILE = BASE_DIR + "values/strings.xml"
+STYLES_FILE = BASE_DIR + "values/styles.xml"
+DRAWABLE_DIR = BASE_DIR + "drawable/"
+LAYOUT_DIR = BASE_DIR + "layout/"
+
 def is_commit_msg_valid(commit_msg):
     for line in commit_msg.splitlines():
         line = line.strip().lower()
@@ -39,27 +47,26 @@
     # otherwise assume in AOSP
     return True
 
-def get_changed_resource_file(base_dir, commit_files):
-    config_file = base_dir + "values/config.xml"
-    string_file = base_dir + "values/strings.xml"
-    styles_file = base_dir + "values/styles.xml"
-    drawable_dir = base_dir + "drawable/"
-    layout_dir = base_dir + "layout/"
-
+def get_changed_resource_file(commit_files):
     for commit_file in commit_files:
-        if commit_file == config_file:
+        if commit_file == STRING_FILE:
+            return STRING_FILE
+        if commit_file == CONFIG_FILE:
             return commit_file
-        if commit_file == string_file:
+        if commit_file == STYLES_FILE:
             return commit_file
-        if commit_file == styles_file:
+        if commit_file.startswith(DRAWABLE_DIR):
             return commit_file
-        if commit_file.startswith(drawable_dir):
-            return commit_file
-        if commit_file.startswith(layout_dir):
+        if commit_file.startswith(LAYOUT_DIR):
             return commit_file
     return None
 
-
+def is_commit_msg_has_translation_bug_id(commit_msg):
+    for line in commit_msg.splitlines():
+        line = line.strip().lower()
+        if line.startswith('bug: 294871353'):
+            return True
+    return False
 
 
 def main():
@@ -68,18 +75,25 @@
     parser.add_argument('commit_files', type=str, nargs='*', help='files changed in the commit')
     args = parser.parse_args()
 
-    base_dir = "service/ServiceWifiResources/res/"
-    overlay_file = base_dir + "values/overlayable.xml"
     commit_msg = args.commit_msg
     commit_files = args.commit_files
 
     if is_in_aosp():
         return 0
 
-    changed_file = get_changed_resource_file(base_dir, commit_files)
+    changed_file = get_changed_resource_file(commit_files)
 
     if not changed_file:
         return 0
+    if changed_file == STRING_FILE:
+        if not is_commit_msg_has_translation_bug_id(commit_msg):
+            print('This commit has changed: "{changed_file}".'.format(changed_file=changed_file))
+            print()
+            print('Please add the following line to your commit message')
+            print()
+            print('Bug: 294871353')
+            print()
+            return 1
 
     if is_commit_msg_valid(commit_msg):
         return 0
@@ -87,7 +101,7 @@
     print('This commit has changed: "{changed_file}".'.format(changed_file=changed_file))
     print()
     print('If this change added/changed/removed overlayable resources used by the Wifi Module, ')
-    print('please update the "{overlay_file}".'.format(overlay_file=overlay_file))
+    print('please update the "{overlay_file}".'.format(overlay_file=OVERLAY_FILE))
     print('and acknowledge you have done so by adding this line to your commit message:')
     print()
     print('Updated-Overlayable: TRUE')