Merge "Don't bring down LOHS when disabling Tethering."
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 72f638a..2caf27f 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -2574,6 +2574,8 @@
             wifiLockManager.handleScreenStateChanged(screenOn);
         }
 
+        mSarManager.handleScreenStateChanged(screenOn);
+
         if (mVerboseLoggingEnabled) log("handleScreenStateChanged Exit: " + screenOn);
     }
 
diff --git a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
index 0fae643..0c06488 100644
--- a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
+++ b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
@@ -23,7 +23,8 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -35,15 +36,18 @@
             "DeletedEphemeralSSIDList";
     private static final String XML_TAG_SSID_LIST = "SSIDList";
 
-    private Set<String> mSsidList;
+    private final Clock mClock;
+    private Map<String, Long> mSsidToTimeMap;
 
-    DeletedEphemeralSsidsStoreData() {}
+    DeletedEphemeralSsidsStoreData(Clock clock) {
+        mClock = clock;
+    }
 
     @Override
     public void serializeData(XmlSerializer out)
             throws XmlPullParserException, IOException {
-        if (mSsidList != null) {
-            XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidList);
+        if (mSsidToTimeMap != null) {
+            XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidToTimeMap);
         }
     }
 
@@ -62,7 +66,17 @@
             }
             switch (valueName[0]) {
                 case XML_TAG_SSID_LIST:
-                    mSsidList = (Set<String>) value;
+                    // Backwards compatibility, this used to be a set.
+                    if (value instanceof Set) {
+                        mSsidToTimeMap = new HashMap<>();
+                        for (String ssid : (Set<String>) value) {
+                            // Mark the deleted time as bootup time for existing entries from
+                            // previous releases.
+                            mSsidToTimeMap.put(ssid, mClock.getWallClockMillis());
+                        }
+                    } else if (value instanceof Map) {
+                        mSsidToTimeMap = (Map<String, Long>) value;
+                    }
                     break;
                 default:
                     throw new XmlPullParserException("Unknown tag under "
@@ -74,7 +88,7 @@
 
     @Override
     public void resetData() {
-        mSsidList = null;
+        mSsidToTimeMap = null;
     }
 
     @Override
@@ -95,19 +109,19 @@
     }
 
     /**
-     * An empty set will be returned for null SSID list.
+     * An empty map will be returned for null SSID list.
      *
-     * @return Set of SSIDs
+     * @return Map of SSIDs
      */
-    public Set<String> getSsidList() {
-        if (mSsidList == null) {
-            return new HashSet<String>();
+    public Map<String, Long> getSsidToTimeMap() {
+        if (mSsidToTimeMap == null) {
+            return new HashMap<String, Long>();
         }
-        return mSsidList;
+        return mSsidToTimeMap;
     }
 
-    public void setSsidList(Set<String> ssidList) {
-        mSsidList = ssidList;
+    public void setSsidToTimeMap(Map<String, Long> ssidMap) {
+        mSsidToTimeMap = ssidMap;
     }
 }
 
diff --git a/service/java/com/android/server/wifi/SarInfo.java b/service/java/com/android/server/wifi/SarInfo.java
index a62307e..7d58065 100644
--- a/service/java/com/android/server/wifi/SarInfo.java
+++ b/service/java/com/android/server/wifi/SarInfo.java
@@ -74,6 +74,7 @@
     public boolean isWifiSapEnabled = false;
     public boolean isWifiScanOnlyEnabled = false;
     public boolean isVoiceCall = false;
+    public boolean isEarPieceActive = false;
     public int attemptedSarScenario = RESET_SAR_SCENARIO;
 
     private boolean mAllWifiDisabled = true;
@@ -82,6 +83,7 @@
     private int mLastReportedSensorState = SAR_SENSOR_FREE_SPACE;
     private boolean mLastReportedIsWifiSapEnabled = false;
     private boolean mLastReportedIsVoiceCall = false;
+    private boolean mLastReportedIsEarPieceActive = false;
     private int mLastReportedScenario = INITIAL_SAR_SCENARIO;
     private long mLastReportedScenarioTs = 0;
 
@@ -113,7 +115,8 @@
         /* Check if some change happened since last successful reporting */
         if ((sensorState != mLastReportedSensorState)
                 || (isWifiSapEnabled != mLastReportedIsWifiSapEnabled)
-                || (isVoiceCall != mLastReportedIsVoiceCall)) {
+                || (isVoiceCall != mLastReportedIsVoiceCall)
+                || (isEarPieceActive != mLastReportedIsEarPieceActive)) {
             return true;
         } else {
             return false;
@@ -129,6 +132,7 @@
         mLastReportedSensorState = sensorState;
         mLastReportedIsWifiSapEnabled = isWifiSapEnabled;
         mLastReportedIsVoiceCall = isVoiceCall;
+        mLastReportedIsEarPieceActive = isEarPieceActive;
         mLastReportedScenario = attemptedSarScenario;
         mLastReportedScenarioTs = System.currentTimeMillis();
 
@@ -169,10 +173,12 @@
         pw.println("    Wifi Client state is: " + isWifiClientEnabled);
         pw.println("    Wifi Soft AP state is: " + isWifiSapEnabled);
         pw.println("    Wifi ScanOnly state is: " + isWifiScanOnlyEnabled);
+        pw.println("    Earpiece state is : " + isEarPieceActive);
         pw.println("Last reported values:");
         pw.println("    Sensor state is: " + sensorStateToString(mLastReportedSensorState));
         pw.println("    Soft AP state is: " + mLastReportedIsWifiSapEnabled);
         pw.println("    Voice Call state is: " + mLastReportedIsVoiceCall);
+        pw.println("    Earpiece state is: " + mLastReportedIsEarPieceActive);
         pw.println("Last reported scenario: " + mLastReportedScenario);
         pw.println("Reported " +  (System.currentTimeMillis() - mLastReportedScenarioTs) / 1000
                 + " seconds ago");
diff --git a/service/java/com/android/server/wifi/SarManager.java b/service/java/com/android/server/wifi/SarManager.java
index 5227d23..a35f65f 100644
--- a/service/java/com/android/server/wifi/SarManager.java
+++ b/service/java/com/android/server/wifi/SarManager.java
@@ -20,12 +20,18 @@
 import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
 import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.net.wifi.WifiManager;
+import android.os.Handler;
 import android.os.Looper;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
@@ -33,6 +39,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.server.wifi.util.WifiHandler;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -49,6 +56,8 @@
  * - It constructs the sar info and send it towards the HAL
  */
 public class SarManager {
+    // Period for checking on voice steam active (in ms)
+    private static final int CHECK_VOICE_STREAM_INTERVAL_MS = 5000;
     /* For Logging */
     private static final String TAG = "WifiSarManager";
     private boolean mVerboseLoggingEnabled = true;
@@ -66,6 +75,10 @@
     private int mSarSensorEventNearHand;
     private int mSarSensorEventNearHead;
 
+    // Device starts with screen on
+    private boolean mScreenOn = false;
+    private boolean mIsVoiceStreamCheckEnabled = false;
+
     /**
      * Other parameters passed in or created in the constructor.
      */
@@ -75,6 +88,7 @@
     private final WifiNative mWifiNative;
     private final SarSensorEventListener mSensorListener;
     private final SensorManager mSensorManager;
+    private final Handler mHandler;
     private final Looper mLooper;
     private final WifiMetrics mWifiMetrics;
 
@@ -91,6 +105,7 @@
         mTelephonyManager = telephonyManager;
         mWifiNative = wifiNative;
         mLooper = looper;
+        mHandler = new WifiHandler(TAG, looper);
         mSensorManager = sensorManager;
         mWifiMetrics = wifiMetrics;
         mPhoneStateListener = new WifiPhoneStateListener(looper);
@@ -104,6 +119,80 @@
         }
     }
 
+    /**
+     * Notify SarManager of screen status change
+     */
+    public void handleScreenStateChanged(boolean screenOn) {
+        if (!mSupportSarVoiceCall) {
+            return;
+        }
+
+        if (mScreenOn == screenOn) {
+            return;
+        }
+
+        if (mVerboseLoggingEnabled) {
+            Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn);
+        }
+
+        mScreenOn = screenOn;
+
+        // Only schedule a voice stream check if screen is turning on, and it is currently not
+        // scheduled
+        if (mScreenOn && !mIsVoiceStreamCheckEnabled) {
+            mHandler.post(() -> {
+                checkAudioDevice();
+            });
+
+            mIsVoiceStreamCheckEnabled = true;
+        }
+    }
+
+    private boolean isVoiceCallOnEarpiece() {
+        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        return (audioManager.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)
+                == AudioManager.DEVICE_OUT_EARPIECE);
+    }
+
+    private boolean isVoiceCallStreamActive() {
+        return AudioSystem.isStreamActive(AudioManager.STREAM_VOICE_CALL, 0);
+    }
+
+    private void checkAudioDevice() {
+        // First Check if audio stream is on
+        boolean voiceStreamActive = isVoiceCallStreamActive();
+        boolean earPieceActive;
+
+        if (voiceStreamActive) {
+            // Check on the audio route
+            earPieceActive = isVoiceCallOnEarpiece();
+
+            if (mVerboseLoggingEnabled) {
+                Log.d(TAG, "EarPiece active = " + earPieceActive);
+            }
+        } else {
+            earPieceActive = false;
+        }
+
+        // If audio route has changed, update SAR
+        if (earPieceActive != mSarInfo.isEarPieceActive) {
+            mSarInfo.isEarPieceActive = earPieceActive;
+            updateSarScenario();
+        }
+
+        // Now should we proceed with the checks
+        if (!mScreenOn && !voiceStreamActive) {
+            // No need to continue checking
+            mIsVoiceStreamCheckEnabled = false;
+        } else {
+            // Schedule another check
+            mHandler.postDelayed(() -> {
+                checkAudioDevice();
+            }, CHECK_VOICE_STREAM_INTERVAL_MS);
+        }
+    }
+
     private void readSarConfigs() {
         mSupportSarTxPowerLimit = mContext.getResources().getBoolean(
                 R.bool.config_wifi_framework_enable_sar_tx_power_limit);
@@ -148,6 +237,7 @@
         if (mSupportSarVoiceCall) {
             /* Listen for Phone State changes */
             registerPhoneStateListener();
+            registerVoiceStreamListener();
         }
 
         /* Only listen for SAR sensor if supported */
@@ -162,6 +252,56 @@
         }
     }
 
+    private void registerVoiceStreamListener() {
+        Log.i(TAG, "Registering for voice stream status");
+
+        // Register for listening to transitions of change of voice stream devices
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        boolean voiceStreamActive = isVoiceCallStreamActive();
+                        if (!voiceStreamActive) {
+                            // No need to proceed, there is no voice call ongoing
+                            return;
+                        }
+
+                        String action = intent.getAction();
+                        int streamType =
+                                intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                        int device = intent.getIntExtra(
+                                AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
+                        int oldDevice = intent.getIntExtra(
+                                AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
+
+                        if (streamType == AudioManager.STREAM_VOICE_CALL) {
+                            boolean earPieceActive = mSarInfo.isEarPieceActive;
+                            if (device == AudioManager.DEVICE_OUT_EARPIECE) {
+                                if (mVerboseLoggingEnabled) {
+                                    Log.d(TAG, "Switching to earpiece : HEAD ON");
+                                    Log.d(TAG, "Old device = " + oldDevice);
+                                }
+                                earPieceActive = true;
+                            } else if (oldDevice == AudioManager.DEVICE_OUT_EARPIECE) {
+                                if (mVerboseLoggingEnabled) {
+                                    Log.d(TAG, "Switching from earpiece : HEAD OFF");
+                                    Log.d(TAG, "New device = " + device);
+                                }
+                                earPieceActive = false;
+                            }
+
+                            if (earPieceActive != mSarInfo.isEarPieceActive) {
+                                mSarInfo.isEarPieceActive = earPieceActive;
+                                updateSarScenario();
+                            }
+                        }
+                    }
+                }, filter, null, mHandler);
+    }
+
     /**
      * Register the phone state listener.
      */
@@ -280,6 +420,10 @@
         /* Report change to HAL if needed */
         if (mSarInfo.isVoiceCall != newIsVoiceCall) {
             mSarInfo.isVoiceCall = newIsVoiceCall;
+
+            if (mVerboseLoggingEnabled) {
+                Log.d(TAG, "Voice Call = " + newIsVoiceCall);
+            }
             updateSarScenario();
         }
     }
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 61157bd..d32bb4b 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -229,6 +229,12 @@
     private static final int WIFI_PNO_RECENCY_SORTING_ENABLED_DEFAULT = 0; // 0 = disabled:
 
     /**
+     * Expiration timeout for deleted ephemeral ssids. (1 day)
+     */
+    @VisibleForTesting
+    public static final long DELETED_EPHEMERAL_SSID_EXPIRY_MS = (long) 1000 * 60 * 60 * 24;
+
+    /**
      * General sorting algorithm of all networks for scanning purposes:
      * Place the configurations in descending order of their |numAssociation| values. If networks
      * have the same |numAssociation|, place the configurations with
@@ -279,11 +285,13 @@
     private final Map<Integer, ScanDetailCache> mScanDetailCaches;
     /**
      * Framework keeps a list of ephemeral SSIDs that where deleted by user,
-     * so as, framework knows not to autoconnect again those SSIDs based on scorer input.
-     * The list is never cleared up.
+     * framework knows not to autoconnect again even if the app/scorer recommends it.
+     * The entries are deleted after 24 hours.
      * The SSIDs are encoded in a String as per definition of WifiConfiguration.SSID field.
+     *
+     * The map stores the SSID and the wall clock time when the network was deleted.
      */
-    private final Set<String> mDeletedEphemeralSSIDs;
+    private final Map<String, Long> mDeletedEphemeralSsidsToTimeMap;
 
     /**
      * Framework keeps a mapping from configKey to the randomized MAC address so that
@@ -393,7 +401,7 @@
 
         mConfiguredNetworks = new ConfigurationMap(userManager);
         mScanDetailCaches = new HashMap<>(16, 0.75f);
-        mDeletedEphemeralSSIDs = new HashSet<>();
+        mDeletedEphemeralSsidsToTimeMap = new HashMap<>();
         mRandomizedMacAddressMapping = new HashMap<>();
 
         // Register store data for network list and deleted ephemeral SSIDs.
@@ -1199,7 +1207,7 @@
             return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
         }
 
-        if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
+        if (mDeletedEphemeralSsidsToTimeMap.remove(config.SSID) != null) {
             if (mVerboseLoggingEnabled) {
                 Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
             }
@@ -2674,21 +2682,34 @@
     }
 
     /**
-     * Check if the provided ephemeral network was deleted by the user or not.
+     * Check if the provided ephemeral network was deleted by the user or not. This call also clears
+     * the SSID from the deleted ephemeral network map, if the duration has expired the
+     * timeout specified by {@link #DELETED_EPHEMERAL_SSID_EXPIRY_MS}.
      *
      * @param ssid caller must ensure that the SSID passed thru this API match
      *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
      * @return true if network was deleted, false otherwise.
      */
     public boolean wasEphemeralNetworkDeleted(String ssid) {
-        return mDeletedEphemeralSSIDs.contains(ssid);
+        if (!mDeletedEphemeralSsidsToTimeMap.containsKey(ssid)) {
+            return false;
+        }
+        long deletedTimeInMs = mDeletedEphemeralSsidsToTimeMap.get(ssid);
+        long nowInMs = mClock.getWallClockMillis();
+        // Clear the ssid from the map if the age > |DELETED_EPHEMERAL_SSID_EXPIRY_MS|.
+        if (nowInMs - deletedTimeInMs > DELETED_EPHEMERAL_SSID_EXPIRY_MS) {
+            mDeletedEphemeralSsidsToTimeMap.remove(ssid);
+            return false;
+        }
+        return true;
     }
 
     /**
      * Disable an ephemeral SSID for the purpose of network selection.
      *
-     * The only way to "un-disable it" is if the user create a network for that SSID and then
-     * forget it.
+     * The network will be re-enabled when:
+     * a) The user creates a network for that SSID and then forgets.
+     * b) The time specified by {@link #DELETED_EPHEMERAL_SSID_EXPIRY_MS} expires after the disable.
      *
      * @param ssid caller must ensure that the SSID passed thru this API match
      *             the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
@@ -2706,8 +2727,10 @@
                 break;
             }
         }
-        mDeletedEphemeralSSIDs.add(ssid);
-        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
+        // Store the ssid & the wall clock time at which the network was disabled.
+        mDeletedEphemeralSsidsToTimeMap.put(ssid, mClock.getWallClockMillis());
+        Log.d(TAG, "Forget ephemeral SSID " + ssid + " num="
+                + mDeletedEphemeralSsidsToTimeMap.size());
         if (foundConfig != null) {
             Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
                     + foundConfig.networkId);
@@ -2720,7 +2743,7 @@
      */
     @VisibleForTesting
     public void clearDeletedEphemeralNetworks() {
-        mDeletedEphemeralSSIDs.clear();
+        mDeletedEphemeralSsidsToTimeMap.clear();
     }
 
     /**
@@ -2887,7 +2910,7 @@
     private void clearInternalData() {
         localLog("clearInternalData: Clearing all internal data");
         mConfiguredNetworks.clear();
-        mDeletedEphemeralSSIDs.clear();
+        mDeletedEphemeralSsidsToTimeMap.clear();
         mRandomizedMacAddressMapping.clear();
         mScanDetailCaches.clear();
         clearLastSelectedNetwork();
@@ -2918,7 +2941,7 @@
                 mConfiguredNetworks.remove(config.networkId);
             }
         }
-        mDeletedEphemeralSSIDs.clear();
+        mDeletedEphemeralSsidsToTimeMap.clear();
         mScanDetailCaches.clear();
         clearLastSelectedNetwork();
         return removedNetworkIds;
@@ -2951,12 +2974,14 @@
      * Helper function to populate the internal (in-memory) data from the retrieved user store
      * (file) data.
      *
-     * @param configurations        list of configurations retrieved from store.
-     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
-     *                              the user.
+     * @param configurations list of configurations retrieved from store.
+     * @param deletedEphemeralSsidsToTimeMap map of ssid's representing the ephemeral networks
+     *                                       deleted by the user to the wall clock time at which
+     *                                       it was deleted.
      */
     private void loadInternalDataFromUserStore(
-            List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
+            List<WifiConfiguration> configurations,
+            Map<String, Long> deletedEphemeralSsidsToTimeMap) {
         for (WifiConfiguration configuration : configurations) {
             configuration.networkId = mNextNetworkId++;
             if (mVerboseLoggingEnabled) {
@@ -2968,9 +2993,7 @@
                 Log.e(TAG, "Failed to add network to config map", e);
             }
         }
-        for (String ssid : deletedEphemeralSSIDs) {
-            mDeletedEphemeralSSIDs.add(ssid);
-        }
+        mDeletedEphemeralSsidsToTimeMap.putAll(deletedEphemeralSsidsToTimeMap);
     }
 
     /**
@@ -2990,20 +3013,22 @@
      * 1. Clears all existing internal data.
      * 2. Sends out the networks changed broadcast after loading all the data.
      *
-     * @param sharedConfigurations  list of  network configurations retrieved from shared store.
-     * @param userConfigurations    list of  network configurations retrieved from user store.
-     * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
-     *                              the user.
+     * @param sharedConfigurations list of network configurations retrieved from shared store.
+     * @param userConfigurations list of network configurations retrieved from user store.
+     * @param deletedEphemeralSsidsToTimeMap map of ssid's representing the ephemeral networks
+     *                                       deleted by the user to the wall clock time at which
+     *                                       it was deleted.
      */
     private void loadInternalData(
             List<WifiConfiguration> sharedConfigurations,
-            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs,
+            List<WifiConfiguration> userConfigurations,
+            Map<String, Long> deletedEphemeralSsidsToTimeMap,
             Map<String, String> macAddressMapping) {
         // Clear out all the existing in-memory lists and load the lists from what was retrieved
         // from the config store.
         clearInternalData();
         loadInternalDataFromSharedStore(sharedConfigurations, macAddressMapping);
-        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
+        loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSsidsToTimeMap);
         generateRandomizedMacAddresses();
         if (mConfiguredNetworks.sizeForAllUsers() == 0) {
             Log.w(TAG, "No stored networks found.");
@@ -3044,7 +3069,7 @@
         }
         loadInternalData(mNetworkListSharedStoreData.getConfigurations(),
                 mNetworkListUserStoreData.getConfigurations(),
-                mDeletedEphemeralSsidsStoreData.getSsidList(),
+                mDeletedEphemeralSsidsStoreData.getSsidToTimeMap(),
                 mRandomizedMacStoreData.getMacMapping());
         return true;
     }
@@ -3073,7 +3098,7 @@
             return false;
         }
         loadInternalDataFromUserStore(mNetworkListUserStoreData.getConfigurations(),
-                mDeletedEphemeralSsidsStoreData.getSsidList());
+                mDeletedEphemeralSsidsStoreData.getSsidToTimeMap());
         return true;
     }
 
@@ -3134,7 +3159,7 @@
         // Setup store data for write.
         mNetworkListSharedStoreData.setConfigurations(sharedConfigurations);
         mNetworkListUserStoreData.setConfigurations(userConfigurations);
-        mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs);
+        mDeletedEphemeralSsidsStoreData.setSsidToTimeMap(mDeletedEphemeralSsidsToTimeMap);
         mRandomizedMacStoreData.setMacMapping(mRandomizedMacAddressMapping);
 
         try {
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index d733553..d239239 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wifi;
 
+import static java.lang.Math.toIntExact;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -158,6 +160,7 @@
      * Clock instance to retrieve timestamps for alarms.
      */
     private final Clock mClock;
+    private final WifiMetrics mWifiMetrics;
     /**
      * Shared config store file instance. There is 1 shared store file:
      * {@link #STORE_FILE_NAME_SHARED_GENERAL}.
@@ -202,15 +205,17 @@
      * @param context     context to use for retrieving the alarm manager.
      * @param looper      looper instance to post alarm timeouts to.
      * @param clock       clock instance to retrieve timestamps for alarms.
+     * @param wifiMetrics Metrics instance.
      * @param sharedStore StoreFile instance pointing to the shared store file. This should
      *                    be retrieved using {@link #createSharedFile()} method.
      */
-    public WifiConfigStore(Context context, Looper looper, Clock clock,
+    public WifiConfigStore(Context context, Looper looper, Clock clock, WifiMetrics wifiMetrics,
             StoreFile sharedStore) {
 
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mEventHandler = new Handler(looper);
         mClock = clock;
+        mWifiMetrics = wifiMetrics;
         mStoreDataList = new ArrayList<>();
 
         // Initialize the store files.
@@ -447,7 +452,11 @@
             }
         }
         long writeTime = mClock.getElapsedSinceBootMillis() - writeStartTime;
-
+        try {
+            mWifiMetrics.noteWifiConfigStoreWriteDuration(toIntExact(writeTime));
+        } catch (ArithmeticException e) {
+            // Silently ignore on any overflow errors.
+        }
         Log.d(TAG, "Writing to stores completed in " + writeTime + " ms.");
     }
 
@@ -475,6 +484,11 @@
             }
         }
         long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
+        try {
+            mWifiMetrics.noteWifiConfigStoreReadDuration(toIntExact(readTime));
+        } catch (ArithmeticException e) {
+            // Silently ignore on any overflow errors.
+        }
         Log.d(TAG, "Reading from all stores completed in " + readTime + " ms.");
     }
 
@@ -504,6 +518,7 @@
             deserializeData(userDataBytes, userStoreFile);
         }
         long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
+        mWifiMetrics.noteWifiConfigStoreReadDuration(toIntExact(readTime));
         Log.d(TAG, "Reading from user stores completed in " + readTime + " ms.");
     }
 
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 437ec0d..c03c46a 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -226,7 +226,7 @@
         // New config store
         mWifiKeyStore = new WifiKeyStore(mKeyStore);
         mWifiConfigStore = new WifiConfigStore(
-                mContext, clientModeImplLooper, mClock,
+                mContext, clientModeImplLooper, mClock, mWifiMetrics,
                 WifiConfigStore.createSharedFile());
         // Config Manager
         mWifiConfigManager = new WifiConfigManager(mContext, mClock,
@@ -234,7 +234,7 @@
                 mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil,
                 mWifiPermissionsWrapper, new NetworkListSharedStoreData(mContext),
                 new NetworkListUserStoreData(mContext),
-                new DeletedEphemeralSsidsStoreData(), new RandomizedMacStoreData(),
+                new DeletedEphemeralSsidsStoreData(mClock), new RandomizedMacStoreData(),
                 mFrameworkFacade, mWifiCoreHandlerThread.getLooper());
         mWifiScoreCard = new WifiScoreCard(mClock, "TODO(b/112196799) seed me properly");
         mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index fe34b0e..86b933c 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wifi;
 
+import static java.lang.StrictMath.toIntExact;
+
 import android.content.Context;
 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
 import android.net.NetworkAgent;
@@ -67,6 +69,7 @@
 import com.android.server.wifi.rtt.RttMetrics;
 import com.android.server.wifi.util.ExternalCallbackTracker;
 import com.android.server.wifi.util.InformationElementUtil;
+import com.android.server.wifi.util.MetricsUtils;
 import com.android.server.wifi.util.ScanResultUtil;
 
 import org.json.JSONArray;
@@ -145,6 +148,15 @@
     public static final int MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD = 2;
     public static final int NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD = 100;
     public static final int MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS = 1000 * 3600; // 1 hour
+    // Histogram for WifiConfigStore IO duration times. Indicates the following 5 buckets (in ms):
+    //   < 50
+    //   [50, 100)
+    //   [100, 150)
+    //   [150, 200)
+    //   [200, 300)
+    //   >= 300
+    private static final int[] WIFI_CONFIG_STORE_IO_DURATION_BUCKET_RANGES_MS =
+            {50, 100, 150, 200, 300};
 
     private Clock mClock;
     private boolean mScreenOn;
@@ -302,6 +314,12 @@
     /** DPP */
     private final DppMetrics mDppMetrics;
 
+    /** WifiConfigStore read duration histogram. */
+    private SparseIntArray mWifiConfigStoreReadDurationHistogram = new SparseIntArray();
+
+    /** WifiConfigStore write duration histogram. */
+    private SparseIntArray mWifiConfigStoreWriteDurationHistogram = new SparseIntArray();
+
     class RouterFingerPrint {
         private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
         RouterFingerPrint() {
@@ -2522,6 +2540,11 @@
                 mWifiP2pMetrics.dump(pw);
                 pw.println("mDppMetrics:");
                 mDppMetrics.dump(pw);
+
+                pw.println("mWifiConfigStoreReadDurationHistogram:"
+                        + mWifiConfigStoreReadDurationHistogram.toString());
+                pw.println("mWifiConfigStoreWriteDurationHistogram:"
+                        + mWifiConfigStoreWriteDurationHistogram.toString());
             }
         }
     }
@@ -2989,6 +3012,12 @@
                     .toArray(new DeviceMobilityStatePnoScanStats[0]);
             mWifiLogProto.wifiP2PStats = mWifiP2pMetrics.consolidateProto();
             mWifiLogProto.wifiDppLog = mDppMetrics.consolidateProto();
+            mWifiLogProto.wifiConfigStoreIo = new WifiMetricsProto.WifiConfigStoreIO();
+            mWifiLogProto.wifiConfigStoreIo.readDurations =
+                    makeWifiConfigStoreIODurationBucketArray(mWifiConfigStoreReadDurationHistogram);
+            mWifiLogProto.wifiConfigStoreIo.writeDurations =
+                    makeWifiConfigStoreIODurationBucketArray(
+                            mWifiConfigStoreWriteDurationHistogram);
         }
     }
 
@@ -3020,6 +3049,27 @@
         return array;
     }
 
+    private WifiMetricsProto.WifiConfigStoreIO.DurationBucket[]
+            makeWifiConfigStoreIODurationBucketArray(SparseIntArray sia) {
+        MetricsUtils.GenericBucket[] genericBuckets =
+                MetricsUtils.linearHistogramToGenericBuckets(sia,
+                        WIFI_CONFIG_STORE_IO_DURATION_BUCKET_RANGES_MS);
+        WifiMetricsProto.WifiConfigStoreIO.DurationBucket[] array =
+                new WifiMetricsProto.WifiConfigStoreIO.DurationBucket[genericBuckets.length];
+        try {
+            for (int i = 0; i < genericBuckets.length; i++) {
+                array[i] = new WifiMetricsProto.WifiConfigStoreIO.DurationBucket();
+                array[i].rangeStartMs = toIntExact(genericBuckets[i].start);
+                array[i].rangeEndMs = toIntExact(genericBuckets[i].end);
+                array[i].count = genericBuckets[i].count;
+            }
+        } catch (ArithmeticException e) {
+            // Return empty array on any overflow errors.
+            array = new WifiMetricsProto.WifiConfigStoreIO.DurationBucket[0];
+        }
+        return array;
+    }
+
     /**
      * Clear all WifiMetrics, except for currentConnectionEvent and Open Network Notification
      * feature enabled state, blacklist size.
@@ -3094,6 +3144,8 @@
                     android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_NO_PROBE;
             mProbeElapsedTimeMsSinceLastUpdate = -1;
             mProbeMcsRateSinceLastUpdate = -1;
+            mWifiConfigStoreReadDurationHistogram.clear();
+            mWifiConfigStoreWriteDurationHistogram.clear();
         }
     }
 
@@ -4174,4 +4226,28 @@
         }
         // TODO(b/112029045): aggregate metrics
     }
+
+    /**
+     * Update wifi config store read duration.
+     *
+     * @param timeMs Time it took to complete the operation, in milliseconds
+     */
+    public void noteWifiConfigStoreReadDuration(int timeMs) {
+        synchronized (mLock) {
+            MetricsUtils.addValueToLinearHistogram(timeMs, mWifiConfigStoreReadDurationHistogram,
+                    WIFI_CONFIG_STORE_IO_DURATION_BUCKET_RANGES_MS);
+        }
+    }
+
+    /**
+     * Update wifi config store write duration.
+     *
+     * @param timeMs Time it took to complete the operation, in milliseconds
+     */
+    public void noteWifiConfigStoreWriteDuration(int timeMs) {
+        synchronized (mLock) {
+            MetricsUtils.addValueToLinearHistogram(timeMs, mWifiConfigStoreWriteDurationHistogram,
+                    WIFI_CONFIG_STORE_IO_DURATION_BUCKET_RANGES_MS);
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java
index ee43845..38d36f8 100644
--- a/service/java/com/android/server/wifi/WifiNetworkFactory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java
@@ -701,22 +701,26 @@
         // Disable Auto-join so that NetworkFactory can take control of the network connection.
         mWifiConnectivityManager.setSpecificNetworkRequestInProgress(true);
 
+        // Copy over the credentials from the app's request and then copy the ssid from user
+        // selection.
+        WifiConfiguration networkToConnect =
+                new WifiConfiguration(mActiveSpecificNetworkRequestSpecifier.wifiConfiguration);
+        networkToConnect.SSID = network.SSID;
         // If the request is for a specific SSID and BSSID, then set WifiConfiguration.BSSID field
         // to prevent roaming.
         if (isActiveRequestForSingleAccessPoint()) {
-            network.BSSID =
+            networkToConnect.BSSID =
                     mActiveSpecificNetworkRequestSpecifier.bssidPatternMatcher.first.toString();
         }
-
         // Mark the network ephemeral so that it's automatically removed at the end of connection.
-        network.ephemeral = true;
-        network.fromWifiNetworkSpecifier = true;
+        networkToConnect.ephemeral = true;
+        networkToConnect.fromWifiNetworkSpecifier = true;
 
         // Store the user selected network.
-        mUserSelectedNetwork = network;
+        mUserSelectedNetwork = networkToConnect;
 
         // Trigger connection to the network.
-        connectToNetwork(network);
+        connectToNetwork(networkToConnect);
     }
 
     private void handleConnectToNetworkUserSelection(WifiConfiguration network) {
@@ -729,7 +733,7 @@
         handleConnectToNetworkUserSelectionInternal(network);
 
         // Add the network to the approved access point map for the app.
-        addNetworkToUserApprovedAccessPointMap(network);
+        addNetworkToUserApprovedAccessPointMap(mUserSelectedNetwork);
     }
 
     private void handleRejectUserSelection() {
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 9f7a2ff..d273c8f 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -2324,7 +2324,7 @@
         /* As long as no voice call is active (in case voice call is supported),
          * no backoff is needed */
         if (sarInfo.sarVoiceCallSupported) {
-            return sarInfo.isVoiceCall;
+            return (sarInfo.isVoiceCall || sarInfo.isEarPieceActive);
         } else {
             return false;
         }
@@ -2339,7 +2339,7 @@
      * Otherwise, an exception is thrown.
      */
     private int frameworkToHalTxPowerScenario_1_1(SarInfo sarInfo) {
-        if (sarInfo.sarVoiceCallSupported && sarInfo.isVoiceCall) {
+        if (sarInfo.sarVoiceCallSupported && (sarInfo.isVoiceCall || sarInfo.isEarPieceActive)) {
             return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
         } else {
             throw new IllegalArgumentException("bad scenario: voice call not active/supported");
@@ -2363,7 +2363,7 @@
         if (sarInfo.sarSapSupported && sarInfo.isWifiSapEnabled) {
             return true;
         }
-        if (sarInfo.sarVoiceCallSupported && sarInfo.isVoiceCall) {
+        if (sarInfo.sarVoiceCallSupported && (sarInfo.isVoiceCall || sarInfo.isEarPieceActive)) {
             return true;
         }
         return false;
@@ -2412,7 +2412,7 @@
                     throw new IllegalArgumentException("bad scenario: Invalid sensor state");
             }
         } else if (sarInfo.sarSapSupported && sarInfo.sarVoiceCallSupported) {
-            if (sarInfo.isVoiceCall) {
+            if (sarInfo.isVoiceCall || sarInfo.isEarPieceActive) {
                 return android.hardware.wifi.V1_2.IWifiChip
                         .TxPowerScenario.ON_HEAD_CELL_ON;
             } else if (sarInfo.isWifiSapEnabled) {
@@ -2423,7 +2423,7 @@
             }
         } else if (sarInfo.sarVoiceCallSupported) {
             /* SAR Sensors and SoftAP not supported, act like V1_1 */
-            if (sarInfo.isVoiceCall) {
+            if (sarInfo.isVoiceCall || sarInfo.isEarPieceActive) {
                 return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
             } else {
                 throw new IllegalArgumentException("bad scenario: voice call not active");
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index fe17f4b..d9d3017 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -382,10 +382,18 @@
 
             // potential transmission mechanism for port/transport-protocol information from
             // Responder (alternative to confirm message)
-            Pair<Integer, Integer> peerServerInfo = NetworkInformationData.parseTlv(message);
+            NetworkInformationData.ParsedResults peerServerInfo = NetworkInformationData.parseTlv(
+                    message);
             if (peerServerInfo != null) {
-                nnriE.getValue().peerPort = peerServerInfo.first;
-                nnriE.getValue().peerTransportProtocol = peerServerInfo.second;
+                if (peerServerInfo.port != 0) {
+                    nnriE.getValue().peerPort = peerServerInfo.port;
+                }
+                if (peerServerInfo.transportProtocol != -1) {
+                    nnriE.getValue().peerTransportProtocol = peerServerInfo.transportProtocol;
+                }
+                if (peerServerInfo.ipv6Override != null) {
+                    nnriE.getValue().peerIpv6Override = peerServerInfo.ipv6Override;
+                }
             }
 
             return null; // ignore this for NDP set up flow: it is used to obtain app_info from Resp
@@ -561,24 +569,51 @@
                 }
             }
 
-            try {
-                nnri.peerIpv6 = Inet6Address.getByAddress(null,
-                        MacAddress.fromBytes(mac).getLinkLocalIpv6FromEui48Mac().getAddress(),
-                        NetworkInterface.getByName(nnri.interfaceName));
-            } catch (SocketException | UnknownHostException e) {
-                Log.e(TAG, "onDataPathConfirm: error obtaining scoped IPv6 address -- " + e);
-                nnri.peerIpv6 = null;
-            }
             // only relevant for the initiator
             if (nnri.networkSpecifier.role
                     == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
-                Pair<Integer, Integer> peerServerInfo = NetworkInformationData.parseTlv(message);
+                NetworkInformationData.ParsedResults peerServerInfo =
+                        NetworkInformationData.parseTlv(message);
                 if (peerServerInfo != null) {
-                    nnri.peerPort = peerServerInfo.first;
-                    nnri.peerTransportProtocol = peerServerInfo.second;
+                    if (peerServerInfo.port != 0) {
+                        nnri.peerPort = peerServerInfo.port;
+                    }
+                    if (peerServerInfo.transportProtocol != -1) {
+                        nnri.peerTransportProtocol = peerServerInfo.transportProtocol;
+                    }
+                    if (peerServerInfo.ipv6Override != null) {
+                        nnri.peerIpv6Override = peerServerInfo.ipv6Override;
+                    }
                 }
             }
 
+            try {
+                if (nnri.peerIpv6Override == null) {
+                    nnri.peerIpv6 = Inet6Address.getByAddress(null,
+                            MacAddress.fromBytes(mac).getLinkLocalIpv6FromEui48Mac().getAddress(),
+                            NetworkInterface.getByName(nnri.interfaceName));
+                } else {
+                    byte[] addr = new byte[16];
+
+                    addr[0] = (byte) 0xfe;
+                    addr[1] = (byte) 0x80;
+                    addr[8] = nnri.peerIpv6Override[0];
+                    addr[9] = nnri.peerIpv6Override[1];
+                    addr[10] = nnri.peerIpv6Override[2];
+                    addr[11] = nnri.peerIpv6Override[3];
+                    addr[12] = nnri.peerIpv6Override[4];
+                    addr[13] = nnri.peerIpv6Override[5];
+                    addr[14] = nnri.peerIpv6Override[6];
+                    addr[15] = nnri.peerIpv6Override[7];
+
+                    nnri.peerIpv6 = Inet6Address.getByAddress(null, addr,
+                            NetworkInterface.getByName(nnri.interfaceName));
+                }
+            } catch (SocketException | UnknownHostException e) {
+                Log.e(TAG, "onDataPathConfirm: error obtaining scoped IPv6 address -- " + e);
+                nnri.peerIpv6 = null;
+            }
+
             if (nnri.peerIpv6 != null) {
                 networkCapabilities.setTransportInfo(
                         new WifiAwareNetworkInfo(nnri.peerIpv6, nnri.peerPort,
@@ -1078,6 +1113,7 @@
         public Inet6Address peerIpv6;
         public int peerPort = 0; // uninitialized (invalid) value
         public int peerTransportProtocol = -1; // uninitialized (invalid) value
+        public byte[] peerIpv6Override = null;
         public WifiAwareNetworkSpecifier networkSpecifier;
         public List<NanDataPathChannelInfo> channelInfo;
         public long startTimestamp = 0; // request is made (initiator) / get request (responder)
@@ -1479,12 +1515,27 @@
             return tlvc.getArray();
         }
 
+        static class ParsedResults {
+            ParsedResults(int port, int transportProtocol, byte[] ipv6Override) {
+                this.port = port;
+                this.transportProtocol = transportProtocol;
+                this.ipv6Override = ipv6Override;
+            }
+
+            public int port = 0;
+            public int transportProtocol = -1;
+            public byte[] ipv6Override = null;
+        }
+
         /**
-         * Parse the TLV and return <port, transportProtocol>.
+         * Parse the TLV and returns:
+         * - Null on parsing error
+         * - <port | 0, transport-protocol | -1, ipv6-override | null> otherwise
          */
-        public static Pair<Integer, Integer> parseTlv(byte[] tlvs) {
+        public static ParsedResults parseTlv(byte[] tlvs) {
             int port = 0;
             int transportProtocol = -1;
+            byte[] ipv6Override = null;
 
             try {
                 TlvBufferUtils.TlvIterable tlvi = new TlvBufferUtils.TlvIterable(1, 2, tlvs);
@@ -1492,17 +1543,21 @@
                 for (TlvBufferUtils.TlvElement tlve : tlvi) {
                     switch (tlve.type) {
                         case IPV6_LL_TYPE:
-                            Log.w(TAG,
-                                    "NetworkInformationData: non-default IPv6 not supporting - "
-                                            + "ignoring");
+                            if (tlve.length != 8) { // 8 bytes in IPv6 address
+                                Log.e(TAG, "NetworkInformationData: invalid IPv6 TLV -- length: "
+                                        + tlve.length);
+                                return null;
+                            }
+                            ipv6Override = tlve.getRawData();
                             break;
                         case SERVICE_INFO_TYPE:
                             Pair<Integer, Integer> serviceInfo = parseServiceInfoTlv(
                                     tlve.getRawData());
-                            if (serviceInfo != null) {
-                                port = serviceInfo.first;
-                                transportProtocol = serviceInfo.second;
+                            if (serviceInfo == null) {
+                                return null;
                             }
+                            port = serviceInfo.first;
+                            transportProtocol = serviceInfo.second;
                             break;
                         default:
                             Log.w(TAG,
@@ -1514,12 +1569,14 @@
                 Log.e(TAG, "NetworkInformationData: error parsing TLV -- " + e);
                 return null;
             }
-            if (port == 0 && transportProtocol == -1) {
-                return null;
-            }
-            return Pair.create(port, transportProtocol);
+            return new ParsedResults(port, transportProtocol, ipv6Override);
         }
 
+        /**
+         * Parse the Service Info TLV:
+         * - Returns null on error
+         * - Returns <port | 0, transport-protocol | -1> otherwise
+         */
         private static Pair<Integer, Integer> parseServiceInfoTlv(byte[] tlv) {
             int port = 0;
             int transportProtocol = -1;
diff --git a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java b/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
index feda70d..b005529 100644
--- a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
+++ b/service/java/com/android/server/wifi/util/DataIntegrityChecker.java
@@ -150,14 +150,11 @@
         EncryptedData encryptedData = null;
         try {
             Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
-            SecretKey secretKeyReference;
-            if (inKeyStore(keyAlias)) {
-                secretKeyReference = getSecretKey(keyAlias);
-            } else {
-                secretKeyReference = createSecretKey(keyAlias);
+            SecretKey secretKeyReference = getOrCreateSecretKey(keyAlias);
+            if (secretKeyReference != null) {
+                cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
+                encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV(), keyAlias);
             }
-            cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference);
-            encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV(), keyAlias);
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "encrypt could not find the algorithm: " + CIPHER_ALGORITHM);
         } catch (NoSuchPaddingException e) {
@@ -177,8 +174,11 @@
         try {
             Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
             GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, encryptedData.getIv());
-            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(encryptedData.getKeyAlias()), spec);
-            decryptedData = cipher.doFinal(encryptedData.getEncryptedData());
+            SecretKey secretKeyReference = getOrCreateSecretKey(encryptedData.getKeyAlias());
+            if (secretKeyReference != null) {
+                cipher.init(Cipher.DECRYPT_MODE, secretKeyReference, spec);
+                decryptedData = cipher.doFinal(encryptedData.getEncryptedData());
+            }
         } catch (NoSuchAlgorithmException e) {
             Log.e(TAG, "decrypt could not find cipher algorithm " + CIPHER_ALGORITHM);
         } catch (NoSuchPaddingException e) {
@@ -195,70 +195,48 @@
         return decryptedData;
     }
 
-    private static SecretKey createSecretKey(String keyAlias) {
-        SecretKey secretKey = null;
-        try {
-            KeyGenerator keyGenerator = KeyGenerator
-                    .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
-
-            KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias,
-                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
-                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
-                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-                    .build();
-
-            keyGenerator.init(keyGenParameterSpec);
-            secretKey = keyGenerator.generateKey();
-        } catch (NoSuchAlgorithmException e) {
-            Log.e(TAG, "createSecretKey cannot find algorithm");
-        } catch (NoSuchProviderException e) {
-            Log.e(TAG, "createSecretKey cannot find crypto provider");
-        } catch (InvalidAlgorithmParameterException e) {
-            Log.e(TAG, "createSecretKey had an invalid algorithm parameter");
-        }
-        return secretKey;
-    }
-
-    private static SecretKey getSecretKey(String keyAlias) {
+    private static SecretKey getOrCreateSecretKey(String keyAlias) {
         SecretKey secretKey = null;
         try {
             KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
             keyStore.load(null);
-            KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
-                    .getEntry(keyAlias, null);
-            secretKey = secretKeyEntry.getSecretKey();
+            if (keyStore.containsAlias(keyAlias)) { // The key exists in key store. Get the key.
+                KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
+                        .getEntry(keyAlias, null);
+                if (secretKeyEntry != null) {
+                    secretKey = secretKeyEntry.getSecretKey();
+                }
+            } else { // The key does not exist in key store. Create the key and store it.
+                KeyGenerator keyGenerator = KeyGenerator
+                        .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
+
+                KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias,
+                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                        .build();
+
+                keyGenerator.init(keyGenParameterSpec);
+                secretKey = keyGenerator.generateKey();
+            }
+        } catch (CertificateException e) {
+            Log.e(TAG, "getOrCreateSecretKey had a certificate exception.");
+        } catch (InvalidAlgorithmParameterException e) {
+            Log.e(TAG, "getOrCreateSecretKey had an invalid algorithm parameter");
         } catch (IOException e) {
-            Log.e(TAG, "getSecretKey had an IO exception");
-        } catch (NoSuchAlgorithmException e) {
-            Log.e(TAG, "getSecretKey could not find the algorithm");
-        }  catch (CertificateException e) {
-            Log.e(TAG, "getSecretKey had a certificate exception");
+            Log.e(TAG, "getOrCreateSecretKey had an IO exception.");
         } catch (KeyStoreException e) {
-            Log.e(TAG, "getSecretKey had a key store exception");
+            Log.e(TAG, "getOrCreateSecretKey cannot find the keystore: " + KEY_STORE);
+        } catch (NoSuchAlgorithmException e) {
+            Log.e(TAG, "getOrCreateSecretKey cannot find algorithm");
+        } catch (NoSuchProviderException e) {
+            Log.e(TAG, "getOrCreateSecretKey cannot find crypto provider");
         } catch (UnrecoverableEntryException e) {
-            Log.e(TAG, "getSecretKey had an entry exception");
+            Log.e(TAG, "getOrCreateSecretKey had an unrecoverable entry exception.");
         }
         return secretKey;
     }
 
-    private static boolean inKeyStore(String keyAlias) {
-        boolean ret = false;
-        try {
-            KeyStore ks = KeyStore.getInstance(KEY_STORE);
-            ks.load(null);
-            ret = ks.containsAlias(keyAlias);
-        } catch (KeyStoreException e) {
-            Log.e(TAG, "inKeyStore cannot find the keystore: " + KEY_STORE);
-        } catch (IOException e) {
-            Log.e(TAG, "inKeyStore had an IO exception.");
-        } catch (NoSuchAlgorithmException e) {
-            Log.e(TAG, "inKeyStore cannot find algorithm.");
-        } catch (CertificateException e) {
-            Log.e(TAG, "inKeyStore had a certificate exception.");
-        }
-        return ret;
-    }
-
     private static void writeIntegrityData(EncryptedData encryptedData, File file) {
         try {
             FileOutputStream fos = new FileOutputStream(file);
diff --git a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
index 01c67e9..702aa99 100644
--- a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
@@ -27,15 +27,16 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Unit tests for {@link com.android.server.wifi.DeletedEphemeralSsidsStoreData}.
@@ -44,18 +45,27 @@
 public class DeletedEphemeralSsidsStoreDataTest {
     private static final String TEST_SSID1 = "SSID 1";
     private static final String TEST_SSID2 = "SSID 2";
-    private static final String TEST_SSID_LIST_XML_STRING =
+    private static final long TEST_SSID1_TSTAMP = 6837367L;
+    private static final long TEST_SSID2_TSTAMP = 4567367L;
+
+    private static final String TEST_SSID_LIST_XML_STRING_MAP =
+            "<map name=\"SSIDList\">\n"
+            + "<long name=\"" + TEST_SSID1 + "\" value=\"" + TEST_SSID1_TSTAMP + "\" />\n"
+            + "<long name=\"" + TEST_SSID2 + "\" value=\"" + TEST_SSID2_TSTAMP + "\" />\n"
+            + "</map>\n";
+    private static final String TEST_SSID_LIST_XML_STRING_SET =
             "<set name=\"SSIDList\">\n"
             + "<string>" + TEST_SSID1 + "</string>\n"
             + "<string>" + TEST_SSID2 + "</string>\n"
             + "</set>\n";
-    private static final byte[] TEST_SSID_LIST_XML_BYTES =
-            TEST_SSID_LIST_XML_STRING.getBytes(StandardCharsets.UTF_8);
+    @Mock Clock mClock;
     private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
 
     @Before
     public void setUp() throws Exception {
-        mDeletedEphemeralSsidsStoreData = new DeletedEphemeralSsidsStoreData();
+        MockitoAnnotations.initMocks(this);
+
+        mDeletedEphemeralSsidsStoreData = new DeletedEphemeralSsidsStoreData(mClock);
     }
 
     /**
@@ -80,12 +90,12 @@
      * @return SSID list
      * @throws Exception
      */
-    private Set<String> deserializeData(byte[] data) throws Exception {
+    private Map<String, Long> deserializeData(byte[] data) throws Exception {
         final XmlPullParser in = Xml.newPullParser();
         final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
         in.setInput(inputStream, StandardCharsets.UTF_8.name());
         mDeletedEphemeralSsidsStoreData.deserializeData(in, in.getDepth());
-        return mDeletedEphemeralSsidsStoreData.getSsidList();
+        return mDeletedEphemeralSsidsStoreData.getSsidToTimeMap();
     }
 
     /**
@@ -129,13 +139,13 @@
      * @throws Exception
      */
     @Test
-    public void serializeSsidList() throws Exception {
-        Set<String> ssidList = new HashSet<>();
-        ssidList.add(TEST_SSID1);
-        ssidList.add(TEST_SSID2);
-        mDeletedEphemeralSsidsStoreData.setSsidList(ssidList);
+    public void serializeSsidToTimeMap() throws Exception {
+        Map<String, Long> ssidToTimeMap = new HashMap<>();
+        ssidToTimeMap.put(TEST_SSID1, TEST_SSID1_TSTAMP);
+        ssidToTimeMap.put(TEST_SSID2, TEST_SSID2_TSTAMP);
+        mDeletedEphemeralSsidsStoreData.setSsidToTimeMap(ssidToTimeMap);
         byte[] actualData = serializeData();
-        assertTrue(Arrays.equals(TEST_SSID_LIST_XML_BYTES, actualData));
+        assertEquals(TEST_SSID_LIST_XML_STRING_MAP, new String(actualData));
     }
 
     /**
@@ -145,10 +155,28 @@
      * @throws Exception
      */
     @Test
-    public void deserializeSsidList() throws Exception {
-        Set<String> ssidList = new HashSet<>();
-        ssidList.add(TEST_SSID1);
-        ssidList.add(TEST_SSID2);
-        assertEquals(ssidList, deserializeData(TEST_SSID_LIST_XML_BYTES));
+    public void deserializeSsidToTimeMap() throws Exception {
+        Map<String, Long> ssidToTimeMap = new HashMap<>();
+        ssidToTimeMap.put(TEST_SSID1, TEST_SSID1_TSTAMP);
+        ssidToTimeMap.put(TEST_SSID2, TEST_SSID2_TSTAMP);
+        assertEquals(ssidToTimeMap, deserializeData(TEST_SSID_LIST_XML_STRING_MAP.getBytes()));
+    }
+
+    /**
+     * Verify that user store SSID list is deserialized correctly using the predefined test XML
+     * data.
+     * This simulates the config store migration from the previous set representation to the new
+     * map of ssid to timestamp representation.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void deserializeSsidToTimeMapFromSet() throws Exception {
+        long currentTimeMs = 67934838L;
+        when(mClock.getWallClockMillis()).thenReturn(currentTimeMs);
+        Map<String, Long> ssidToTimeMap = new HashMap<>();
+        ssidToTimeMap.put(TEST_SSID1, currentTimeMs);
+        ssidToTimeMap.put(TEST_SSID2, currentTimeMs);
+        assertEquals(ssidToTimeMap, deserializeData(TEST_SSID_LIST_XML_STRING_SET.getBytes()));
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java b/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java
index aec4ca6..23edbd7 100644
--- a/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SarInfoTest.java
@@ -231,6 +231,21 @@
     }
 
     /**
+     * Test a change in earpiece status, shouldReport should return true
+     * Note: will need to report once before making the change to remove
+     * the effect of sensor state change.
+     */
+    @Test
+    public void testSarInfo_earpiece_wifi_enabled() throws Exception {
+        mSarInfo.isWifiClientEnabled = true;
+        assertTrue(mSarInfo.shouldReport());
+        mSarInfo.reportingSuccessful();
+
+        mSarInfo.isEarPieceActive = true;
+        assertTrue(mSarInfo.shouldReport());
+    }
+
+    /**
      * Test starting SAP, shouldReport should return true
      * Note: will need to report once before starting SAP to remove
      * the effect of sensor state change.
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index ce2fe4b..4ff61e2 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -79,6 +79,7 @@
 @SmallTest
 public class WifiConfigManagerTest {
 
+    private static final String TEST_SSID = "\"test_ssid\"";
     private static final String TEST_BSSID = "0a:08:5c:67:89:00";
     private static final long TEST_WALLCLOCK_CREATION_TIME_MILLIS = 9845637;
     private static final long TEST_WALLCLOCK_UPDATE_TIME_MILLIS = 75455637;
@@ -192,8 +193,7 @@
                 .thenReturn(true);
 
         when(mWifiConfigStore.areStoresPresent()).thenReturn(true);
-        setupStoreDataForRead(new ArrayList<WifiConfiguration>(),
-                new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForRead(new ArrayList<>(), new ArrayList<>(), new HashMap<>());
 
         when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt()))
                 .thenReturn(false);
@@ -348,8 +348,7 @@
         sharedConfigList.add(openNetwork);
 
         // Setup xml storage
-        setupStoreDataForRead(sharedConfigList, new ArrayList<WifiConfiguration>(),
-                new HashSet<String>());
+        setupStoreDataForRead(sharedConfigList, new ArrayList<>(), new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -2665,6 +2664,8 @@
         setupUserProfiles(user2);
 
         int appId = 674;
+        long currentTimeMs = 67823;
+        when(mClock.getWallClockMillis()).thenReturn(currentTimeMs);
 
         // Create 3 networks. 1 for user1, 1 for user2 and 1 shared.
         final WifiConfiguration user1Network = WifiConfigurationTestUtil.createPskNetwork();
@@ -2688,7 +2689,13 @@
                 add(user1Network);
             }
         };
-        setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>());
+        Map<String, Long> deletedSsidsToTimeMap = new HashMap<String, Long>() {
+            {
+                put(TEST_SSID, currentTimeMs);
+            }
+
+        };
+        setupStoreDataForRead(sharedNetworks, user1Networks, deletedSsidsToTimeMap);
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -2706,6 +2713,7 @@
         }
         assertTrue(sharedNetwork1Id != WifiConfiguration.INVALID_NETWORK_ID);
         assertTrue(sharedNetwork2Id != WifiConfiguration.INVALID_NETWORK_ID);
+        assertTrue(mWifiConfigManager.wasEphemeralNetworkDeleted(TEST_SSID));
 
         // Set up the user 2 store data that is loaded at user switch.
         List<WifiConfiguration> user2Networks = new ArrayList<WifiConfiguration>() {
@@ -2713,7 +2721,7 @@
                 add(user2Network);
             }
         };
-        setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+        setupStoreDataForUserRead(user2Networks, new HashMap<>());
         // Now switch the user to user 2 and ensure that shared network's IDs have not changed.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         mWifiConfigManager.handleUserSwitch(user2);
@@ -2733,6 +2741,7 @@
         }
         assertEquals(sharedNetwork1Id, updatedSharedNetwork1Id);
         assertEquals(sharedNetwork2Id, updatedSharedNetwork2Id);
+        assertFalse(mWifiConfigManager.wasEphemeralNetworkDeleted(TEST_SSID));
     }
 
     /**
@@ -2770,7 +2779,7 @@
                 add(user1Network);
             }
         };
-        setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, user1Networks, new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -2790,7 +2799,7 @@
                 add(user2Network);
             }
         };
-        setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+        setupStoreDataForUserRead(user2Networks, new HashMap<>());
         // Now switch the user to user 2 and ensure that user 1's private network has been removed.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2);
@@ -2841,8 +2850,7 @@
                 add(sharedNetwork);
             }
         };
-        setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
-                new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, new ArrayList<>(), new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -2852,7 +2860,7 @@
                 add(user2Network);
             }
         };
-        setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+        setupStoreDataForUserRead(user2Networks, new HashMap<>());
         // Now switch the user to user 2 and ensure that no private network has been removed.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2);
@@ -2894,8 +2902,7 @@
                 add(user2Network);
             }
         };
-        setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
-                new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, new ArrayList<>(), new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -2905,7 +2912,7 @@
                 add(user1Network);
             }
         };
-        setupStoreDataForUserRead(userNetworks, new HashSet<String>());
+        setupStoreDataForUserRead(userNetworks, new HashMap<>());
         mWifiConfigManager.handleUserUnlock(user1);
         verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class));
         // Capture the written data for the user 1 and ensure that it corresponds to what was
@@ -2976,15 +2983,14 @@
                 add(passpointConfig);
             }
         };
-        setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
-                new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, new ArrayList<>(), new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
         assertEquals(1, mWifiConfigManager.getConfiguredNetworks().size());
 
         // Unlock the owner of the legacy Passpoint configuration, verify it is removed from
         // the configured networks (migrated to PasspointManager).
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashMap<>());
         mWifiConfigManager.handleUserUnlock(user1);
         verify(mWifiConfigStore).switchUserStoresAndRead(any(List.class));
         Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList =
@@ -3008,7 +3014,7 @@
         // Set up the internal data first.
         assertTrue(mWifiConfigManager.loadFromStore());
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         // user2 is unlocked and switched to foreground.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         mWifiConfigManager.handleUserSwitch(user2);
@@ -3044,7 +3050,7 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
                 .switchUserStoresAndRead(any(List.class));
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         // Unlock the user2 and ensure that we read the data now.
         mWifiConfigManager.handleUserUnlock(user2);
         mContextConfigStoreMockOrder.verify(mWifiConfigStore)
@@ -3106,7 +3112,7 @@
                 add(user1Network);
             }
         };
-        setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, user1Networks, new HashMap<>());
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
@@ -3135,7 +3141,7 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
                 .switchUserStoresAndRead(any(List.class));
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         // Unlock the user1 (default user) for the first time and ensure that we read the data.
         mWifiConfigManager.handleUserUnlock(user1);
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
@@ -3161,7 +3167,7 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
                 .switchUserStoresAndRead(any(List.class));
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashMap<>());
         // Read from store now.
         assertTrue(mWifiConfigManager.loadFromStore());
         mContextConfigStoreMockOrder.verify(mWifiConfigStore)
@@ -3193,7 +3199,7 @@
         mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
 
         // Unlock the user2 and ensure that we read from the user store.
-        setupStoreDataForUserRead(new ArrayList<>(), new HashSet<>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         mWifiConfigManager.handleUserUnlock(user2);
         mContextConfigStoreMockOrder.verify(mWifiConfigStore)
                 .switchUserStoresAndRead(any(List.class));
@@ -3212,7 +3218,7 @@
         // Set up the internal data first.
         assertTrue(mWifiConfigManager.loadFromStore());
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         // user2 is unlocked and switched to foreground.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         mWifiConfigManager.handleUserSwitch(user2);
@@ -3314,7 +3320,7 @@
         assertTrue(mWifiConfigManager.loadFromStore());
         verify(mWifiConfigStore).read();
 
-        setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+        setupStoreDataForUserRead(new ArrayList<>(), new HashMap<>());
         // Now switch the user to user 2.
         when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
         mWifiConfigManager.handleUserSwitch(user2);
@@ -4191,8 +4197,7 @@
                 add(peapSimNetwork);
             }
         };
-        setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
-                new HashSet<String>());
+        setupStoreDataForRead(sharedNetworks, new ArrayList<>(), new HashMap<>());
 
         // 1. Call resetSimNetworks with true(SIM is present).
         mWifiConfigManager.resetSimNetworks(true);
@@ -4217,6 +4222,41 @@
         assertFalse(retrievedPeapNetwork.enterpriseConfig.getAnonymousIdentity().isEmpty());
     }
 
+    /**
+     * Verifies the deletion of ephemeral network using
+     * {@link WifiConfigManager#disableEphemeralNetwork(String)}.
+     */
+    @Test
+    public void testDisableEphemeralNetwork() throws Exception {
+        WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        ephemeralNetwork.ephemeral = true;
+        List<WifiConfiguration> networks = new ArrayList<>();
+        networks.add(ephemeralNetwork);
+
+        verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork);
+
+        List<WifiConfiguration> retrievedNetworks =
+                mWifiConfigManager.getConfiguredNetworksWithPasswords();
+        WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+                networks, retrievedNetworks);
+
+        // Disable the ephemeral network.
+        long disableTimeMs = 546643L;
+        long currentTimeMs = disableTimeMs;
+        when(mClock.getWallClockMillis()).thenReturn(currentTimeMs);
+        mWifiConfigManager.disableEphemeralNetwork(ephemeralNetwork.SSID);
+
+        // Before the expiry of timeout.
+        currentTimeMs = disableTimeMs + WifiConfigManager.DELETED_EPHEMERAL_SSID_EXPIRY_MS - 1;
+        when(mClock.getWallClockMillis()).thenReturn(currentTimeMs);
+        assertTrue(mWifiConfigManager.wasEphemeralNetworkDeleted(ephemeralNetwork.SSID));
+
+        // After the expiry of timeout.
+        currentTimeMs = disableTimeMs + WifiConfigManager.DELETED_EPHEMERAL_SSID_EXPIRY_MS + 1;
+        when(mClock.getWallClockMillis()).thenReturn(currentTimeMs);
+        assertFalse(mWifiConfigManager.wasEphemeralNetworkDeleted(ephemeralNetwork.SSID));
+    }
+
     private NetworkUpdateResult verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
             boolean withNetworkSettings,
             boolean withProfileOwnerPolicy,
@@ -4452,11 +4492,11 @@
      * after WifiConfigStore#read.
      */
     private void setupStoreDataForRead(List<WifiConfiguration> sharedConfigurations,
-            List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSsids) {
+            List<WifiConfiguration> userConfigurations, Map<String, Long> deletedEphemeralSsids) {
         when(mNetworkListSharedStoreData.getConfigurations())
                 .thenReturn(sharedConfigurations);
         when(mNetworkListUserStoreData.getConfigurations()).thenReturn(userConfigurations);
-        when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids);
+        when(mDeletedEphemeralSsidsStoreData.getSsidToTimeMap()).thenReturn(deletedEphemeralSsids);
     }
 
     /**
@@ -4464,9 +4504,9 @@
      * after WifiConfigStore#switchUserStoresAndRead.
      */
     private void setupStoreDataForUserRead(List<WifiConfiguration> userConfigurations,
-            Set<String> deletedEphemeralSsids) {
+            Map<String, Long> deletedEphemeralSsids) {
         when(mNetworkListUserStoreData.getConfigurations()).thenReturn(userConfigurations);
-        when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids);
+        when(mDeletedEphemeralSsidsStoreData.getSsidToTimeMap()).thenReturn(deletedEphemeralSsids);
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 6693d86..64e762b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -46,10 +46,9 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 
 /**
  * Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
@@ -122,9 +121,9 @@
                     + "</Network>\n"
                     + "</NetworkList>\n"
                     + "<DeletedEphemeralSSIDList>\n"
-                    + "<set name=\"SSIDList\">\n"
-                    + "<string>%s</string>\n"
-                    + "</set>\n"
+                    + "<map name=\"SSIDList\">\n"
+                    + "<long name=\"%s\" value=\"0\" />\n"
+                    + "</map>\n"
                     + "</DeletedEphemeralSSIDList>\n"
                     + "</WifiConfigStoreData>\n";
 
@@ -147,6 +146,7 @@
     private TestAlarmManager mAlarmManager;
     private TestLooper mLooper;
     @Mock private Clock mClock;
+    @Mock private WifiMetrics mWifiMetrics;
     private MockStoreFile mSharedStore;
     private MockStoreFile mUserStore;
     private MockStoreFile mUserNetworkSuggestionsStore;
@@ -188,7 +188,8 @@
     public void setUp() throws Exception {
         setupMocks();
 
-        mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mSharedStore);
+        mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mWifiMetrics,
+                mSharedStore);
         // Enable verbose logging before tests.
         mWifiConfigStore.enableVerboseLogging(true);
     }
@@ -216,6 +217,8 @@
         assertFalse(mSharedStore.isStoreWritten());
         assertFalse(mUserStore.isStoreWritten());
         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
+
+        verify(mWifiMetrics, never()).noteWifiConfigStoreWriteDuration(anyInt());
     }
 
     /**
@@ -236,6 +239,8 @@
         assertTrue(mSharedStore.isStoreWritten());
         assertTrue(mUserStore.isStoreWritten());
         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
+
+        verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
     }
 
     /**
@@ -262,6 +267,8 @@
         assertTrue(mSharedStore.isStoreWritten());
         assertTrue(mUserStore.isStoreWritten());
         assertFalse(mUserNetworkSuggestionsStore.isStoreWritten());
+
+        verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
     }
 
     /**
@@ -366,6 +373,9 @@
         mWifiConfigStore.read();
         assertEquals(TEST_USER_DATA, mUserStoreData.getData());
         assertEquals(TEST_SHARE_DATA, mSharedStoreData.getData());
+
+        verify(mWifiMetrics, times(2)).noteWifiConfigStoreReadDuration(anyInt());
+        verify(mWifiMetrics).noteWifiConfigStoreWriteDuration(anyInt());
     }
 
     /**
@@ -494,25 +504,25 @@
 
         // Setup deleted ephemeral SSID list.
         DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
-                new DeletedEphemeralSsidsStoreData();
+                new DeletedEphemeralSsidsStoreData(mClock);
         mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
-        String testSsid = "Test SSID";
-        Set<String> ssidList = new HashSet<>();
-        ssidList.add(testSsid);
+        String testSsid = "\"Test SSID\"";
+        Map<String, Long> ssidMap = new HashMap<>();
+        ssidMap.put(testSsid, 0L);
 
         // Setup user store XML bytes.
         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
                 openNetwork.configKey().replaceAll("\"", "&quot;"),
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
                 openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
-                openNetwork.getRandomizedMacAddress(), testSsid);
+                openNetwork.getRandomizedMacAddress(), testSsid.replaceAll("\"", "&quot;"));
         byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
         mUserStore.storeRawDataToWrite(xmlBytes);
 
         mWifiConfigStore.switchUserStoresAndRead(mUserStores);
         WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
                 userConfigs, networkList.getConfigurations());
-        assertEquals(ssidList, deletedEphemeralSsids.getSsidList());
+        assertEquals(ssidMap, deletedEphemeralSsids.getSsidToTimeMap());
     }
 
     /**
@@ -540,25 +550,23 @@
 
         // Setup deleted ephemeral SSID list store data.
         DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
-                new DeletedEphemeralSsidsStoreData();
+                new DeletedEphemeralSsidsStoreData(mClock);
         mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
         String testSsid = "Test SSID";
-        Set<String> ssidList = new HashSet<>();
-        ssidList.add(testSsid);
-        deletedEphemeralSsids.setSsidList(ssidList);
+        Map<String, Long> ssidMap = new HashMap<>();
+        ssidMap.put(testSsid, 0L);
+        deletedEphemeralSsids.setSsidToTimeMap(ssidMap);
 
         // Setup expected XML bytes.
         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
                 openNetwork.configKey().replaceAll("\"", "&quot;"),
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
                 openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName,
-                openNetwork.getRandomizedMacAddress(), testSsid);
-        byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
+                openNetwork.getRandomizedMacAddress(), testSsid.replaceAll("\"", "&quot;"));
 
         mWifiConfigStore.write(true);
-        assertEquals(xmlBytes.length, mUserStore.getStoreBytes().length);
         // Verify the user store content.
-        assertTrue(Arrays.equals(xmlBytes, mUserStore.getStoreBytes()));
+        assertEquals(xmlString, new String(mUserStore.getStoreBytes()));
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index aeaba18..c114aa1 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
@@ -645,6 +645,8 @@
         assertCommonConfigurationElementsEqual(expected, actual);
         assertEquals(expected.networkId, actual.networkId);
         assertEquals(expected.ephemeral, actual.ephemeral);
+        assertEquals(expected.fromWifiNetworkSuggestion, actual.fromWifiNetworkSuggestion);
+        assertEquals(expected.fromWifiNetworkSpecifier, actual.fromWifiNetworkSpecifier);
         assertEquals(expected.trusted, actual.trusted);
     }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 570f821..c40e148 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -3048,4 +3048,77 @@
         assertEquals(WifiUsabilityStats.LABEL_BAD, statsList[1].label);
         assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, statsList[1].triggerType);
     }
+
+    /**
+     * Test the generation of 'WifiConfigStoreIODuration' read histograms.
+     */
+    @Test
+    public void testWifiConfigStoreReadDurationsHistogramGeneration() throws Exception {
+        mWifiMetrics.noteWifiConfigStoreReadDuration(10);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(20);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(100);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(90);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(130);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(250);
+        mWifiMetrics.noteWifiConfigStoreReadDuration(600);
+
+        dumpProtoAndDeserialize();
+
+        assertEquals(5, mDecodedProto.wifiConfigStoreIo.readDurations.length);
+        assertEquals(0, mDecodedProto.wifiConfigStoreIo.writeDurations.length);
+
+        assertEquals(Integer.MIN_VALUE,
+                mDecodedProto.wifiConfigStoreIo.readDurations[0].rangeStartMs);
+        assertEquals(50, mDecodedProto.wifiConfigStoreIo.readDurations[0].rangeEndMs);
+        assertEquals(2, mDecodedProto.wifiConfigStoreIo.readDurations[0].count);
+
+        assertEquals(50, mDecodedProto.wifiConfigStoreIo.readDurations[1].rangeStartMs);
+        assertEquals(100, mDecodedProto.wifiConfigStoreIo.readDurations[1].rangeEndMs);
+        assertEquals(1, mDecodedProto.wifiConfigStoreIo.readDurations[1].count);
+
+        assertEquals(100, mDecodedProto.wifiConfigStoreIo.readDurations[2].rangeStartMs);
+        assertEquals(150, mDecodedProto.wifiConfigStoreIo.readDurations[2].rangeEndMs);
+        assertEquals(2, mDecodedProto.wifiConfigStoreIo.readDurations[2].count);
+
+        assertEquals(200, mDecodedProto.wifiConfigStoreIo.readDurations[3].rangeStartMs);
+        assertEquals(300, mDecodedProto.wifiConfigStoreIo.readDurations[3].rangeEndMs);
+        assertEquals(1, mDecodedProto.wifiConfigStoreIo.readDurations[3].count);
+
+        assertEquals(300, mDecodedProto.wifiConfigStoreIo.readDurations[4].rangeStartMs);
+        assertEquals(Integer.MAX_VALUE,
+                mDecodedProto.wifiConfigStoreIo.readDurations[4].rangeEndMs);
+        assertEquals(1, mDecodedProto.wifiConfigStoreIo.readDurations[4].count);
+    }
+
+    /**
+     * Test the generation of 'WifiConfigStoreIODuration' write histograms.
+     */
+    @Test
+    public void testWifiConfigStoreWriteDurationsHistogramGeneration() throws Exception {
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(10);
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(40);
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(60);
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(90);
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(534);
+        mWifiMetrics.noteWifiConfigStoreWriteDuration(345);
+
+        dumpProtoAndDeserialize();
+
+        assertEquals(0, mDecodedProto.wifiConfigStoreIo.readDurations.length);
+        assertEquals(3, mDecodedProto.wifiConfigStoreIo.writeDurations.length);
+
+        assertEquals(Integer.MIN_VALUE,
+                mDecodedProto.wifiConfigStoreIo.writeDurations[0].rangeStartMs);
+        assertEquals(50, mDecodedProto.wifiConfigStoreIo.writeDurations[0].rangeEndMs);
+        assertEquals(2, mDecodedProto.wifiConfigStoreIo.writeDurations[0].count);
+
+        assertEquals(50, mDecodedProto.wifiConfigStoreIo.writeDurations[1].rangeStartMs);
+        assertEquals(100, mDecodedProto.wifiConfigStoreIo.writeDurations[1].rangeEndMs);
+        assertEquals(2, mDecodedProto.wifiConfigStoreIo.writeDurations[1].count);
+
+        assertEquals(300, mDecodedProto.wifiConfigStoreIo.writeDurations[2].rangeStartMs);
+        assertEquals(Integer.MAX_VALUE,
+                mDecodedProto.wifiConfigStoreIo.writeDurations[2].rangeEndMs);
+        assertEquals(2, mDecodedProto.wifiConfigStoreIo.writeDurations[2].count);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
index a745ca1..975b430 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java
@@ -69,6 +69,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -101,6 +102,7 @@
     private static final String TEST_BSSID_4 = "45:34:34:56:ee:ff";
     private static final String TEST_BSSID_1_2_OUI = "12:34:23:00:00:00";
     private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_WPA_PRESHARED_KEY = "\"password123\"";
 
     @Mock Context mContext;
     @Mock ActivityManager mActivityManager;
@@ -971,8 +973,9 @@
         assertNotNull(networkRequestUserSelectionCallback);
 
         // Now trigger user selection to one of the network.
-        WifiConfiguration selectedNetwork = WifiConfigurationTestUtil.createOpenNetwork();
-        networkRequestUserSelectionCallback.select(selectedNetwork);
+        mSelectedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+        mSelectedNetwork.SSID = "\"" + mTestScanDatas[0].getResults()[0].SSID + "\"";
+        networkRequestUserSelectionCallback.select(mSelectedNetwork);
         mLooper.dispatchAll();
 
         // Cancel periodic scans.
@@ -986,9 +989,14 @@
                 wifiConfigurationCaptor.capture(), eq(TEST_UID_1), eq(TEST_PACKAGE_NAME_1));
         WifiConfiguration network =  wifiConfigurationCaptor.getValue();
         assertNotNull(network);
-        WifiConfigurationTestUtil.assertConfigurationEqual(selectedNetwork, network);
-        assertTrue(network.ephemeral);
-        assertTrue(network.fromWifiNetworkSpecifier);
+        WifiConfiguration expectedWifiConfiguration =
+                new WifiConfiguration(((WifiNetworkSpecifier) mNetworkRequest.networkCapabilities
+                        .getNetworkSpecifier()).wifiConfiguration);
+        expectedWifiConfiguration.SSID = mSelectedNetwork.SSID;
+        expectedWifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
+        expectedWifiConfiguration.ephemeral = true;
+        expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
+        WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
 
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mClientModeImpl).sendMessage(messageCaptor.capture());
@@ -1012,16 +1020,17 @@
                 mNetworkRequestUserSelectionCallback.getValue();
         assertNotNull(networkRequestUserSelectionCallback);
 
-        WifiConfiguration selectedNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        mSelectedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+        mSelectedNetwork.SSID = "\"" + mTestScanDatas[0].getResults()[0].SSID + "\"";
 
         // Have a saved network with the same configuration.
-        WifiConfiguration matchingSavedNetwork = new WifiConfiguration(selectedNetwork);
+        WifiConfiguration matchingSavedNetwork = new WifiConfiguration(mSelectedNetwork);
         matchingSavedNetwork.networkId = TEST_NETWORK_ID_1;
-        when(mWifiConfigManager.getConfiguredNetwork(selectedNetwork.configKey()))
+        when(mWifiConfigManager.getConfiguredNetwork(mSelectedNetwork.configKey()))
                 .thenReturn(matchingSavedNetwork);
 
         // Now trigger user selection to one of the network.
-        networkRequestUserSelectionCallback.select(selectedNetwork);
+        networkRequestUserSelectionCallback.select(mSelectedNetwork);
         mLooper.dispatchAll();
 
         // Cancel periodic scans.
@@ -1062,6 +1071,7 @@
                         MacAddress.BROADCAST_ADDRESS);
         WifiConfiguration wifiConfiguration = new WifiConfiguration();
         wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
         WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
                 ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1,
                 TEST_PACKAGE_NAME_1);
@@ -1075,6 +1085,7 @@
 
         // Now trigger user selection to the network.
         mSelectedNetwork = ScanResultUtil.createNetworkFromScanResult(matchingScanResult);
+        mSelectedNetwork.SSID = "\"" + matchingScanResult.SSID + "\"";
         INetworkRequestUserSelectionCallback networkRequestUserSelectionCallback =
                 mNetworkRequestUserSelectionCallback.getValue();
         assertNotNull(networkRequestUserSelectionCallback);
@@ -1088,11 +1099,15 @@
                 wifiConfigurationCaptor.capture(), eq(TEST_UID_1), eq(TEST_PACKAGE_NAME_1));
         WifiConfiguration network =  wifiConfigurationCaptor.getValue();
         assertNotNull(network);
-        WifiConfiguration expectedWifiConfiguration = new WifiConfiguration(mSelectedNetwork);
+        WifiConfiguration expectedWifiConfiguration =
+                new WifiConfiguration(((WifiNetworkSpecifier) mNetworkRequest.networkCapabilities
+                        .getNetworkSpecifier()).wifiConfiguration);
+        expectedWifiConfiguration.SSID = mSelectedNetwork.SSID;
+        expectedWifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
         expectedWifiConfiguration.BSSID = matchingScanResult.BSSID;
+        expectedWifiConfiguration.ephemeral = true;
+        expectedWifiConfiguration.fromWifiNetworkSpecifier = true;
         WifiConfigurationTestUtil.assertConfigurationEqual(expectedWifiConfiguration, network);
-        assertTrue(network.ephemeral);
-        assertTrue(network.fromWifiNetworkSpecifier);
 
         // Verify connection message.
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
@@ -1149,7 +1164,8 @@
         // Fail the request after all the retries are exhausted.
         verify(mNetworkRequestMatchCallback).onAbort();
         // Verify that we sent the connection failure callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // Verify we reset the network request handling.
         verify(mWifiConnectivityManager).setSpecificNetworkRequestInProgress(false);
     }
@@ -1176,7 +1192,8 @@
         // Fail the request after all the retries are exhausted.
         verify(mNetworkRequestMatchCallback).onAbort();
         // Verify that we sent the connection failure callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         mInOrder.verify(mAlarmManager).cancel(
                 mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
@@ -1207,7 +1224,8 @@
 
         verify(mNetworkRequestMatchCallback).onAbort();
         // Verify that we sent the connection failure callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         mInOrder.verify(mAlarmManager).cancel(
                 mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
@@ -1230,8 +1248,7 @@
                 WifiMetrics.ConnectionEvent.FAILURE_DHCP, connectedNetwork);
 
         // Verify that we did not send the connection failure callback.
-        verify(mNetworkRequestMatchCallback, never())
-                .onUserSelectionConnectFailure(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback, never()).onUserSelectionConnectFailure(any());
         // verify we canceled the timeout alarm.
         verify(mAlarmManager, never())
                 .cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
@@ -1251,7 +1268,8 @@
         validateConnectionRetryAttempts();
 
         // Verify that we sent the connection failure callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         mInOrder.verify(mAlarmManager).cancel(
                 mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
@@ -1272,7 +1290,8 @@
                 WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
 
         // Verify that we sent the connection success callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
     }
@@ -1292,8 +1311,7 @@
                 WifiMetrics.ConnectionEvent.FAILURE_NONE, connectedNetwork);
 
         // verify that we did not send out the success callback and did not stop the alarm timeout.
-        verify(mNetworkRequestMatchCallback, never())
-                .onUserSelectionConnectSuccess(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback, never()).onUserSelectionConnectSuccess(any());
         verify(mAlarmManager, never())
                 .cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
 
@@ -1302,7 +1320,8 @@
                 WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
 
         // Verify that we sent the connection success callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
     }
@@ -1320,7 +1339,8 @@
                 WifiMetrics.ConnectionEvent.FAILURE_NONE, mSelectedNetwork);
 
         // Verify that we sent the connection success callback.
-        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(mSelectedNetwork);
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                argThat(new WifiConfigMatcher(mSelectedNetwork)));
         // verify we canceled the timeout alarm.
         verify(mAlarmManager).cancel(mConnectionTimeoutAlarmListenerArgumentCaptor.getValue());
 
@@ -2163,6 +2183,7 @@
         Pair<MacAddress, MacAddress> bssidPatternMatch =
                 Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
         WifiConfiguration wifiConfiguration = WifiConfigurationTestUtil.createPskNetwork();
+        wifiConfiguration.preSharedKey = TEST_WPA_PRESHARED_KEY;
         WifiNetworkSpecifier specifier = new WifiNetworkSpecifier(
                 ssidPatternMatch, bssidPatternMatch, wifiConfiguration, TEST_UID_1,
                 TEST_PACKAGE_NAME_1);
@@ -2336,6 +2357,20 @@
         WorkSource workSource = mWorkSourceArgumentCaptor.getValue();
         assertNotNull(workSource);
         assertEquals(TEST_UID_1, workSource.get(0));
+    }
 
+    class WifiConfigMatcher implements ArgumentMatcher<WifiConfiguration> {
+        private final WifiConfiguration mConfig;
+
+        WifiConfigMatcher(WifiConfiguration config) {
+            assertNotNull(config);
+            mConfig = config;
+        }
+
+        @Override
+        public boolean matches(WifiConfiguration otherConfig) {
+            if (otherConfig == null) return false;
+            return mConfig.configKey().equals(otherConfig.configKey());
+        }
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index c2c8f7b..d05d797 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -2576,6 +2576,35 @@
     }
 
     /**
+     * Test the selectTxPowerScenario HIDL method invocation with no sensor support, but with
+     * SAP and voice call support.
+     * When earpiece is active, should result in cell with near head scenario
+     * Using IWifiChip 1.2 interface
+     */
+    @Test
+    public void testEarPieceScenarios_SelectTxPowerV1_2() throws RemoteException {
+        // Create a SAR info record (with sensor and SAP support)
+        SarInfo sarInfo = new SarInfo();
+        sarInfo.sarVoiceCallSupported = true;
+        sarInfo.sarSapSupported = true;
+        sarInfo.sarSensorSupported = false;
+
+        sarInfo.isEarPieceActive = true;
+
+        // Expose the 1.2 IWifiChip.
+        mWifiVendorHal = new WifiVendorHalSpyV1_2(mHalDeviceManager, mLooper.getLooper());
+        when(mIWifiChipV12.selectTxPowerScenario_1_2(anyInt())).thenReturn(mWifiStatusSuccess);
+
+        // ON_HEAD_CELL_ON
+        assertTrue(mWifiVendorHal.startVendorHalSta());
+        assertTrue(mWifiVendorHal.selectTxPowerScenario(sarInfo));
+        verify(mIWifiChipV12).selectTxPowerScenario_1_2(
+                eq(android.hardware.wifi.V1_2.IWifiChip.TxPowerScenario.ON_HEAD_CELL_ON));
+        verify(mIWifiChipV12, never()).resetTxPowerScenario();
+        mWifiVendorHal.stopVendorHal();
+    }
+
+    /**
      * Test the selectTxPowerScenario HIDL method invocation with sensor related scenarios
      * to IWifiChip 1.2 interface
      */
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index c923633..069e865 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -74,7 +74,6 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.test.TestLooper;
-import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
 
@@ -854,8 +853,13 @@
      */
     @Test
     public void testDataPathInitiatorNetInfoLargeValuesExp1() throws Exception {
-        testDataPathInitiatorUtilityMore(false, true, true, false, true, false,
-                buildTlv((1 << 16) - 1, (1 << 8) - 1, true), (1 << 16) - 1, (1 << 8) - 1);
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                buildTlv((1 << 16) - 1, (1 << 8) - 1, true), (1 << 16) - 1, (1 << 8) - 1,
+                linkLocalIpv6Address);
     }
 
     /**
@@ -863,8 +867,12 @@
      */
     @Test
     public void testDataPathInitiatorNetInfoLargeValuesExp2() throws Exception {
-        testDataPathInitiatorUtilityMore(false, true, true, false, true, false,
-                buildTlv(1 << 15, 1 << 7, true), 1 << 15, 1 << 7);
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                buildTlv(1 << 15, 1 << 7, true), 1 << 15, 1 << 7, linkLocalIpv6Address);
     }
 
     /**
@@ -872,8 +880,27 @@
      */
     @Test
     public void testDataPathInitiatorNetInfoLargeValuesExp3() throws Exception {
-        testDataPathInitiatorUtilityMore(false, true, true, false, true, false,
-                buildTlv((1 << 15) - 1, (1 << 7) - 1, true), (1 << 15) - 1, (1 << 7) - 1);
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                buildTlv((1 << 15) - 1, (1 << 7) - 1, true), (1 << 15) - 1, (1 << 7) - 1,
+                linkLocalIpv6Address);
+    }
+
+    /**
+     * Verify that an TLV configuration with an IPv6 override works correctly.
+     */
+    @Test
+    public void testDataPathInitiatorNetInfoIpv6Override() throws Exception {
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        final byte[] testVector =
+                new byte[]{0x00, 0x08, 0x00, 0x00, (byte) 0xb3, (byte) 0xe1, (byte) 0xff,
+                        (byte) 0xfe, 0x7a, 0x2f, (byte) 0xa2};
+
+        testDataPathInitiatorUtilityMore(false, true, true, false, true, false, peerDataPathMac,
+                testVector, 0, -1, "fe80::b3:e1ff:fe7a:2fa2");
     }
 
     /**
@@ -1082,10 +1109,10 @@
                 new byte[]{0x01, 0x0d, 0x00, 0x50, 0x6f, (byte) 0x9a, 0x02, 0x00, 0x02, 0x00, 0x58,
                         0x1b, 0x01, 0x01, 0x00, 0x06};
 
-        Pair<Integer, Integer> parsed =
+        WifiAwareDataPathStateManager.NetworkInformationData.ParsedResults parsed =
                 WifiAwareDataPathStateManager.NetworkInformationData.parseTlv(testVector);
-        assertEquals(port, (int) parsed.first);
-        assertEquals(transportProtocol, (int) parsed.second);
+        assertEquals(port, (int) parsed.port);
+        assertEquals(transportProtocol, (int) parsed.transportProtocol);
     }
 
     /*
@@ -1211,13 +1238,19 @@
     private void testDataPathInitiatorUtility(boolean useDirect, boolean provideMac,
             boolean providePmk, boolean providePassphrase, boolean getConfirmation,
             boolean immediateHalFailure) throws Exception {
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+        String linkLocalIpv6Address = MacAddress.fromBytes(
+                peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getHostAddress();
+
         testDataPathInitiatorUtilityMore(useDirect, provideMac, providePmk, providePassphrase,
-                getConfirmation, immediateHalFailure, null, 0, -1);
+                getConfirmation, immediateHalFailure, peerDataPathMac, null, 0, -1,
+                linkLocalIpv6Address);
     }
 
     private void testDataPathInitiatorUtilityMore(boolean useDirect, boolean provideMac,
             boolean providePmk, boolean providePassphrase, boolean getConfirmation,
-            boolean immediateHalFailure, byte[] peerToken, int port, int transportProtocol)
+            boolean immediateHalFailure, byte[] peerDataPathMac, byte[] peerToken, int port,
+            int transportProtocol, String ipv6Address)
             throws Exception {
         final int clientId = 123;
         final byte pubSubId = 58;
@@ -1226,7 +1259,6 @@
         final byte[] pmk = "01234567890123456789012345678901".getBytes();
         final String passphrase = "some passphrase";
         final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
-        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
 
         ArgumentCaptor<Messenger> messengerCaptor = ArgumentCaptor.forClass(Messenger.class);
         ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
@@ -1300,9 +1332,7 @@
             inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
             WifiAwareNetworkInfo netInfo =
                     (WifiAwareNetworkInfo) netCapCaptor.getValue().getTransportInfo();
-            assertArrayEquals(MacAddress.fromBytes(
-                    peerDataPathMac).getLinkLocalIpv6FromEui48Mac().getAddress(),
-                    netInfo.getPeerIpv6Addr().getAddress());
+            assertEquals(ipv6Address, netInfo.getPeerIpv6Addr().getHostAddress());
             assertEquals(port, netInfo.getPort());
             assertEquals(transportProtocol, netInfo.getTransportProtocol());
         } else {