Merge "[Wifi connected score] Update values after reset"
diff --git a/libwifi_system/hostapd_manager.cpp b/libwifi_system/hostapd_manager.cpp
index 658eecd..8c7de32 100644
--- a/libwifi_system/hostapd_manager.cpp
+++ b/libwifi_system/hostapd_manager.cpp
@@ -78,10 +78,6 @@
 }  // namespace
 
 bool HostapdManager::StartHostapd() {
-  if (!SupplicantManager::EnsureEntropyFileExists()) {
-    LOG(WARNING) << "Wi-Fi entropy file was not created";
-  }
-
   if (property_set("ctl.start", kHostapdServiceName) != 0) {
     LOG(ERROR) << "Failed to start SoftAP";
     return false;
diff --git a/libwifi_system/supplicant_manager.cpp b/libwifi_system/supplicant_manager.cpp
index 28010ec..be0c33a 100644
--- a/libwifi_system/supplicant_manager.cpp
+++ b/libwifi_system/supplicant_manager.cpp
@@ -40,12 +40,6 @@
 const char kSupplicantServiceName[] = "wpa_supplicant";
 constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
 
-const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
-
-const unsigned char kDummyKey[21] = {0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, 0x68,
-                                     0x47, 0x84, 0x99, 0xa9, 0x2b, 0x1c, 0xd3,
-                                     0xee, 0xff, 0xf1, 0xe2, 0xf3, 0xf4, 0xf5};
-
 int ensure_config_file_exists(const char* config_file) {
   char buf[2048];
   int srcfd, destfd;
@@ -152,10 +146,6 @@
    */
   (void)ensure_config_file_exists(kP2pConfigFile);
 
-  if (!EnsureEntropyFileExists()) {
-    LOG(ERROR) << "Wi-Fi entropy file was not created";
-  }
-
   /*
    * Get a reference to the status property, so we can distinguish
    * the case where it goes stopped => running => stopped (i.e.,
@@ -225,43 +215,5 @@
   return false;  // Failed to read service status from init.
 }
 
-bool SupplicantManager::EnsureEntropyFileExists() {
-  int ret;
-  int destfd;
-
-  ret = access(kWiFiEntropyFile, R_OK | W_OK);
-  if ((ret == 0) || (errno == EACCES)) {
-    if ((ret != 0) &&
-        (chmod(kWiFiEntropyFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
-      PLOG(ERROR) << "Cannot set RW to " << kWiFiEntropyFile;
-      return false;
-    }
-    return true;
-  }
-  destfd = TEMP_FAILURE_RETRY(open(kWiFiEntropyFile, O_CREAT | O_RDWR, 0660));
-  if (destfd < 0) {
-    PLOG(ERROR) << "Cannot create " << kWiFiEntropyFile;
-    return false;
-  }
-
-  if (TEMP_FAILURE_RETRY(write(destfd, kDummyKey, sizeof(kDummyKey))) !=
-      sizeof(kDummyKey)) {
-    PLOG(ERROR) << "Error writing " << kWiFiEntropyFile;
-    close(destfd);
-    return false;
-  }
-  close(destfd);
-
-  /* chmod is needed because open() didn't set permisions properly */
-  if (chmod(kWiFiEntropyFile, 0660) < 0) {
-    PLOG(ERROR) << "Error changing permissions of " << kWiFiEntropyFile
-                << " to 0600 ";
-    unlink(kWiFiEntropyFile);
-    return false;
-  }
-
-  return true;
-}
-
 }  // namespace wifi_system
 }  // namespace android
diff --git a/service/java/com/android/server/wifi/ExtendedWifiInfo.java b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
new file mode 100644
index 0000000..5edbd34
--- /dev/null
+++ b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.net.wifi.WifiInfo;
+
+/**
+ * Extends WifiInfo with the methods for computing the averaged packet rates
+ */
+public class ExtendedWifiInfo extends WifiInfo {
+    private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
+    private static final double FILTER_TIME_CONSTANT = 3000.0;
+
+    private long mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+
+    @Override
+    public void reset() {
+        super.reset();
+        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+    }
+
+    /**
+     * Updates the packet rates using link layer stats
+     *
+     * @param stats WifiLinkLayerStats
+     * @param timeStamp time in milliseconds
+     */
+    public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
+        if (stats != null) {
+            long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
+            long txretries = stats.retries_be + stats.retries_bk
+                    + stats.retries_vi + stats.retries_vo;
+            long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
+            long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
+                    + stats.lostmpdu_vi + stats.lostmpdu_vo;
+
+            if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
+                    && mLastPacketCountUpdateTimeStamp < timeStamp
+                    && txBad <= txbad
+                    && txSuccess <= txgood
+                    && rxSuccess <= rxgood
+                    && txRetries <= txretries) {
+                long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
+                double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
+                double currentSampleWeight = 1.0 - lastSampleWeight;
+
+                txBadRate = txBadRate * lastSampleWeight
+                        + (txbad - txBad) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                txSuccessRate = txSuccessRate * lastSampleWeight
+                        + (txgood - txSuccess) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                rxSuccessRate = rxSuccessRate * lastSampleWeight
+                        + (rxgood - rxSuccess) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                txRetriesRate = txRetriesRate * lastSampleWeight
+                        + (txretries - txRetries) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+            } else {
+                txBadRate = 0;
+                txSuccessRate = 0;
+                rxSuccessRate = 0;
+                txRetriesRate = 0;
+            }
+            txBad = txbad;
+            txSuccess = txgood;
+            rxSuccess = rxgood;
+            txRetries = txretries;
+            mLastPacketCountUpdateTimeStamp = timeStamp;
+        } else {
+            txBad = 0;
+            txSuccess = 0;
+            rxSuccess = 0;
+            txRetries = 0;
+            txBadRate = 0;
+            txSuccessRate = 0;
+            rxSuccessRate = 0;
+            txRetriesRate = 0;
+            mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+        }
+    }
+
+    /**
+     * This function is less powerful and used if the WifiLinkLayerStats API is not implemented
+     * at the Wifi HAL
+     *
+     * @hide
+     */
+    public void updatePacketRates(long txPackets, long rxPackets) {
+        //paranoia
+        txBad = 0;
+        txRetries = 0;
+        txBadRate = 0;
+        txRetriesRate = 0;
+        if (txSuccess <= txPackets && rxSuccess <= rxPackets) {
+            txSuccessRate = (txSuccessRate * 0.5)
+                    + ((double) (txPackets - txSuccess) * 0.5);
+            rxSuccessRate = (rxSuccessRate * 0.5)
+                    + ((double) (rxPackets - rxSuccess) * 0.5);
+        } else {
+            txBadRate = 0;
+            txRetriesRate = 0;
+        }
+        txSuccess = txPackets;
+        rxSuccess = rxPackets;
+    }
+
+
+}
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index 2c16444..31124d5 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -79,6 +79,17 @@
                 contentObserver);
     }
 
+    /**
+     * Helper method for classes to unregister a ContentObserver
+     * {@see ContentResolver#unregisterContentObserver(ContentObserver)}.
+     *
+     * @param context
+     * @param contentObserver
+     */
+    public void unregisterContentObserver(Context context, ContentObserver contentObserver) {
+        context.getContentResolver().unregisterContentObserver(contentObserver);
+    }
+
     public IBinder getService(String serviceName) {
         return ServiceManager.getService(serviceName);
     }
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index 1a2d43d..d65f31f 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -17,6 +17,7 @@
 package com.android.server.wifi;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.wifi.V1_0.IWifi;
 import android.hardware.wifi.V1_0.IWifiApIface;
 import android.hardware.wifi.V1_0.IWifiChip;
@@ -35,7 +36,6 @@
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.Handler;
 import android.os.HwRemoteBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -103,13 +103,14 @@
      * single copy kept.
      *
      * @param listener ManagerStatusListener listener object.
-     * @param handler Handler on which to dispatch listener. Null implies a new Handler based on
-     *                the current looper.
+     * @param handler Handler on which to dispatch listener. Null implies the listener will be
+     *                invoked synchronously from the context of the client which triggered the
+     *                state change.
      */
-    public void registerStatusListener(ManagerStatusListener listener, Handler handler) {
+    public void registerStatusListener(@NonNull ManagerStatusListener listener,
+            @Nullable Handler handler) {
         synchronized (mLock) {
-            if (!mManagerStatusListeners.add(new ManagerStatusListenerProxy(listener,
-                    handler == null ? new Handler(Looper.myLooper()) : handler))) {
+            if (!mManagerStatusListeners.add(new ManagerStatusListenerProxy(listener, handler))) {
                 Log.w(TAG, "registerStatusListener: duplicate registration ignored");
             }
         }
@@ -198,36 +199,37 @@
      * @param destroyedListener Optional (nullable) listener to call when the allocated interface
      *                          is removed. Will only be registered and used if an interface is
      *                          created successfully.
-     * @param handler The Handler on which to dispatch the listener. A null implies a new Handler
-     *                based on the current looper.
+     * @param handler Handler on which to dispatch listener. Null implies the listener will be
+     *                invoked synchronously from the context of the client which triggered the
+     *                iface destruction.
      * @return A newly created interface - or null if the interface could not be created.
      */
-    public IWifiStaIface createStaIface(InterfaceDestroyedListener destroyedListener,
-            Handler handler) {
+    public IWifiStaIface createStaIface(@Nullable InterfaceDestroyedListener destroyedListener,
+            @Nullable Handler handler) {
         return (IWifiStaIface) createIface(IfaceType.STA, destroyedListener, handler);
     }
 
     /**
      * Create AP interface if possible (see createStaIface doc).
      */
-    public IWifiApIface createApIface(InterfaceDestroyedListener destroyedListener,
-            Handler handler) {
+    public IWifiApIface createApIface(@Nullable InterfaceDestroyedListener destroyedListener,
+            @Nullable Handler handler) {
         return (IWifiApIface) createIface(IfaceType.AP, destroyedListener, handler);
     }
 
     /**
      * Create P2P interface if possible (see createStaIface doc).
      */
-    public IWifiP2pIface createP2pIface(InterfaceDestroyedListener destroyedListener,
-            Handler handler) {
+    public IWifiP2pIface createP2pIface(@Nullable InterfaceDestroyedListener destroyedListener,
+            @Nullable Handler handler) {
         return (IWifiP2pIface) createIface(IfaceType.P2P, destroyedListener, handler);
     }
 
     /**
      * Create NAN interface if possible (see createStaIface doc).
      */
-    public IWifiNanIface createNanIface(InterfaceDestroyedListener destroyedListener,
-            Handler handler) {
+    public IWifiNanIface createNanIface(@Nullable InterfaceDestroyedListener destroyedListener,
+            @Nullable Handler handler) {
         return (IWifiNanIface) createIface(IfaceType.NAN, destroyedListener, handler);
     }
 
@@ -269,11 +271,16 @@
      * and false on failure. This listener is in addition to the one registered when the interface
      * was created - allowing non-creators to monitor interface status.
      *
-     * Listener called-back on the specified handler - or on the current looper if a null is passed.
+     * @param destroyedListener Listener to call when the allocated interface is removed.
+     *                          Will only be registered and used if an interface is created
+     *                          successfully.
+     * @param handler Handler on which to dispatch listener. Null implies the listener will be
+     *                invoked synchronously from the context of the client which triggered the
+     *                iface destruction.
      */
     public boolean registerDestroyedListener(IWifiIface iface,
-            InterfaceDestroyedListener destroyedListener,
-            Handler handler) {
+            @NonNull InterfaceDestroyedListener destroyedListener,
+            @Nullable Handler handler) {
         String name = getName(iface);
         if (DBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + name);
 
@@ -304,11 +311,12 @@
      * @param ifaceType The interface type (IfaceType) to be monitored.
      * @param listener Listener to call when an interface of the requested
      *                 type could be created
-     * @param handler The Handler on which to dispatch the listener. A null implies a new Handler
-     *                on the current looper.
+     * @param handler Handler on which to dispatch listener. Null implies the listener will be
+     *                invoked synchronously from the context of the client which triggered the
+     *                mode change.
      */
     public void registerInterfaceAvailableForRequestListener(int ifaceType,
-            InterfaceAvailableForRequestListener listener, Handler handler) {
+            @NonNull InterfaceAvailableForRequestListener listener, @Nullable Handler handler) {
         if (DBG) Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
 
         synchronized (mLock) {
@@ -1215,9 +1223,8 @@
 
     private class ManagerStatusListenerProxy  extends
             ListenerProxy<ManagerStatusListener> {
-        ManagerStatusListenerProxy(ManagerStatusListener statusListener,
-                Handler handler) {
-            super(statusListener, handler, true, "ManagerStatusListenerProxy");
+        ManagerStatusListenerProxy(ManagerStatusListener statusListener, Handler handler) {
+            super(statusListener, handler, "ManagerStatusListenerProxy");
         }
 
         @Override
@@ -1478,7 +1485,7 @@
             for (int type: IFACE_TYPES_BY_PRIORITY) {
                 if (chipInfo.ifaces[type].length != 0) {
                     if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType,
-                            chipInfo.ifaces[ifaceType].length != 0)) {
+                            chipInfo.ifaces)) {
                         if (DBG) {
                             Log.d(TAG, "Couldn't delete existing type " + type
                                     + " interfaces for requested type");
@@ -1508,8 +1515,7 @@
             }
 
             if (tooManyInterfaces > 0) { // may need to delete some
-                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType,
-                        chipInfo.ifaces[ifaceType].length != 0)) {
+                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType, chipInfo.ifaces)) {
                     if (DBG) {
                         Log.d(TAG, "Would need to delete some higher priority interfaces");
                     }
@@ -1585,37 +1591,48 @@
      * Returns true if we're allowed to delete the existing interface type for the requested
      * interface type.
      *
-     * Rules:
-     * 1. Request for AP or STA will destroy any other interface (except see #4 and #5)
-     * 2. Request for P2P will destroy NAN-only
-     * 3. Request for NAN will not destroy any interface
-     * --
-     * 4. No interface will be destroyed for a requested interface of the same type
-     * 5. No interface will be destroyed if one of the requested interfaces already exists
+     * Rules - applies in order:
+     *
+     * General rules:
+     * 1. No interface will be destroyed for a requested interface of the same type
+     * 2. No interface will be destroyed if one of the requested interfaces already exists
+     * 3. If there are >1 interface of an existing type, then it is ok to destroy that type
+     *    interface
+     *
+     * Type-specific rules (but note that the general rules are appied first):
+     * 4. Request for AP or STA will destroy any other interface
+     * 5. Request for P2P will destroy NAN-only (but will destroy a second STA per #3)
+     * 6. Request for NAN will not destroy any interface (but will destroy a second STA per #3)
+
      */
     private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
-            int requestedIfaceType, boolean requestedIfaceTypeAlreadyExists) {
-        // rule 5
-        if (requestedIfaceTypeAlreadyExists) {
-            return false;
-        }
-
-        // rule 4
+            int requestedIfaceType, WifiIfaceInfo[][] currentIfaces) {
+        // rule 1
         if (existingIfaceType == requestedIfaceType) {
             return false;
         }
 
+        // rule 2
+        if (currentIfaces[requestedIfaceType].length != 0) {
+            return false;
+        }
+
         // rule 3
+        if (currentIfaces[existingIfaceType].length > 1) {
+            return true;
+        }
+
+        // rule 6
         if (requestedIfaceType == IfaceType.NAN) {
             return false;
         }
 
-        // rule 2
+        // rule 5
         if (requestedIfaceType == IfaceType.P2P) {
             return existingIfaceType == IfaceType.NAN;
         }
 
-        // rule 1, the requestIfaceType is either AP or STA
+        // rule 4, the requestIfaceType is either AP or STA
         return true;
     }
 
@@ -1902,11 +1919,8 @@
     }
 
     private abstract class ListenerProxy<LISTENER>  {
-        private static final int LISTENER_TRIGGERED = 0;
-
         protected LISTENER mListener;
         private Handler mHandler;
-        private boolean mFrontOfQueue;
 
         // override equals & hash to make sure that the container HashSet is unique with respect to
         // the contained listener
@@ -1921,23 +1935,20 @@
         }
 
         void trigger() {
-            if (mFrontOfQueue) {
-                mHandler.postAtFrontOfQueue(() -> {
-                    action();
-                });
-            } else {
+            if (mHandler != null) {
                 mHandler.post(() -> {
                     action();
                 });
+            } else {
+                action();
             }
         }
 
         protected abstract void action();
 
-        ListenerProxy(LISTENER listener, Handler handler, boolean frontOfQueue, String tag) {
+        ListenerProxy(LISTENER listener, Handler handler, String tag) {
             mListener = listener;
             mHandler = handler;
-            mFrontOfQueue = frontOfQueue;
         }
     }
 
@@ -1947,8 +1958,7 @@
         InterfaceDestroyedListenerProxy(@NonNull String ifaceName,
                                         InterfaceDestroyedListener destroyedListener,
                                         Handler handler) {
-            super(destroyedListener, handler == null ? new Handler(Looper.myLooper()) : handler,
-                    true, "InterfaceDestroyedListenerProxy");
+            super(destroyedListener, handler, "InterfaceDestroyedListenerProxy");
             mIfaceName = ifaceName;
         }
 
@@ -1962,8 +1972,7 @@
             ListenerProxy<InterfaceAvailableForRequestListener> {
         InterfaceAvailableForRequestListenerProxy(
                 InterfaceAvailableForRequestListener destroyedListener, Handler handler) {
-            super(destroyedListener, handler == null ? new Handler(Looper.myLooper()) : handler,
-                    false, "InterfaceAvailableForRequestListenerProxy");
+            super(destroyedListener, handler, "InterfaceAvailableForRequestListenerProxy");
         }
 
         @Override
diff --git a/service/java/com/android/server/wifi/LegacyConnectedScore.java b/service/java/com/android/server/wifi/LegacyConnectedScore.java
index facab0a..3027696 100644
--- a/service/java/com/android/server/wifi/LegacyConnectedScore.java
+++ b/service/java/com/android/server/wifi/LegacyConnectedScore.java
@@ -63,6 +63,10 @@
     private boolean mMultiBandScanResults;
     private boolean mIsHomeNetwork;
     private int mScore = 0;
+    private int mBadRssiCount;
+    private int mLinkStuckCount;
+    private int mLowRssiCount;
+
 
     LegacyConnectedScore(Context context, WifiConfigManager wifiConfigManager, Clock clock) {
         super(clock);
@@ -109,32 +113,32 @@
             rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
         }
 
-        if ((wifiInfo.txBadRate >= 1)
-                && (wifiInfo.txSuccessRate < MAX_SUCCESS_RATE_OF_STUCK_LINK)
+        if ((wifiInfo.txBadRate * 5 >= 1)
+                && (wifiInfo.txSuccessRate * 5 < MAX_SUCCESS_RATE_OF_STUCK_LINK)
                 && rssi < rssiThreshLow) {
             // Link is stuck
-            if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
-                wifiInfo.linkStuckCount += 1;
+            if (mLinkStuckCount < MAX_STUCK_LINK_COUNT) {
+                mLinkStuckCount += 1;
             }
-        } else if (wifiInfo.txBadRate < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
-            if (wifiInfo.linkStuckCount > 0) {
-                wifiInfo.linkStuckCount -= 1;
+        } else if (wifiInfo.txBadRate * 5 < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
+            if (mLinkStuckCount > 0) {
+                mLinkStuckCount -= 1;
             }
         }
 
         if (rssi < rssiThreshBad) {
-            if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
-                wifiInfo.badRssiCount += 1;
+            if (mBadRssiCount < MAX_BAD_RSSI_COUNT) {
+                mBadRssiCount += 1;
             }
         } else if (rssi < rssiThreshLow) {
-            wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
-            if (wifiInfo.badRssiCount > 0) {
+            mLowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
+            if (mBadRssiCount > 0) {
                 // Decrement bad Rssi count
-                wifiInfo.badRssiCount -= 1;
+                mBadRssiCount -= 1;
             }
         } else {
-            wifiInfo.badRssiCount = 0;
-            wifiInfo.lowRssiCount = 0;
+            mBadRssiCount = 0;
+            mLowRssiCount = 0;
         }
 
         // Ugh, we need to finish the score calculation while we have wifiInfo
@@ -155,6 +159,9 @@
     @Override
     public void reset() {
         mScore = 0;
+        mBadRssiCount = 0;
+        mLinkStuckCount = 0;
+        mLowRssiCount = 0;
     }
 
     /**
@@ -182,18 +189,19 @@
 
         int linkSpeed = wifiInfo.getLinkSpeed();
 
-        if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
+        if (mLinkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
             // Once link gets stuck for more than 3 seconds, start reducing the score
-            score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
+            score = score - LINK_STUCK_PENALTY * (mLinkStuckCount - 1);
         }
 
         if (linkSpeed < linkspeedThreshBad) {
             score -= BAD_LINKSPEED_PENALTY;
-        } else if ((linkSpeed >= linkspeedThreshGood) && (wifiInfo.txSuccessRate > 5)) {
+        } else if ((linkSpeed >= linkspeedThreshGood)
+                    && (wifiInfo.txSuccessRate > 1)) {
             score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone doesn't kill us
         }
 
-        score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
+        score -= mBadRssiCount * BAD_RSSI_COUNT_PENALTY + mLowRssiCount;
 
         if (rssi >= rssiThreshSaturated) score += 5;
 
diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
index 59d4ab2..97f6b6f 100644
--- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
@@ -25,8 +25,6 @@
 import com.android.internal.R;
 import com.android.server.wifi.util.TelephonyUtil;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -242,96 +240,83 @@
 
         for (ScanDetail scanDetail : scanDetails) {
             ScanResult scanResult = scanDetail.getScanResult();
-            int highestScoreOfScanResult = Integer.MIN_VALUE;
-            int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;
 
             // One ScanResult can be associated with more than one networks, hence we calculate all
             // the scores and use the highest one as the ScanResult's score.
-            List<WifiConfiguration> associatedConfigurations = null;
-            WifiConfiguration associatedConfiguration =
+            WifiConfiguration network =
                     mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
 
-            if (associatedConfiguration == null) {
+            if (network == null) {
                 continue;
-            } else {
-                associatedConfigurations =
-                    new ArrayList<>(Arrays.asList(associatedConfiguration));
             }
 
-            for (WifiConfiguration network : associatedConfigurations) {
-                /**
-                 * Ignore Passpoint and Ephemeral networks. They are configured networks,
-                 * but without being persisted to the storage. They are evaluated by
-                 * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
-                 * respectively.
-                 */
-                if (network.isPasspoint() || network.isEphemeral()) {
-                    continue;
-                }
+            /**
+             * Ignore Passpoint and Ephemeral networks. They are configured networks,
+             * but without being persisted to the storage. They are evaluated by
+             * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
+             * respectively.
+             */
+            if (network.isPasspoint() || network.isEphemeral()) {
+                continue;
+            }
 
-                WifiConfiguration.NetworkSelectionStatus status =
-                        network.getNetworkSelectionStatus();
-                status.setSeenInLastQualifiedNetworkSelection(true);
+            WifiConfiguration.NetworkSelectionStatus status =
+                    network.getNetworkSelectionStatus();
+            status.setSeenInLastQualifiedNetworkSelection(true);
 
-                if (!status.isNetworkEnabled()) {
-                    continue;
-                } else if (network.BSSID != null &&  !network.BSSID.equals("any")
-                        && !network.BSSID.equals(scanResult.BSSID)) {
-                    // App has specified the only BSSID to connect for this
-                    // configuration. So only the matching ScanResult can be a candidate.
-                    localLog("Network " + WifiNetworkSelector.toNetworkString(network)
-                            + " has specified BSSID " + network.BSSID + ". Skip "
-                            + scanResult.BSSID);
-                    continue;
-                } else if (TelephonyUtil.isSimConfig(network)
-                        && !mWifiConfigManager.isSimPresent()) {
-                    // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
-                    continue;
-                }
+            if (!status.isNetworkEnabled()) {
+                continue;
+            } else if (network.BSSID != null &&  !network.BSSID.equals("any")
+                    && !network.BSSID.equals(scanResult.BSSID)) {
+                // App has specified the only BSSID to connect for this
+                // configuration. So only the matching ScanResult can be a candidate.
+                localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+                        + " has specified BSSID " + network.BSSID + ". Skip "
+                        + scanResult.BSSID);
+                continue;
+            } else if (TelephonyUtil.isSimConfig(network)
+                    && !mWifiConfigManager.isSimPresent()) {
+                // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
+                continue;
+            }
 
-                int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
-                        scoreHistory);
+            int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
+                    scoreHistory);
 
-                // Set candidate ScanResult for all saved networks to ensure that users can
-                // override network selection. See WifiNetworkSelector#setUserConnectChoice.
-                // TODO(b/36067705): consider alternative designs to push filtering/selecting of
-                // user connect choice networks to RecommendedNetworkEvaluator.
-                if (score > status.getCandidateScore() || (score == status.getCandidateScore()
-                        && status.getCandidate() != null
-                        && scanResult.level > status.getCandidate().level)) {
-                    mWifiConfigManager.setNetworkCandidateScanResult(
-                            network.networkId, scanResult, score);
-                }
+            // Set candidate ScanResult for all saved networks to ensure that users can
+            // override network selection. See WifiNetworkSelector#setUserConnectChoice.
+            // TODO(b/36067705): consider alternative designs to push filtering/selecting of
+            // user connect choice networks to RecommendedNetworkEvaluator.
+            if (score > status.getCandidateScore() || (score == status.getCandidateScore()
+                    && status.getCandidate() != null
+                    && scanResult.level > status.getCandidate().level)) {
+                mWifiConfigManager.setNetworkCandidateScanResult(
+                        network.networkId, scanResult, score);
+            }
 
-                // If the network is marked to use external scores, or is an open network with
-                // curate saved open networks enabled, do not consider it for network selection.
-                if (network.useExternalScores) {
-                    localLog("Network " + WifiNetworkSelector.toNetworkString(network)
-                            + " has external score.");
-                    continue;
-                }
-
-                if (score > highestScoreOfScanResult) {
-                    highestScoreOfScanResult = score;
-                    candidateIdOfScanResult = network.networkId;
-                }
+            // If the network is marked to use external scores, or is an open network with
+            // curate saved open networks enabled, do not consider it for network selection.
+            if (network.useExternalScores) {
+                localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+                        + " has external score.");
+                continue;
             }
 
             if (connectableNetworks != null) {
                 connectableNetworks.add(Pair.create(scanDetail,
-                        mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult)));
+                        mWifiConfigManager.getConfiguredNetwork(network.networkId)));
             }
 
-            if (highestScoreOfScanResult > highestScore
-                    || (highestScoreOfScanResult == highestScore
+            if (score > highestScore
+                    || (score == highestScore
                     && scanResultCandidate != null
                     && scanResult.level > scanResultCandidate.level)) {
-                highestScore = highestScoreOfScanResult;
+                highestScore = score;
                 scanResultCandidate = scanResult;
                 mWifiConfigManager.setNetworkCandidateScanResult(
-                        candidateIdOfScanResult, scanResultCandidate, highestScore);
+                        network.networkId, scanResultCandidate, highestScore);
                 // Reload the network config with the updated info.
-                candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult);
+                candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId);
             }
         }
 
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index e045c39..2200618 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -23,27 +23,31 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.net.InterfaceConfiguration;
 import android.net.wifi.IApInterface;
-import android.net.wifi.IApInterfaceEventCallback;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiManager;
+import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.net.BaseNetworkObserver;
+import com.android.server.wifi.WifiNative.SoftApListener;
 import com.android.server.wifi.util.ApConfigUtil;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Locale;
 
 /**
@@ -53,8 +57,15 @@
 public class SoftApManager implements ActiveModeManager {
     private static final String TAG = "SoftApManager";
 
-    private final Context mContext;
+    // Minimum limit to use for timeout delay if the value from overlay setting is too small.
+    private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000;  // 10 minutes
 
+    @VisibleForTesting
+    public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
+            + " Soft AP Send Message Timeout";
+
+    private final Context mContext;
+    private final FrameworkFacade mFrameworkFacade;
     private final WifiNative mWifiNative;
 
     private final String mCountryCode;
@@ -74,18 +85,16 @@
     private final int mMode;
     private WifiConfiguration mApConfig;
 
-    private int mNumAssociatedStations = 0;
-
     /**
-     * Listener for AP Interface events.
+     * Listener for soft AP events.
      */
-    public class ApInterfaceListener extends IApInterfaceEventCallback.Stub {
+    private final SoftApListener mSoftApListener = new SoftApListener() {
         @Override
         public void onNumAssociatedStationsChanged(int numStations) {
             mStateMachine.sendMessage(
                     SoftApStateMachine.CMD_NUM_ASSOCIATED_STATIONS_CHANGED, numStations);
         }
-    }
+    };
 
     /**
      * Listener for soft AP state changes.
@@ -101,6 +110,7 @@
 
     public SoftApManager(Context context,
                          Looper looper,
+                         FrameworkFacade framework,
                          WifiNative wifiNative,
                          String countryCode,
                          Listener listener,
@@ -110,9 +120,8 @@
                          WifiApConfigStore wifiApConfigStore,
                          @NonNull SoftApModeConfiguration apConfig,
                          WifiMetrics wifiMetrics) {
-        mStateMachine = new SoftApStateMachine(looper);
-
         mContext = context;
+        mFrameworkFacade = framework;
         mWifiNative = wifiNative;
         mCountryCode = countryCode;
         mListener = listener;
@@ -128,6 +137,7 @@
             mApConfig = config;
         }
         mWifiMetrics = wifiMetrics;
+        mStateMachine = new SoftApStateMachine(looper);
     }
 
     /**
@@ -145,29 +155,6 @@
     }
 
     /**
-     * Get number of stations associated with this soft AP
-     */
-    @VisibleForTesting
-    public int getNumAssociatedStations() {
-        return mNumAssociatedStations;
-    }
-
-    /**
-     * Set number of stations associated with this soft AP
-     * @param numStations Number of connected stations
-     */
-    private void setNumAssociatedStations(int numStations) {
-        if (mNumAssociatedStations == numStations) {
-            return;
-        }
-        mNumAssociatedStations = numStations;
-        Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
-
-        // TODO:(b/63906412) send it up to settings.
-        mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations, mMode);
-    }
-
-    /**
      * Update AP state.
      * @param newState new AP state
      * @param currentState current AP state
@@ -227,71 +214,24 @@
                 return ERROR_GENERIC;
             }
         }
-
-        int encryptionType = getIApInterfaceEncryptionType(localConfig);
-
         if (localConfig.hiddenSSID) {
             Log.d(TAG, "SoftAP is a hidden network");
         }
-
-        try {
-            // Note that localConfig.SSID is intended to be either a hex string or "double quoted".
-            // However, it seems that whatever is handing us these configurations does not obey
-            // this convention.
-            boolean success = mApInterface.writeHostapdConfig(
-                    localConfig.SSID.getBytes(StandardCharsets.UTF_8), localConfig.hiddenSSID,
-                    localConfig.apChannel, encryptionType,
-                    (localConfig.preSharedKey != null)
-                            ? localConfig.preSharedKey.getBytes(StandardCharsets.UTF_8)
-                            : new byte[0]);
-            if (!success) {
-                Log.e(TAG, "Failed to write hostapd configuration");
-                return ERROR_GENERIC;
-            }
-
-            success = mApInterface.startHostapd(new ApInterfaceListener());
-            if (!success) {
-                Log.e(TAG, "Failed to start hostapd.");
-                return ERROR_GENERIC;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception in starting soft AP: " + e);
+        if (!mWifiNative.startSoftAp(localConfig, mSoftApListener)) {
+            Log.e(TAG, "Soft AP start failed");
+            return ERROR_GENERIC;
         }
-
         Log.d(TAG, "Soft AP is started");
 
         return SUCCESS;
     }
 
-    private static int getIApInterfaceEncryptionType(WifiConfiguration localConfig) {
-        int encryptionType;
-        switch (localConfig.getAuthType()) {
-            case KeyMgmt.NONE:
-                encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
-                break;
-            case KeyMgmt.WPA_PSK:
-                encryptionType = IApInterface.ENCRYPTION_TYPE_WPA;
-                break;
-            case KeyMgmt.WPA2_PSK:
-                encryptionType = IApInterface.ENCRYPTION_TYPE_WPA2;
-                break;
-            default:
-                // We really shouldn't default to None, but this was how NetworkManagementService
-                // used to do this.
-                encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
-                break;
-        }
-        return encryptionType;
-    }
-
     /**
      * Teardown soft AP.
      */
     private void stopSoftAp() {
-        try {
-            mApInterface.stopHostapd();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception in stopping soft AP: " + e);
+        if (!mWifiNative.stopSoftAp()) {
+            Log.d(TAG, "Soft AP stop failed");
             return;
         }
         Log.d(TAG, "Soft AP is stopped");
@@ -301,15 +241,18 @@
         // Commands for the state machine.
         public static final int CMD_START = 0;
         public static final int CMD_STOP = 1;
-        public static final int CMD_AP_INTERFACE_BINDER_DEATH = 2;
+        public static final int CMD_WIFICOND_BINDER_DEATH = 2;
         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
         public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4;
+        public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
+        public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
 
         private final State mIdleState = new IdleState();
         private final State mStartedState = new StartedState();
 
-        private final StateMachineDeathRecipient mDeathRecipient =
-                new StateMachineDeathRecipient(this, CMD_AP_INTERFACE_BINDER_DEATH);
+        private final WifiNative.WificondDeathEventHandler mWificondDeathRecipient = () -> {
+            sendMessage(CMD_WIFICOND_BINDER_DEATH);
+        };
 
         private NetworkObserver mNetworkObserver;
 
@@ -342,7 +285,7 @@
         private class IdleState extends State {
             @Override
             public void enter() {
-                mDeathRecipient.unlinkToDeath();
+                mWifiNative.deregisterWificondDeathHandler();
                 unregisterObserver();
             }
 
@@ -363,9 +306,7 @@
                         }
                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
-                        setNumAssociatedStations(0);
-                        if (!mDeathRecipient.linkToDeath(mApInterface.asBinder())) {
-                            mDeathRecipient.unlinkToDeath();
+                        if (!mWifiNative.registerWificondDeathHandler(mWificondDeathRecipient)) {
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                     WifiManager.WIFI_AP_STATE_ENABLING,
                                     WifiManager.SAP_START_FAILURE_GENERAL);
@@ -373,12 +314,11 @@
                                     false, WifiManager.SAP_START_FAILURE_GENERAL);
                             break;
                         }
-
                         try {
                             mNetworkObserver = new NetworkObserver(mApInterfaceName);
                             mNwService.registerObserver(mNetworkObserver);
                         } catch (RemoteException e) {
-                            mDeathRecipient.unlinkToDeath();
+                            mWifiNative.deregisterWificondDeathHandler();
                             unregisterObserver();
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                           WifiManager.WIFI_AP_STATE_ENABLING,
@@ -394,7 +334,7 @@
                             if (result == ERROR_NO_CHANNEL) {
                                 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
                             }
-                            mDeathRecipient.unlinkToDeath();
+                            mWifiNative.deregisterWificondDeathHandler();
                             unregisterObserver();
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                           WifiManager.WIFI_AP_STATE_ENABLING,
@@ -427,6 +367,92 @@
         private class StartedState extends State {
             private boolean mIfaceIsUp;
 
+            private int mNumAssociatedStations;
+
+            private boolean mTimeoutEnabled;
+            private int mTimeoutDelay;
+            private WakeupMessage mSoftApTimeoutMessage;
+            private SoftApTimeoutEnabledSettingObserver mSettingObserver;
+
+            /**
+            * Observer for timeout settings changes.
+            */
+            private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
+                SoftApTimeoutEnabledSettingObserver(Handler handler) {
+                    super(handler);
+                }
+
+                public void register() {
+                    mFrameworkFacade.registerContentObserver(mContext,
+                            Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
+                            true, this);
+                    mTimeoutEnabled = getValue();
+                }
+
+                public void unregister() {
+                    mFrameworkFacade.unregisterContentObserver(mContext, this);
+                }
+
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
+                            getValue() ? 1 : 0);
+                }
+
+                private boolean getValue() {
+                    boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
+                            Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
+                    return enabled;
+                }
+            }
+
+            private int getConfigSoftApTimeoutDelay() {
+                int delay = mContext.getResources().getInteger(
+                        R.integer.config_wifi_framework_soft_ap_timeout_delay);
+                if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
+                    delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
+                    Log.w(TAG, "Overriding timeout delay with minimum limit value");
+                }
+                Log.d(TAG, "Timeout delay: " + delay);
+                return delay;
+            }
+
+            private void scheduleTimeoutMessage() {
+                if (!mTimeoutEnabled) {
+                    return;
+                }
+                mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
+                Log.d(TAG, "Timeout message scheduled");
+            }
+
+            private void cancelTimeoutMessage() {
+                mSoftApTimeoutMessage.cancel();
+                Log.d(TAG, "Timeout message canceled");
+            }
+
+            /**
+             * Set number of stations associated with this soft AP
+             * @param numStations Number of connected stations
+             */
+            private void setNumAssociatedStations(int numStations) {
+                if (mNumAssociatedStations == numStations) {
+                    return;
+                }
+                mNumAssociatedStations = numStations;
+                Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
+
+                // TODO:(b/63906412) send it up to settings.
+                mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations,
+                        mMode);
+
+                if (mNumAssociatedStations == 0) {
+                    scheduleTimeoutMessage();
+                } else {
+                    cancelTimeoutMessage();
+                }
+            }
+
             private void onUpChanged(boolean isUp) {
                 if (isUp == mIfaceIsUp) {
                     return;  // no change
@@ -440,9 +466,7 @@
                 } else {
                     // TODO: handle the case where the interface was up, but goes down
                 }
-
                 mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
-                setNumAssociatedStations(0);
             }
 
             @Override
@@ -456,6 +480,30 @@
                 if (config != null) {
                     onUpChanged(config.isUp());
                 }
+
+                mTimeoutDelay = getConfigSoftApTimeoutDelay();
+                Handler handler = mStateMachine.getHandler();
+                mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
+                        SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
+                        SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
+                mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
+
+                if (mSettingObserver != null) {
+                    mSettingObserver.register();
+                }
+                Log.d(TAG, "Resetting num stations on start");
+                mNumAssociatedStations = 0;
+                scheduleTimeoutMessage();
+            }
+
+            @Override
+            public void exit() {
+                if (mSettingObserver != null) {
+                    mSettingObserver.unregister();
+                }
+                Log.d(TAG, "Resetting num stations on stop");
+                mNumAssociatedStations = 0;
+                cancelTimeoutMessage();
             }
 
             @Override
@@ -466,8 +514,22 @@
                             Log.e(TAG, "Invalid number of associated stations: " + message.arg1);
                             break;
                         }
+                        Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED");
                         setNumAssociatedStations(message.arg1);
                         break;
+                    case CMD_TIMEOUT_TOGGLE_CHANGED:
+                        boolean isEnabled = (message.arg1 == 1);
+                        if (mTimeoutEnabled == isEnabled) {
+                            break;
+                        }
+                        mTimeoutEnabled = isEnabled;
+                        if (!mTimeoutEnabled) {
+                            cancelTimeoutMessage();
+                        }
+                        if (mTimeoutEnabled && mNumAssociatedStations == 0) {
+                            scheduleTimeoutMessage();
+                        }
+                        break;
                     case CMD_INTERFACE_STATUS_CHANGED:
                         if (message.obj != mNetworkObserver) {
                             // This is from some time before the most recent configuration.
@@ -479,13 +541,23 @@
                     case CMD_START:
                         // Already started, ignore this command.
                         break;
-                    case CMD_AP_INTERFACE_BINDER_DEATH:
+                    case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
+                        if (!mTimeoutEnabled) {
+                            Log.wtf(TAG, "Timeout message received while timeout is disabled."
+                                    + " Dropping.");
+                            break;
+                        }
+                        if (mNumAssociatedStations != 0) {
+                            Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
+                            break;
+                        }
+                        Log.i(TAG, "Timeout message received. Stopping soft AP.");
+                    case CMD_WIFICOND_BINDER_DEATH:
                     case CMD_STOP:
                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
-                        setNumAssociatedStations(0);
                         stopSoftAp();
-                        if (message.what == CMD_AP_INTERFACE_BINDER_DEATH) {
+                        if (message.what == CMD_WIFICOND_BINDER_DEATH) {
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                           WifiManager.WIFI_AP_STATE_DISABLING,
                                           WifiManager.SAP_START_FAILURE_GENERAL);
diff --git a/service/java/com/android/server/wifi/StateMachineDeathRecipient.java b/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
deleted file mode 100644
index 64c4bee..0000000
--- a/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import com.android.internal.util.StateMachine;
-
-/**
- * Allows StateMachine instances to subscribe to binder death.
- *
- * @hide
- */
-public class StateMachineDeathRecipient implements DeathRecipient {
-
-    private final StateMachine mStateMachine;
-    private final int mDeathCommand;
-    private IBinder mLinkedBinder;
-
-    /**
-     * Construct a StateMachineDeathRecipient.
-     *
-     * @param sm StateMachine instance to receive a message upon Binder death.
-     * @param command message to send the state machine.
-     */
-    public StateMachineDeathRecipient(StateMachine sm, int command) {
-        mStateMachine = sm;
-        mDeathCommand = command;
-    }
-
-    /**
-     * Listen for the death of a binder.
-     *
-     * This method will unlink from death notifications from any
-     * previously linked IBinder instance.
-     *
-     * @param binder remote object to listen for death.
-     * @return true iff we have successfully subscribed to death notifications of a live
-     *         IBinder instance.
-     */
-    public boolean linkToDeath(IBinder binder) {
-        unlinkToDeath();
-        try {
-            binder.linkToDeath(this, 0);
-        } catch (RemoteException e) {
-            // The remote has already died.
-            return false;
-        }
-        mLinkedBinder = binder;
-        return true;
-    }
-
-    /**
-     * Unlink from notifications from the last linked IBinder instance.
-     */
-    public void unlinkToDeath() {
-        if (mLinkedBinder == null) {
-            return;
-        }
-        mLinkedBinder.unlinkToDeath(this, 0);
-        mLinkedBinder = null;
-    }
-
-    /**
-     * Called by the binder subsystem upon remote object death.
-     */
-    @Override
-    public void binderDied() {
-        mStateMachine.sendMessage(mDeathCommand);
-    }
-}
\ No newline at end of file
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
index c93e110..664b052 100644
--- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -53,6 +53,7 @@
 import android.util.Pair;
 import android.util.SparseArray;
 
+import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler;
 import com.android.server.wifi.hotspot2.AnqpEvent;
 import com.android.server.wifi.hotspot2.IconEvent;
 import com.android.server.wifi.hotspot2.WnmData;
@@ -97,8 +98,15 @@
     // Supplicant HAL interface objects
     private IServiceManager mIServiceManager = null;
     private ISupplicant mISupplicant;
-    private ISupplicantStaIface mISupplicantStaIface;
-    private ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
+    private HashMap<String, ISupplicantStaIface> mISupplicantStaIfaces = new HashMap<>();
+    private HashMap<String, ISupplicantStaIfaceCallback> mISupplicantStaIfaceCallbacks =
+            new HashMap<>();
+    private HashMap<String, SupplicantStaNetworkHal> mCurrentNetworkRemoteHandles = new HashMap<>();
+    private HashMap<String, WifiConfiguration> mCurrentNetworkLocalConfigs = new HashMap<>();
+    private SupplicantDeathEventHandler mDeathEventHandler;
+    private final Context mContext;
+    private final WifiMonitor mWifiMonitor;
+
     private final IServiceNotification mServiceNotificationCallback =
             new IServiceNotification.Stub() {
         public void onRegistration(String fqName, String name, boolean preexisting) {
@@ -107,11 +115,11 @@
                     Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
                             + ", " + name + " preexisting=" + preexisting);
                 }
-                if (!initSupplicantService() || !initSupplicantStaIface()) {
-                    Log.e(TAG, "initalizing ISupplicantIfaces failed.");
+                if (!initSupplicantService()) {
+                    Log.e(TAG, "initalizing ISupplicant failed.");
                     supplicantServiceDiedHandler();
                 } else {
-                    Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
+                    Log.i(TAG, "Completed initialization of ISupplicant.");
                 }
             }
         }
@@ -132,16 +140,10 @@
                 }
             };
 
-    private String mIfaceName;
-    private SupplicantStaNetworkHal mCurrentNetworkRemoteHandle;
-    private WifiConfiguration mCurrentNetworkLocalConfig;
-    private final Context mContext;
-    private final WifiMonitor mWifiMonitor;
 
     public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) {
         mContext = context;
         mWifiMonitor = monitor;
-        mISupplicantStaIfaceCallback = new SupplicantStaIfaceHalCallback();
     }
 
     /**
@@ -184,7 +186,7 @@
                 Log.i(TAG, "Registering ISupplicant service ready callback.");
             }
             mISupplicant = null;
-            mISupplicantStaIface = null;
+            mISupplicantStaIfaces.clear();
             if (mIServiceManager != null) {
                 // Already have an IServiceManager and serviceNotification registered, don't
                 // don't register another.
@@ -253,11 +255,11 @@
         return true;
     }
 
-    private boolean linkToSupplicantStaIfaceDeath() {
+    private boolean linkToSupplicantStaIfaceDeath(ISupplicantStaIface iface) {
         synchronized (mLock) {
-            if (mISupplicantStaIface == null) return false;
+            if (iface == null) return false;
             try {
-                if (!mISupplicantStaIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
+                if (!iface.linkToDeath(mSupplicantDeathRecipient, 0)) {
                     Log.wtf(TAG, "Error on linkToDeath on ISupplicantStaIface");
                     supplicantServiceDiedHandler();
                     return false;
@@ -270,16 +272,23 @@
         }
     }
 
-    private int getCurrentNetworkId() {
+    private int getCurrentNetworkId(@NonNull String ifaceName) {
         synchronized (mLock) {
-            if (mCurrentNetworkLocalConfig == null) {
+            WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
+            if (currentConfig == null) {
                 return WifiConfiguration.INVALID_NETWORK_ID;
             }
-            return mCurrentNetworkLocalConfig.networkId;
+            return currentConfig.networkId;
         }
     }
 
-    private boolean initSupplicantStaIface() {
+    /**
+     * Setup a STA interface for the specified iface name.
+     *
+     * @param ifaceName Name of the interface.
+     * @return true on success, false otherwise.
+     */
+    public boolean setupIface(@NonNull String ifaceName) {
         synchronized (mLock) {
             /** List all supplicant Ifaces */
             final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
@@ -294,6 +303,7 @@
                 });
             } catch (RemoteException e) {
                 Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
+                supplicantServiceDiedHandler(ifaceName);
                 return false;
             }
             if (supplicantIfaces.size() == 0) {
@@ -301,9 +311,8 @@
                 return false;
             }
             Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
-            Mutable<String> ifaceName = new Mutable<>();
             for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
-                if (ifaceInfo.type == IfaceType.STA) {
+                if (ifaceInfo.type == IfaceType.STA && ifaceName.equals(ifaceInfo.name)) {
                     try {
                         mISupplicant.getInterface(ifaceInfo,
                                 (SupplicantStatus status, ISupplicantIface iface) -> {
@@ -315,33 +324,99 @@
                             });
                     } catch (RemoteException e) {
                         Log.e(TAG, "ISupplicant.getInterface exception: " + e);
+                        supplicantServiceDiedHandler(ifaceName);
                         return false;
                     }
-                    ifaceName.value = ifaceInfo.name;
                     break;
                 }
             }
             if (supplicantIface.value == null) {
-                Log.e(TAG, "initSupplicantStaIface got null iface");
+                Log.e(TAG, "setupSupplicantStaIface got null iface");
                 return false;
             }
-            mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value);
-            mIfaceName = ifaceName.value;
-            if (!linkToSupplicantStaIfaceDeath()) {
+            ISupplicantStaIface iface = getStaIfaceMockable(supplicantIface.value);
+            if (!linkToSupplicantStaIfaceDeath(iface)) {
                 return false;
             }
-            if (!registerCallback(mISupplicantStaIfaceCallback)) {
+            ISupplicantStaIfaceCallback callback = new SupplicantStaIfaceHalCallback(ifaceName);
+            if (!registerCallback(iface, callback)) {
                 return false;
             }
+            mISupplicantStaIfaces.put(ifaceName, iface);
+            mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
             return true;
         }
     }
 
+    /**
+     * Registers a death notification for supplicant.
+     * @return Returns true on success.
+     */
+    public boolean registerDeathHandler(@NonNull SupplicantDeathEventHandler handler) {
+        if (mDeathEventHandler != null) {
+            Log.e(TAG, "Death handler already present");
+            return false;
+        }
+        mDeathEventHandler = handler;
+        return true;
+    }
+
+    /**
+     * Deregisters a death notification for supplicant.
+     * @return Returns true on success.
+     */
+    public boolean deregisterDeathHandler() {
+        if (mDeathEventHandler == null) {
+            Log.e(TAG, "No Death handler present");
+            return false;
+        }
+        mDeathEventHandler = null;
+        return true;
+    }
+
+    /**
+     * Teardown a STA interface for the specified iface name.
+     *
+     * @param ifaceName Name of the interface.
+     * @return true on success, false otherwise.
+     */
+    public boolean teardownIface(@NonNull String ifaceName) {
+        synchronized (mLock) {
+            if (mISupplicantStaIfaces.remove(ifaceName) == null) {
+                return false;
+            }
+            mISupplicantStaIfaceCallbacks.remove(ifaceName);
+            // TODO(b/69162019): There are no HIDL calls to bring down interfaces
+            // from wpa_supplicant yet!
+            return true;
+        }
+    }
+
+    private void clearState() {
+        synchronized (mLock) {
+            mISupplicant = null;
+            mISupplicantStaIfaces.clear();
+            mCurrentNetworkLocalConfigs.clear();
+            mCurrentNetworkRemoteHandles.clear();
+        }
+    }
+
+    private void supplicantServiceDiedHandler(@NonNull String ifaceName) {
+        synchronized (mLock) {
+            mWifiMonitor.broadcastSupplicantDisconnectionEvent(ifaceName);
+            clearState();
+        }
+    }
+
     private void supplicantServiceDiedHandler() {
         synchronized (mLock) {
-            mISupplicant = null;
-            mISupplicantStaIface = null;
-            mWifiMonitor.broadcastSupplicantDisconnectionEvent(mIfaceName);
+            if (mDeathEventHandler != null) {
+                mDeathEventHandler.onDeath();
+            }
+            for (String ifaceName : mISupplicantStaIfaces.keySet()) {
+                mWifiMonitor.broadcastSupplicantDisconnectionEvent(ifaceName);
+            }
+            clearState();
         }
     }
 
@@ -359,7 +434,7 @@
      */
     public boolean isInitializationComplete() {
         synchronized (mLock) {
-            return mISupplicantStaIface != null;
+            return mISupplicant != null;
         }
     }
 
@@ -385,6 +460,27 @@
     }
 
     /**
+     * Helper method to look up the network object for the specified iface.
+     */
+    private ISupplicantStaIface getStaIface(@NonNull String ifaceName) {
+        return mISupplicantStaIfaces.get(ifaceName);
+    }
+
+    /**
+     * Helper method to look up the network object for the specified iface.
+     */
+    private SupplicantStaNetworkHal getCurrentNetworkRemoteHandle(@NonNull String ifaceName) {
+        return mCurrentNetworkRemoteHandles.get(ifaceName);
+    }
+
+    /**
+     * Helper method to look up the network config or the specified iface.
+     */
+    private WifiConfiguration getCurrentNetworkLocalConfig(@NonNull String ifaceName) {
+        return mCurrentNetworkLocalConfigs.get(ifaceName);
+    }
+
+    /**
      * Add a network configuration to wpa_supplicant.
      *
      * @param config Config corresponding to the network.
@@ -392,14 +488,14 @@
      * for the current network.
      */
     private Pair<SupplicantStaNetworkHal, WifiConfiguration>
-            addNetworkAndSaveConfig(WifiConfiguration config) {
+            addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
         synchronized (mLock) {
             logi("addSupplicantStaNetwork via HIDL");
             if (config == null) {
                 loge("Cannot add NULL network!");
                 return null;
             }
-            SupplicantStaNetworkHal network = addNetwork();
+            SupplicantStaNetworkHal network = addNetwork(ifaceName);
             if (network == null) {
                 loge("Failed to add a network!");
                 return null;
@@ -412,7 +508,7 @@
             }
             if (!saveSuccess) {
                 loge("Failed to save variables for: " + config.configKey());
-                if (!removeAllNetworks()) {
+                if (!removeAllNetworks(ifaceName)) {
                     loge("Failed to remove all networks on failure.");
                 }
                 return null;
@@ -428,47 +524,50 @@
      * networks and saves |config|.
      * 2. Select the new network in wpa_supplicant.
      *
+     * @param ifaceName Name of the interface.
      * @param config WifiConfiguration parameters for the provided network.
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
-    public boolean connectToNetwork(@NonNull WifiConfiguration config) {
+    public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
         synchronized (mLock) {
             logd("connectToNetwork " + config.configKey());
-            if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
+            WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
+            if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
                 String networkSelectionBSSID = config.getNetworkSelectionStatus()
                         .getNetworkSelectionBSSID();
                 String networkSelectionBSSIDCurrent =
-                        mCurrentNetworkLocalConfig.getNetworkSelectionStatus()
-                                .getNetworkSelectionBSSID();
+                        currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
                 if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
                     logd("Network is already saved, will not trigger remove and add operation.");
                 } else {
                     logd("Network is already saved, but need to update BSSID.");
                     if (!setCurrentNetworkBssid(
+                            ifaceName,
                             config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
                         loge("Failed to set current network BSSID.");
                         return false;
                     }
-                    mCurrentNetworkLocalConfig = new WifiConfiguration(config);
+                    mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
                 }
             } else {
-                mCurrentNetworkRemoteHandle = null;
-                mCurrentNetworkLocalConfig = null;
-                if (!removeAllNetworks()) {
+                mCurrentNetworkRemoteHandles.remove(ifaceName);
+                mCurrentNetworkLocalConfigs.remove(ifaceName);
+                if (!removeAllNetworks(ifaceName)) {
                     loge("Failed to remove existing networks");
                     return false;
                 }
                 Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
-                        addNetworkAndSaveConfig(config);
+                        addNetworkAndSaveConfig(ifaceName, config);
                 if (pair == null) {
                     loge("Failed to add/save network configuration: " + config.configKey());
                     return false;
                 }
-                mCurrentNetworkRemoteHandle = pair.first;
-                mCurrentNetworkLocalConfig = pair.second;
+                mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
+                mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
             }
-
-            if (!mCurrentNetworkRemoteHandle.select()) {
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
+            if (networkHandle == null || !networkHandle.select()) {
                 loge("Failed to select network configuration: " + config.configKey());
                 return false;
             }
@@ -485,23 +584,27 @@
      * 2. Set the new bssid for the network in wpa_supplicant.
      * 3. Trigger reassociate command to wpa_supplicant.
      *
+     * @param ifaceName Name of the interface.
      * @param config WifiConfiguration parameters for the provided network.
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
-    public boolean roamToNetwork(WifiConfiguration config) {
+    public boolean roamToNetwork(@NonNull String ifaceName, WifiConfiguration config) {
         synchronized (mLock) {
-            if (getCurrentNetworkId() != config.networkId) {
+            if (getCurrentNetworkId(ifaceName) != config.networkId) {
                 Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
-                        + "Current network ID: " + getCurrentNetworkId());
-                return connectToNetwork(config);
+                        + "Current network ID: " + getCurrentNetworkId(ifaceName));
+                return connectToNetwork(ifaceName, config);
             }
             String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
             logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
-            if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) {
+
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "roamToNetwork");
+            if (networkHandle == null || !networkHandle.setBssid(bssid)) {
                 loge("Failed to set new bssid on network: " + config.configKey());
                 return false;
             }
-            if (!reassociate()) {
+            if (!reassociate(ifaceName)) {
                 loge("Failed to trigger reassociate");
                 return false;
             }
@@ -512,21 +615,22 @@
     /**
      * Load all the configured networks from wpa_supplicant.
      *
+     * @param ifaceName     Name of the interface.
      * @param configs       Map of configuration key to configuration objects corresponding to all
      *                      the networks.
      * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
      * @return true if succeeds, false otherwise.
      */
-    public boolean loadNetworks(Map<String, WifiConfiguration> configs,
+    public boolean loadNetworks(@NonNull String ifaceName, Map<String, WifiConfiguration> configs,
                                 SparseArray<Map<String, String>> networkExtras) {
         synchronized (mLock) {
-            List<Integer> networkIds = listNetworks();
+            List<Integer> networkIds = listNetworks(ifaceName);
             if (networkIds == null) {
                 Log.e(TAG, "Failed to list networks");
                 return false;
             }
             for (Integer networkId : networkIds) {
-                SupplicantStaNetworkHal network = getNetwork(networkId);
+                SupplicantStaNetworkHal network = getNetwork(ifaceName, networkId);
                 if (network == null) {
                     Log.e(TAG, "Failed to get network with ID: " + networkId);
                     return false;
@@ -555,7 +659,7 @@
                 if (duplicateConfig != null) {
                     // The network is already known. Overwrite the duplicate entry.
                     Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
-                    removeNetwork(duplicateConfig.networkId);
+                    removeNetwork(ifaceName, duplicateConfig.networkId);
                     networkExtras.remove(duplicateConfig.networkId);
                 }
             }
@@ -567,37 +671,40 @@
      * Remove the request |networkId| from supplicant if it's the current network,
      * if the current configured network matches |networkId|.
      *
+     * @param ifaceName Name of the interface.
      * @param networkId network id of the network to be removed from supplicant.
      */
-    public void removeNetworkIfCurrent(int networkId) {
+    public void removeNetworkIfCurrent(@NonNull String ifaceName, int networkId) {
         synchronized (mLock) {
-            if (getCurrentNetworkId() == networkId) {
+            if (getCurrentNetworkId(ifaceName) == networkId) {
                 // Currently we only save 1 network in supplicant.
-                removeAllNetworks();
+                removeAllNetworks(ifaceName);
             }
         }
     }
 
     /**
      * Remove all networks from supplicant
+     *
+     * @param ifaceName Name of the interface.
      */
-    public boolean removeAllNetworks() {
+    public boolean removeAllNetworks(@NonNull String ifaceName) {
         synchronized (mLock) {
-            ArrayList<Integer> networks = listNetworks();
+            ArrayList<Integer> networks = listNetworks(ifaceName);
             if (networks == null) {
                 Log.e(TAG, "removeAllNetworks failed, got null networks");
                 return false;
             }
             for (int id : networks) {
-                if (!removeNetwork(id)) {
+                if (!removeNetwork(ifaceName, id)) {
                     Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
                     return false;
                 }
             }
             // Reset current network info.  Probably not needed once we add support to remove/reset
             // current network on receiving disconnection event from supplicant (b/32898136).
-            mCurrentNetworkLocalConfig = null;
-            mCurrentNetworkRemoteHandle = null;
+            mCurrentNetworkRemoteHandles.remove(ifaceName);
+            mCurrentNetworkLocalConfigs.remove(ifaceName);
             return true;
         }
     }
@@ -605,113 +712,152 @@
     /**
      * Set the currently configured network's bssid.
      *
+     * @param ifaceName Name of the interface.
      * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
      * @return true if succeeds, false otherwise.
      */
-    public boolean setCurrentNetworkBssid(String bssidStr) {
+    public boolean setCurrentNetworkBssid(@NonNull String ifaceName, String bssidStr) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.setBssid(bssidStr);
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(ifaceName, "setCurrentNetworkBssid");
+            if (networkHandle == null) return false;
+            return networkHandle.setBssid(bssidStr);
         }
     }
 
     /**
      * Get the currently configured network's WPS NFC token.
      *
+     * @param ifaceName Name of the interface.
      * @return Hex string corresponding to the WPS NFC token.
      */
-    public String getCurrentNetworkWpsNfcConfigurationToken() {
+    public String getCurrentNetworkWpsNfcConfigurationToken(@NonNull String ifaceName) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return null;
-            return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken();
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "getCurrentNetworkWpsNfcConfigurationToken");
+            if (networkHandle == null) return null;
+            return networkHandle.getWpsNfcConfigurationToken();
         }
     }
 
     /**
      * Get the eap anonymous identity for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @return anonymous identity string if succeeds, null otherwise.
      */
-    public String getCurrentNetworkEapAnonymousIdentity() {
+    public String getCurrentNetworkEapAnonymousIdentity(@NonNull String ifaceName) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return null;
-            return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity();
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "getCurrentNetworkEapAnonymousIdentity");
+            if (networkHandle == null) return null;
+            return networkHandle.fetchEapAnonymousIdentity();
         }
     }
 
     /**
      * Send the eap identity response for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @param identityStr String to send.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) {
+    public boolean sendCurrentNetworkEapIdentityResponse(
+            @NonNull String ifaceName, String identityStr) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr);
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapIdentityResponse");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapIdentityResponse(identityStr);
         }
     }
 
     /**
      * Send the eap sim gsm auth response for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @param paramsStr String to send.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) {
+    public boolean sendCurrentNetworkEapSimGsmAuthResponse(
+            @NonNull String ifaceName, String paramsStr) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapSimGsmAuthResponse");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
         }
     }
 
     /**
      * Send the eap sim gsm auth failure for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapSimGsmAuthFailure() {
+    public boolean sendCurrentNetworkEapSimGsmAuthFailure(@NonNull String ifaceName) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure();
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapSimGsmAuthFailure");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapSimGsmAuthFailure();
         }
     }
 
     /**
      * Send the eap sim umts auth response for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @param paramsStr String to send.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) {
+    public boolean sendCurrentNetworkEapSimUmtsAuthResponse(
+            @NonNull String ifaceName, String paramsStr) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapSimUmtsAuthResponse");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
         }
     }
 
     /**
      * Send the eap sim umts auts response for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @param paramsStr String to send.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) {
+    public boolean sendCurrentNetworkEapSimUmtsAutsResponse(
+            @NonNull String ifaceName, String paramsStr) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapSimUmtsAutsResponse");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
         }
     }
 
     /**
      * Send the eap sim umts auth failure for the currently configured network.
      *
+     * @param ifaceName Name of the interface.
      * @return true if succeeds, false otherwise.
      */
-    public boolean sendCurrentNetworkEapSimUmtsAuthFailure() {
+    public boolean sendCurrentNetworkEapSimUmtsAuthFailure(@NonNull String ifaceName) {
         synchronized (mLock) {
-            if (mCurrentNetworkRemoteHandle == null) return false;
-            return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure();
+            SupplicantStaNetworkHal networkHandle =
+                    checkSupplicantStaNetworkAndLogFailure(
+                            ifaceName, "sendCurrentNetworkEapSimUmtsAuthFailure");
+            if (networkHandle == null) return false;
+            return networkHandle.sendNetworkEapSimUmtsAuthFailure();
         }
     }
 
@@ -720,13 +866,14 @@
      *
      * @return The ISupplicantNetwork object for the new network, or null if the call fails
      */
-    private SupplicantStaNetworkHal addNetwork() {
+    private SupplicantStaNetworkHal addNetwork(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "addNetwork";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return null;
             Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
             try {
-                mISupplicantStaIface.addNetwork((SupplicantStatus status,
+                iface.addNetwork((SupplicantStatus status,
                         ISupplicantNetwork network) -> {
                     if (checkStatusAndLogFailure(status, methodStr)) {
                         newNetwork.value = network;
@@ -737,6 +884,7 @@
             }
             if (newNetwork.value != null) {
                 return getStaNetworkMockable(
+                        ifaceName,
                         ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
             } else {
                 return null;
@@ -749,12 +897,13 @@
      *
      * @return true if request is sent successfully, false otherwise.
      */
-    private boolean removeNetwork(int id) {
+    private boolean removeNetwork(@NonNull String ifaceName, int id) {
         synchronized (mLock) {
             final String methodStr = "removeNetwork";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
+                SupplicantStatus status = iface.removeNetwork(id);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -766,15 +915,16 @@
     /**
      * Use this to mock the creation of SupplicantStaNetworkHal instance.
      *
+     * @param ifaceName Name of the interface.
      * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
      * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
      * the call fails
      */
     protected SupplicantStaNetworkHal getStaNetworkMockable(
-            ISupplicantStaNetwork iSupplicantStaNetwork) {
+            @NonNull String ifaceName, ISupplicantStaNetwork iSupplicantStaNetwork) {
         synchronized (mLock) {
             SupplicantStaNetworkHal network =
-                    new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
+                    new SupplicantStaNetworkHal(iSupplicantStaNetwork, ifaceName, mContext,
                             mWifiMonitor);
             if (network != null) {
                 network.enableVerboseLogging(mVerboseLoggingEnabled);
@@ -787,14 +937,14 @@
      * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
      * the call fails
      */
-    private SupplicantStaNetworkHal getNetwork(int id) {
+    private SupplicantStaNetworkHal getNetwork(@NonNull String ifaceName, int id) {
         synchronized (mLock) {
             final String methodStr = "getNetwork";
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return null;
             Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
             try {
-                mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
-                        ISupplicantNetwork network) -> {
+                iface.getNetwork(id, (SupplicantStatus status, ISupplicantNetwork network) -> {
                     if (checkStatusAndLogFailure(status, methodStr)) {
                         gotNetwork.value = network;
                     }
@@ -804,6 +954,7 @@
             }
             if (gotNetwork.value != null) {
                 return getStaNetworkMockable(
+                        ifaceName,
                         ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
             } else {
                 return null;
@@ -812,12 +963,13 @@
     }
 
     /** See ISupplicantStaNetwork.hal for documentation */
-    private boolean registerCallback(ISupplicantStaIfaceCallback callback) {
+    private boolean registerCallback(
+            ISupplicantStaIface iface, ISupplicantStaIfaceCallback callback) {
         synchronized (mLock) {
             final String methodStr = "registerCallback";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            if (iface == null) return false;
             try {
-                SupplicantStatus status =  mISupplicantStaIface.registerCallback(callback);
+                SupplicantStatus status =  iface.registerCallback(callback);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -830,14 +982,14 @@
      * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
      * null if the call fails
      */
-    private java.util.ArrayList<Integer> listNetworks() {
+    private java.util.ArrayList<Integer> listNetworks(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "listNetworks";
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return null;
             Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
             try {
-                mISupplicantStaIface.listNetworks((SupplicantStatus status,
-                        java.util.ArrayList<Integer> networkIds) -> {
+                iface.listNetworks((SupplicantStatus status, ArrayList<Integer> networkIds) -> {
                     if (checkStatusAndLogFailure(status, methodStr)) {
                         networkIdList.value = networkIds;
                     }
@@ -852,15 +1004,17 @@
     /**
      * Set WPS device name.
      *
+     * @param ifaceName Name of the interface.
      * @param name String to be set.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsDeviceName(String name) {
+    public boolean setWpsDeviceName(@NonNull String ifaceName, String name) {
         synchronized (mLock) {
             final String methodStr = "setWpsDeviceName";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceName(name);
+                SupplicantStatus status = iface.setWpsDeviceName(name);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -872,10 +1026,11 @@
     /**
      * Set WPS device type.
      *
+     * @param ifaceName Name of the interface.
      * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsDeviceType(String typeStr) {
+    public boolean setWpsDeviceType(@NonNull String ifaceName, String typeStr) {
         synchronized (mLock) {
             try {
                 Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
@@ -892,7 +1047,7 @@
                 byteBuffer.putShort(categ);
                 byteBuffer.put(oui);
                 byteBuffer.putShort(subCateg);
-                return setWpsDeviceType(bytes);
+                return setWpsDeviceType(ifaceName, bytes);
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + typeStr, e);
                 return false;
@@ -900,12 +1055,13 @@
         }
     }
 
-    private boolean setWpsDeviceType(byte[/* 8 */] type) {
+    private boolean setWpsDeviceType(@NonNull String ifaceName, byte[/* 8 */] type) {
         synchronized (mLock) {
             final String methodStr = "setWpsDeviceType";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsDeviceType(type);
+                SupplicantStatus status = iface.setWpsDeviceType(type);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -917,15 +1073,17 @@
     /**
      * Set WPS manufacturer.
      *
+     * @param ifaceName Name of the interface.
      * @param manufacturer String to be set.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsManufacturer(String manufacturer) {
+    public boolean setWpsManufacturer(@NonNull String ifaceName, String manufacturer) {
         synchronized (mLock) {
             final String methodStr = "setWpsManufacturer";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsManufacturer(manufacturer);
+                SupplicantStatus status = iface.setWpsManufacturer(manufacturer);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -937,15 +1095,17 @@
     /**
      * Set WPS model name.
      *
+     * @param ifaceName Name of the interface.
      * @param modelName String to be set.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsModelName(String modelName) {
+    public boolean setWpsModelName(@NonNull String ifaceName, String modelName) {
         synchronized (mLock) {
             final String methodStr = "setWpsModelName";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsModelName(modelName);
+                SupplicantStatus status = iface.setWpsModelName(modelName);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -957,15 +1117,17 @@
     /**
      * Set WPS model number.
      *
+     * @param ifaceName Name of the interface.
      * @param modelNumber String to be set.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsModelNumber(String modelNumber) {
+    public boolean setWpsModelNumber(@NonNull String ifaceName, String modelNumber) {
         synchronized (mLock) {
             final String methodStr = "setWpsModelNumber";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsModelNumber(modelNumber);
+                SupplicantStatus status = iface.setWpsModelNumber(modelNumber);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -977,15 +1139,17 @@
     /**
      * Set WPS serial number.
      *
+     * @param ifaceName Name of the interface.
      * @param serialNumber String to be set.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsSerialNumber(String serialNumber) {
+    public boolean setWpsSerialNumber(@NonNull String ifaceName, String serialNumber) {
         synchronized (mLock) {
             final String methodStr = "setWpsSerialNumber";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsSerialNumber(serialNumber);
+                SupplicantStatus status = iface.setWpsSerialNumber(serialNumber);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -997,26 +1161,28 @@
     /**
      * Set WPS config methods
      *
+     * @param ifaceName Name of the interface.
      * @param configMethodsStr List of config methods.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setWpsConfigMethods(String configMethodsStr) {
+    public boolean setWpsConfigMethods(@NonNull String ifaceName, String configMethodsStr) {
         synchronized (mLock) {
             short configMethodsMask = 0;
             String[] configMethodsStrArr = configMethodsStr.split("\\s+");
             for (int i = 0; i < configMethodsStrArr.length; i++) {
                 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
             }
-            return setWpsConfigMethods(configMethodsMask);
+            return setWpsConfigMethods(ifaceName, configMethodsMask);
         }
     }
 
-    private boolean setWpsConfigMethods(short configMethods) {
+    private boolean setWpsConfigMethods(@NonNull String ifaceName, short configMethods) {
         synchronized (mLock) {
             final String methodStr = "setWpsConfigMethods";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setWpsConfigMethods(configMethods);
+                SupplicantStatus status = iface.setWpsConfigMethods(configMethods);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1028,14 +1194,16 @@
     /**
      * Trigger a reassociation even if the iface is currently connected.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean reassociate() {
+    public boolean reassociate(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "reassociate";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.reassociate();
+                SupplicantStatus status = iface.reassociate();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1047,14 +1215,16 @@
     /**
      * Trigger a reconnection if the iface is disconnected.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean reconnect() {
+    public boolean reconnect(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "reconnect";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.reconnect();
+                SupplicantStatus status = iface.reconnect();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1066,14 +1236,16 @@
     /**
      * Trigger a disconnection from the currently connected network.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean disconnect() {
+    public boolean disconnect(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "disconnect";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.disconnect();
+                SupplicantStatus status = iface.disconnect();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1085,15 +1257,17 @@
     /**
      * Enable or disable power save mode.
      *
+     * @param ifaceName Name of the interface.
      * @param enable true to enable, false to disable.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setPowerSave(boolean enable) {
+    public boolean setPowerSave(@NonNull String ifaceName, boolean enable) {
         synchronized (mLock) {
             final String methodStr = "setPowerSave";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
+                SupplicantStatus status = iface.setPowerSave(enable);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1105,13 +1279,15 @@
     /**
      * Initiate TDLS discover with the specified AP.
      *
+     * @param ifaceName Name of the interface.
      * @param macAddress MAC Address of the AP.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean initiateTdlsDiscover(String macAddress) {
+    public boolean initiateTdlsDiscover(@NonNull String ifaceName, String macAddress) {
         synchronized (mLock) {
             try {
-                return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
+                return initiateTdlsDiscover(
+                        ifaceName, NativeUtil.macAddressToByteArray(macAddress));
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + macAddress, e);
                 return false;
@@ -1119,12 +1295,13 @@
         }
     }
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
+    private boolean initiateTdlsDiscover(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
         synchronized (mLock) {
             final String methodStr = "initiateTdlsDiscover";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
+                SupplicantStatus status = iface.initiateTdlsDiscover(macAddress);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1136,13 +1313,14 @@
     /**
      * Initiate TDLS setup with the specified AP.
      *
+     * @param ifaceName Name of the interface.
      * @param macAddress MAC Address of the AP.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean initiateTdlsSetup(String macAddress) {
+    public boolean initiateTdlsSetup(@NonNull String ifaceName, String macAddress) {
         synchronized (mLock) {
             try {
-                return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
+                return initiateTdlsSetup(ifaceName, NativeUtil.macAddressToByteArray(macAddress));
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + macAddress, e);
                 return false;
@@ -1150,12 +1328,13 @@
         }
     }
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
+    private boolean initiateTdlsSetup(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
         synchronized (mLock) {
             final String methodStr = "initiateTdlsSetup";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
+                SupplicantStatus status = iface.initiateTdlsSetup(macAddress);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1166,13 +1345,15 @@
 
     /**
      * Initiate TDLS teardown with the specified AP.
+     * @param ifaceName Name of the interface.
      * @param macAddress MAC Address of the AP.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean initiateTdlsTeardown(String macAddress) {
+    public boolean initiateTdlsTeardown(@NonNull String ifaceName, String macAddress) {
         synchronized (mLock) {
             try {
-                return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
+                return initiateTdlsTeardown(
+                        ifaceName, NativeUtil.macAddressToByteArray(macAddress));
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + macAddress, e);
                 return false;
@@ -1181,12 +1362,13 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
+    private boolean initiateTdlsTeardown(@NonNull String ifaceName, byte[/* 6 */] macAddress) {
         synchronized (mLock) {
             final String methodStr = "initiateTdlsTeardown";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
+                SupplicantStatus status = iface.initiateTdlsTeardown(macAddress);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1198,16 +1380,19 @@
     /**
      * Request the specified ANQP elements |elements| from the specified AP |bssid|.
      *
+     * @param ifaceName Name of the interface.
      * @param bssid BSSID of the AP
      * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
      * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
+    public boolean initiateAnqpQuery(@NonNull String ifaceName, String bssid,
+                                     ArrayList<Short> infoElements,
                                      ArrayList<Integer> hs20SubTypes) {
         synchronized (mLock) {
             try {
                 return initiateAnqpQuery(
+                        ifaceName,
                         NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + bssid, e);
@@ -1217,14 +1402,15 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
+    private boolean initiateAnqpQuery(@NonNull String ifaceName, byte[/* 6 */] macAddress,
             java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
         synchronized (mLock) {
             final String methodStr = "initiateAnqpQuery";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
-                        infoElements, subTypes);
+                SupplicantStatus status = iface.initiateAnqpQuery(
+                        macAddress, infoElements, subTypes);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1236,14 +1422,16 @@
     /**
      * Request the specified ANQP ICON from the specified AP |bssid|.
      *
+     * @param ifaceName Name of the interface.
      * @param bssid BSSID of the AP
      * @param fileName Name of the file to request.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean initiateHs20IconQuery(String bssid, String fileName) {
+    public boolean initiateHs20IconQuery(@NonNull String ifaceName, String bssid, String fileName) {
         synchronized (mLock) {
             try {
-                return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
+                return initiateHs20IconQuery(
+                        ifaceName, NativeUtil.macAddressToByteArray(bssid), fileName);
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + bssid, e);
                 return false;
@@ -1252,13 +1440,14 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
+    private boolean initiateHs20IconQuery(@NonNull String ifaceName,
+                                          byte[/* 6 */] macAddress, String fileName) {
         synchronized (mLock) {
             final String methodStr = "initiateHs20IconQuery";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
-                        fileName);
+                SupplicantStatus status = iface.initiateHs20IconQuery(macAddress, fileName);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1270,15 +1459,17 @@
     /**
      * Makes a callback to HIDL to getMacAddress from supplicant
      *
+     * @param ifaceName Name of the interface.
      * @return string containing the MAC address, or null on a failed call
      */
-    public String getMacAddress() {
+    public String getMacAddress(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "getMacAddress";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return null;
             Mutable<String> gotMac = new Mutable<>();
             try {
-                mISupplicantStaIface.getMacAddress((SupplicantStatus status,
+                iface.getMacAddress((SupplicantStatus status,
                         byte[/* 6 */] macAddr) -> {
                     if (checkStatusAndLogFailure(status, methodStr)) {
                         gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
@@ -1294,14 +1485,16 @@
     /**
      * Start using the added RX filters.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean startRxFilter() {
+    public boolean startRxFilter(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "startRxFilter";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.startRxFilter();
+                SupplicantStatus status = iface.startRxFilter();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1313,14 +1506,16 @@
     /**
      * Stop using the added RX filters.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean stopRxFilter() {
+    public boolean stopRxFilter(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "stopRxFilter";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
+                SupplicantStatus status = iface.stopRxFilter();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1332,11 +1527,12 @@
     /**
      * Add an RX filter.
      *
+     * @param ifaceName Name of the interface.
      * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
      *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean addRxFilter(int type) {
+    public boolean addRxFilter(@NonNull String ifaceName, int type) {
         synchronized (mLock) {
             byte halType;
             switch (type) {
@@ -1350,16 +1546,17 @@
                     Log.e(TAG, "Invalid Rx Filter type: " + type);
                     return false;
             }
-            return addRxFilter(halType);
+            return addRxFilter(ifaceName, halType);
         }
     }
 
-    public boolean addRxFilter(byte type) {
+    private boolean addRxFilter(@NonNull String ifaceName, byte type) {
         synchronized (mLock) {
             final String methodStr = "addRxFilter";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
+                SupplicantStatus status = iface.addRxFilter(type);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1371,11 +1568,12 @@
     /**
      * Remove an RX filter.
      *
+     * @param ifaceName Name of the interface.
      * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
      *        {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean removeRxFilter(int type) {
+    public boolean removeRxFilter(@NonNull String ifaceName, int type) {
         synchronized (mLock) {
             byte halType;
             switch (type) {
@@ -1389,16 +1587,17 @@
                     Log.e(TAG, "Invalid Rx Filter type: " + type);
                     return false;
             }
-            return removeRxFilter(halType);
+            return removeRxFilter(ifaceName, halType);
         }
     }
 
-    public boolean removeRxFilter(byte type) {
+    private boolean removeRxFilter(@NonNull String ifaceName, byte type) {
         synchronized (mLock) {
             final String methodStr = "removeRxFilter";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
+                SupplicantStatus status = iface.removeRxFilter(type);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1410,12 +1609,13 @@
     /**
      * Set Bt co existense mode.
      *
+     * @param ifaceName Name of the interface.
      * @param mode one of the above {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_DISABLED},
      *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_ENABLED} or
      *             {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_SENSE}.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setBtCoexistenceMode(int mode) {
+    public boolean setBtCoexistenceMode(@NonNull String ifaceName, int mode) {
         synchronized (mLock) {
             byte halMode;
             switch (mode) {
@@ -1432,16 +1632,17 @@
                     Log.e(TAG, "Invalid Bt Coex mode: " + mode);
                     return false;
             }
-            return setBtCoexistenceMode(halMode);
+            return setBtCoexistenceMode(ifaceName, halMode);
         }
     }
 
-    private boolean setBtCoexistenceMode(byte mode) {
+    private boolean setBtCoexistenceMode(@NonNull String ifaceName, byte mode) {
         synchronized (mLock) {
             final String methodStr = "setBtCoexistenceMode";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
+                SupplicantStatus status = iface.setBtCoexistenceMode(mode);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1452,16 +1653,18 @@
 
     /** Enable or disable BT coexistence mode.
      *
+     * @param ifaceName Name of the interface.
      * @param enable true to enable, false to disable.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
+    public boolean setBtCoexistenceScanModeEnabled(@NonNull String ifaceName, boolean enable) {
         synchronized (mLock) {
             final String methodStr = "setBtCoexistenceScanModeEnabled";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
                 SupplicantStatus status =
-                        mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
+                        iface.setBtCoexistenceScanModeEnabled(enable);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1473,15 +1676,17 @@
     /**
      * Enable or disable suspend mode optimizations.
      *
+     * @param ifaceName Name of the interface.
      * @param enable true to enable, false otherwise.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setSuspendModeEnabled(boolean enable) {
+    public boolean setSuspendModeEnabled(@NonNull String ifaceName, boolean enable) {
         synchronized (mLock) {
             final String methodStr = "setSuspendModeEnabled";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
+                SupplicantStatus status = iface.setSuspendModeEnabled(enable);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1493,23 +1698,25 @@
     /**
      * Set country code.
      *
+     * @param ifaceName Name of the interface.
      * @param codeStr 2 byte ASCII string. For ex: US, CA.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setCountryCode(String codeStr) {
+    public boolean setCountryCode(@NonNull String ifaceName, String codeStr) {
         synchronized (mLock) {
             if (TextUtils.isEmpty(codeStr)) return false;
-            return setCountryCode(NativeUtil.stringToByteArray(codeStr));
+            return setCountryCode(ifaceName, NativeUtil.stringToByteArray(codeStr));
         }
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean setCountryCode(byte[/* 2 */] code) {
+    private boolean setCountryCode(@NonNull String ifaceName, byte[/* 2 */] code) {
         synchronized (mLock) {
             final String methodStr = "setCountryCode";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
+                SupplicantStatus status = iface.setCountryCode(code);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1521,15 +1728,17 @@
     /**
      * Start WPS pin registrar operation with the specified peer and pin.
      *
+     * @param ifaceName Name of the interface.
      * @param bssidStr BSSID of the peer.
      * @param pin Pin to be used.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean startWpsRegistrar(String bssidStr, String pin) {
+    public boolean startWpsRegistrar(@NonNull String ifaceName, String bssidStr, String pin) {
         synchronized (mLock) {
             if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
             try {
-                return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
+                return startWpsRegistrar(
+                        ifaceName, NativeUtil.macAddressToByteArray(bssidStr), pin);
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + bssidStr, e);
                 return false;
@@ -1538,12 +1747,13 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean startWpsRegistrar(byte[/* 6 */] bssid, String pin) {
+    private boolean startWpsRegistrar(@NonNull String ifaceName, byte[/* 6 */] bssid, String pin) {
         synchronized (mLock) {
             final String methodStr = "startWpsRegistrar";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.startWpsRegistrar(bssid, pin);
+                SupplicantStatus status = iface.startWpsRegistrar(bssid, pin);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1555,13 +1765,14 @@
     /**
      * Start WPS pin display operation with the specified peer.
      *
+     * @param ifaceName Name of the interface.
      * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean startWpsPbc(String bssidStr) {
+    public boolean startWpsPbc(@NonNull String ifaceName, String bssidStr) {
         synchronized (mLock) {
             try {
-                return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
+                return startWpsPbc(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + bssidStr, e);
                 return false;
@@ -1570,12 +1781,13 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private boolean startWpsPbc(byte[/* 6 */] bssid) {
+    private boolean startWpsPbc(@NonNull String ifaceName, byte[/* 6 */] bssid) {
         synchronized (mLock) {
             final String methodStr = "startWpsPbc";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.startWpsPbc(bssid);
+                SupplicantStatus status = iface.startWpsPbc(bssid);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1587,16 +1799,18 @@
     /**
      * Start WPS pin keypad operation with the specified pin.
      *
+     * @param ifaceName Name of the interface.
      * @param pin Pin to be used.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean startWpsPinKeypad(String pin) {
+    public boolean startWpsPinKeypad(@NonNull String ifaceName, String pin) {
         if (TextUtils.isEmpty(pin)) return false;
         synchronized (mLock) {
             final String methodStr = "startWpsPinKeypad";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.startWpsPinKeypad(pin);
+                SupplicantStatus status = iface.startWpsPinKeypad(pin);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1608,13 +1822,14 @@
     /**
      * Start WPS pin display operation with the specified peer.
      *
+     * @param ifaceName Name of the interface.
      * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
      * @return new pin generated on success, null otherwise.
      */
-    public String startWpsPinDisplay(String bssidStr) {
+    public String startWpsPinDisplay(@NonNull String ifaceName, String bssidStr) {
         synchronized (mLock) {
             try {
-                return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
+                return startWpsPinDisplay(ifaceName, NativeUtil.macAddressToByteArray(bssidStr));
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "Illegal argument " + bssidStr, e);
                 return null;
@@ -1623,13 +1838,14 @@
     }
 
     /** See ISupplicantStaIface.hal for documentation */
-    private String startWpsPinDisplay(byte[/* 6 */] bssid) {
+    private String startWpsPinDisplay(@NonNull String ifaceName, byte[/* 6 */] bssid) {
         synchronized (mLock) {
             final String methodStr = "startWpsPinDisplay";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return null;
             final Mutable<String> gotPin = new Mutable<>();
             try {
-                mISupplicantStaIface.startWpsPinDisplay(bssid,
+                iface.startWpsPinDisplay(bssid,
                         (SupplicantStatus status, String pin) -> {
                             if (checkStatusAndLogFailure(status, methodStr)) {
                                 gotPin.value = pin;
@@ -1645,14 +1861,16 @@
     /**
      * Cancels any ongoing WPS requests.
      *
+     * @param ifaceName Name of the interface.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean cancelWps() {
+    public boolean cancelWps(@NonNull String ifaceName) {
         synchronized (mLock) {
             final String methodStr = "cancelWps";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.cancelWps();
+                SupplicantStatus status = iface.cancelWps();
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1664,15 +1882,17 @@
     /**
      * Sets whether to use external sim for SIM/USIM processing.
      *
+     * @param ifaceName Name of the interface.
      * @param useExternalSim true to enable, false otherwise.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setExternalSim(boolean useExternalSim) {
+    public boolean setExternalSim(@NonNull String ifaceName, boolean useExternalSim) {
         synchronized (mLock) {
             final String methodStr = "setExternalSim";
-            if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.setExternalSim(useExternalSim);
+                SupplicantStatus status = iface.setExternalSim(useExternalSim);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1682,12 +1902,13 @@
     }
 
     /** See ISupplicant.hal for documentation */
-    public boolean enableAutoReconnect(boolean enable) {
+    public boolean enableAutoReconnect(@NonNull String ifaceName, boolean enable) {
         synchronized (mLock) {
             final String methodStr = "enableAutoReconnect";
-            if (!checkSupplicantAndLogFailure(methodStr)) return false;
+            ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
+            if (iface == null) return false;
             try {
-                SupplicantStatus status = mISupplicantStaIface.enableAutoReconnect(enable);
+                SupplicantStatus status = iface.enableAutoReconnect(enable);
                 return checkStatusAndLogFailure(status, methodStr);
             } catch (RemoteException e) {
                 handleRemoteException(e, methodStr);
@@ -1775,13 +1996,30 @@
     /**
      * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
      */
-    private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
+    private ISupplicantStaIface checkSupplicantStaIfaceAndLogFailure(
+            @NonNull String ifaceName, final String methodStr) {
         synchronized (mLock) {
-            if (mISupplicantStaIface == null) {
+            ISupplicantStaIface iface = getStaIface(ifaceName);
+            if (iface == null) {
                 Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
-                return false;
+                return null;
             }
-            return true;
+            return iface;
+        }
+    }
+
+    /**
+     * Returns false if SupplicantStaNetwork is null, and logs failure to call methodStr
+     */
+    private SupplicantStaNetworkHal checkSupplicantStaNetworkAndLogFailure(
+            @NonNull String ifaceName, final String methodStr) {
+        synchronized (mLock) {
+            SupplicantStaNetworkHal networkHal = getCurrentNetworkRemoteHandle(ifaceName);
+            if (networkHal == null) {
+                Log.e(TAG, "Can't call " + methodStr + ", SupplicantStaNetwork is null");
+                return null;
+            }
+            return networkHal;
         }
     }
 
@@ -1938,8 +2176,13 @@
     }
 
     private class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub {
+        private String mIfaceName;
         private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch
 
+        SupplicantStaIfaceHalCallback(@NonNull String ifaceName) {
+            mIfaceName = ifaceName;
+        }
+
         /**
          * Parses the provided payload into an ANQP element.
          *
@@ -2008,10 +2251,11 @@
                 mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
                 if (newSupplicantState == SupplicantState.COMPLETED) {
                     mWifiMonitor.broadcastNetworkConnectionEvent(
-                            mIfaceName, getCurrentNetworkId(), bssidStr);
+                            mIfaceName, getCurrentNetworkId(mIfaceName), bssidStr);
                 }
                 mWifiMonitor.broadcastSupplicantStateChangeEvent(
-                        mIfaceName, getCurrentNetworkId(), wifiSsid, bssidStr, newSupplicantState);
+                        mIfaceName, getCurrentNetworkId(mIfaceName), wifiSsid,
+                        bssidStr, newSupplicantState);
             }
         }
 
diff --git a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
index 09f457e..bfc51f6 100644
--- a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
+++ b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
@@ -145,6 +145,8 @@
         return mThresholdMinimumRssi + mThresholdAdjustment;
     }
 
+    private double mMinimumPpsForMeasuringSuccess = 2.0;
+
     /**
      * Adjusts the threshold if appropriate
      * <p>
@@ -158,11 +160,12 @@
         if (mThresholdAdjustment < -7) return;
         if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return;
         if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return;
-        if (wifiInfo.txSuccessRate < 10) return;
-        if (wifiInfo.rxSuccessRate < 10) return;
-        double probabilityOfSuccessfulTx = (
-                wifiInfo.txSuccessRate / (wifiInfo.txSuccessRate + wifiInfo.txBadRate)
-        );
+        double txSuccessPps = wifiInfo.txSuccessRate;
+        double rxSuccessPps = wifiInfo.rxSuccessRate;
+        if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return;
+        if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return;
+        double txBadPps = wifiInfo.txBadRate;
+        double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps);
         if (probabilityOfSuccessfulTx >= 0.2) {
             // May want this amount to vary with how close to threshold we are
             mThresholdAdjustment -= 0.5;
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
new file mode 100644
index 0000000..5775117
--- /dev/null
+++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.util.ArraySet;
+
+import com.android.server.wifi.WifiConfigStore.StoreData;
+import com.android.server.wifi.util.XmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Config store data for Wifi Wake.
+ */
+public class WakeupConfigStoreData implements StoreData {
+    private static final String TAG = "WakeupConfigStoreData";
+
+    private static final String XML_TAG_IS_ACTIVE = "IsActive";
+    private static final String XML_TAG_NETWORK_SECTION = "Network";
+    private static final String XML_TAG_SSID = "SSID";
+    private static final String XML_TAG_SECURITY = "Security";
+
+    private final DataSource<Boolean> mIsActiveDataSource;
+    private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    /**
+     * Interface defining a data source for the store data.
+     *
+     * @param <T> Type of data source
+     */
+    public interface DataSource<T> {
+        /**
+         * Returns the data from the data source.
+         */
+        T getData();
+
+        /**
+         * Updates the data in the data source.
+         *
+         * @param data Data retrieved from the store
+         */
+        void setData(T data);
+    }
+
+    /**
+     * Creates the config store data with its data sources.
+     *
+     * @param isActiveDataSource Data source for isActive
+     * @param networkDataSource Data source for the locked network list
+     */
+    public WakeupConfigStoreData(
+            DataSource<Boolean> isActiveDataSource,
+            DataSource<Set<ScanResultMatchInfo>> networkDataSource) {
+        mIsActiveDataSource = isActiveDataSource;
+        mNetworkDataSource = networkDataSource;
+    }
+
+    @Override
+    public void serializeData(XmlSerializer out, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Share data not supported");
+        }
+
+        XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData());
+
+        for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) {
+            writeNetwork(out, scanResultMatchInfo);
+        }
+    }
+
+    /**
+     * Writes a {@link ScanResultMatchInfo} to an XML output stream.
+     *
+     * @param out XML output stream
+     * @param scanResultMatchInfo The ScanResultMatchInfo to serizialize
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private void writeNetwork(XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo)
+            throws XmlPullParserException, IOException {
+        XmlUtil.writeNextSectionStart(out, XML_TAG_NETWORK_SECTION);
+
+        XmlUtil.writeNextValue(out, XML_TAG_SSID, scanResultMatchInfo.networkSsid);
+        XmlUtil.writeNextValue(out, XML_TAG_SECURITY, scanResultMatchInfo.networkType);
+
+        XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION);
+    }
+
+    @Override
+    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Shared data not supported");
+        }
+
+        boolean isActive = (Boolean) XmlUtil.readNextValueWithName(in, XML_TAG_IS_ACTIVE);
+        mIsActiveDataSource.setData(isActive);
+
+        Set<ScanResultMatchInfo> networks = new ArraySet<>();
+        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_NETWORK_SECTION, outerTagDepth)) {
+            networks.add(parseNetwork(in, outerTagDepth + 1));
+        }
+
+        mNetworkDataSource.setData(networks);
+    }
+
+    /**
+     * Parses a {@link ScanResultMatchInfo} from an XML input stream.
+     *
+     * @param in XML input stream
+     * @param outerTagDepth XML tag depth of the containing section
+     * @return The {@link ScanResultMatchInfo}
+     * @throws IOException
+     * @throws XmlPullParserException
+     */
+    private ScanResultMatchInfo parseNetwork(XmlPullParser in, int outerTagDepth)
+            throws IOException, XmlPullParserException {
+        ScanResultMatchInfo scanResultMatchInfo = new ScanResultMatchInfo();
+        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
+            String[] valueName = new String[1];
+            Object value = XmlUtil.readCurrentValue(in, valueName);
+            if (valueName[0] == null) {
+                throw new XmlPullParserException("Missing value name");
+            }
+            switch (valueName[0]) {
+                case XML_TAG_SSID:
+                    scanResultMatchInfo.networkSsid = (String) value;
+                    break;
+                case XML_TAG_SECURITY:
+                    scanResultMatchInfo.networkType = (int) value;
+                    break;
+                default:
+                    throw new XmlPullParserException("Unknown tag under " + TAG + ": "
+                            + valueName[0]);
+            }
+        }
+
+        return scanResultMatchInfo;
+    }
+
+    @Override
+    public void resetData(boolean shared) {
+        if (!shared) {
+            mNetworkDataSource.setData(Collections.emptySet());
+            mIsActiveDataSource.setData(false);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return TAG;
+    }
+
+    @Override
+    public boolean supportShareData() {
+        return false;
+    }
+}
diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java
index a3c095a..8787bfd 100644
--- a/service/java/com/android/server/wifi/WakeupController.java
+++ b/service/java/com/android/server/wifi/WakeupController.java
@@ -24,6 +24,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * WakeupController is responsible managing Auto Wifi.
  *
@@ -38,16 +41,26 @@
     private final Handler mHandler;
     private final FrameworkFacade mFrameworkFacade;
     private final ContentObserver mContentObserver;
+    private final WakeupLock mWakeupLock;
+    private final WifiConfigManager mWifiConfigManager;
 
     /** Whether this feature is enabled in Settings. */
     private boolean mWifiWakeupEnabled;
 
+    /** Whether the WakeupController is currently active. */
+    private boolean mIsActive = false;
+
     public WakeupController(
             Context context,
             Looper looper,
+            WakeupLock wakeupLock,
+            WifiConfigManager wifiConfigManager,
+            WifiConfigStore wifiConfigStore,
             FrameworkFacade frameworkFacade) {
         mContext = context;
         mHandler = new Handler(looper);
+        mWakeupLock = wakeupLock;
+        mWifiConfigManager = wifiConfigManager;
         mFrameworkFacade = frameworkFacade;
         mContentObserver = new ContentObserver(mHandler) {
             @Override
@@ -59,6 +72,19 @@
         mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
                 Settings.Global.WIFI_WAKEUP_ENABLED), true, mContentObserver);
         mContentObserver.onChange(false /* selfChange */);
+
+        // registering the store data here has the effect of reading the persisted value of the
+        // data sources after system boot finishes
+        WakeupConfigStoreData wakeupConfigStoreData =
+                new WakeupConfigStoreData(new IsActiveDataSource(), mWakeupLock.getDataSource());
+        wifiConfigStore.registerStoreData(wakeupConfigStoreData);
+    }
+
+    private void setActive(boolean isActive) {
+        if (mIsActive != isActive) {
+            mIsActive = isActive;
+            mWifiConfigManager.saveToStore(false /* forceWrite */);
+        }
     }
 
     /**
@@ -71,4 +97,26 @@
     boolean isEnabled() {
         return mWifiWakeupEnabled;
     }
+
+    /** Dumps wakeup controller state. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Dump of WakeupController");
+        pw.println("mWifiWakeupEnabled: " + mWifiWakeupEnabled);
+        pw.println("USE_PLATFORM_WIFI_WAKE: " + USE_PLATFORM_WIFI_WAKE);
+        pw.println("mIsActive: " + mIsActive);
+        mWakeupLock.dump(fd, pw, args);
+    }
+
+    private class IsActiveDataSource implements WakeupConfigStoreData.DataSource<Boolean> {
+
+        @Override
+        public Boolean getData() {
+            return mIsActive;
+        }
+
+        @Override
+        public void setData(Boolean data) {
+            mIsActive = data;
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WakeupLock.java b/service/java/com/android/server/wifi/WakeupLock.java
new file mode 100644
index 0000000..1fcd9f8
--- /dev/null
+++ b/service/java/com/android/server/wifi/WakeupLock.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A lock to determine whether Auto Wifi can re-enable Wifi.
+ *
+ * <p>Wakeuplock manages a list of networks to determine whether the device's location has changed.
+ */
+public class WakeupLock {
+
+    private static final String TAG = WakeupLock.class.getSimpleName();
+
+    @VisibleForTesting
+    static final int CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 3;
+
+
+    private final WifiConfigManager mWifiConfigManager;
+    private final Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>();
+
+    public WakeupLock(WifiConfigManager wifiConfigManager) {
+        mWifiConfigManager = wifiConfigManager;
+    }
+
+    /**
+     * Initializes the WakeupLock with the given {@link ScanResultMatchInfo} list.
+     *
+     * <p>This saves the wakeup lock to the store.
+     *
+     * @param scanResultList list of ScanResultMatchInfos to start the lock with
+     */
+    public void initialize(Collection<ScanResultMatchInfo> scanResultList) {
+        mLockedNetworks.clear();
+        for (ScanResultMatchInfo scanResultMatchInfo : scanResultList) {
+            mLockedNetworks.put(scanResultMatchInfo, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
+        }
+
+        Log.d(TAG, "Lock initialized. Number of networks: " + mLockedNetworks.size());
+
+        mWifiConfigManager.saveToStore(false /* forceWrite */);
+    }
+
+    /**
+     * Updates the lock with the given {@link ScanResultMatchInfo} list.
+     *
+     * <p>If a network in the lock is not present in the list, reduce the number of scans
+     * required to evict by one. Remove any entries in the list with 0 scans required to evict. If
+     * any entries in the lock are removed, the store is updated.
+     *
+     * @param scanResultList list of present ScanResultMatchInfos to update the lock with
+     */
+    public void update(Collection<ScanResultMatchInfo> scanResultList) {
+        boolean hasChanged = false;
+        Iterator<Map.Entry<ScanResultMatchInfo, Integer>> it =
+                mLockedNetworks.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<ScanResultMatchInfo, Integer> entry = it.next();
+
+            // if present in scan list, reset to max
+            if (scanResultList.contains(entry.getKey())) {
+                entry.setValue(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
+                continue;
+            }
+
+            // decrement and remove if necessary
+            entry.setValue(entry.getValue() - 1);
+            if (entry.getValue() <= 0) {
+                it.remove();
+                hasChanged = true;
+            }
+        }
+
+        if (hasChanged) {
+            mWifiConfigManager.saveToStore(false /* forceWrite */);
+        }
+    }
+
+    /**
+     * Returns whether the internal network set is empty.
+     */
+    public boolean isEmpty() {
+        return mLockedNetworks.isEmpty();
+    }
+
+    /** Returns the data source for the WakeupLock config store data. */
+    public WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> getDataSource() {
+        return new WakeupLockDataSource();
+    }
+
+    /** Dumps wakeup lock contents. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("WakeupLock: ");
+        pw.println("Locked networks: " + mLockedNetworks.size());
+        for (Map.Entry<ScanResultMatchInfo, Integer> entry : mLockedNetworks.entrySet()) {
+            pw.println(entry.getKey() + ", scans to evict: " + entry.getValue());
+        }
+    }
+
+    private class WakeupLockDataSource
+            implements WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> {
+
+        @Override
+        public Set<ScanResultMatchInfo> getData() {
+            return mLockedNetworks.keySet();
+        }
+
+        @Override
+        public void setData(Set<ScanResultMatchInfo> data) {
+            mLockedNetworks.clear();
+            for (ScanResultMatchInfo network : data) {
+                mLockedNetworks.put(network, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
+            }
+
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/WifiCertManager.java b/service/java/com/android/server/wifi/WifiCertManager.java
deleted file mode 100644
index e180f51..0000000
--- a/service/java/com/android/server/wifi/WifiCertManager.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.app.admin.IDevicePolicyManager;
-import android.content.Context;
-import android.os.Environment;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.server.net.DelayedDiskWrite;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Manager class for affiliated Wifi certificates.
- */
-public class WifiCertManager {
-    private static final String TAG = "WifiCertManager";
-    private static final String SEP = "\n";
-
-    private final Context mContext;
-    private final Set<String> mAffiliatedUserOnlyCerts = new HashSet<String>();
-    private final String mConfigFile;
-
-    private static final String CONFIG_FILE =
-            Environment.getDataDirectory() + "/misc/wifi/affiliatedcerts.txt";
-
-    private final DelayedDiskWrite mWriter = new DelayedDiskWrite();
-
-
-    WifiCertManager(Context context) {
-        this(context, CONFIG_FILE);
-    }
-
-    WifiCertManager(Context context, String configFile) {
-        mContext = context;
-        mConfigFile = configFile;
-        final byte[] bytes = readConfigFile();
-        if (bytes == null) {
-            // Config file does not exist or empty.
-            return;
-        }
-
-        String[] keys = new String(bytes, StandardCharsets.UTF_8).split(SEP);
-        for (String key : keys) {
-            mAffiliatedUserOnlyCerts.add(key);
-        }
-
-        // Remove keys that no longer exist in KeyStore.
-        if (mAffiliatedUserOnlyCerts.retainAll(Arrays.asList(listClientCertsForAllUsers()))) {
-            writeConfig();
-        }
-    }
-
-    /** @param  key Unprefixed cert key to hide from unaffiliated users. */
-    public void hideCertFromUnaffiliatedUsers(String key) {
-        if (mAffiliatedUserOnlyCerts.add(Credentials.USER_PRIVATE_KEY + key)) {
-            writeConfig();
-        }
-    }
-
-    /** @return Prefixed cert keys that are visible to the current user. */
-    public String[] listClientCertsForCurrentUser() {
-        HashSet<String> results = new HashSet<String>();
-
-        String[] keys = listClientCertsForAllUsers();
-        if (isAffiliatedUser()) {
-            return keys;
-        }
-
-        for (String key : keys) {
-            if (!mAffiliatedUserOnlyCerts.contains(key)) {
-                results.add(key);
-            }
-        }
-        return results.toArray(new String[results.size()]);
-    }
-
-    private void writeConfig() {
-        String[] values =
-                mAffiliatedUserOnlyCerts.toArray(new String[mAffiliatedUserOnlyCerts.size()]);
-        String value = TextUtils.join(SEP, values);
-        writeConfigFile(value.getBytes(StandardCharsets.UTF_8));
-    }
-
-    protected byte[] readConfigFile() {
-        byte[] bytes = null;
-        try {
-            final File file = new File(mConfigFile);
-            final long fileSize = file.exists() ? file.length() : 0;
-            if (fileSize == 0 || fileSize >= Integer.MAX_VALUE) {
-                // Config file is empty/corrupted/non-existing.
-                return bytes;
-            }
-
-            bytes = new byte[(int) file.length()];
-            final DataInputStream stream = new DataInputStream(new FileInputStream(file));
-            stream.readFully(bytes);
-        } catch (IOException e) {
-            Log.e(TAG, "readConfigFile: failed to read " + e, e);
-        }
-        return bytes;
-    }
-
-    protected void writeConfigFile(byte[] payload) {
-        final byte[] data = payload;
-        mWriter.write(mConfigFile, new DelayedDiskWrite.Writer() {
-            public void onWriteCalled(DataOutputStream out) throws IOException {
-                out.write(data, 0, data.length);
-            }
-        });
-    }
-
-    protected String[] listClientCertsForAllUsers() {
-        return KeyStore.getInstance().list(Credentials.USER_PRIVATE_KEY, UserHandle.myUserId());
-    }
-
-    protected boolean isAffiliatedUser() {
-        IDevicePolicyManager pm = IDevicePolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
-        boolean result = false;
-        try {
-            result = pm.isAffiliatedUser();
-        } catch (Exception e) {
-            Log.e(TAG, "failed to check user affiliation", e);
-        }
-        return result;
-    }
-}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index f14a57f..dc48163 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -85,7 +85,6 @@
     private final WifiVendorHal mWifiVendorHal;
     private final WifiStateMachine mWifiStateMachine;
     private final WifiSettingsStore mSettingsStore;
-    private final WifiCertManager mCertManager;
     private final OpenNetworkNotifier mOpenNetworkNotifier;
     private final WifiLockManager mLockManager;
     private final WifiController mWifiController;
@@ -225,14 +224,14 @@
                 wifiStateMachineLooper, UserManager.get(mContext),
                 this, mBackupManagerProxy, mCountryCode, mWifiNative,
                 new WrongPasswordNotifier(mContext, mFrameworkFacade));
-        mCertManager = new WifiCertManager(mContext);
         mOpenNetworkNotifier = new OpenNetworkNotifier(mContext,
                 mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics,
                 mWifiConfigManager, mWifiConfigStore, mWifiStateMachine,
                 new OpenNetworkRecommender(),
                 new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
         mWakeupController = new WakeupController(mContext,
-                mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade);
+                mWifiStateMachineHandlerThread.getLooper(), new WakeupLock(mWifiConfigManager),
+                mWifiConfigManager, mWifiConfigStore, mFrameworkFacade);
         mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
         mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
                 mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
@@ -304,10 +303,6 @@
         return mSettingsStore;
     }
 
-    public WifiCertManager getWifiCertManager() {
-        return mCertManager;
-    }
-
     public WifiLockManager getWifiLockManager() {
         return mLockManager;
     }
@@ -386,10 +381,9 @@
                                            @NonNull IApInterface apInterface,
                                            @NonNull String ifaceName,
                                            @NonNull SoftApModeConfiguration config) {
-        return new SoftApManager(mContext, mWifiServiceHandlerThread.getLooper(),
-                                 mWifiNative, mCountryCode.getCountryCode(),
-                                 listener, apInterface, ifaceName, nmService,
-                                 mWifiApConfigStore, config, mWifiMetrics);
+        return new SoftApManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
+                mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), listener, apInterface,
+                ifaceName, nmService, mWifiApConfigStore, config, mWifiMetrics);
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiLinkLayerStats.java b/service/java/com/android/server/wifi/WifiLinkLayerStats.java
new file mode 100644
index 0000000..0c58670
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiLinkLayerStats.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import java.util.Arrays;
+
+/**
+ * A class representing link layer statistics collected over a Wifi Interface.
+ */
+
+/**
+ * {@hide}
+ */
+public class WifiLinkLayerStats {
+
+    /** Number of beacons received from our own AP */
+    public int beacon_rx;
+
+    /** RSSI of management frames */
+    public int rssi_mgmt;
+
+    /* Packet counters */
+
+    /** WME Best Effort Access Category received mpdu */
+    public long rxmpdu_be;
+    /** WME Best Effort Access Category transmitted mpdu */
+    public long txmpdu_be;
+    /** WME Best Effort Access Category lost mpdu */
+    public long lostmpdu_be;
+    /** WME Best Effort Access Category number of transmission retries */
+    public long retries_be;
+
+    /** WME Background Access Category received mpdu */
+    public long rxmpdu_bk;
+    /** WME Background Access Category transmitted mpdu */
+    public long txmpdu_bk;
+    /** WME Background Access Category lost mpdu */
+    public long lostmpdu_bk;
+    /** WME Background Access Category number of transmission retries */
+    public long retries_bk;
+
+    /** WME Video Access Category received mpdu */
+    public long rxmpdu_vi;
+    /** WME Video Access Category transmitted mpdu */
+    public long txmpdu_vi;
+    /** WME Video Access Category lost mpdu */
+    public long lostmpdu_vi;
+    /** WME Video Access Category number of transmission retries */
+    public long retries_vi;
+
+    /** WME Voice Access Category received mpdu */
+    public long rxmpdu_vo;
+    /** WME Voice Access Category transmitted mpdu */
+    public long txmpdu_vo;
+    /** WME Voice Access Category lost mpdu */
+    public long lostmpdu_vo;
+    /** WME Voice Access Category number of transmission retries */
+    public long retries_vo;
+
+    /**
+     * Cumulative milliseconds when radio is awake
+     */
+    public int on_time;
+    /**
+     * Cumulative milliseconds of active transmission
+     */
+    public int tx_time;
+    /**
+     * Cumulative milliseconds per level of active transmission
+     */
+    public int[] tx_time_per_level;
+    /**
+     * Cumulative milliseconds of active receive
+     */
+    public int rx_time;
+    /**
+     * Cumulative milliseconds when radio is awake due to scan
+     */
+    public int on_time_scan;
+
+    /**
+     * TimeStamp - absolute milliseconds from boot when these stats were sampled.
+     */
+    public long timeStampInMs;
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append(" WifiLinkLayerStats: ").append('\n');
+
+        sbuf.append(" my bss beacon rx: ").append(Integer.toString(this.beacon_rx)).append('\n');
+        sbuf.append(" RSSI mgmt: ").append(Integer.toString(this.rssi_mgmt)).append('\n');
+        sbuf.append(" BE : ").append(" rx=").append(Long.toString(this.rxmpdu_be))
+                .append(" tx=").append(Long.toString(this.txmpdu_be))
+                .append(" lost=").append(Long.toString(this.lostmpdu_be))
+                .append(" retries=").append(Long.toString(this.retries_be)).append('\n');
+        sbuf.append(" BK : ").append(" rx=").append(Long.toString(this.rxmpdu_bk))
+                .append(" tx=").append(Long.toString(this.txmpdu_bk))
+                .append(" lost=").append(Long.toString(this.lostmpdu_bk))
+                .append(" retries=").append(Long.toString(this.retries_bk)).append('\n');
+        sbuf.append(" VI : ").append(" rx=").append(Long.toString(this.rxmpdu_vi))
+                .append(" tx=").append(Long.toString(this.txmpdu_vi))
+                .append(" lost=").append(Long.toString(this.lostmpdu_vi))
+                .append(" retries=").append(Long.toString(this.retries_vi)).append('\n');
+        sbuf.append(" VO : ").append(" rx=").append(Long.toString(this.rxmpdu_vo))
+                .append(" tx=").append(Long.toString(this.txmpdu_vo))
+                .append(" lost=").append(Long.toString(this.lostmpdu_vo))
+                .append(" retries=").append(Long.toString(this.retries_vo)).append('\n');
+        sbuf.append(" on_time : ").append(Integer.toString(this.on_time))
+                .append(" rx_time=").append(Integer.toString(this.rx_time))
+                .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n')
+                .append(" tx_time=").append(Integer.toString(this.tx_time))
+                .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level));
+        sbuf.append(" ts=" + timeStampInMs);
+        return sbuf.toString();
+    }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index cda1cf6..3118761 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -25,7 +25,6 @@
 import android.net.wifi.RttManager.ResponderConfig;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiWakeReasonAndCounts;
 import android.os.SystemClock;
@@ -111,7 +110,7 @@
             Log.e(mTAG, "Failed to start HAL for client mode");
             return Pair.create(SETUP_FAILURE_HAL, null);
         }
-        IClientInterface iClientInterface = mWificondControl.setupDriverForClientMode(ifaceName);
+        IClientInterface iClientInterface = mWificondControl.setupInterfaceForClientMode(ifaceName);
         if (iClientInterface == null) {
             return Pair.create(SETUP_FAILURE_WIFICOND, null);
         }
@@ -132,7 +131,7 @@
             Log.e(mTAG, "Failed to start HAL for AP mode");
             return Pair.create(SETUP_FAILURE_HAL, null);
         }
-        IApInterface iApInterface = mWificondControl.setupDriverForSoftApMode(ifaceName);
+        IApInterface iApInterface = mWificondControl.setupInterfaceForSoftApMode(ifaceName);
         if (iApInterface == null) {
             return Pair.create(SETUP_FAILURE_WIFICOND, null);
         }
@@ -153,6 +152,105 @@
         }
     }
 
+    /**
+     * TODO(b/69426063): NEW API Surface for interface management. This will eventually
+     * deprecate the other interface management API's above. But, for now there will be
+     * some duplication to ease transition.
+     */
+    /**
+     * Initialize the native modules.
+     *
+     * @return true on success, false otherwise.
+     */
+    public boolean initialize() {
+        return false;
+    }
+
+    /**
+     * Callback to notify when the status of one of the native daemons
+     * (wificond, wpa_supplicant & vendor HAL) changes.
+     */
+    public interface StatusListener {
+        /**
+         * @param allReady Indicates if all the native daemons are ready for operation or not.
+         */
+        void onStatusChanged(boolean allReady);
+    }
+
+    /**
+     * Register a StatusListener to get notified about any status changes from the native daemons.
+     *
+     * It is safe to re-register the same callback object - duplicates are detected and only a
+     * single copy kept.
+     *
+     * @param listener StatusListener listener object.
+     */
+    public void registerStatusListener(@NonNull StatusListener listener) {
+    }
+
+    /**
+     * Callback to notify when the associated interface is destroyed, up or down.
+     */
+    public interface InterfaceCallback {
+        /**
+         * Interface destroyed by HalDeviceManager.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onDestroyed(String ifaceName);
+
+        /**
+         * Interface is up.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onUp(String ifaceName);
+
+        /**
+         * Interface is down.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onDown(String ifaceName);
+    }
+
+    /**
+     * Setup an interface for Client mode operations.
+     *
+     * This method configures an interface in STA mode in all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param interfaceCallback Associated callback for notifying status changes for the iface.
+     * @return Returns the name of the allocated interface, will be null on failure.
+     */
+    public String setupInterfaceForClientMode(@NonNull InterfaceCallback interfaceCallback) {
+        return null;
+    }
+
+    /**
+     * Setup an interface for Soft AP mode operations.
+     *
+     * This method configures an interface in AP mode in all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param interfaceCallback Associated callback for notifying status changes for the iface.
+     * @return Returns the name of the allocated interface, will be null on failure.
+     */
+    public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
+        return null;
+    }
+
+    /**
+     * Teardown an interface in Client/AP mode.
+     *
+     * This method tears down the associated interface from all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param ifaceName Name of the interface.
+     */
+    public void teardownInterface(@NonNull String ifaceName) {
+    }
+
     /********************************************************
      * Wificond operations
      ********************************************************/
@@ -179,6 +277,32 @@
     }
 
     /**
+     * Callback to notify wificond death.
+     */
+    public interface WificondDeathEventHandler {
+        /**
+         * Invoked when the wificond dies.
+         */
+        void onDeath();
+    }
+
+    /**
+     * Registers a death notification for wificond.
+     * @return Returns true on success.
+     */
+    public boolean registerWificondDeathHandler(@NonNull WificondDeathEventHandler handler) {
+        return mWificondControl.registerDeathHandler(handler);
+    }
+
+    /**
+     * Deregisters a death notification for wificond.
+     * @return Returns true on success.
+     */
+    public boolean deregisterWificondDeathHandler() {
+        return mWificondControl.deregisterDeathHandler();
+    }
+
+    /**
     * Disable wpa_supplicant via wificond.
     * @return Returns true on success.
     */
@@ -200,7 +324,7 @@
     * Returns null on failure.
     */
     public SignalPollResult signalPoll() {
-        return mWificondControl.signalPoll();
+        return mWificondControl.signalPoll(mInterfaceName);
     }
 
     /**
@@ -209,7 +333,7 @@
     * Returns null on failure.
     */
     public TxPacketCounters getTxPacketCounters() {
-        return mWificondControl.getTxPacketCounters();
+        return mWificondControl.getTxPacketCounters(mInterfaceName);
     }
 
     /**
@@ -235,7 +359,7 @@
      * @return Returns true on success.
      */
     public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
-        return mWificondControl.scan(freqs, hiddenNetworkSSIDs);
+        return mWificondControl.scan(mInterfaceName, freqs, hiddenNetworkSSIDs);
     }
 
     /**
@@ -244,7 +368,8 @@
      * Returns an empty ArrayList on failure.
      */
     public ArrayList<ScanDetail> getScanResults() {
-        return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN);
+        return mWificondControl.getScanResults(
+                mInterfaceName, WificondControl.SCAN_TYPE_SINGLE_SCAN);
     }
 
     /**
@@ -253,7 +378,7 @@
      * Returns an empty ArrayList on failure.
      */
     public ArrayList<ScanDetail> getPnoScanResults() {
-        return mWificondControl.getScanResults(WificondControl.SCAN_TYPE_PNO_SCAN);
+        return mWificondControl.getScanResults(mInterfaceName, WificondControl.SCAN_TYPE_PNO_SCAN);
     }
 
     /**
@@ -262,7 +387,7 @@
      * @return true on success.
      */
     public boolean startPnoScan(PnoSettings pnoSettings) {
-        return mWificondControl.startPnoScan(pnoSettings);
+        return mWificondControl.startPnoScan(mInterfaceName, pnoSettings);
     }
 
     /**
@@ -270,7 +395,37 @@
      * @return true on success.
      */
     public boolean stopPnoScan() {
-        return mWificondControl.stopPnoScan();
+        return mWificondControl.stopPnoScan(mInterfaceName);
+    }
+
+    /**
+     * Callbacks for SoftAp interface.
+     */
+    public interface SoftApListener {
+        /**
+         * Invoked when the number of associated stations changes.
+         */
+        void onNumAssociatedStationsChanged(int numStations);
+    }
+
+    /**
+     * Start Soft AP operation using the provided configuration.
+     *
+     * @param config Configuration to use for the soft ap created.
+     * @param listener Callback for AP events.
+     * @return true on success, false otherwise.
+     */
+    public boolean startSoftAp(WifiConfiguration config, SoftApListener listener) {
+        return mWificondControl.startSoftAp(mInterfaceName, config, listener);
+    }
+
+    /**
+     * Stop the ongoing Soft AP operation.
+     *
+     * @return true on success, false otherwise.
+     */
+    public boolean stopSoftAp() {
+        return mWificondControl.stopSoftAp(mInterfaceName);
     }
 
     /********************************************************
@@ -278,7 +433,26 @@
      ********************************************************/
 
     /**
-     * This method is called repeatedly until the connection to wpa_supplicant is established.
+     * Callback to notify supplicant death.
+     */
+    public interface SupplicantDeathEventHandler {
+        /**
+         * Invoked when the supplicant dies.
+         */
+        void onDeath();
+    }
+
+    /**
+     * Registers a death notification for supplicant.
+     * @return Returns true on success.
+     */
+    public boolean registerSupplicantDeathHandler(@NonNull SupplicantDeathEventHandler handler) {
+        return mSupplicantStaIfaceHal.registerDeathHandler(handler);
+    }
+
+    /**
+     * This method is called repeatedly until the connection to wpa_supplicant is
+     * established and a STA iface is setup.
      *
      * @return true if connection is established, false otherwise.
      * TODO: Add unit tests for these once we remove the legacy code.
@@ -290,14 +464,19 @@
             return false;
         }
         // Check if the initialization is complete.
-        return mSupplicantStaIfaceHal.isInitializationComplete();
+        if (!mSupplicantStaIfaceHal.isInitializationComplete()) {
+            return false;
+        }
+        // Setup the STA iface once connection is established.
+        return mSupplicantStaIfaceHal.setupIface(mInterfaceName);
     }
 
     /**
      * Close supplicant connection.
      */
     public void closeSupplicantConnection() {
-        // Nothing to do for HIDL.
+        // Setup the STA iface once connection is established.
+        mSupplicantStaIfaceHal.teardownIface(mInterfaceName);
     }
 
     /**
@@ -315,7 +494,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean reconnect() {
-        return mSupplicantStaIfaceHal.reconnect();
+        return mSupplicantStaIfaceHal.reconnect(mInterfaceName);
     }
 
     /**
@@ -324,7 +503,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean reassociate() {
-        return mSupplicantStaIfaceHal.reassociate();
+        return mSupplicantStaIfaceHal.reassociate(mInterfaceName);
     }
 
     /**
@@ -333,7 +512,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean disconnect() {
-        return mSupplicantStaIfaceHal.disconnect();
+        return mSupplicantStaIfaceHal.disconnect(mInterfaceName);
     }
 
     /**
@@ -342,7 +521,7 @@
      * @return string containing the MAC address, or null on a failed call
      */
     public String getMacAddress() {
-        return mSupplicantStaIfaceHal.getMacAddress();
+        return mSupplicantStaIfaceHal.getMacAddress(mInterfaceName);
     }
 
     public static final int RX_FILTER_TYPE_V4_MULTICAST = 0;
@@ -372,10 +551,10 @@
      * The  SETSUSPENDOPT driver command overrides the filtering rules
      */
     public boolean startFilteringMulticastV4Packets() {
-        return mSupplicantStaIfaceHal.stopRxFilter()
+        return mSupplicantStaIfaceHal.stopRxFilter(mInterfaceName)
                 && mSupplicantStaIfaceHal.removeRxFilter(
-                RX_FILTER_TYPE_V4_MULTICAST)
-                && mSupplicantStaIfaceHal.startRxFilter();
+                        mInterfaceName, RX_FILTER_TYPE_V4_MULTICAST)
+                && mSupplicantStaIfaceHal.startRxFilter(mInterfaceName);
     }
 
     /**
@@ -383,10 +562,10 @@
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      */
     public boolean stopFilteringMulticastV4Packets() {
-        return mSupplicantStaIfaceHal.stopRxFilter()
+        return mSupplicantStaIfaceHal.stopRxFilter(mInterfaceName)
                 && mSupplicantStaIfaceHal.addRxFilter(
-                RX_FILTER_TYPE_V4_MULTICAST)
-                && mSupplicantStaIfaceHal.startRxFilter();
+                        mInterfaceName, RX_FILTER_TYPE_V4_MULTICAST)
+                && mSupplicantStaIfaceHal.startRxFilter(mInterfaceName);
     }
 
     /**
@@ -394,10 +573,10 @@
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      */
     public boolean startFilteringMulticastV6Packets() {
-        return mSupplicantStaIfaceHal.stopRxFilter()
+        return mSupplicantStaIfaceHal.stopRxFilter(mInterfaceName)
                 && mSupplicantStaIfaceHal.removeRxFilter(
-                RX_FILTER_TYPE_V6_MULTICAST)
-                && mSupplicantStaIfaceHal.startRxFilter();
+                        mInterfaceName, RX_FILTER_TYPE_V6_MULTICAST)
+                && mSupplicantStaIfaceHal.startRxFilter(mInterfaceName);
     }
 
     /**
@@ -405,10 +584,10 @@
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      */
     public boolean stopFilteringMulticastV6Packets() {
-        return mSupplicantStaIfaceHal.stopRxFilter()
+        return mSupplicantStaIfaceHal.stopRxFilter(mInterfaceName)
                 && mSupplicantStaIfaceHal.addRxFilter(
-                RX_FILTER_TYPE_V6_MULTICAST)
-                && mSupplicantStaIfaceHal.startRxFilter();
+                        mInterfaceName, RX_FILTER_TYPE_V6_MULTICAST)
+                && mSupplicantStaIfaceHal.startRxFilter(mInterfaceName);
     }
 
     public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED  = 0;
@@ -423,7 +602,7 @@
       * @return Whether the mode was successfully set.
       */
     public boolean setBluetoothCoexistenceMode(int mode) {
-        return mSupplicantStaIfaceHal.setBtCoexistenceMode(mode);
+        return mSupplicantStaIfaceHal.setBtCoexistenceMode(mInterfaceName, mode);
     }
 
     /**
@@ -435,7 +614,8 @@
      * @return {@code true} if the command succeeded, {@code false} otherwise.
      */
     public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
-        return mSupplicantStaIfaceHal.setBtCoexistenceScanModeEnabled(setCoexScanMode);
+        return mSupplicantStaIfaceHal.setBtCoexistenceScanModeEnabled(
+                mInterfaceName, setCoexScanMode);
     }
 
     /**
@@ -445,7 +625,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setSuspendOptimizations(boolean enabled) {
-        return mSupplicantStaIfaceHal.setSuspendModeEnabled(enabled);
+        return mSupplicantStaIfaceHal.setSuspendModeEnabled(mInterfaceName, enabled);
     }
 
     /**
@@ -455,7 +635,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setCountryCode(String countryCode) {
-        return mSupplicantStaIfaceHal.setCountryCode(countryCode);
+        return mSupplicantStaIfaceHal.setCountryCode(mInterfaceName, countryCode);
     }
 
     /**
@@ -466,10 +646,10 @@
      */
     public void startTdls(String macAddr, boolean enable) {
         if (enable) {
-            mSupplicantStaIfaceHal.initiateTdlsDiscover(macAddr);
-            mSupplicantStaIfaceHal.initiateTdlsSetup(macAddr);
+            mSupplicantStaIfaceHal.initiateTdlsDiscover(mInterfaceName, macAddr);
+            mSupplicantStaIfaceHal.initiateTdlsSetup(mInterfaceName, macAddr);
         } else {
-            mSupplicantStaIfaceHal.initiateTdlsTeardown(macAddr);
+            mSupplicantStaIfaceHal.initiateTdlsTeardown(mInterfaceName, macAddr);
         }
     }
 
@@ -480,7 +660,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean startWpsPbc(String bssid) {
-        return mSupplicantStaIfaceHal.startWpsPbc(bssid);
+        return mSupplicantStaIfaceHal.startWpsPbc(mInterfaceName, bssid);
     }
 
     /**
@@ -490,7 +670,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean startWpsPinKeypad(String pin) {
-        return mSupplicantStaIfaceHal.startWpsPinKeypad(pin);
+        return mSupplicantStaIfaceHal.startWpsPinKeypad(mInterfaceName, pin);
     }
 
     /**
@@ -500,7 +680,7 @@
      * @return new pin generated on success, null otherwise.
      */
     public String startWpsPinDisplay(String bssid) {
-        return mSupplicantStaIfaceHal.startWpsPinDisplay(bssid);
+        return mSupplicantStaIfaceHal.startWpsPinDisplay(mInterfaceName, bssid);
     }
 
     /**
@@ -510,7 +690,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setExternalSim(boolean external) {
-        return mSupplicantStaIfaceHal.setExternalSim(external);
+        return mSupplicantStaIfaceHal.setExternalSim(mInterfaceName, external);
     }
 
     /**
@@ -529,11 +709,14 @@
      */
     public boolean simAuthResponse(int id, String type, String response) {
         if (SIM_AUTH_RESP_TYPE_GSM_AUTH.equals(type)) {
-            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse(response);
+            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse(
+                    mInterfaceName, response);
         } else if (SIM_AUTH_RESP_TYPE_UMTS_AUTH.equals(type)) {
-            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(response);
+            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(
+                    mInterfaceName, response);
         } else if (SIM_AUTH_RESP_TYPE_UMTS_AUTS.equals(type)) {
-            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAutsResponse(response);
+            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAutsResponse(
+                    mInterfaceName, response);
         } else {
             return false;
         }
@@ -545,7 +728,7 @@
      * @return true if succeeds, false otherwise.
      */
     public boolean simAuthFailedResponse(int id) {
-        return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure();
+        return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure(mInterfaceName);
     }
 
     /**
@@ -554,7 +737,7 @@
      * @return true if succeeds, false otherwise.
      */
     public boolean umtsAuthFailedResponse(int id) {
-        return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure();
+        return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure(mInterfaceName);
     }
 
     /**
@@ -564,7 +747,8 @@
      * @return true if succeeds, false otherwise.
      */
     public boolean simIdentityResponse(int id, String response) {
-        return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(response);
+        return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(
+                mInterfaceName, response);
     }
 
     /**
@@ -573,7 +757,7 @@
      * @return anonymous identity string if succeeds, null otherwise.
      */
     public String getEapAnonymousIdentity() {
-        return mSupplicantStaIfaceHal.getCurrentNetworkEapAnonymousIdentity();
+        return mSupplicantStaIfaceHal.getCurrentNetworkEapAnonymousIdentity(mInterfaceName);
     }
 
     /**
@@ -584,7 +768,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean startWpsRegistrar(String bssid, String pin) {
-        return mSupplicantStaIfaceHal.startWpsRegistrar(bssid, pin);
+        return mSupplicantStaIfaceHal.startWpsRegistrar(mInterfaceName, bssid, pin);
     }
 
     /**
@@ -593,7 +777,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean cancelWps() {
-        return mSupplicantStaIfaceHal.cancelWps();
+        return mSupplicantStaIfaceHal.cancelWps(mInterfaceName);
     }
 
     /**
@@ -603,7 +787,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setDeviceName(String name) {
-        return mSupplicantStaIfaceHal.setWpsDeviceName(name);
+        return mSupplicantStaIfaceHal.setWpsDeviceName(mInterfaceName, name);
     }
 
     /**
@@ -613,7 +797,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setDeviceType(String type) {
-        return mSupplicantStaIfaceHal.setWpsDeviceType(type);
+        return mSupplicantStaIfaceHal.setWpsDeviceType(mInterfaceName, type);
     }
 
     /**
@@ -623,7 +807,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setConfigMethods(String cfg) {
-        return mSupplicantStaIfaceHal.setWpsConfigMethods(cfg);
+        return mSupplicantStaIfaceHal.setWpsConfigMethods(mInterfaceName, cfg);
     }
 
     /**
@@ -633,7 +817,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setManufacturer(String value) {
-        return mSupplicantStaIfaceHal.setWpsManufacturer(value);
+        return mSupplicantStaIfaceHal.setWpsManufacturer(mInterfaceName, value);
     }
 
     /**
@@ -643,7 +827,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setModelName(String value) {
-        return mSupplicantStaIfaceHal.setWpsModelName(value);
+        return mSupplicantStaIfaceHal.setWpsModelName(mInterfaceName, value);
     }
 
     /**
@@ -653,7 +837,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setModelNumber(String value) {
-        return mSupplicantStaIfaceHal.setWpsModelNumber(value);
+        return mSupplicantStaIfaceHal.setWpsModelNumber(mInterfaceName, value);
     }
 
     /**
@@ -663,7 +847,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean setSerialNumber(String value) {
-        return mSupplicantStaIfaceHal.setWpsSerialNumber(value);
+        return mSupplicantStaIfaceHal.setWpsSerialNumber(mInterfaceName, value);
     }
 
     /**
@@ -672,7 +856,7 @@
      * @param enabled true to enable, false to disable.
      */
     public void setPowerSave(boolean enabled) {
-        mSupplicantStaIfaceHal.setPowerSave(enabled);
+        mSupplicantStaIfaceHal.setPowerSave(mInterfaceName, enabled);
     }
 
     /**
@@ -693,7 +877,7 @@
      * @return true if request is sent successfully, false otherwise.
      */
     public boolean enableStaAutoReconnect(boolean enable) {
-        return mSupplicantStaIfaceHal.enableAutoReconnect(enable);
+        return mSupplicantStaIfaceHal.enableAutoReconnect(mInterfaceName, enable);
     }
 
     /**
@@ -706,7 +890,7 @@
      */
     public boolean migrateNetworksFromSupplicant(Map<String, WifiConfiguration> configs,
                                                  SparseArray<Map<String, String>> networkExtras) {
-        return mSupplicantStaIfaceHal.loadNetworks(configs, networkExtras);
+        return mSupplicantStaIfaceHal.loadNetworks(mInterfaceName, configs, networkExtras);
     }
 
     /**
@@ -724,8 +908,8 @@
      */
     public boolean connectToNetwork(WifiConfiguration configuration) {
         // Abort ongoing scan before connect() to unblock connection request.
-        mWificondControl.abortScan();
-        return mSupplicantStaIfaceHal.connectToNetwork(configuration);
+        mWificondControl.abortScan(mInterfaceName);
+        return mSupplicantStaIfaceHal.connectToNetwork(mInterfaceName, configuration);
     }
 
     /**
@@ -743,8 +927,8 @@
      */
     public boolean roamToNetwork(WifiConfiguration configuration) {
         // Abort ongoing scan before connect() to unblock roaming request.
-        mWificondControl.abortScan();
-        return mSupplicantStaIfaceHal.roamToNetwork(configuration);
+        mWificondControl.abortScan(mInterfaceName);
+        return mSupplicantStaIfaceHal.roamToNetwork(mInterfaceName, configuration);
     }
 
     /**
@@ -764,7 +948,7 @@
      * @return {@code true} if it succeeds, {@code false} otherwise
      */
     public boolean removeAllNetworks() {
-        return mSupplicantStaIfaceHal.removeAllNetworks();
+        return mSupplicantStaIfaceHal.removeAllNetworks(mInterfaceName);
     }
 
     /**
@@ -773,7 +957,7 @@
      * @return true if successful, false otherwise.
      */
     public boolean setConfiguredNetworkBSSID(String bssid) {
-        return mSupplicantStaIfaceHal.setCurrentNetworkBssid(bssid);
+        return mSupplicantStaIfaceHal.setCurrentNetworkBssid(mInterfaceName, bssid);
     }
 
     /**
@@ -796,7 +980,8 @@
         }
         ArrayList<Integer> hs20SubtypeList = new ArrayList<>();
         hs20SubtypeList.addAll(hs20Subtypes);
-        return mSupplicantStaIfaceHal.initiateAnqpQuery(bssid, anqpIdList, hs20SubtypeList);
+        return mSupplicantStaIfaceHal.initiateAnqpQuery(
+                mInterfaceName, bssid, anqpIdList, hs20SubtypeList);
     }
 
     /**
@@ -810,7 +995,7 @@
             Log.e(mTAG, "Invalid arguments for Icon request.");
             return false;
         }
-        return mSupplicantStaIfaceHal.initiateHs20IconQuery(bssid, fileName);
+        return mSupplicantStaIfaceHal.initiateHs20IconQuery(mInterfaceName, bssid, fileName);
     }
 
     /**
@@ -819,7 +1004,7 @@
      * @return Hex string corresponding to the WPS NFC token.
      */
     public String getCurrentNetworkWpsNfcConfigurationToken() {
-        return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken();
+        return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken(mInterfaceName);
     }
 
     /** Remove the request |networkId| from supplicant if it's the current network,
@@ -828,7 +1013,7 @@
      * @param networkId network id of the network to be removed from supplicant.
      */
     public void removeNetworkIfCurrent(int networkId) {
-        mSupplicantStaIfaceHal.removeNetworkIfCurrent(networkId);
+        mSupplicantStaIfaceHal.removeNetworkIfCurrent(mInterfaceName, networkId);
     }
 
     /********************************************************
@@ -862,7 +1047,11 @@
             Log.i(mTAG, "Vendor HAL not supported, Ignore start...");
             return true;
         }
-        return mWifiVendorHal.startVendorHal(isStaMode);
+        if (isStaMode) {
+            return mWifiVendorHal.startVendorHalSta();
+        } else {
+            return mWifiVendorHal.startVendorHalAp();
+        }
     }
 
     /**
@@ -899,7 +1088,7 @@
      * @return true for success. false for failure
      */
     public boolean getBgScanCapabilities(ScanCapabilities capabilities) {
-        return mWifiVendorHal.getBgScanCapabilities(capabilities);
+        return mWifiVendorHal.getBgScanCapabilities(mInterfaceName, capabilities);
     }
 
     public static class ChannelSettings {
@@ -1049,39 +1238,39 @@
      * @return true for success
      */
     public boolean startBgScan(ScanSettings settings, ScanEventHandler eventHandler) {
-        return mWifiVendorHal.startBgScan(settings, eventHandler);
+        return mWifiVendorHal.startBgScan(mInterfaceName, settings, eventHandler);
     }
 
     /**
      * Stops any ongoing backgound scan
      */
     public void stopBgScan() {
-        mWifiVendorHal.stopBgScan();
+        mWifiVendorHal.stopBgScan(mInterfaceName);
     }
 
     /**
      * Pauses an ongoing backgound scan
      */
     public void pauseBgScan() {
-        mWifiVendorHal.pauseBgScan();
+        mWifiVendorHal.pauseBgScan(mInterfaceName);
     }
 
     /**
      * Restarts a paused scan
      */
     public void restartBgScan() {
-        mWifiVendorHal.restartBgScan();
+        mWifiVendorHal.restartBgScan(mInterfaceName);
     }
 
     /**
      * Gets the latest scan results received.
      */
     public WifiScanner.ScanData[] getBgScanResults() {
-        return mWifiVendorHal.getBgScanResults();
+        return mWifiVendorHal.getBgScanResults(mInterfaceName);
     }
 
-    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
-        return mWifiVendorHal.getWifiLinkLayerStats();
+    public WifiLinkLayerStats getWifiLinkLayerStats() {
+        return mWifiVendorHal.getWifiLinkLayerStats(mInterfaceName);
     }
 
     /**
@@ -1090,7 +1279,7 @@
      * @return bitmask defined by WifiManager.WIFI_FEATURE_*
      */
     public int getSupportedFeatureSet() {
-        return mWifiVendorHal.getSupportedFeatureSet();
+        return mWifiVendorHal.getSupportedFeatureSet(mInterfaceName);
     }
 
     public static interface RttEventHandler {
@@ -1147,7 +1336,7 @@
      * @return true for success
      */
     public boolean setScanningMacOui(byte[] oui) {
-        return mWifiVendorHal.setScanningMacOui(oui);
+        return mWifiVendorHal.setScanningMacOui(mInterfaceName, oui);
     }
 
     /**
@@ -1161,7 +1350,7 @@
      * Get the APF (Android Packet Filter) capabilities of the device
      */
     public ApfCapabilities getApfCapabilities() {
-        return mWifiVendorHal.getApfCapabilities();
+        return mWifiVendorHal.getApfCapabilities(mInterfaceName);
     }
 
     /**
@@ -1171,7 +1360,7 @@
      * @return true for success
      */
     public boolean installPacketFilter(byte[] filter) {
-        return mWifiVendorHal.installPacketFilter(filter);
+        return mWifiVendorHal.installPacketFilter(mInterfaceName, filter);
     }
 
     /**
@@ -1181,7 +1370,7 @@
      * @return true for success
      */
     public boolean setCountryCodeHal(String countryCode) {
-        return mWifiVendorHal.setCountryCodeHal(countryCode);
+        return mWifiVendorHal.setCountryCodeHal(mInterfaceName, countryCode);
     }
 
     //---------------------------------------------------------------------------------
@@ -1521,7 +1710,7 @@
      * @return true for success, false otherwise.
      */
     public boolean startPktFateMonitoring() {
-        return mWifiVendorHal.startPktFateMonitoring();
+        return mWifiVendorHal.startPktFateMonitoring(mInterfaceName);
     }
 
     /**
@@ -1530,14 +1719,14 @@
      * @return true for success, false otherwise.
      */
     public boolean getTxPktFates(TxFateReport[] reportBufs) {
-        return mWifiVendorHal.getTxPktFates(reportBufs);
+        return mWifiVendorHal.getTxPktFates(mInterfaceName, reportBufs);
     }
 
     /**
      * Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started.
      */
     public boolean getRxPktFates(RxFateReport[] reportBufs) {
-        return mWifiVendorHal.getRxPktFates(reportBufs);
+        return mWifiVendorHal.getRxPktFates(mInterfaceName, reportBufs);
     }
 
     /**
@@ -1556,7 +1745,7 @@
             Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
             srcMac[i] = hexVal.byteValue();
         }
-        return mWifiVendorHal.startSendingOffloadedPacket(
+        return mWifiVendorHal.startSendingOffloadedPacket(mInterfaceName,
                 slot, srcMac, keepAlivePacket, period);
     }
 
@@ -1567,7 +1756,7 @@
      * @return 0 for success, -1 for error
      */
     public int stopSendingOffloadedPacket(int slot) {
-        return mWifiVendorHal.stopSendingOffloadedPacket(slot);
+        return mWifiVendorHal.stopSendingOffloadedPacket(mInterfaceName, slot);
     }
 
     public static interface WifiRssiEventHandler {
@@ -1584,11 +1773,12 @@
      */
     public int startRssiMonitoring(byte maxRssi, byte minRssi,
                                    WifiRssiEventHandler rssiEventHandler) {
-        return mWifiVendorHal.startRssiMonitoring(maxRssi, minRssi, rssiEventHandler);
+        return mWifiVendorHal.startRssiMonitoring(
+                mInterfaceName, maxRssi, minRssi, rssiEventHandler);
     }
 
     public int stopRssiMonitoring() {
-        return mWifiVendorHal.stopRssiMonitoring();
+        return mWifiVendorHal.stopRssiMonitoring(mInterfaceName);
     }
 
     /**
@@ -1607,7 +1797,7 @@
      * @return true for success, false otherwise.
      */
     public boolean configureNeighborDiscoveryOffload(boolean enabled) {
-        return mWifiVendorHal.configureNeighborDiscoveryOffload(enabled);
+        return mWifiVendorHal.configureNeighborDiscoveryOffload(mInterfaceName, enabled);
     }
 
     // Firmware roaming control.
@@ -1625,7 +1815,7 @@
      * @return true for success, false otherwise.
      */
     public boolean getRoamingCapabilities(RoamingCapabilities capabilities) {
-        return mWifiVendorHal.getRoamingCapabilities(capabilities);
+        return mWifiVendorHal.getRoamingCapabilities(mInterfaceName, capabilities);
     }
 
     /**
@@ -1640,7 +1830,7 @@
      * @return error code returned from HAL.
      */
     public int enableFirmwareRoaming(int state) {
-        return mWifiVendorHal.enableFirmwareRoaming(state);
+        return mWifiVendorHal.enableFirmwareRoaming(mInterfaceName, state);
     }
 
     /**
@@ -1656,7 +1846,7 @@
      */
     public boolean configureRoaming(RoamingConfig config) {
         Log.d(mTAG, "configureRoaming ");
-        return mWifiVendorHal.configureRoaming(config);
+        return mWifiVendorHal.configureRoaming(mInterfaceName, config);
     }
 
     /**
@@ -1665,7 +1855,7 @@
     public boolean resetRoamingConfiguration() {
         // Pass in an empty RoamingConfig object which translates to zero size
         // blacklist and whitelist to reset the firmware roaming configuration.
-        return mWifiVendorHal.configureRoaming(new RoamingConfig());
+        return mWifiVendorHal.configureRoaming(mInterfaceName, new RoamingConfig());
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index 071fc07..46ded1c 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -152,9 +152,8 @@
         boolean hasQualifiedRssi =
                 (wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
                         || (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
-        // getTxSuccessRate() and getRxSuccessRate() returns the packet rate in per 5 seconds unit.
-        boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
-                || (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
+        boolean hasActiveStream = (wifiInfo.txSuccessRate > mStayOnNetworkMinimumTxRate)
+                || (wifiInfo.rxSuccessRate > mStayOnNetworkMinimumRxRate);
         if (hasQualifiedRssi && hasActiveStream) {
             localLog("Stay on current network because of good RSSI and ongoing traffic");
             return true;
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index e5281ef..1c258e1 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -216,7 +216,7 @@
             history = new LinkedList<>(mLinkMetricsHistory);
         }
         pw.println("time,session,rssi,filtered_rssi,rssi_threshold,"
-                + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1,s2");
+                + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx_pps,s0,s1,s2");
         for (String line : history) {
             pw.println(line);
         }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index e77fefb..0fb4bfb 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -69,10 +69,10 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
 import android.net.wifi.WifiScanner;
+import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.AsyncTask;
@@ -170,8 +170,6 @@
     final WifiSettingsStore mSettingsStore;
     /* Logs connection events and some general router and scan stats */
     private final WifiMetrics mWifiMetrics;
-    /* Manages affiliated certificates for current user */
-    private final WifiCertManager mCertManager;
 
     private final WifiInjector mWifiInjector;
     /* Backup/Restore Module */
@@ -433,7 +431,6 @@
         mPowerManager = mContext.getSystemService(PowerManager.class);
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        mCertManager = mWifiInjector.getWifiCertManager();
         mWifiLockManager = mWifiInjector.getWifiLockManager();
         mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
         HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
@@ -1733,12 +1730,19 @@
     @Override
     public WifiInfo getConnectionInfo(String callingPackage) {
         enforceAccessPermission();
-        mLog.info("getConnectionInfo uid=%").c(Binder.getCallingUid()).flush();
+        int uid = Binder.getCallingUid();
+        mLog.info("getConnectionInfo uid=%").c(uid).flush();
         /*
          * Make sure we have the latest information, by sending
          * a status request to the supplicant.
          */
-        return mWifiStateMachine.syncRequestConnectionInfo(callingPackage);
+        long ident = Binder.clearCallingIdentity();
+        try {
+            WifiInfo result = mWifiStateMachine.syncRequestConnectionInfo(callingPackage, uid);
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     /**
@@ -2410,38 +2414,6 @@
         return mWifiStateMachine.getAggressiveHandover();
     }
 
-    @Override
-    public void setAllowScansWithTraffic(int enabled) {
-        enforceAccessPermission();
-        mLog.info("setAllowScansWithTraffic uid=% enabled=%")
-                .c(Binder.getCallingUid())
-                .c(enabled).flush();
-        mWifiStateMachine.setAllowScansWithTraffic(enabled);
-    }
-
-    @Override
-    public int getAllowScansWithTraffic() {
-        enforceAccessPermission();
-        mLog.info("getAllowScansWithTraffic uid=%").c(Binder.getCallingUid()).flush();
-        return mWifiStateMachine.getAllowScansWithTraffic();
-    }
-
-    @Override
-    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        enforceChangePermission();
-        mLog.info("setEnableAutoJoinWhenAssociated uid=% enabled=%")
-                .c(Binder.getCallingUid())
-                .c(enabled).flush();
-        return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled);
-    }
-
-    @Override
-    public boolean getEnableAutoJoinWhenAssociated() {
-        enforceAccessPermission();
-        mLog.info("getEnableAutoJoinWhenAssociated uid=%").c(Binder.getCallingUid()).flush();
-        return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
-    }
-
     /* Return the Wifi Connection statistics object */
     @Override
     public WifiConnectionStatistics getConnectionStatistics() {
@@ -2549,14 +2521,6 @@
         return sb.toString();
     }
 
-    public void hideCertFromUnaffiliatedUsers(String alias) {
-        mCertManager.hideCertFromUnaffiliatedUsers(alias);
-    }
-
-    public String[] listClientCertsForCurrentUser() {
-        return mCertManager.listClientCertsForCurrentUser();
-    }
-
     /**
      * Enable/disable WifiConnectivityManager at runtime
      *
@@ -2659,4 +2623,33 @@
         restoreNetworks(wifiConfigurations);
         Slog.d(TAG, "Restored supplicant backup data");
     }
+
+    /**
+     * Starts subscription provisioning with a provider
+     *
+     * @param provider {@link OsuProvider} the provider to provision with
+     * @param callback {@link IProvisoningCallback} the callback object to inform status
+     */
+    @Override
+    public void startSubscriptionProvisioning(OsuProvider provider,
+            IProvisioningCallback callback) {
+        if (provider == null) {
+            throw new IllegalArgumentException("Provider must not be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Callback must not be null");
+        }
+        enforceNetworkSettingsPermission();
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_PASSPOINT)) {
+            throw new UnsupportedOperationException("Passpoint not enabled");
+        }
+        final int uid = Binder.getCallingUid();
+        mLog.trace("startSubscriptionProvisioning uid=%").c(uid).flush();
+        if (mWifiStateMachine.syncStartSubscriptionProvisioning(uid, provider,
+                callback, mWifiStateMachineChannel)) {
+            mLog.trace("Subscription provisioning started with %")
+                    .c(provider.toString()).flush();
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 2c8c0b7..1521bb9 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -29,9 +29,7 @@
 import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
 import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
 
-import android.Manifest;
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
@@ -39,7 +37,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
@@ -71,18 +68,17 @@
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.WpsResult;
 import android.net.wifi.WpsResult.Status;
+import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.os.BatteryStats;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -128,6 +124,7 @@
 import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
 import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
 
 import java.io.BufferedReader;
 import java.io.FileDescriptor;
@@ -181,9 +178,10 @@
 
     private static final String EXTRA_OSU_ICON_QUERY_BSSID = "BSSID";
     private static final String EXTRA_OSU_ICON_QUERY_FILENAME = "FILENAME";
+    private static final String EXTRA_OSU_PROVIDER = "OsuProvider";
 
     private boolean mVerboseLoggingEnabled = false;
-
+    private final WifiPermissionsWrapper mWifiPermissionsWrapper;
     /* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
      * the corresponding BSSID.
      */
@@ -250,8 +248,9 @@
     private String mLastBssid;
     private int mLastNetworkId; // The network Id we successfully joined
     private boolean mIsLinkDebouncing = false;
-    private final StateMachineDeathRecipient mDeathRecipient =
-            new StateMachineDeathRecipient(this, CMD_CLIENT_INTERFACE_BINDER_DEATH);
+    private final WifiNative.WificondDeathEventHandler mWificondDeathRecipient = () -> {
+        sendMessage(CMD_WIFICOND_BINDER_DEATH);
+    };
     private final WifiNative.VendorHalDeathEventHandler mVendorHalDeathRecipient = () -> {
         sendMessage(CMD_VENDOR_HAL_HWBINDER_DEATH);
     };
@@ -367,7 +366,7 @@
     private DhcpResults mDhcpResults;
 
     // NOTE: Do not return to clients - see syncRequestConnectionInfo()
-    private final WifiInfo mWifiInfo;
+    private final ExtendedWifiInfo mWifiInfo;
     private NetworkInfo mNetworkInfo;
     private final NetworkCapabilities mDfltNetworkCapabilities;
     private SupplicantStateTracker mSupplicantStateTracker;
@@ -698,8 +697,6 @@
     /* Enable/Disable WifiConnectivityManager */
     static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER               = BASE + 166;
 
-    /* Enable/Disable AutoJoin when associated */
-    static final int CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED                = BASE + 167;
 
     /* Get all matching Passpoint configurations */
     static final int CMD_GET_ALL_MATCHING_CONFIGS                       = BASE + 168;
@@ -728,8 +725,8 @@
     /* used to indicate that the foreground user was switched */
     static final int CMD_USER_STOP                                      = BASE + 207;
 
-    /* Signals that IClientInterface instance underpinning our state is dead. */
-    private static final int CMD_CLIENT_INTERFACE_BINDER_DEATH          = BASE + 250;
+    /* Signals that wificond is dead. */
+    private static final int CMD_WIFICOND_BINDER_DEATH          = BASE + 250;
 
     /* Signals that the Vendor HAL instance underpinning our state is dead. */
     private static final int CMD_VENDOR_HAL_HWBINDER_DEATH              = BASE + 251;
@@ -740,6 +737,9 @@
     /* Used to set the tx power limit for SAR during the start of a phone call. */
     private static final int CMD_SELECT_TX_POWER_SCENARIO               = BASE + 253;
 
+    // Start subscription provisioning with a given provider
+    private static final int CMD_START_SUBSCRIPTION_PROVISIONING        = BASE + 254;
+
     // For message logging.
     private static final Class[] sMessageClasses = {
             AsyncChannel.class, WifiStateMachine.class, DhcpClient.class };
@@ -794,8 +794,6 @@
      */
     private long mSupplicantScanIntervalMs;
 
-    private boolean mEnableAutoJoinWhenAssociated;
-    private int mAlwaysEnableScansWhileAssociated;
     private final int mThresholdQualifiedRssi24;
     private final int mThresholdQualifiedRssi5;
     private final int mThresholdSaturatedRssi24;
@@ -946,8 +944,9 @@
 
         mWifiMonitor = mWifiInjector.getWifiMonitor();
         mWifiDiagnostics = mWifiInjector.makeWifiDiagnostics(mWifiNative);
+        mWifiPermissionsWrapper = mWifiInjector.getWifiPermissionsWrapper();
 
-        mWifiInfo = new WifiInfo();
+        mWifiInfo = new ExtendedWifiInfo();
         mSupplicantStateTracker =
                 mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
 
@@ -1035,8 +1034,6 @@
                 com.android.internal.R.string.config_wifi_tcp_buffers);
 
         // Load Device configs
-        mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
-                R.bool.config_wifi_framework_enable_associated_network_selection);
         mThresholdQualifiedRssi24 = context.getResources().getInteger(
                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
         mThresholdQualifiedRssi5 = context.getResources().getInteger(
@@ -1246,6 +1243,7 @@
         mWifiNative.enableVerboseLogging(verbose);
         mWifiConfigManager.enableVerboseLogging(verbose);
         mSupplicantStateTracker.enableVerboseLogging(verbose);
+        mPasspointManager.enableVerboseLogging(verbose);
     }
 
     private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL";
@@ -1274,26 +1272,6 @@
         // mWifiConfigManager.trimANQPCache(true);
     }
 
-    public void setAllowScansWithTraffic(int enabled) {
-        mAlwaysEnableScansWhileAssociated = enabled;
-    }
-
-    public int getAllowScansWithTraffic() {
-        return mAlwaysEnableScansWhileAssociated;
-    }
-
-    /*
-     * Dynamically turn on/off if switching networks while connected is allowd.
-     */
-    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        sendMessage(CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED, enabled ? 1 : 0);
-        return true;
-    }
-
-    public boolean getEnableAutoJoinWhenAssociated() {
-        return mEnableAutoJoinWhenAssociated;
-    }
-
     private boolean setRandomMacOui() {
         String oui = mContext.getResources().getString(R.string.config_wifi_random_mac_oui);
         if (TextUtils.isEmpty(oui)) {
@@ -1466,9 +1444,8 @@
     WifiLinkLayerStats getWifiLinkLayerStats() {
         WifiLinkLayerStats stats = null;
         if (mWifiLinkLayerStatsSupported > 0) {
-            String name = "wlan0";
-            stats = mWifiNative.getWifiLinkLayerStats(name);
-            if (name != null && stats == null && mWifiLinkLayerStatsSupported > 0) {
+            stats = mWifiNative.getWifiLinkLayerStats();
+            if (stats == null && mWifiLinkLayerStatsSupported > 0) {
                 mWifiLinkLayerStatsSupported -= 1;
             } else if (stats != null) {
                 lastLinkLayerStatsUpdate = mClock.getWallClockMillis();
@@ -1758,20 +1735,18 @@
     /**
      * Get status information for the current connection, if any.
      *
+     * @param callingPackage string indicating the calling package of the caller
+     * @param uid the calling uid
      * @return a {@link WifiInfo} object containing information about the current connection
      */
-    public WifiInfo syncRequestConnectionInfo(String callingPackage) {
-        int uid = Binder.getCallingUid();
+    public WifiInfo syncRequestConnectionInfo(String callingPackage, int uid) {
         WifiInfo result = new WifiInfo(mWifiInfo);
-        if (uid == Process.myUid()) return result;
         boolean hideBssidAndSsid = true;
         result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
 
-        IPackageManager packageManager = AppGlobals.getPackageManager();
-
         try {
-            if (packageManager.checkUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS,
-                    uid) == PackageManager.PERMISSION_GRANTED) {
+            if (mWifiPermissionsWrapper.getLocalMacAddressPermission(uid)
+                    == PackageManager.PERMISSION_GRANTED) {
                 result.setMacAddress(mWifiInfo.getMacAddress());
             }
             if (mWifiPermissionsUtil.canAccessScanResults(
@@ -2011,6 +1986,25 @@
     }
 
     /**
+     * Start subscription provisioning synchronously
+     *
+     * @param provider {@link OsuProvider} the provider to provision with
+     * @param callback {@link IProvisioningCallback} callback for provisioning status
+     * @return boolean true indicates provisioning was started, false otherwise
+     */
+    public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider,
+            IProvisioningCallback callback, AsyncChannel channel) {
+        Message msg = Message.obtain();
+        msg.what = CMD_START_SUBSCRIPTION_PROVISIONING;
+        msg.arg1 = callingUid;
+        msg.obj = callback;
+        msg.getData().putParcelable(EXTRA_OSU_PROVIDER, provider);
+        Message resultMsg = channel.sendMessageSynchronously(msg);
+        boolean result = resultMsg.arg1 != 0;
+        resultMsg.recycle();
+        return result;
+    }
+    /**
      * Get connection statistics synchronously
      *
      * @param channel
@@ -2287,6 +2281,7 @@
         } else {
             pw.println("mWifiConnectivityManager is not initialized");
         }
+        mWifiInjector.getWakeupController().dump(fd, pw, args);
     }
 
     public void handleUserSwitch(int userId) {
@@ -3255,16 +3250,18 @@
                 || stateChangeResult.wifiSsid.toString().isEmpty()) && isLinkDebouncing()) {
             return state;
         }
-        // Network id is only valid when we start connecting
+        // Network id and SSID are only valid when we start connecting
         if (SupplicantState.isConnecting(state)) {
             mWifiInfo.setNetworkId(lookupFrameworkNetworkId(stateChangeResult.networkId));
+            mWifiInfo.setBSSID(stateChangeResult.BSSID);
+            mWifiInfo.setSSID(stateChangeResult.wifiSsid);
         } else {
+            // Reset parameters according to WifiInfo.reset()
             mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+            mWifiInfo.setBSSID(null);
+            mWifiInfo.setSSID(null);
         }
 
-        mWifiInfo.setBSSID(stateChangeResult.BSSID);
-        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
-
         final WifiConfiguration config = getCurrentWifiConfiguration();
         if (config != null) {
             mWifiInfo.setEphemeral(config.ephemeral);
@@ -3896,6 +3893,8 @@
                     break;
                 case CMD_INITIALIZE:
                     ok = mWifiNative.initializeVendorHal(mVendorHalDeathRecipient);
+                    mPasspointManager.initializeProvisioner(
+                            mWifiInjector.getWifiServiceHandlerThread().getLooper());
                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_BOOT_COMPLETED:
@@ -4028,6 +4027,9 @@
                 case CMD_GET_MATCHING_OSU_PROVIDERS:
                     replyToMessage(message, message.what, new ArrayList<OsuProvider>());
                     break;
+                case CMD_START_SUBSCRIPTION_PROVISIONING:
+                    replyToMessage(message, message.what, 0);
+                    break;
                 case CMD_IP_CONFIGURATION_SUCCESSFUL:
                 case CMD_IP_CONFIGURATION_LOST:
                 case CMD_IP_REACHABILITY_LOST:
@@ -4109,7 +4111,7 @@
                         mWifiNative.stopFilteringMulticastV4Packets();
                     }
                     break;
-                case CMD_CLIENT_INTERFACE_BINDER_DEATH:
+                case CMD_WIFICOND_BINDER_DEATH:
                     Log.e(TAG, "wificond died unexpectedly. Triggering recovery");
                     mWifiMetrics.incrementNumWificondCrashes();
                     mWifiDiagnostics.captureBugReportData(
@@ -4148,7 +4150,7 @@
             // Tearing down the client interfaces below is going to stop our supplicant.
             mWifiMonitor.stopAllMonitoring();
 
-            mDeathRecipient.unlinkToDeath();
+            mWifiNative.deregisterWificondDeathHandler();
             mWifiNative.tearDown();
         }
 
@@ -4171,7 +4173,7 @@
                         incrementMetricsForSetupFailure(statusAndInterface.first);
                     }
                     if (mClientInterface == null
-                            || !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
+                            || !mWifiNative.registerWificondDeathHandler(mWificondDeathRecipient)) {
                         setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                         cleanup();
                         break;
@@ -4528,15 +4530,6 @@
                 case CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER:
                     mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
                     break;
-                case CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED:
-                    final boolean allowed = (message.arg1 > 0);
-                    boolean old_state = mEnableAutoJoinWhenAssociated;
-                    mEnableAutoJoinWhenAssociated = allowed;
-                    if (!old_state && allowed && mScreenOn
-                            && getCurrentState() == mConnectedState) {
-                        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
-                    }
-                    break;
                 case CMD_SELECT_TX_POWER_SCENARIO:
                     int txPowerScenario = message.arg1;
                     logd("Setting Tx power scenario to " + txPowerScenario);
@@ -5156,6 +5149,14 @@
                     replyToMessage(message, message.what,
                             mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj));
                     break;
+                case CMD_START_SUBSCRIPTION_PROVISIONING:
+                    IProvisioningCallback callback = (IProvisioningCallback) message.obj;
+                    OsuProvider provider =
+                            (OsuProvider) message.getData().getParcelable(EXTRA_OSU_PROVIDER);
+                    int res = mPasspointManager.startSubscriptionProvisioning(
+                                    message.arg1, provider, callback) ? 1 : 0;
+                    replyToMessage(message, message.what, res);
+                    break;
                 case CMD_RECONNECT:
                     WorkSource workSource = (WorkSource) message.obj;
                     mWifiConnectivityManager.forceConnectivityScan(workSource);
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
index c49b645..de970ee 100644
--- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.INetworkManagementService;
@@ -26,6 +25,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
@@ -47,12 +47,13 @@
 
     private final WifiInjector mWifiInjector;
     private final Looper mLooper;
+    private final WifiNative mWifiNative;
     private final INetworkManagementService mNMService;
 
-    private IWificond mWificond;
-
     private Queue<SoftApModeConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>();
 
+    private String mInterfaceName;
+
     /* The base for wifi message types */
     static final int BASE = Protocol.BASE_WIFI;
 
@@ -67,22 +68,17 @@
 
     WifiStateMachinePrime(WifiInjector wifiInjector,
                           Looper looper,
+                          WifiNative wifiNative,
                           INetworkManagementService nmService) {
         mWifiInjector = wifiInjector;
         mLooper = looper;
+        mWifiNative = wifiNative;
         mNMService = nmService;
 
-        // Clean up existing interfaces in wificond.
-        // This ensures that the framework and wificond are in a consistent state after a framework
-        // restart.
-        try {
-            mWificond = mWifiInjector.makeWificond();
-            if (mWificond != null) {
-                mWificond.tearDownInterfaces();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "wificond died during framework startup");
-        }
+        mInterfaceName = mWifiNative.getInterfaceName();
+
+        // make sure we do not have leftover state in the event of a restart
+        mWifiNative.tearDown();
     }
 
     /**
@@ -211,23 +207,13 @@
             return HANDLED;
         }
 
-        private void tearDownInterfaces() {
-            if (mWificond != null) {
-                try {
-                    mWificond.tearDownInterfaces();
-                } catch (RemoteException e) {
-                    // There is very little we can do here
-                    Log.e(TAG, "Failed to tear down interfaces via wificond");
-                }
-                mWificond = null;
-            }
-            return;
+        private void cleanup() {
+            mWifiNative.tearDown();
         }
 
         class ClientModeState extends State {
             @Override
             public void enter() {
-                mWificond = mWifiInjector.makeWificond();
             }
 
             @Override
@@ -240,7 +226,7 @@
 
             @Override
             public void exit() {
-                tearDownInterfaces();
+                cleanup();
             }
         }
 
@@ -280,17 +266,27 @@
                 // Continue with setup since we are changing modes
                 mApInterface = null;
 
+                Pair<Integer, IApInterface> statusAndInterface =
+                        mWifiNative.setupForSoftApMode(mInterfaceName);
+                if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
+                    mApInterface = statusAndInterface.second;
+                } else {
+                    // TODO: incorporate metrics - or this particular one will be done in WifiNative
+                    //incrementMetricsForSetupFailure(statusAndInterface.first);
+                }
+                if (mApInterface == null) {
+                    // TODO: update battery stats that a failure occured - best to do in
+                    // initialization Failed.  Keeping line copied from WSM for a reference
+                    //setWifiApState(WIFI_AP_STATE_FAILED,
+                    //        WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
+                    initializationFailed("Could not get IApInterface instance");
+                    return;
+                }
+
                 try {
-                    mWificond = mWifiInjector.makeWificond();
-                    mApInterface = mWificond.createApInterface(
-                            mWifiInjector.getWifiNative().getInterfaceName());
                     mIfaceName = mApInterface.getInterfaceName();
                 } catch (RemoteException e) {
-                    initializationFailed(
-                            "Could not get IApInterface instance or its name from wificond");
-                    return;
-                } catch (NullPointerException e) {
-                    initializationFailed("wificond failure when initializing softap mode");
+                    initializationFailed("Could not get IApInterface name");
                     return;
                 }
                 mModeStateMachine.transitionTo(mSoftAPModeActiveState);
@@ -325,7 +321,7 @@
 
             @Override
             public void exit() {
-                tearDownInterfaces();
+                cleanup();
             }
 
             protected IApInterface getInterface() {
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 3f39e45..f1d2add 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -59,7 +59,6 @@
 import android.net.wifi.RttManager.ResponderConfig;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
@@ -67,6 +66,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.util.MutableInt;
@@ -74,10 +74,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener;
 import com.android.server.wifi.util.BitMask;
 import com.android.server.wifi.util.NativeUtil;
 
+import libcore.util.NonNull;
+
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Set;
 
 /**
@@ -139,7 +143,7 @@
     }
 
     /**
-     * Logs if the argument is false.
+     * Logs the argument along with the method name.
      *
      * Always returns its argument.
      */
@@ -159,6 +163,26 @@
     }
 
     /**
+     * Logs the argument along with the method name.
+     *
+     * Always returns its argument.
+     */
+    private String stringResult(String result) {
+        if (mVerboseLog == sNoLog) return result;
+        // Currently only seen if verbose logging is on
+
+        Thread cur = Thread.currentThread();
+        StackTraceElement[] trace = cur.getStackTrace();
+
+        mVerboseLog.err("% returns %")
+                .c(niceMethodName(trace, 3))
+                .c(result)
+                .flush();
+
+        return result;
+    }
+
+    /**
      * Logs at method entry
      *
      * @param format string with % placeholders
@@ -201,9 +225,9 @@
 
     // Vendor HAL HIDL interface objects.
     private IWifiChip mIWifiChip;
-    private IWifiStaIface mIWifiStaIface;
-    private IWifiApIface mIWifiApIface;
     private IWifiRttController mIWifiRttController;
+    private HashMap<String, IWifiStaIface> mIWifiStaIfaces = new HashMap<>();
+    private HashMap<String, IWifiApIface> mIWifiApIfaces = new HashMap<>();
     private final HalDeviceManager mHalDeviceManager;
     private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
     private final IWifiStaIfaceEventCallback mIWifiStaIfaceEventCallback;
@@ -271,7 +295,16 @@
      * @return true for success
      */
     public boolean startVendorHalAp() {
-        return startVendorHal(AP_MODE);
+        synchronized (sLock) {
+            if (!startVendorHal()) {
+                return false;
+            }
+            if (TextUtils.isEmpty(createApIface(null))) {
+                stopVendorHal();
+                return false;
+            }
+            return true;
+        }
     }
 
     /**
@@ -280,85 +313,182 @@
      * @return true for success
      */
     public boolean startVendorHalSta() {
-        return startVendorHal(STA_MODE);
+        synchronized (sLock) {
+            if (!startVendorHal()) {
+                return false;
+            }
+            if (TextUtils.isEmpty(createStaIface(null))) {
+                stopVendorHal();
+                return false;
+            }
+            return true;
+        }
     }
 
-    public static final boolean STA_MODE = true;
-    public static final boolean AP_MODE = false;
-
     /**
-     * Bring up the HIDL Vendor HAL and configure for STA mode or AP mode.
-     *
-     * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
+     * Bring up the HIDL Vendor HAL.
+     * @return true on success, false otherwise.
      */
-    public boolean startVendorHal(boolean isStaMode) {
+    public boolean startVendorHal() {
         synchronized (sLock) {
-            if (mIWifiStaIface != null) return boolResult(false);
-            if (mIWifiApIface != null) return boolResult(false);
             if (!mHalDeviceManager.start()) {
-                return startFailedTo("start the vendor HAL");
-            }
-            IWifiIface iface;
-            if (isStaMode) {
-                mIWifiStaIface = mHalDeviceManager.createStaIface(null, null);
-                if (mIWifiStaIface == null) {
-                    return startFailedTo("create STA Iface");
-                }
-                iface = (IWifiIface) mIWifiStaIface;
-                if (!registerStaIfaceCallback()) {
-                    return startFailedTo("register sta iface callback");
-                }
-                mIWifiRttController = mHalDeviceManager.createRttController();
-                if (mIWifiRttController == null) {
-                    return startFailedTo("create RTT controller");
-                }
-                if (!registerRttEventCallback()) {
-                    return startFailedTo("register RTT iface callback");
-                }
-                enableLinkLayerStats();
-            } else {
-                mIWifiApIface = mHalDeviceManager.createApIface(null, null);
-                if (mIWifiApIface == null) {
-                    return startFailedTo("create AP Iface");
-                }
-                iface = (IWifiIface) mIWifiApIface;
-            }
-            mIWifiChip = mHalDeviceManager.getChip(iface);
-            if (mIWifiChip == null) {
-                return startFailedTo("get the chip created for the Iface");
-            }
-            if (!registerChipCallback()) {
-                return startFailedTo("register chip callback");
+                mLog.err("Failed to start vendor HAL");
+                return false;
             }
             mLog.i("Vendor Hal started successfully");
             return true;
         }
     }
 
+    /** Helper method to lookup the corresponding STA iface object using iface name. */
+    private IWifiStaIface getStaIface(@NonNull String ifaceName) {
+        synchronized (sLock) {
+            return mIWifiStaIfaces.get(ifaceName);
+        }
+    }
+
     /**
-     * Logs a message and cleans up after a failing start attempt
+     * Create a STA iface using {@link HalDeviceManager}.
      *
-     * The lock should be held.
-     * @param message describes what was being attempted
-     * @return false
+     * @param destroyedListener Listener to be invoked when the interface is destroyed.
+     * @return iface name on success, null otherwise.
      */
-    private boolean startFailedTo(String message) {
-        mVerboseLog.err("Failed to %. Vendor Hal start failed").c(message).flush();
-        mHalDeviceManager.stop();
-        clearState();
-        return false;
+    public String createStaIface(InterfaceDestroyedListener destroyedListener) {
+        synchronized (sLock) {
+            IWifiStaIface iface;
+            iface = mHalDeviceManager.createStaIface(destroyedListener, null);
+            if (iface == null) {
+                mLog.err("Failed to create STA iface");
+                return stringResult(null);
+            }
+            String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);
+            if (TextUtils.isEmpty(ifaceName)) {
+                mLog.err("Failed to get iface name");
+                return stringResult(null);
+            }
+            if (!registerStaIfaceCallback(iface)) {
+                mLog.err("Failed to register STA iface callback");
+                return stringResult(null);
+            }
+            mIWifiRttController = mHalDeviceManager.createRttController();
+            if (mIWifiRttController == null) {
+                mLog.err("Failed to create RTT controller");
+                return stringResult(null);
+            }
+            if (!registerRttEventCallback()) {
+                mLog.err("Failed to register RTT controller callback");
+                return stringResult(null);
+            }
+            if (!retrieveWifiChip((IWifiIface) iface)) {
+                mLog.err("Failed to get wifi chip");
+                return stringResult(null);
+            }
+            enableLinkLayerStats(iface);
+            mIWifiStaIfaces.put(ifaceName, iface);
+            return ifaceName;
+        }
+    }
+
+    /**
+     * Remove a STA iface using {@link HalDeviceManager}.
+     *
+     * @param ifaceName Name of the interface being removed.
+     * @return true on success, false otherwise.
+     */
+    public boolean removeStaIface(@NonNull String ifaceName) {
+        synchronized (sLock) {
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
+
+            if (!mHalDeviceManager.removeIface((IWifiIface) iface)) {
+                mLog.err("Failed to remove STA iface");
+                return boolResult(false);
+            }
+            mIWifiStaIfaces.remove(ifaceName);
+            return true;
+        }
+    }
+
+    /** Helper method to lookup the corresponding AP iface object using iface name. */
+    private IWifiApIface getApIface(@NonNull String ifaceName) {
+        synchronized (sLock) {
+            return mIWifiApIfaces.get(ifaceName);
+        }
+    }
+
+    /**
+     * Create a AP iface using {@link HalDeviceManager}.
+     *
+     * @param destroyedListener Listener to be invoked when the interface is destroyed.
+     * @return iface name on success, null otherwise.
+     */
+    public String createApIface(InterfaceDestroyedListener destroyedListener) {
+        synchronized (sLock) {
+            IWifiApIface iface;
+            iface = mHalDeviceManager.createApIface(destroyedListener, null);
+            if (iface == null) {
+                mLog.err("Failed to create AP iface");
+                return stringResult(null);
+            }
+            String ifaceName = mHalDeviceManager.getName((IWifiIface) iface);
+            if (TextUtils.isEmpty(ifaceName)) {
+                mLog.err("Failed to get iface name");
+                return stringResult(null);
+            }
+            if (!retrieveWifiChip((IWifiIface) iface)) {
+                mLog.err("Failed to get wifi chip");
+                return stringResult(null);
+            }
+            mIWifiApIfaces.put(ifaceName, iface);
+            return ifaceName;
+        }
+    }
+
+    /**
+     * Remove an AP iface using {@link HalDeviceManager}.
+     *
+     * @param ifaceName Name of the interface being removed.
+     * @return true on success, false otherwise.
+     */
+    public boolean removeApIface(@NonNull String ifaceName) {
+        synchronized (sLock) {
+            IWifiApIface iface = getApIface(ifaceName);
+            if (iface == null) return boolResult(false);
+
+            if (!mHalDeviceManager.removeIface((IWifiIface) iface)) {
+                mLog.err("Failed to remove AP iface");
+                return boolResult(false);
+            }
+            mIWifiApIfaces.remove(ifaceName);
+            return true;
+        }
+    }
+
+    private boolean retrieveWifiChip(IWifiIface iface) {
+        synchronized (sLock) {
+            mIWifiChip = mHalDeviceManager.getChip(iface);
+            if (mIWifiChip == null) {
+                mLog.err("Failed to get the chip created for the Iface");
+                return false;
+            }
+            if (!registerChipCallback()) {
+                mLog.err("Failed to register chip callback");
+                return false;
+            }
+            return true;
+        }
     }
 
     /**
      * Registers the sta iface callback.
      */
-    private boolean registerStaIfaceCallback() {
+    private boolean registerStaIfaceCallback(IWifiStaIface iface) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            if (iface == null) return boolResult(false);
             if (mIWifiStaIfaceEventCallback == null) return boolResult(false);
             try {
                 WifiStatus status =
-                        mIWifiStaIface.registerEventCallback(mIWifiStaIfaceEventCallback);
+                        iface.registerEventCallback(mIWifiStaIfaceEventCallback);
                 return ok(status);
             } catch (RemoteException e) {
                 handleRemoteException(e);
@@ -390,6 +520,7 @@
     private boolean registerRttEventCallback() {
         synchronized (sLock) {
             if (mIWifiRttController == null) return boolResult(false);
+            if (mRttEventCallback == null) return boolResult(false);
             try {
                 WifiStatus status = mIWifiRttController.registerEventCallback(mRttEventCallback);
                 return ok(status);
@@ -418,36 +549,39 @@
      */
     private void clearState() {
         mIWifiChip = null;
-        mIWifiStaIface = null;
-        mIWifiApIface = null;
+        mIWifiStaIfaces.clear();
+        mIWifiApIfaces.clear();
         mIWifiRttController = null;
         mDriverDescription = null;
         mFirmwareDescription = null;
     }
 
     /**
-     * Tests whether the HAL is running or not
+     * Tests whether the HAL is started and atleast one iface is up.
      */
     public boolean isHalStarted() {
         // For external use only. Methods in this class should test for null directly.
         synchronized (sLock) {
-            return (mIWifiStaIface != null || mIWifiApIface != null);
+            return (!mIWifiStaIfaces.isEmpty() || !mIWifiApIfaces.isEmpty());
         }
     }
 
     /**
      * Gets the scan capabilities
      *
+     * @param ifaceName Name of the interface.
      * @param capabilities object to be filled in
      * @return true for success, false for failure
      */
-    public boolean getBgScanCapabilities(WifiNative.ScanCapabilities capabilities) {
+    public boolean getBgScanCapabilities(
+            @NonNull String ifaceName, WifiNative.ScanCapabilities capabilities) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 MutableBoolean ans = new MutableBoolean(false);
                 WifiNative.ScanCapabilities out = capabilities;
-                mIWifiStaIface.getBackgroundScanCapabilities((status, cap) -> {
+                iface.getBackgroundScanCapabilities((status, cap) -> {
                             if (!ok(status)) return;
                             mVerboseLog.info("scan capabilities %").c(cap.toString()).flush();
                             out.max_scan_cache_size = cap.maxCacheSize;
@@ -582,24 +716,27 @@
      *
      * Any ongoing scan will be stopped first
      *
+     * @param ifaceName    Name of the interface.
      * @param settings     to control the scan
      * @param eventHandler to call with the results
      * @return true for success
      */
-    public boolean startBgScan(WifiNative.ScanSettings settings,
+    public boolean startBgScan(@NonNull String ifaceName,
+                               WifiNative.ScanSettings settings,
                                WifiNative.ScanEventHandler eventHandler) {
         WifiStatus status;
         if (eventHandler == null) return boolResult(false);
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 if (mScan != null && !mScan.paused) {
-                    ok(mIWifiStaIface.stopBackgroundScan(mScan.cmdId));
+                    ok(iface.stopBackgroundScan(mScan.cmdId));
                     mScan = null;
                 }
                 mLastScanCmdId = (mLastScanCmdId % 9) + 1; // cycle through non-zero single digits
                 CurrentBackgroundScan scan = new CurrentBackgroundScan(mLastScanCmdId, settings);
-                status = mIWifiStaIface.startBackgroundScan(scan.cmdId, scan.param);
+                status = iface.startBackgroundScan(scan.cmdId, scan.param);
                 if (!ok(status)) return false;
                 scan.eventHandler = eventHandler;
                 mScan = scan;
@@ -614,14 +751,17 @@
 
     /**
      * Stops any ongoing backgound scan
+     *
+     * @param ifaceName Name of the interface.
      */
-    public void stopBgScan() {
+    public void stopBgScan(@NonNull String ifaceName) {
         WifiStatus status;
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return;
             try {
                 if (mScan != null) {
-                    ok(mIWifiStaIface.stopBackgroundScan(mScan.cmdId));
+                    ok(iface.stopBackgroundScan(mScan.cmdId));
                     mScan = null;
                 }
             } catch (RemoteException e) {
@@ -632,14 +772,17 @@
 
     /**
      * Pauses an ongoing backgound scan
+     *
+     * @param ifaceName Name of the interface.
      */
-    public void pauseBgScan() {
+    public void pauseBgScan(@NonNull String ifaceName) {
         WifiStatus status;
         synchronized (sLock) {
             try {
-                if (mIWifiStaIface == null) return;
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface == null) return;
                 if (mScan != null && !mScan.paused) {
-                    status = mIWifiStaIface.stopBackgroundScan(mScan.cmdId);
+                    status = iface.stopBackgroundScan(mScan.cmdId);
                     if (!ok(status)) return;
                     mScan.paused = true;
                 }
@@ -651,14 +794,17 @@
 
     /**
      * Restarts a paused background scan
+     *
+     * @param ifaceName Name of the interface.
      */
-    public void restartBgScan() {
+    public void restartBgScan(@NonNull String ifaceName) {
         WifiStatus status;
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return;
             try {
                 if (mScan != null && mScan.paused) {
-                    status = mIWifiStaIface.startBackgroundScan(mScan.cmdId, mScan.param);
+                    status = iface.startBackgroundScan(mScan.cmdId, mScan.param);
                     if (!ok(status)) return;
                     mScan.paused = false;
                 }
@@ -672,10 +818,13 @@
      * Gets the latest scan results received from the HIDL interface callback.
      * TODO(b/35754840): This hop to fetch scan results after callback is unnecessary. Refactor
      * WifiScanner to use the scan results from the callback.
+     *
+     * @param ifaceName Name of the interface.
      */
-    public WifiScanner.ScanData[] getBgScanResults() {
+    public WifiScanner.ScanData[] getBgScanResults(@NonNull String ifaceName) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return null;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return null;
             if (mScan == null) return null;
             return mScan.latestScanResults;
         }
@@ -686,17 +835,19 @@
      *
      * Note - we always enable link layer stats on a STA interface.
      *
+     * @param ifaceName Name of the interface.
      * @return the statistics, or null if unable to do so
      */
-    public WifiLinkLayerStats getWifiLinkLayerStats() {
+    public WifiLinkLayerStats getWifiLinkLayerStats(@NonNull String ifaceName) {
         class AnswerBox {
             public StaLinkLayerStats value = null;
         }
         AnswerBox answer = new AnswerBox();
         synchronized (sLock) {
             try {
-                if (mIWifiStaIface == null) return null;
-                mIWifiStaIface.getLinkLayerStats((status, stats) -> {
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface == null) return null;
+                iface.getLinkLayerStats((status, stats) -> {
                     if (!ok(status)) return;
                     answer.value = stats;
                 });
@@ -716,7 +867,6 @@
     static WifiLinkLayerStats frameworkFromHalLinkLayerStats(StaLinkLayerStats stats) {
         if (stats == null) return null;
         WifiLinkLayerStats out = new WifiLinkLayerStats();
-        // unpopulated: out.status, out.SSID, out.BSSID
         out.beacon_rx = stats.iface.beaconRx;
         out.rssi_mgmt = stats.iface.avgRssiMgmt;
         // Statistics are broken out by Wireless Multimedia Extensions categories
@@ -752,7 +902,7 @@
             out.rx_time = radioStats.rxTimeInMs;
             out.on_time_scan = radioStats.onTimeInMsForScan;
         }
-        // unused: stats.timeStampInMs;
+        out.timeStampInMs = stats.timeStampInMs;
         return out;
     }
 
@@ -763,12 +913,14 @@
      * Enables the linkLayerStats in the Hal.
      *
      * This is called unconditionally whenever we create a STA interface.
+     *
+     * @param iface Iface object.
      */
-    private void enableLinkLayerStats() {
+    private void enableLinkLayerStats(IWifiStaIface iface) {
         synchronized (sLock) {
             try {
                 WifiStatus status;
-                status = mIWifiStaIface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
+                status = iface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
                 if (!ok(status)) {
                     mLog.e("unable to enable link layer stats collection");
                 }
@@ -877,9 +1029,10 @@
      *
      * The result may differ depending on the mode (STA or AP)
      *
+     * @param ifaceName Name of the interface.
      * @return bitmask defined by WifiManager.WIFI_FEATURE_*
      */
-    public int getSupportedFeatureSet() {
+    public int getSupportedFeatureSet(@NonNull String ifaceName) {
         int featureSet = 0;
         if (!mHalDeviceManager.isStarted()) {
             return featureSet; // TODO: can't get capabilities with Wi-Fi down
@@ -893,8 +1046,9 @@
                         feat.value = wifiFeatureMaskFromChipCapabilities(capabilities);
                     });
                 }
-                if (mIWifiStaIface != null) {
-                    mIWifiStaIface.getCapabilities((status, capabilities) -> {
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface != null) {
+                    iface.getCapabilities((status, capabilities) -> {
                         if (!ok(status)) return;
                         feat.value |= wifiFeatureMaskFromStaCapabilities(capabilities);
                     });
@@ -1433,16 +1587,18 @@
      * An OUI {Organizationally Unique Identifier} is a 24-bit number that
      * uniquely identifies a vendor or manufacturer.
      *
+     * @param ifaceName Name of the interface.
      * @param oui
      * @return true for success
      */
-    public boolean setScanningMacOui(byte[] oui) {
+    public boolean setScanningMacOui(@NonNull String ifaceName, byte[] oui) {
         if (oui == null) return boolResult(false);
         if (oui.length != 3) return boolResult(false);
         synchronized (sLock) {
             try {
-                if (mIWifiStaIface == null) return boolResult(false);
-                WifiStatus status = mIWifiStaIface.setScanningMacOui(oui);
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface == null) return boolResult(false);
+                WifiStatus status = iface.setScanningMacOui(oui);
                 if (!ok(status)) return false;
                 return true;
             } catch (RemoteException e) {
@@ -1454,16 +1610,20 @@
 
     /**
      * Get the APF (Android Packet Filter) capabilities of the device
+     *
+     * @param ifaceName Name of the interface.
+     * @return APF capabilities object.
      */
-    public ApfCapabilities getApfCapabilities() {
+    public ApfCapabilities getApfCapabilities(@NonNull String ifaceName) {
         class AnswerBox {
             public ApfCapabilities value = sNoApfCapabilities;
         }
         synchronized (sLock) {
             try {
-                if (mIWifiStaIface == null) return sNoApfCapabilities;
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface == null) return sNoApfCapabilities;
                 AnswerBox box = new AnswerBox();
-                mIWifiStaIface.getApfPacketFilterCapabilities((status, capabilities) -> {
+                iface.getApfPacketFilterCapabilities((status, capabilities) -> {
                     if (!ok(status)) return;
                     box.value = new ApfCapabilities(
                         /* apfVersionSupported */   capabilities.version,
@@ -1483,10 +1643,11 @@
     /**
      * Installs an APF program on this iface, replacing any existing program.
      *
+     * @param ifaceName Name of the interface.
      * @param filter is the android packet filter program
      * @return true for success
      */
-    public boolean installPacketFilter(byte[] filter) {
+    public boolean installPacketFilter(@NonNull String ifaceName, byte[] filter) {
         int cmdId = 0; // We only aspire to support one program at a time
         if (filter == null) return boolResult(false);
         // Copy the program before taking the lock.
@@ -1494,8 +1655,9 @@
         enter("filter length %").c(filter.length).flush();
         synchronized (sLock) {
             try {
-                if (mIWifiStaIface == null) return boolResult(false);
-                WifiStatus status = mIWifiStaIface.installApfPacketFilter(cmdId, program);
+                IWifiStaIface iface = getStaIface(ifaceName);
+                if (iface == null) return boolResult(false);
+                WifiStatus status = iface.installApfPacketFilter(cmdId, program);
                 if (!ok(status)) return false;
                 return true;
             } catch (RemoteException e) {
@@ -1508,10 +1670,11 @@
     /**
      * Set country code for this AP iface.
      *
+     * @param ifaceName Name of the interface.
      * @param countryCode - two-letter country code (as ISO 3166)
      * @return true for success
      */
-    public boolean setCountryCodeHal(String countryCode) {
+    public boolean setCountryCodeHal(@NonNull String ifaceName, String countryCode) {
         if (countryCode == null) return boolResult(false);
         if (countryCode.length() != 2) return boolResult(false);
         byte[] code;
@@ -1522,8 +1685,9 @@
         }
         synchronized (sLock) {
             try {
-                if (mIWifiApIface == null) return boolResult(false);
-                WifiStatus status = mIWifiApIface.setCountryCode(code);
+                IWifiApIface iface = getApIface(ifaceName);
+                if (iface == null) return boolResult(false);
+                WifiStatus status = iface.setCountryCode(code);
                 if (!ok(status)) return false;
                 return true;
             } catch (RemoteException e) {
@@ -1809,13 +1973,15 @@
      * <p>
      * Once started, monitoring remains active until HAL is unloaded.
      *
+     * @param ifaceName Name of the interface.
      * @return true for success
      */
-    public boolean startPktFateMonitoring() {
+    public boolean startPktFateMonitoring(@NonNull String ifaceName) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
-                WifiStatus status = mIWifiStaIface.startDebugPacketFateMonitoring();
+                WifiStatus status = iface.startDebugPacketFateMonitoring();
                 return ok(status);
             } catch (RemoteException e) {
                 handleRemoteException(e);
@@ -1898,16 +2064,18 @@
      * <p>
      * Reports the outbound frames for the most recent association (space allowing).
      *
+     * @param ifaceName Name of the interface.
      * @param reportBufs
      * @return true for success
      */
-    public boolean getTxPktFates(WifiNative.TxFateReport[] reportBufs) {
+    public boolean getTxPktFates(@NonNull String ifaceName, WifiNative.TxFateReport[] reportBufs) {
         if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 MutableBoolean ok = new MutableBoolean(false);
-                mIWifiStaIface.getDebugTxPacketFates((status, fates) -> {
+                iface.getDebugTxPacketFates((status, fates) -> {
                             if (!ok(status)) return;
                             int i = 0;
                             for (WifiDebugTxPacketFateReport fate : fates) {
@@ -1938,16 +2106,18 @@
      * <p>
      * Reports the inbound frames for the most recent association (space allowing).
      *
+     * @param ifaceName Name of the interface.
      * @param reportBufs
      * @return true for success
      */
-    public boolean getRxPktFates(WifiNative.RxFateReport[] reportBufs) {
+    public boolean getRxPktFates(@NonNull String ifaceName, WifiNative.RxFateReport[] reportBufs) {
         if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 MutableBoolean ok = new MutableBoolean(false);
-                mIWifiStaIface.getDebugRxPacketFates((status, fates) -> {
+                iface.getDebugRxPacketFates((status, fates) -> {
                             if (!ok(status)) return;
                             int i = 0;
                             for (WifiDebugRxPacketFateReport fate : fates) {
@@ -1976,6 +2146,7 @@
     /**
      * Start sending the specified keep alive packets periodically.
      *
+     * @param ifaceName Name of the interface.
      * @param slot
      * @param srcMac
      * @param keepAlivePacket
@@ -1983,16 +2154,18 @@
      * @return 0 for success, -1 for error
      */
     public int startSendingOffloadedPacket(
-            int slot, byte[] srcMac, KeepalivePacketData keepAlivePacket, int periodInMs) {
+            @NonNull String ifaceName, int slot, byte[] srcMac,
+            KeepalivePacketData keepAlivePacket, int periodInMs) {
         enter("slot=% periodInMs=%").c(slot).c(periodInMs).flush();
 
         ArrayList<Byte> data = NativeUtil.byteArrayToArrayList(keepAlivePacket.data);
         short protocol = (short) (keepAlivePacket.protocol);
 
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return -1;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return -1;
             try {
-                WifiStatus status = mIWifiStaIface.startSendingKeepAlivePackets(
+                WifiStatus status = iface.startSendingKeepAlivePackets(
                         slot,
                         data,
                         protocol,
@@ -2011,16 +2184,18 @@
     /**
      * Stop sending the specified keep alive packets.
      *
+     * @param ifaceName Name of the interface.
      * @param slot id - same as startSendingOffloadedPacket call.
      * @return 0 for success, -1 for error
      */
-    public int stopSendingOffloadedPacket(int slot) {
+    public int stopSendingOffloadedPacket(@NonNull String ifaceName, int slot) {
         enter("slot=%").c(slot).flush();
 
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return -1;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return -1;
             try {
-                WifiStatus status = mIWifiStaIface.stopSendingKeepAlivePackets(slot);
+                WifiStatus status = iface.stopSendingKeepAlivePackets(slot);
                 if (!ok(status)) return -1;
                 return 0;
             } catch (RemoteException e) {
@@ -2044,22 +2219,24 @@
     /**
      * Start RSSI monitoring on the currently connected access point.
      *
+     * @param ifaceName        Name of the interface.
      * @param maxRssi          Maximum RSSI threshold.
      * @param minRssi          Minimum RSSI threshold.
      * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
      * @return 0 for success, -1 for failure
      */
-    public int startRssiMonitoring(byte maxRssi, byte minRssi,
+    public int startRssiMonitoring(@NonNull String ifaceName, byte maxRssi, byte minRssi,
                                    WifiNative.WifiRssiEventHandler rssiEventHandler) {
         enter("maxRssi=% minRssi=%").c(maxRssi).c(minRssi).flush();
         if (maxRssi <= minRssi) return -1;
         if (rssiEventHandler == null) return -1;
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return -1;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return -1;
             try {
-                mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
+                iface.stopRssiMonitoring(sRssiMonCmdId);
                 WifiStatus status;
-                status = mIWifiStaIface.startRssiMonitoring(sRssiMonCmdId, maxRssi, minRssi);
+                status = iface.startRssiMonitoring(sRssiMonCmdId, maxRssi, minRssi);
                 if (!ok(status)) return -1;
                 mWifiRssiEventHandler = rssiEventHandler;
                 return 0;
@@ -2073,15 +2250,16 @@
     /**
      * Stop RSSI monitoring
      *
+     * @param ifaceName Name of the interface.
      * @return 0 for success, -1 for failure
      */
-    public int stopRssiMonitoring() {
+    public int stopRssiMonitoring(@NonNull String ifaceName) {
         synchronized (sLock) {
             mWifiRssiEventHandler = null;
-            if (mIWifiStaIface == null) return -1;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return -1;
             try {
-                mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
-                WifiStatus status = mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
+                WifiStatus status = iface.stopRssiMonitoring(sRssiMonCmdId);
                 if (!ok(status)) return -1;
                 return 0;
             } catch (RemoteException e) {
@@ -2158,14 +2336,17 @@
     /**
      * Enable/Disable Neighbour discovery offload functionality in the firmware.
      *
+     * @param ifaceName Name of the interface.
      * @param enabled true to enable, false to disable.
+     * @return true for success, false for failure
      */
-    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
+    public boolean configureNeighborDiscoveryOffload(@NonNull String ifaceName, boolean enabled) {
         enter("enabled=%").c(enabled).flush();
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
-                WifiStatus status = mIWifiStaIface.enableNdOffload(enabled);
+                WifiStatus status = iface.enableNdOffload(enabled);
                 if (!ok(status)) return false;
             } catch (RemoteException e) {
                 handleRemoteException(e);
@@ -2180,16 +2361,19 @@
     /**
      * Query the firmware roaming capabilities.
      *
+     * @param ifaceName Name of the interface.
      * @param capabilities object to be filled in
      * @return true for success; false for failure
      */
-    public boolean getRoamingCapabilities(WifiNative.RoamingCapabilities capabilities) {
+    public boolean getRoamingCapabilities(@NonNull String ifaceName,
+                                          WifiNative.RoamingCapabilities capabilities) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 MutableBoolean ok = new MutableBoolean(false);
                 WifiNative.RoamingCapabilities out = capabilities;
-                mIWifiStaIface.getRoamingCapabilities((status, cap) -> {
+                iface.getRoamingCapabilities((status, cap) -> {
                     if (!ok(status)) return;
                     out.maxBlacklistSize = cap.maxBlacklistSize;
                     out.maxWhitelistSize = cap.maxWhitelistSize;
@@ -2206,12 +2390,14 @@
     /**
      * Enable/disable firmware roaming.
      *
+     * @param ifaceName Name of the interface.
      * @param state the intended roaming state
      * @return SUCCESS, FAILURE, or BUSY
      */
-    public int enableFirmwareRoaming(int state) {
+    public int enableFirmwareRoaming(@NonNull String ifaceName, int state) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return WifiStatusCode.ERROR_NOT_STARTED;
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return WifiStatusCode.ERROR_NOT_STARTED;
             try {
                 byte val;
                 switch (state) {
@@ -2226,7 +2412,7 @@
                         return WifiStatusCode.ERROR_INVALID_ARGS;
                 }
 
-                WifiStatus status = mIWifiStaIface.setRoamingState(val);
+                WifiStatus status = iface.setRoamingState(val);
                 mVerboseLog.d("setRoamingState returned " + status.code);
                 return status.code;
             } catch (RemoteException e) {
@@ -2239,12 +2425,14 @@
     /**
      * Set firmware roaming configurations.
      *
+     * @param ifaceName Name of the interface.
      * @param config new roaming configuration object
      * @return true for success; false for failure
      */
-    public boolean configureRoaming(WifiNative.RoamingConfig config) {
+    public boolean configureRoaming(@NonNull String ifaceName, WifiNative.RoamingConfig config) {
         synchronized (sLock) {
-            if (mIWifiStaIface == null) return boolResult(false);
+            IWifiStaIface iface = getStaIface(ifaceName);
+            if (iface == null) return boolResult(false);
             try {
                 StaRoamingConfig roamingConfig = new StaRoamingConfig();
 
@@ -2275,7 +2463,7 @@
                     }
                 }
 
-                WifiStatus status = mIWifiStaIface.configureRoaming(roamingConfig);
+                WifiStatus status = iface.configureRoaming(roamingConfig);
                 if (!ok(status)) return false;
             } catch (RemoteException e) {
                 handleRemoteException(e);
diff --git a/service/java/com/android/server/wifi/WificondControl.java b/service/java/com/android/server/wifi/WificondControl.java
index 9f58715..68da98f 100644
--- a/service/java/com/android/server/wifi/WificondControl.java
+++ b/service/java/com/android/server/wifi/WificondControl.java
@@ -18,18 +18,22 @@
 
 import android.annotation.NonNull;
 import android.net.wifi.IApInterface;
+import android.net.wifi.IApInterfaceEventCallback;
 import android.net.wifi.IClientInterface;
 import android.net.wifi.IPnoScanEvent;
 import android.net.wifi.IScanEvent;
 import android.net.wifi.IWifiScannerImpl;
 import android.net.wifi.IWificond;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.server.wifi.WifiNative.SoftApListener;
 import com.android.server.wifi.hotspot2.NetworkDetail;
 import com.android.server.wifi.util.InformationElementUtil;
 import com.android.server.wifi.util.NativeUtil;
@@ -41,14 +45,17 @@
 import com.android.server.wifi.wificond.PnoSettings;
 import com.android.server.wifi.wificond.SingleScanSettings;
 
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * This class provides methods for WifiNative to send control commands to wificond.
  * NOTE: This class should only be used from WifiNative.
  */
-public class WificondControl {
+public class WificondControl implements IBinder.DeathRecipient {
     private boolean mVerboseLoggingEnabled = false;
 
     private static final String TAG = "WificondControl";
@@ -65,26 +72,31 @@
 
     // Cached wificond binder handlers.
     private IWificond mWificond;
-    private IClientInterface mClientInterface;
-    private IApInterface mApInterface;
-    private IWifiScannerImpl mWificondScanner;
-    private IScanEvent mScanEventHandler;
-    private IPnoScanEvent mPnoScanEventHandler;
-
-    private String mClientInterfaceName;
-
+    private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>();
+    private HashMap<String, IApInterface> mApInterfaces = new HashMap<>();
+    private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>();
+    private HashMap<String, IScanEvent> mScanEventHandlers = new HashMap<>();
+    private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
+    private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>();
+    private WifiNative.WificondDeathEventHandler mDeathEventHandler;
 
     private class ScanEventHandler extends IScanEvent.Stub {
+        private String mIfaceName;
+
+        ScanEventHandler(@NonNull String ifaceName) {
+            mIfaceName = ifaceName;
+        }
+
         @Override
         public void OnScanResultReady() {
             Log.d(TAG, "Scan result ready event");
-            mWifiMonitor.broadcastScanResultEvent(mClientInterfaceName);
+            mWifiMonitor.broadcastScanResultEvent(mIfaceName);
         }
 
         @Override
         public void OnScanFailed() {
             Log.d(TAG, "Scan failed event");
-            mWifiMonitor.broadcastScanFailedEvent(mClientInterfaceName);
+            mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
         }
     }
 
@@ -96,10 +108,16 @@
     }
 
     private class PnoScanEventHandler extends IPnoScanEvent.Stub {
+        private String mIfaceName;
+
+        PnoScanEventHandler(@NonNull String ifaceName) {
+            mIfaceName = ifaceName;
+        }
+
         @Override
         public void OnPnoNetworkFound() {
             Log.d(TAG, "Pno scan result event");
-            mWifiMonitor.broadcastPnoScanResultEvent(mClientInterfaceName);
+            mWifiMonitor.broadcastPnoScanResultEvent(mIfaceName);
             mWifiInjector.getWifiMetrics().incrementPnoFoundNetworkEventCount();
         }
 
@@ -122,6 +140,38 @@
         }
     }
 
+    /**
+     * Listener for AP Interface events.
+     */
+    private class ApInterfaceEventCallback extends IApInterfaceEventCallback.Stub {
+        private SoftApListener mSoftApListener;
+
+        ApInterfaceEventCallback(SoftApListener listener) {
+            mSoftApListener = listener;
+        }
+
+        @Override
+        public void onNumAssociatedStationsChanged(int numStations) {
+            mSoftApListener.onNumAssociatedStationsChanged(numStations);
+        }
+    }
+
+    /**
+     * Called by the binder subsystem upon remote object death.
+     * Invoke all the register death handlers and clear state.
+     */
+    @Override
+    public void binderDied() {
+        Log.e(TAG, "Wificond died!");
+        if (mDeathEventHandler != null) {
+            mDeathEventHandler.onDeath();
+        }
+        clearState();
+        // Invalidate the global wificond handle on death. Will be refereshed
+        // on the next setup call.
+        mWificond = null;
+    }
+
     /** Enable or disable verbose logging of WificondControl.
      *  @param enable True to enable verbose logging. False to disable verbose logging.
      */
@@ -130,15 +180,64 @@
     }
 
     /**
-    * Setup driver for client mode via wificond.
-    * @return An IClientInterface as wificond client interface binder handler.
-    * Returns null on failure.
-    */
-    public IClientInterface setupDriverForClientMode(@NonNull String ifaceName) {
-        Log.d(TAG, "Setting up driver for client mode");
+     * Registers a death notification for wificond.
+     * @return Returns true on success.
+     */
+    public boolean registerDeathHandler(@NonNull WifiNative.WificondDeathEventHandler handler) {
+        if (mDeathEventHandler != null) {
+            Log.e(TAG, "Death handler already present");
+            return false;
+        }
+        mDeathEventHandler = handler;
+        return true;
+    }
+
+    /**
+     * Deregisters a death notification for wificond.
+     * @return Returns true on success.
+     */
+    public boolean deregisterDeathHandler() {
+        if (mDeathEventHandler == null) {
+            Log.e(TAG, "No Death handler present");
+            return false;
+        }
+        mDeathEventHandler = null;
+        return true;
+    }
+
+    /**
+     * Helper method to retrieve the global wificond handle and register for
+     * death notifications.
+     */
+    private boolean retrieveWificondAndRegisterForDeath() {
+        if (mWificond != null) {
+            Log.d(TAG, "Wificond handle already retrieved");
+            // We already have a wificond handle.
+            return true;
+        }
         mWificond = mWifiInjector.makeWificond();
         if (mWificond == null) {
             Log.e(TAG, "Failed to get reference to wificond");
+            return false;
+        }
+        try {
+            mWificond.asBinder().linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register death notification for wificond");
+            // The remote has already died.
+            return false;
+        }
+        return true;
+    }
+
+    /**
+    * Setup interface for client mode via wificond.
+    * @return An IClientInterface as wificond client interface binder handler.
+    * Returns null on failure.
+    */
+    public IClientInterface setupInterfaceForClientMode(@NonNull String ifaceName) {
+        Log.d(TAG, "Setting up interface for client mode");
+        if (!retrieveWificondAndRegisterForDeath()) {
             return null;
         }
 
@@ -157,19 +256,21 @@
         Binder.allowBlocking(clientInterface.asBinder());
 
         // Refresh Handlers
-        mClientInterface = clientInterface;
+        mClientInterfaces.put(ifaceName, clientInterface);
         try {
-            mClientInterfaceName = clientInterface.getInterfaceName();
-            mWificondScanner = mClientInterface.getWifiScannerImpl();
-            if (mWificondScanner == null) {
+            IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
+            if (wificondScanner == null) {
                 Log.e(TAG, "Failed to get WificondScannerImpl");
                 return null;
             }
-            Binder.allowBlocking(mWificondScanner.asBinder());
-            mScanEventHandler = new ScanEventHandler();
-            mWificondScanner.subscribeScanEvents(mScanEventHandler);
-            mPnoScanEventHandler = new PnoScanEventHandler();
-            mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);
+            mWificondScanners.put(ifaceName, wificondScanner);
+            Binder.allowBlocking(wificondScanner.asBinder());
+            ScanEventHandler scanEventHandler = new ScanEventHandler(ifaceName);
+            mScanEventHandlers.put(ifaceName,  scanEventHandler);
+            wificondScanner.subscribeScanEvents(scanEventHandler);
+            PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(ifaceName);
+            mPnoScanEventHandlers.put(ifaceName,  pnoScanEventHandler);
+            wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
         }
@@ -178,15 +279,49 @@
     }
 
     /**
-    * Setup driver for softAp mode via wificond.
+     * Teardown a specific STA interface configured in wificond.
+     *
+     * @return Returns true on success.
+     */
+    public boolean tearDownClientInterface(@NonNull String ifaceName) {
+        boolean success;
+        try {
+            IWifiScannerImpl scannerImpl = mWificondScanners.get(ifaceName);
+            if (scannerImpl != null) {
+                scannerImpl.unsubscribeScanEvents();
+                scannerImpl.unsubscribePnoScanEvents();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to unsubscribe wificond scanner due to remote exception");
+            return false;
+        }
+
+        try {
+            success = mWificond.tearDownClientInterface(ifaceName);
+        } catch (RemoteException e1) {
+            Log.e(TAG, "Failed to teardown client interface due to remote exception");
+            return false;
+        }
+        if (!success) {
+            Log.e(TAG, "Failed to teardown client interface");
+            return false;
+        }
+
+        mClientInterfaces.remove(ifaceName);
+        mWificondScanners.remove(ifaceName);
+        mScanEventHandlers.remove(ifaceName);
+        mPnoScanEventHandlers.remove(ifaceName);
+        return true;
+    }
+
+    /**
+    * Setup interface for softAp mode via wificond.
     * @return An IApInterface as wificond Ap interface binder handler.
     * Returns null on failure.
     */
-    public IApInterface setupDriverForSoftApMode(@NonNull String ifaceName) {
-        Log.d(TAG, "Setting up driver for soft ap mode");
-        mWificond = mWifiInjector.makeWificond();
-        if (mWificond == null) {
-            Log.e(TAG, "Failed to get reference to wificond");
+    public IApInterface setupInterfaceForSoftApMode(@NonNull String ifaceName) {
+        Log.d(TAG, "Setting up interface for soft ap mode");
+        if (!retrieveWificondAndRegisterForDeath()) {
             return null;
         }
 
@@ -205,12 +340,33 @@
         Binder.allowBlocking(apInterface.asBinder());
 
         // Refresh Handlers
-        mApInterface = apInterface;
-
+        mApInterfaces.put(ifaceName, apInterface);
         return apInterface;
     }
 
     /**
+     * Teardown a specific AP interface configured in wificond.
+     *
+     * @return Returns true on success.
+     */
+    public boolean tearDownSoftApInterface(@NonNull String ifaceName) {
+        boolean success;
+        try {
+            success = mWificond.tearDownApInterface(ifaceName);
+        } catch (RemoteException e1) {
+            Log.e(TAG, "Failed to teardown AP interface due to remote exception");
+            return false;
+        }
+        if (!success) {
+            Log.e(TAG, "Failed to teardown AP interface");
+            return false;
+        }
+        mApInterfaces.remove(ifaceName);
+        mApInterfaceListeners.remove(ifaceName);
+        return true;
+    }
+
+    /**
     * Teardown all interfaces configured in wificond.
     * @return Returns true on success.
     */
@@ -218,26 +374,17 @@
         Log.d(TAG, "tearing down interfaces in wificond");
         // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
         // could be used to cleanup before we setup any interfaces.
-        mWificond = mWifiInjector.makeWificond();
-        if (mWificond == null) {
-            Log.e(TAG, "Failed to get reference to wificond");
+        if (!retrieveWificondAndRegisterForDeath()) {
             return false;
         }
 
         try {
-            if (mWificondScanner != null) {
-                mWificondScanner.unsubscribeScanEvents();
-                mWificondScanner.unsubscribePnoScanEvents();
+            for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
+                entry.getValue().unsubscribeScanEvents();
+                entry.getValue().unsubscribePnoScanEvents();
             }
             mWificond.tearDownInterfaces();
-
-            // Refresh handlers
-            mClientInterface = null;
-            mWificondScanner = null;
-            mPnoScanEventHandler = null;
-            mScanEventHandler = null;
-            mApInterface = null;
-
+            clearState();
             return true;
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to tear down interfaces due to remote exception");
@@ -246,13 +393,17 @@
         return false;
     }
 
+    /** Helper function to look up the interface handle using name */
+    private IClientInterface getClientInterface(@NonNull String ifaceName) {
+        return mClientInterfaces.get(ifaceName);
+    }
+
     /**
     * Disable wpa_supplicant via wificond.
     * @return Returns true on success.
     */
     public boolean disableSupplicant() {
-        if (mWificond == null) {
-            Log.e(TAG, "No valid handler");
+        if (!retrieveWificondAndRegisterForDeath()) {
             return false;
         }
         try {
@@ -268,11 +419,9 @@
     * @return Returns true on success.
     */
     public boolean enableSupplicant() {
-        if (mWificond == null) {
-            Log.e(TAG, "No valid wificond handler");
+        if (!retrieveWificondAndRegisterForDeath()) {
             return false;
         }
-
         try {
             return mWificond.enableSupplicant();
         } catch (RemoteException e) {
@@ -282,19 +431,21 @@
     }
 
     /**
-    * Request signal polling to wificond.
-    * Returns an SignalPollResult object.
-    * Returns null on failure.
-    */
-    public WifiNative.SignalPollResult signalPoll() {
-        if (mClientInterface == null) {
+     * Request signal polling to wificond.
+     * @param ifaceName Name of the interface.
+     * Returns an SignalPollResult object.
+     * Returns null on failure.
+     */
+    public WifiNative.SignalPollResult signalPoll(@NonNull String ifaceName) {
+        IClientInterface iface = getClientInterface(ifaceName);
+        if (iface == null) {
             Log.e(TAG, "No valid wificond client interface handler");
             return null;
         }
 
         int[] resultArray;
         try {
-            resultArray = mClientInterface.signalPoll();
+            resultArray = iface.signalPoll();
             if (resultArray == null || resultArray.length != 3) {
                 Log.e(TAG, "Invalid signal poll result from wificond");
                 return null;
@@ -311,19 +462,21 @@
     }
 
     /**
-    * Fetch TX packet counters on current connection from wificond.
-    * Returns an TxPacketCounters object.
-    * Returns null on failure.
-    */
-    public WifiNative.TxPacketCounters getTxPacketCounters() {
-        if (mClientInterface == null) {
+     * Fetch TX packet counters on current connection from wificond.
+     * @param ifaceName Name of the interface.
+     * Returns an TxPacketCounters object.
+     * Returns null on failure.
+     */
+    public WifiNative.TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
+        IClientInterface iface = getClientInterface(ifaceName);
+        if (iface == null) {
             Log.e(TAG, "No valid wificond client interface handler");
             return null;
         }
 
         int[] resultArray;
         try {
-            resultArray = mClientInterface.getPacketCounters();
+            resultArray = iface.getPacketCounters();
             if (resultArray == null || resultArray.length != 2) {
                 Log.e(TAG, "Invalid signal poll result from wificond");
                 return null;
@@ -338,23 +491,30 @@
         return counters;
     }
 
+    /** Helper function to look up the scanner impl handle using name */
+    private IWifiScannerImpl getScannerImpl(@NonNull String ifaceName) {
+        return mWificondScanners.get(ifaceName);
+    }
+
     /**
     * Fetch the latest scan result from kernel via wificond.
+    * @param ifaceName Name of the interface.
     * @return Returns an ArrayList of ScanDetail.
     * Returns an empty ArrayList on failure.
     */
-    public ArrayList<ScanDetail> getScanResults(int scanType) {
+    public ArrayList<ScanDetail> getScanResults(@NonNull String ifaceName, int scanType) {
         ArrayList<ScanDetail> results = new ArrayList<>();
-        if (mWificondScanner == null) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return results;
         }
         try {
             NativeScanResult[] nativeResults;
             if (scanType == SCAN_TYPE_SINGLE_SCAN) {
-                nativeResults = mWificondScanner.getScanResults();
+                nativeResults = scannerImpl.getScanResults();
             } else {
-                nativeResults = mWificondScanner.getPnoScanResults();
+                nativeResults = scannerImpl.getPnoScanResults();
             }
             for (NativeScanResult result : nativeResults) {
                 WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
@@ -409,12 +569,16 @@
 
     /**
      * Start a scan using wificond for the given parameters.
+     * @param ifaceName Name of the interface.
      * @param freqs list of frequencies to scan for, if null scan all supported channels.
      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
      * @return Returns true on success.
      */
-    public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
-        if (mWificondScanner == null) {
+    public boolean scan(@NonNull String ifaceName,
+                        Set<Integer> freqs,
+                        Set<String> hiddenNetworkSSIDs) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return false;
         }
@@ -443,7 +607,7 @@
         }
 
         try {
-            return mWificondScanner.scan(settings);
+            return scannerImpl.scan(settings);
         } catch (RemoteException e1) {
             Log.e(TAG, "Failed to request scan due to remote exception");
         }
@@ -452,11 +616,13 @@
 
     /**
      * Start PNO scan.
+     * @param ifaceName Name of the interface.
      * @param pnoSettings Pno scan configuration.
      * @return true on success.
      */
-    public boolean startPnoScan(WifiNative.PnoSettings pnoSettings) {
-        if (mWificondScanner == null) {
+    public boolean startPnoScan(@NonNull String ifaceName, WifiNative.PnoSettings pnoSettings) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return false;
         }
@@ -482,7 +648,7 @@
         }
 
         try {
-            boolean success = mWificondScanner.startPnoScan(settings);
+            boolean success = scannerImpl.startPnoScan(settings);
             mWifiInjector.getWifiMetrics().incrementPnoScanStartAttempCount();
             if (!success) {
                 mWifiInjector.getWifiMetrics().incrementPnoScanFailedCount();
@@ -496,15 +662,17 @@
 
     /**
      * Stop PNO scan.
+     * @param ifaceName Name of the interface.
      * @return true on success.
      */
-    public boolean stopPnoScan() {
-        if (mWificondScanner == null) {
+    public boolean stopPnoScan(@NonNull String ifaceName) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return false;
         }
         try {
-            return mWificondScanner.stopPnoScan();
+            return scannerImpl.stopPnoScan();
         } catch (RemoteException e1) {
             Log.e(TAG, "Failed to stop pno scan due to remote exception");
         }
@@ -513,20 +681,21 @@
 
     /**
      * Abort ongoing single scan.
+     * @param ifaceName Name of the interface.
      */
-    public void abortScan() {
-        if (mWificondScanner == null) {
+    public void abortScan(@NonNull String ifaceName) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return;
         }
         try {
-            mWificondScanner.abortScan();
+            scannerImpl.abortScan();
         } catch (RemoteException e1) {
             Log.e(TAG, "Failed to request abortScan due to remote exception");
         }
     }
 
-
     /**
      * Query the list of valid frequencies for the provided band.
      * The result depends on the on the country code that has been set.
@@ -560,4 +729,115 @@
         }
         return null;
     }
+
+    /** Helper function to look up the interface handle using name */
+    private IApInterface getApInterface(@NonNull String ifaceName) {
+        return mApInterfaces.get(ifaceName);
+    }
+
+    /**
+     * Start Soft AP operation using the provided configuration.
+     *
+     * @param ifaceName Name of the interface.
+     * @param config Configuration to use for the soft ap created.
+     * @param listener Callback for AP events.
+     * @return true on success, false otherwise.
+     */
+    public boolean startSoftAp(@NonNull String ifaceName,
+                               WifiConfiguration config,
+                               SoftApListener listener) {
+        IApInterface iface = getApInterface(ifaceName);
+        if (iface == null) {
+            Log.e(TAG, "No valid ap interface handler");
+            return false;
+        }
+        int encryptionType = getIApInterfaceEncryptionType(config);
+        try {
+            // TODO(b/67745880) Note that config.SSID is intended to be either a
+            // hex string or "double quoted".
+            // However, it seems that whatever is handing us these configurations does not obey
+            // this convention.
+            boolean success = iface.writeHostapdConfig(
+                    config.SSID.getBytes(StandardCharsets.UTF_8), config.hiddenSSID,
+                    config.apChannel, encryptionType,
+                    (config.preSharedKey != null)
+                            ? config.preSharedKey.getBytes(StandardCharsets.UTF_8)
+                            : new byte[0]);
+            if (!success) {
+                Log.e(TAG, "Failed to write hostapd configuration");
+                return false;
+            }
+            IApInterfaceEventCallback  callback = new ApInterfaceEventCallback(listener);
+            mApInterfaceListeners.put(ifaceName, callback);
+            success = iface.startHostapd(callback);
+            if (!success) {
+                Log.e(TAG, "Failed to start hostapd.");
+                return false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in starting soft AP: " + e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Stop the ongoing Soft AP operation.
+     *
+     * @param ifaceName Name of the interface.
+     * @return true on success, false otherwise.
+     */
+    public boolean stopSoftAp(@NonNull String ifaceName) {
+        IApInterface iface = getApInterface(ifaceName);
+        if (iface == null) {
+            Log.e(TAG, "No valid ap interface handler");
+            return false;
+        }
+        try {
+            boolean success = iface.stopHostapd();
+            if (!success) {
+                Log.e(TAG, "Failed to stop hostapd.");
+                return false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in stopping soft AP: " + e);
+            return false;
+        }
+        mApInterfaceListeners.remove(ifaceName);
+        return true;
+    }
+
+    private static int getIApInterfaceEncryptionType(WifiConfiguration localConfig) {
+        int encryptionType;
+        switch (localConfig.getAuthType()) {
+            case WifiConfiguration.KeyMgmt.NONE:
+                encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
+                break;
+            case WifiConfiguration.KeyMgmt.WPA_PSK:
+                encryptionType = IApInterface.ENCRYPTION_TYPE_WPA;
+                break;
+            case WifiConfiguration.KeyMgmt.WPA2_PSK:
+                encryptionType = IApInterface.ENCRYPTION_TYPE_WPA2;
+                break;
+            default:
+                // We really shouldn't default to None, but this was how NetworkManagementService
+                // used to do this.
+                encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
+                break;
+        }
+        return encryptionType;
+    }
+
+    /**
+     * Clear all internal handles.
+     */
+    private void clearState() {
+        // Refresh handlers
+        mClientInterfaces.clear();
+        mWificondScanners.clear();
+        mPnoScanEventHandlers.clear();
+        mScanEventHandlers.clear();
+        mApInterfaces.clear();
+        mApInterfaceListeners.clear();
+    }
 }
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
index d6bec5f..f0a86bb 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -83,7 +83,7 @@
                             awareIsDown();
                         }
                     }
-                }, null);
+                }, mHandler);
         if (mHalDeviceManager.isStarted()) {
             mHalDeviceManager.registerInterfaceAvailableForRequestListener(
                     IfaceType.NAN, mInterfaceAvailableForRequestListener, mHandler);
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index 421d9ac..62cc3ec 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -329,8 +329,8 @@
             enforceNetworkStackPermission();
         }
 
-        if (message != null
-                && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
+        if (message != null && message.length
+                > mStateManager.getCharacteristics().getMaxServiceSpecificInfoLength()) {
             throw new IllegalArgumentException(
                     "Message length longer than supported by device characteristics");
         }
diff --git a/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java b/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java
new file mode 100644
index 0000000..f1bf117
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/OsuNetworkConnection.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Responsible for setup/monitor on Wi-Fi state and connection to the OSU AP.
+ */
+public class OsuNetworkConnection {
+    private static final String TAG = "OsuNetworkConnection";
+
+    private final Context mContext;
+
+    private boolean mVerboseLoggingEnabled = false;
+    private WifiManager mWifiManager;
+    private Callbacks mCallbacks;
+    private boolean mConnected = false;
+    private int mNetworkId = -1;
+    private boolean mWifiEnabled = false;
+
+    /**
+     * Callbacks on Wi-Fi connection state changes.
+     */
+    public interface Callbacks {
+        /**
+         * Invoked when network connection is established with IP connectivity.
+         *
+         * @param network {@link Network} associated with the connected network.
+         */
+        void onConnected(Network network);
+
+        /**
+         * Invoked when the targeted network is disconnected.
+         */
+        void onDisconnected();
+
+        /**
+         * Invoked when a timer tracking connection request is not reset by successfull connection.
+         */
+        void onTimeOut();
+
+        /**
+         * Invoked when Wifi is enabled.
+         */
+        void onWifiEnabled();
+
+        /**
+         * Invoked when Wifi is disabled.
+         */
+        void onWifiDisabled();
+    }
+
+    /**
+     * Create an instance of {@link NetworkConnection} for the specified Wi-Fi network.
+     * @param context The application context
+     */
+    public OsuNetworkConnection(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Called to initialize tracking of wifi state and network events by registering for the
+     * corresponding intents.
+     */
+    public void init(Handler handler) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                    handleNetworkStateChanged(
+                            intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO),
+                            intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
+                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                    int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                            WifiManager.WIFI_STATE_UNKNOWN);
+                    handleWifiStateChanged(state);
+                }
+            }
+        };
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mContext.registerReceiver(receiver, filter, null, handler);
+        mWifiEnabled = mWifiManager.isWifiEnabled();
+    }
+
+    /**
+     * Disconnect, if required in the two cases
+     * - still connected to the OSU AP
+     * - connection to OSU AP was requested and in progress
+     */
+    public void disconnectIfNeeded() {
+        if (mNetworkId < 0) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "No connection to tear down");
+            }
+            return;
+        }
+        mWifiManager.removeNetwork(mNetworkId);
+        mNetworkId = -1;
+        mConnected = false;
+        if (mCallbacks != null) {
+            mCallbacks.onDisconnected();
+        }
+    }
+
+    /**
+     * Register for network and Wifi state events
+     * @param callbacks The callbacks to be invoked on network change events
+     */
+    public void setEventCallback(Callbacks callbacks) {
+        mCallbacks = callbacks;
+    }
+
+    /**
+     * Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi
+     * network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network).
+     * When network access identifier is provided, OSEN is used.
+     *
+     * @param ssid The SSID to connect to
+     * @param nai Network access identifier of the network
+     *
+     * @return boolean true if connection was successfully initiated
+     */
+    public boolean connect(WifiSsid ssid, String nai) {
+        if (mConnected) {
+            if (mVerboseLoggingEnabled) {
+                // Already connected
+                Log.v(TAG, "Connect called twice");
+            }
+            return true;
+        }
+        if (!mWifiManager.isWifiEnabled()) {
+            Log.w(TAG, "Wifi is not enabled");
+            return false;
+        }
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"" + ssid.toString() + "\"";
+        if (TextUtils.isEmpty(nai)) {
+            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        } else {
+            // TODO(sohanirao): Handle OSEN.
+            Log.w(TAG, "OSEN not supported");
+            return false;
+        }
+        mNetworkId = mWifiManager.addNetwork(config);
+        if (mNetworkId < 0) {
+            Log.e(TAG, "Unable to add network");
+            return false;
+        }
+        if (!mWifiManager.enableNetwork(mNetworkId, true)) {
+            Log.e(TAG, "Unable to enable network " + mNetworkId);
+            disconnectIfNeeded();
+            return false;
+        }
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "Current network ID " + mNetworkId);
+        }
+        // TODO(sohanirao): setup alarm to time out the connection attempt.
+        return true;
+    }
+
+    /**
+     * Method to update logging level in this class
+     * @param verbose more than 0 enables verbose logging
+     */
+    public void enableVerboseLogging(int verbose) {
+        mVerboseLoggingEnabled = verbose > 0 ? true : false;
+    }
+
+    /**
+     * Handle network state changed events.
+     *
+     * @param networkInfo {@link NetworkInfo} indicating the current network state
+     * @param wifiInfo {@link WifiInfo} associated with the current network when connected
+     */
+    private void handleNetworkStateChanged(NetworkInfo networkInfo, WifiInfo wifiInfo) {
+        if (networkInfo == null) {
+            Log.w(TAG, "NetworkInfo not provided for network state changed event");
+            return;
+        }
+        switch (networkInfo.getDetailedState()) {
+            case CONNECTED:
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "Connected event received");
+                }
+                if (wifiInfo == null) {
+                    Log.w(TAG, "WifiInfo not provided for network state changed event");
+                    return;
+                }
+                handleConnectedEvent(wifiInfo);
+                break;
+            case DISCONNECTED:
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "Disconnected event received");
+                }
+                disconnectIfNeeded();
+                break;
+            default:
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "Ignore uninterested state: " + networkInfo.getDetailedState());
+                }
+                break;
+        }
+    }
+
+    /**
+     * Handle network connected event.
+     *
+     * @param wifiInfo {@link WifiInfo} associated with the current connection
+     */
+    private void handleConnectedEvent(WifiInfo wifiInfo) {
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "handleConnectedEvent " + wifiInfo.getNetworkId());
+        }
+        if (wifiInfo.getNetworkId() != mNetworkId) {
+            disconnectIfNeeded();
+            return;
+        }
+        if (!mConnected) {
+            mConnected = true;
+            if (mCallbacks != null) {
+                mCallbacks.onConnected(mWifiManager.getCurrentNetwork());
+            }
+        }
+    }
+
+    /**
+     * Handle Wifi state change event
+     */
+    private void handleWifiStateChanged(int state) {
+        if (state == WifiManager.WIFI_STATE_DISABLED && mWifiEnabled) {
+            mWifiEnabled = false;
+            if (mCallbacks != null) mCallbacks.onWifiDisabled();
+        }
+        if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiEnabled) {
+            mWifiEnabled = true;
+            if (mCallbacks != null) mCallbacks.onWifiEnabled();
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 3580c83..fec3dd8 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -33,8 +33,10 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -99,6 +101,7 @@
     private final WifiConfigManager mWifiConfigManager;
     private final CertificateVerifier mCertVerifier;
     private final WifiMetrics mWifiMetrics;
+    private final PasspointProvisioner mPasspointProvisioner;
 
     // Counter used for assigning unique identifier to each provider.
     private long mProviderIndex;
@@ -212,10 +215,27 @@
         mProviderIndex = 0;
         wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigStoreData(
                 mKeyStore, mSimAccessor, new DataSourceHandler()));
+        mPasspointProvisioner = objectFactory.makePasspointProvisioner(context,
+                objectFactory.makeOsuNetworkConnection(context));
         sPasspointManager = this;
     }
 
     /**
+     * Initializes the provisioning flow with a looper
+     */
+    public void initializeProvisioner(Looper looper) {
+        mPasspointProvisioner.init(looper);
+    }
+
+    /**
+     * Enable verbose logging
+     * @param verbose more than 0 enables verbose logging
+     */
+    public void enableVerboseLogging(int verbose) {
+        mPasspointProvisioner.enableVerboseLogging(verbose);
+    }
+
+    /**
      * Add or update a Passpoint provider with the given configuration.
      *
      * Each provider is uniquely identified by its FQDN (Fully Qualified Domain Name).
@@ -704,4 +724,16 @@
         mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider);
         return true;
     }
+
+    /**
+     * Start the subscription provisioning flow with a provider.
+     * @param callingUid integer indicating the uid of the caller
+     * @param provider {@link OsuProvider} the provider to subscribe to
+     * @param callback {@link IProvisioningCallback} callback to update status to the caller
+     * @return boolean return value from the provisioning method
+     */
+    public boolean startSubscriptionProvisioning(int callingUid, OsuProvider provider,
+            IProvisioningCallback callback) {
+        return mPasspointProvisioner.startSubscriptionProvisioning(callingUid, provider, callback);
+    }
 }
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
index c41c49a..71fc47a 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi.hotspot2;
 
+import android.content.Context;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 
 import com.android.server.wifi.Clock;
@@ -95,4 +96,25 @@
     public CertificateVerifier makeCertificateVerifier() {
         return new CertificateVerifier();
     }
+
+    /**
+     * Create an instance of {@link PasspointProvisioner}.
+     *
+     * @param context
+     * @return {@link PasspointProvisioner}
+     */
+    public PasspointProvisioner makePasspointProvisioner(Context context,
+            OsuNetworkConnection osuNetworkConnection) {
+        return new PasspointProvisioner(context, osuNetworkConnection);
+    }
+
+    /**
+     * Create an instance of {@link OsuNetworkConnection}.
+     *
+     * @param context
+     * @return {@link OsuNetworkConnection}
+     */
+    public OsuNetworkConnection makeOsuNetworkConnection(Context context) {
+        return new OsuNetworkConnection(context);
+    }
 }
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
new file mode 100644
index 0000000..d3bb773
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvisioner.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.content.Context;
+import android.net.Network;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.net.wifi.hotspot2.ProvisioningCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Provides methods to carry out provisioning flow
+ */
+public class PasspointProvisioner {
+    private static final String TAG = "PasspointProvisioner";
+
+    private static final int PROVISIONING_STATUS = 0;
+    private static final int PROVISIONING_FAILURE = 1;
+
+    private final Context mContext;
+    private final ProvisioningStateMachine mProvisioningStateMachine;
+    private final OsuNetworkCallbacks mOsuNetworkCallbacks;
+    private final OsuNetworkConnection mOsuNetworkConnection;
+
+    private int mCallingUid;
+    private boolean mVerboseLoggingEnabled = false;
+
+    PasspointProvisioner(Context context, OsuNetworkConnection osuNetworkConnection) {
+        mContext = context;
+        mOsuNetworkConnection = osuNetworkConnection;
+        mProvisioningStateMachine = new ProvisioningStateMachine();
+        mOsuNetworkCallbacks = new OsuNetworkCallbacks();
+    }
+
+    /**
+     * Sets up for provisioning
+     * @param looper Looper on which the Provisioning state machine will run
+     */
+    public void init(Looper looper) {
+        mProvisioningStateMachine.start(new Handler(looper));
+        mOsuNetworkConnection.init(mProvisioningStateMachine.getHandler());
+    }
+
+    /**
+     * Enable verbose logging to help debug failures
+     * @param level integer indicating verbose logging enabled if > 0
+     */
+    public void enableVerboseLogging(int level) {
+        mVerboseLoggingEnabled = (level > 0) ? true : false;
+        mOsuNetworkConnection.enableVerboseLogging(level);
+    }
+
+    /**
+     * Start provisioning flow with a given provider.
+     * @param callingUid calling uid.
+     * @param provider {@link OsuProvider} to provision with.
+     * @param callback {@link IProvisioningCallback} to provide provisioning status.
+     * @return boolean value, true if provisioning was started, false otherwise.
+     *
+     * Implements HS2.0 provisioning flow with a given HS2.0 provider.
+     */
+    public boolean startSubscriptionProvisioning(int callingUid, OsuProvider provider,
+            IProvisioningCallback callback) {
+        mCallingUid = callingUid;
+
+        Log.v(TAG, "Provisioning started with " + provider.toString());
+
+        mProvisioningStateMachine.getHandler().post(() -> {
+            mProvisioningStateMachine.startProvisioning(provider, callback);
+        });
+
+        return true;
+    }
+
+    /**
+     * Handles the provisioning flow state transitions
+     */
+    class ProvisioningStateMachine {
+        private static final String TAG = "ProvisioningStateMachine";
+
+        private static final int DEFAULT_STATE                             = 0;
+        private static final int INITIAL_STATE                             = 1;
+        private static final int WAITING_TO_CONNECT                        = 2;
+        private static final int OSU_AP_CONNECTED                          = 3;
+        private static final int FAILED_STATE                              = 4;
+
+        private OsuProvider mOsuProvider;
+        private IProvisioningCallback mProvisioningCallback;
+        private Handler mHandler;
+        private int mState;
+
+        ProvisioningStateMachine() {
+            mState = DEFAULT_STATE;
+        }
+
+        /**
+         * Initializes and starts the state machine with a handler to handle incoming events
+         */
+        public void start(Handler handler) {
+            mHandler = handler;
+            changeState(INITIAL_STATE);
+        }
+
+        /**
+         * Returns the handler on which a runnable can be posted
+         * @return Handler State Machine's handler
+         */
+        public Handler getHandler() {
+            return mHandler;
+        }
+
+        /**
+         * Start Provisioning with the Osuprovider and invoke callbacks
+         * @param provider OsuProvider to provision with
+         * @param callback IProvisioningCallback to invoke callbacks on
+         */
+        public void startProvisioning(OsuProvider provider, IProvisioningCallback callback) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "startProvisioning received in state=" + mState);
+            }
+            if (mState != INITIAL_STATE) {
+                Log.v(TAG, "State Machine needs to be reset before starting provisioning");
+                resetStateMachine();
+            }
+            mProvisioningCallback = callback;
+            mOsuProvider = provider;
+
+            // Register for network and wifi state events during provisioning flow
+            mOsuNetworkConnection.setEventCallback(mOsuNetworkCallbacks);
+
+            if (mOsuNetworkConnection.connect(mOsuProvider.getOsuSsid(),
+                    mOsuProvider.getNetworkAccessIdentifier())) {
+                invokeProvisioningCallback(PROVISIONING_STATUS,
+                        ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
+                changeState(WAITING_TO_CONNECT);
+            } else {
+                invokeProvisioningCallback(PROVISIONING_FAILURE,
+                        ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
+                enterFailedState();
+            }
+        }
+
+        /**
+         * Handle Wifi Disable event
+         */
+        public void handleWifiDisabled() {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Wifi Disabled in state=" + mState);
+            }
+            if (mState == INITIAL_STATE || mState == FAILED_STATE) {
+                Log.w(TAG, "Wifi Disable unhandled in state=" + mState);
+                return;
+            }
+            invokeProvisioningCallback(PROVISIONING_FAILURE,
+                    ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
+            enterFailedState();
+        }
+
+        private void resetStateMachine() {
+            // Set to null so that no callbacks are invoked during reset
+            mProvisioningCallback = null;
+            if (mState != INITIAL_STATE || mState != FAILED_STATE) {
+                // Transition through Failed state to clean up
+                enterFailedState();
+            }
+            changeState(INITIAL_STATE);
+        }
+
+        /**
+         * Connected event received
+         * @param network Network object for this connection
+         */
+        public void handleConnectedEvent(Network network) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Connected event received in state=" + mState);
+            }
+            if (mState != WAITING_TO_CONNECT) {
+                // Not waiting for a connection
+                Log.w(TAG, "Connection event unhandled in state=" + mState);
+                return;
+            }
+            invokeProvisioningCallback(PROVISIONING_STATUS,
+                    ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
+            changeState(OSU_AP_CONNECTED);
+        }
+
+        /**
+         * Disconnect event received
+         */
+        public void handleDisconnect() {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "Connection failed in state=" + mState);
+            }
+            if (mState == INITIAL_STATE || mState == FAILED_STATE) {
+                Log.w(TAG, "Disconnect event unhandled in state=" + mState);
+                return;
+            }
+            invokeProvisioningCallback(PROVISIONING_FAILURE,
+                    ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
+            enterFailedState();
+        }
+
+        private void changeState(int nextState) {
+            if (nextState != mState) {
+                if (mVerboseLoggingEnabled) {
+                    Log.v(TAG, "Changing state from " + mState + " -> " + nextState);
+                }
+                mState = nextState;
+            }
+        }
+
+        private void invokeProvisioningCallback(int callbackType, int status) {
+            if (mProvisioningCallback == null) {
+                Log.e(TAG, "Provisioning callback " + callbackType + " with status " + status
+                        + " not invoked, callback is null");
+                return;
+            }
+            try {
+                if (callbackType == PROVISIONING_STATUS) {
+                    mProvisioningCallback.onProvisioningStatus(status);
+                } else {
+                    mProvisioningCallback.onProvisioningFailure(status);
+                }
+            } catch (RemoteException e) {
+                if (callbackType == PROVISIONING_STATUS) {
+                    Log.e(TAG, "Remote Exception while posting Provisioning status " + status);
+                } else {
+                    Log.e(TAG, "Remote Exception while posting Provisioning failure " + status);
+                }
+            }
+        }
+
+        private void enterFailedState() {
+            changeState(FAILED_STATE);
+            mOsuNetworkConnection.setEventCallback(null);
+            mOsuNetworkConnection.disconnectIfNeeded();
+        }
+    }
+
+    /**
+     * Callbacks for network and wifi events
+     */
+    class OsuNetworkCallbacks implements OsuNetworkConnection.Callbacks {
+
+        OsuNetworkCallbacks() {
+        }
+
+        @Override
+        public void onConnected(Network network) {
+            Log.v(TAG, "onConnected to " + network);
+            if (network == null) {
+                mProvisioningStateMachine.getHandler().post(() -> {
+                    mProvisioningStateMachine.handleDisconnect();
+                });
+            } else {
+                mProvisioningStateMachine.getHandler().post(() -> {
+                    mProvisioningStateMachine.handleConnectedEvent(network);
+                });
+            }
+        }
+
+        @Override
+        public void onDisconnected() {
+            Log.v(TAG, "onDisconnected");
+            mProvisioningStateMachine.getHandler().post(() -> {
+                mProvisioningStateMachine.handleDisconnect();
+            });
+        }
+
+        @Override
+        public void onTimeOut() {
+            Log.v(TAG, "Timed out waiting for connection to OSU AP");
+            mProvisioningStateMachine.getHandler().post(() -> {
+                mProvisioningStateMachine.handleDisconnect();
+            });
+        }
+
+        @Override
+        public void onWifiEnabled() {
+            Log.v(TAG, "onWifiEnabled");
+        }
+
+        @Override
+        public void onWifiDisabled() {
+            Log.v(TAG, "onWifiDisabled");
+            mProvisioningStateMachine.getHandler().post(() -> {
+                mProvisioningStateMachine.handleWifiDisabled();
+            });
+        }
+    }
+}
diff --git a/service/java/com/android/server/wifi/rtt/RttNative.java b/service/java/com/android/server/wifi/rtt/RttNative.java
index dd54c24..e085352 100644
--- a/service/java/com/android/server/wifi/rtt/RttNative.java
+++ b/service/java/com/android/server/wifi/rtt/RttNative.java
@@ -27,14 +27,13 @@
 import android.hardware.wifi.V1_0.WifiChannelWidthInMhz;
 import android.hardware.wifi.V1_0.WifiStatus;
 import android.hardware.wifi.V1_0.WifiStatusCode;
-import android.net.wifi.ScanResult;
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.ResponderConfig;
 import android.os.RemoteException;
 import android.util.Log;
 
 import com.android.server.wifi.HalDeviceManager;
-import com.android.server.wifi.util.NativeUtil;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -204,123 +203,116 @@
 
         // Skipping any configurations which have an error (printing out a message).
         // The caller will only get results for valid configurations.
-        for (RangingRequest.RttPeer peer: request.mRttPeers) {
+        for (ResponderConfig responder: request.mRttPeers) {
             RttConfig config = new RttConfig();
 
-            if (peer instanceof RangingRequest.RttPeerAp) {
-                ScanResult scanResult = ((RangingRequest.RttPeerAp) peer).scanResult;
+            System.arraycopy(responder.macAddress.toByteArray(), 0, config.addr, 0,
+                    config.addr.length);
 
-                byte[] addr = NativeUtil.macAddressToByteArray(scanResult.BSSID);
-                if (addr.length != config.addr.length) {
-                    Log.e(TAG, "Invalid configuration: unexpected BSSID length -- " + scanResult);
-                    continue;
-                }
-                System.arraycopy(addr, 0, config.addr, 0, config.addr.length);
+            try {
+                config.type = responder.supports80211mc ? RttType.TWO_SIDED : RttType.ONE_SIDED;
+                config.peer = halRttPeerTypeFromResponderType(responder.responderType);
+                config.channel.width = halChannelWidthFromResponderChannelWidth(
+                        responder.channelWidth);
+                config.channel.centerFreq = responder.frequency;
+                config.channel.centerFreq0 = responder.centerFreq0;
+                config.channel.centerFreq1 = responder.centerFreq1;
+                config.bw = halRttChannelBandwidthFromResponderChannelWidth(responder.channelWidth);
+                config.preamble = halRttPreambleFromResponderPreamble(responder.preamble);
 
-                try {
-                    config.type =
-                            scanResult.is80211mcResponder() ? RttType.TWO_SIDED : RttType.ONE_SIDED;
-                    config.peer = RttPeerType.AP;
-                    config.channel.width = halChannelWidthFromScanResult(
-                            scanResult.channelWidth);
-                    config.channel.centerFreq = scanResult.frequency;
-                    if (scanResult.centerFreq0 > 0) {
-                        config.channel.centerFreq0 = scanResult.centerFreq0;
-                    }
-                    if (scanResult.centerFreq1 > 0) {
-                        config.channel.centerFreq1 = scanResult.centerFreq1;
-                    }
+                config.mustRequestLci = false;
+                config.mustRequestLcr = false;
+                if (config.peer == RttPeerType.NAN) {
+                    config.burstPeriod = 0;
+                    config.numBurst = 0;
+                    config.numFramesPerBurst = 5;
+                    config.numRetriesPerRttFrame = 3;
+                    config.numRetriesPerFtmr = 3;
+                    config.burstDuration = 15;
+                } else { // AP + all non-NAN requests
                     config.burstPeriod = 0;
                     config.numBurst = 0;
                     config.numFramesPerBurst = 8;
                     config.numRetriesPerRttFrame = 0;
                     config.numRetriesPerFtmr = 0;
-                    config.mustRequestLci = false;
-                    config.mustRequestLcr = false;
                     config.burstDuration = 15;
-                    config.bw = halChannelBandwidthFromScanResult(scanResult.channelWidth);
-                    if (config.bw == RttBw.BW_80MHZ || config.bw == RttBw.BW_160MHZ) {
-                        config.preamble = RttPreamble.VHT;
-                    } else {
-                        config.preamble = RttPreamble.HT;
-                    }
-                } catch (IllegalArgumentException e) {
-                    Log.e(TAG, "Invalid configuration: " + e.getMessage());
-                    continue;
                 }
-            } else if (peer instanceof RangingRequest.RttPeerAware) {
-                RangingRequest.RttPeerAware rttPeerAware = (RangingRequest.RttPeerAware) peer;
-
-                if (rttPeerAware.peerMacAddress == null
-                        || rttPeerAware.peerMacAddress.length != config.addr.length) {
-                    Log.e(TAG, "Invalid configuration: null MAC or incorrect length");
-                    continue;
-                }
-                System.arraycopy(rttPeerAware.peerMacAddress, 0, config.addr, 0,
-                        config.addr.length);
-
-                config.type = RttType.TWO_SIDED;
-                config.peer = RttPeerType.NAN;
-                config.channel.width = WifiChannelWidthInMhz.WIDTH_80;
-                config.channel.centerFreq = 5200;
-                config.channel.centerFreq0 = 5210;
-                config.channel.centerFreq1 = 0;
-                config.burstPeriod = 0;
-                config.numBurst = 0;
-                config.numFramesPerBurst = 5;
-                config.numRetriesPerRttFrame = 3;
-                config.numRetriesPerFtmr = 3;
-                config.mustRequestLci = false;
-                config.mustRequestLcr = false;
-                config.burstDuration = 15;
-                config.preamble = RttPreamble.VHT;
-                config.bw = RttBw.BW_80MHZ;
-            } else {
-                Log.e(TAG, "convertRangingRequestToRttConfigs: unknown request type -- "
-                        + peer.getClass().getCanonicalName());
-                return null;
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Invalid configuration: " + e.getMessage());
+                continue;
             }
 
             rttConfigs.add(config);
         }
 
-
         return rttConfigs;
     }
 
-    static int halChannelWidthFromScanResult(int scanResultChannelWidth) {
-        switch (scanResultChannelWidth) {
-            case ScanResult.CHANNEL_WIDTH_20MHZ:
-                return WifiChannelWidthInMhz.WIDTH_20;
-            case ScanResult.CHANNEL_WIDTH_40MHZ:
-                return WifiChannelWidthInMhz.WIDTH_40;
-            case ScanResult.CHANNEL_WIDTH_80MHZ:
-                return WifiChannelWidthInMhz.WIDTH_80;
-            case ScanResult.CHANNEL_WIDTH_160MHZ:
-                return WifiChannelWidthInMhz.WIDTH_160;
-            case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
-                return WifiChannelWidthInMhz.WIDTH_80P80;
+    static int halRttPeerTypeFromResponderType(int responderType) {
+        switch (responderType) {
+            case ResponderConfig.RESPONDER_AP:
+                return RttPeerType.AP;
+            case ResponderConfig.RESPONDER_STA:
+                return RttPeerType.STA;
+            case ResponderConfig.RESPONDER_P2P_GO:
+                return RttPeerType.P2P_GO;
+            case ResponderConfig.RESPONDER_P2P_CLIENT:
+                return RttPeerType.P2P_CLIENT;
+            case ResponderConfig.RESPONDER_AWARE:
+                return RttPeerType.NAN;
             default:
                 throw new IllegalArgumentException(
-                        "halChannelWidthFromScanResult: bad " + scanResultChannelWidth);
+                        "halRttPeerTypeFromResponderType: bad " + responderType);
         }
     }
 
-    static int halChannelBandwidthFromScanResult(int scanResultChannelWidth) {
-        switch (scanResultChannelWidth) {
-            case ScanResult.CHANNEL_WIDTH_20MHZ:
+    static int halChannelWidthFromResponderChannelWidth(int responderChannelWidth) {
+        switch (responderChannelWidth) {
+            case ResponderConfig.CHANNEL_WIDTH_20MHZ:
+                return WifiChannelWidthInMhz.WIDTH_20;
+            case ResponderConfig.CHANNEL_WIDTH_40MHZ:
+                return WifiChannelWidthInMhz.WIDTH_40;
+            case ResponderConfig.CHANNEL_WIDTH_80MHZ:
+                return WifiChannelWidthInMhz.WIDTH_80;
+            case ResponderConfig.CHANNEL_WIDTH_160MHZ:
+                return WifiChannelWidthInMhz.WIDTH_160;
+            case ResponderConfig.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
+                return WifiChannelWidthInMhz.WIDTH_80P80;
+            default:
+                throw new IllegalArgumentException(
+                        "halChannelWidthFromResponderChannelWidth: bad " + responderChannelWidth);
+        }
+    }
+
+    static int halRttChannelBandwidthFromResponderChannelWidth(int responderChannelWidth) {
+        switch (responderChannelWidth) {
+            case ResponderConfig.CHANNEL_WIDTH_20MHZ:
                 return RttBw.BW_20MHZ;
-            case ScanResult.CHANNEL_WIDTH_40MHZ:
+            case ResponderConfig.CHANNEL_WIDTH_40MHZ:
                 return RttBw.BW_40MHZ;
-            case ScanResult.CHANNEL_WIDTH_80MHZ:
+            case ResponderConfig.CHANNEL_WIDTH_80MHZ:
                 return RttBw.BW_80MHZ;
-            case ScanResult.CHANNEL_WIDTH_160MHZ:
+            case ResponderConfig.CHANNEL_WIDTH_160MHZ:
                 return RttBw.BW_160MHZ;
-            case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
+            case ResponderConfig.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
                 return RttBw.BW_160MHZ;
             default:
                 throw new IllegalArgumentException(
-                        "halChannelBandwidthFromScanResult: bad " + scanResultChannelWidth);
+                        "halRttChannelBandwidthFromHalBandwidth: bad " + responderChannelWidth);
+        }
+    }
+
+    static int halRttPreambleFromResponderPreamble(int responderPreamble) {
+        switch (responderPreamble) {
+            case ResponderConfig.PREAMBLE_LEGACY:
+                return RttPreamble.LEGACY;
+            case ResponderConfig.PREAMBLE_HT:
+                return RttPreamble.HT;
+            case ResponderConfig.PREAMBLE_VHT:
+                return RttPreamble.VHT;
+            default:
+                throw new IllegalArgumentException(
+                        "halRttPreambleFromResponderPreamble: bad " + responderPreamble);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/rtt/RttService.java b/service/java/com/android/server/wifi/rtt/RttService.java
index 5c2cec1..afffbee 100644
--- a/service/java/com/android/server/wifi/rtt/RttService.java
+++ b/service/java/com/android/server/wifi/rtt/RttService.java
@@ -43,14 +43,14 @@
 
     @Override
     public void onStart() {
-        Log.i(TAG, "Registering " + Context.WIFI_RTT2_SERVICE);
-        publishBinderService(Context.WIFI_RTT2_SERVICE, mImpl);
+        Log.i(TAG, "Registering " + Context.WIFI_RTT_RANGING_SERVICE);
+        publishBinderService(Context.WIFI_RTT_RANGING_SERVICE, mImpl);
     }
 
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            Log.i(TAG, "Starting " + Context.WIFI_RTT2_SERVICE);
+            Log.i(TAG, "Starting " + Context.WIFI_RTT_RANGING_SERVICE);
 
             WifiInjector wifiInjector = WifiInjector.getInstance();
             if (wifiInjector == null) {
diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
index 401daab..3417f74 100644
--- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
+++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -24,15 +24,15 @@
 import android.content.pm.PackageManager;
 import android.hardware.wifi.V1_0.RttResult;
 import android.hardware.wifi.V1_0.RttStatus;
-import android.net.wifi.ScanResult;
+import android.net.MacAddress;
 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
 import android.net.wifi.aware.IWifiAwareManager;
-import android.net.wifi.aware.PeerHandle;
 import android.net.wifi.rtt.IRttCallback;
 import android.net.wifi.rtt.IWifiRttManager;
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
 import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.ResponderConfig;
 import android.net.wifi.rtt.WifiRttManager;
 import android.os.Binder;
 import android.os.Handler;
@@ -43,15 +43,12 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.Log;
+import android.util.SparseIntArray;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.WakeupMessage;
 import com.android.server.wifi.Clock;
-import com.android.server.wifi.util.NativeUtil;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
-import libcore.util.HexEncoding;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -79,14 +76,16 @@
 
     private RttServiceSynchronized mRttServiceSynchronized;
 
-    @VisibleForTesting
-    public static final String HAL_RANGING_TIMEOUT_TAG = TAG + " HAL Ranging Timeout";
+    /* package */ static final String HAL_RANGING_TIMEOUT_TAG = TAG + " HAL Ranging Timeout";
 
     private static final long HAL_RANGING_TIMEOUT_MS = 5_000; // 5 sec
 
     // TODO: b/69323456 convert to a settable value
     /* package */ static final long BACKGROUND_PROCESS_EXEC_GAP_MS = 1_800_000; // 30 min
 
+    // arbitrary, larger than anything reasonable
+    /* package */ static final int MAX_QUEUED_PER_UID = 20;
+
     public RttServiceImpl(Context context) {
         mContext = context;
     }
@@ -349,24 +348,8 @@
 
         private void cancelRanging(RttRequestInfo rri) {
             ArrayList<byte[]> macAddresses = new ArrayList<>();
-            for (RangingRequest.RttPeer peer : rri.request.mRttPeers) {
-                if (peer instanceof RangingRequest.RttPeerAp) {
-                    ScanResult scanResult =
-                            ((RangingRequest.RttPeerAp) peer).scanResult;
-
-                    byte[] addr = NativeUtil.macAddressToByteArray(scanResult.BSSID);
-                    if (addr.length != 6) {
-                        Log.e(TAG, "Invalid configuration: unexpected BSSID length -- "
-                                + peer);
-                        continue;
-                    }
-                    macAddresses.add(addr);
-                } else if (peer instanceof RangingRequest.RttPeerAware) {
-                    if (((RangingRequest.RttPeerAware) peer).peerMacAddress != null) {
-                        macAddresses.add(
-                                ((RangingRequest.RttPeerAware) peer).peerMacAddress);
-                    }
-                }
+            for (ResponderConfig peer : rri.request.mRttPeers) {
+                macAddresses.add(peer.macAddress.toByteArray());
             }
 
             mRttNative.rangeCancel(rri.cmdId, macAddresses);
@@ -477,6 +460,19 @@
         private void queueRangingRequest(int uid, WorkSource workSource, IBinder binder,
                 IBinder.DeathRecipient dr, String callingPackage, RangingRequest request,
                 IRttCallback callback) {
+            if (isRequestorSpamming(workSource)) {
+                Log.w(TAG,
+                        "Work source " + workSource + " is spamming, dropping request: " + request);
+                binder.unlinkToDeath(dr, 0);
+                try {
+                    callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RttServiceSynchronized.queueRangingRequest: spamming, callback "
+                            + "failed -- " + e);
+                }
+                return;
+            }
+
             RttRequestInfo newRequest = new RttRequestInfo();
             newRequest.uid = uid;
             newRequest.workSource = workSource;
@@ -494,6 +490,30 @@
             executeNextRangingRequestIfPossible(false);
         }
 
+        private boolean isRequestorSpamming(WorkSource ws) {
+            if (VDBG) Log.v(TAG, "isRequestorSpamming: ws" + ws);
+
+            SparseIntArray counts = new SparseIntArray();
+
+            for (RttRequestInfo rri : mRttRequestQueue) {
+                for (int i = 0; i < rri.workSource.size(); ++i) {
+                    int uid = rri.workSource.get(i);
+                    counts.put(uid, counts.get(uid) + 1);
+                }
+            }
+
+            for (int i = 0; i < ws.size(); ++i) {
+                if (counts.get(ws.get(i)) < MAX_QUEUED_PER_UID) {
+                    return false;
+                }
+            }
+
+            if (VDBG) {
+                Log.v(TAG, "isRequestorSpamming: ws=" + ws + ", someone is spamming: " + counts);
+            }
+            return true;
+        }
+
         private void executeNextRangingRequestIfPossible(boolean popFirst) {
             if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: popFirst=" + popFirst);
 
@@ -584,7 +604,7 @@
         /**
          * Perform pre-execution throttling checks:
          * - If all uids in ws are in background then check last execution and block if request is
-         *   more frequent than permitted
+         * more frequent than permitted
          * - If executing (i.e. permitted) then update execution time
          *
          * Returns true to permit execution, false to abort it.
@@ -652,12 +672,9 @@
          */
         private boolean processAwarePeerHandles(RttRequestInfo request) {
             List<Integer> peerIdsNeedingTranslation = new ArrayList<>();
-            for (RangingRequest.RttPeer rttPeer: request.request.mRttPeers) {
-                if (rttPeer instanceof RangingRequest.RttPeerAware) {
-                    RangingRequest.RttPeerAware awarePeer = (RangingRequest.RttPeerAware) rttPeer;
-                    if (awarePeer.peerHandle != null && awarePeer.peerMacAddress == null) {
-                        peerIdsNeedingTranslation.add(awarePeer.peerHandle.peerId);
-                    }
+            for (ResponderConfig rttPeer : request.request.mRttPeers) {
+                if (rttPeer.peerHandle != null && rttPeer.macAddress == null) {
+                    peerIdsNeedingTranslation.add(rttPeer.peerHandle.peerId);
                 }
             }
 
@@ -713,12 +730,10 @@
                         + ", peerIdToMacMap=" + peerIdToMacMap);
             }
 
-            for (RangingRequest.RttPeer rttPeer: request.request.mRttPeers) {
-                if (rttPeer instanceof RangingRequest.RttPeerAware) {
-                    RangingRequest.RttPeerAware awarePeer = (RangingRequest.RttPeerAware) rttPeer;
-                    if (awarePeer.peerHandle != null && awarePeer.peerMacAddress == null) {
-                        awarePeer.peerMacAddress = peerIdToMacMap.get(awarePeer.peerHandle.peerId);
-                    }
+            for (ResponderConfig rttPeer : request.request.mRttPeers) {
+                if (rttPeer.peerHandle != null && rttPeer.macAddress == null) {
+                    rttPeer.macAddress = MacAddress.fromBytes(
+                            peerIdToMacMap.get(rttPeer.peerHandle.peerId));
                 }
             }
 
@@ -780,52 +795,43 @@
          */
         private List<RangingResult> postProcessResults(RangingRequest request,
                 List<RttResult> results) {
-            Map<String, RttResult> resultEntries = new HashMap<>();
-            for (RttResult result: results) {
-                resultEntries.put(new String(HexEncoding.encode(result.addr)), result);
+            Map<MacAddress, RttResult> resultEntries = new HashMap<>();
+            for (RttResult result : results) {
+                resultEntries.put(MacAddress.fromBytes(result.addr), result);
             }
 
             List<RangingResult> finalResults = new ArrayList<>(request.mRttPeers.size());
 
-            for (RangingRequest.RttPeer peer: request.mRttPeers) {
-                byte[] addr;
-                if (peer instanceof RangingRequest.RttPeerAp) {
-                    addr = NativeUtil.macAddressToByteArray(
-                            ((RangingRequest.RttPeerAp) peer).scanResult.BSSID);
-                } else if (peer instanceof RangingRequest.RttPeerAware) {
-                    addr = ((RangingRequest.RttPeerAware) peer).peerMacAddress;
-                } else {
-                    Log.w(TAG, "postProcessResults: unknown peer type -- " + peer.getClass());
-                    continue;
-                }
-
-                RttResult resultForRequest = resultEntries.get(
-                        new String(HexEncoding.encode(addr)));
+            for (ResponderConfig peer : request.mRttPeers) {
+                RttResult resultForRequest = resultEntries.get(peer.macAddress);
                 if (resultForRequest == null) {
                     if (VDBG) {
-                        Log.v(TAG, "postProcessResults: missing=" + new String(
-                                HexEncoding.encode(addr)));
+                        Log.v(TAG, "postProcessResults: missing=" + peer.macAddress);
                     }
-                    finalResults.add(
-                            new RangingResult(RangingResult.STATUS_FAIL, addr, 0, 0, 0, 0));
+                    if (peer.peerHandle == null) {
+                        finalResults.add(
+                                new RangingResult(RangingResult.STATUS_FAIL, peer.macAddress, 0, 0,
+                                        0, 0));
+                    } else {
+                        finalResults.add(
+                                new RangingResult(RangingResult.STATUS_FAIL, peer.peerHandle, 0, 0,
+                                        0, 0));
+                    }
                 } else {
                     int status = resultForRequest.status == RttStatus.SUCCESS
                             ? RangingResult.STATUS_SUCCESS : RangingResult.STATUS_FAIL;
-                    PeerHandle peerHandle = null;
-                    if (peer instanceof RangingRequest.RttPeerAware) {
-                        peerHandle = ((RangingRequest.RttPeerAware) peer).peerHandle;
-                    }
-
-                    if (peerHandle == null) {
+                    if (peer.peerHandle == null) {
                         finalResults.add(
-                                new RangingResult(status, addr, resultForRequest.distanceInMm,
-                                        resultForRequest.distanceSdInMm, resultForRequest.rssi,
-                                        resultForRequest.timeStampInUs));
+                                new RangingResult(status, peer.macAddress,
+                                        resultForRequest.distanceInMm,
+                                        resultForRequest.distanceSdInMm,
+                                        resultForRequest.rssi, resultForRequest.timeStampInUs));
                     } else {
                         finalResults.add(
-                                new RangingResult(status, peerHandle, resultForRequest.distanceInMm,
-                                        resultForRequest.distanceSdInMm, resultForRequest.rssi,
-                                        resultForRequest.timeStampInUs));
+                                new RangingResult(status, peer.peerHandle,
+                                        resultForRequest.distanceInMm,
+                                        resultForRequest.distanceSdInMm,
+                                        resultForRequest.rssi, resultForRequest.timeStampInUs));
                     }
                 }
             }
diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
index 72a7a27..9afe061 100644
--- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -35,7 +35,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -66,28 +65,16 @@
 
     private final Object mSettingsLock = new Object();
 
-    // Next scan settings to apply when the previous scan completes
-    private WifiNative.ScanSettings mPendingSingleScanSettings = null;
-    private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null;
-
     private ArrayList<ScanDetail> mNativeScanResults;
     private WifiScanner.ScanData mLatestSingleScanResult =
             new WifiScanner.ScanData(0, 0, new ScanResult[0]);
 
-    // Settings for the currently running scan, null if no scan active
+    // Settings for the currently running single scan, null if no scan active
     private LastScanSettings mLastScanSettings = null;
+    // Settings for the currently running pno scan, null if no scan active
+    private LastPnoScanSettings mLastPnoScanSettings = null;
 
-    // Pno related info.
-    private WifiNative.PnoSettings mPnoSettings = null;
-    private WifiNative.PnoEventHandler mPnoEventHandler;
     private final boolean mHwPnoScanSupported;
-    private final HwPnoDebouncer mHwPnoDebouncer;
-    private final HwPnoDebouncer.Listener mHwPnoDebouncerListener = new HwPnoDebouncer.Listener() {
-        public void onPnoScanFailed() {
-            Log.e(TAG, "Pno scan failure received");
-            reportPnoScanFailure();
-        }
-    };
 
     /**
      * Duration to wait before timing out a scan.
@@ -114,7 +101,6 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mEventHandler = new Handler(looper, this);
         mClock = clock;
-        mHwPnoDebouncer = new HwPnoDebouncer(mWifiNative, mAlarmManager, mEventHandler, mClock);
 
         // Check if the device supports HW PNO scans.
         mHwPnoScanSupported = mContext.getResources().getBoolean(
@@ -131,10 +117,9 @@
     @Override
     public void cleanup() {
         synchronized (mSettingsLock) {
-            mPendingSingleScanSettings = null;
-            mPendingSingleScanEventHandler = null;
             stopHwPnoScan();
             mLastScanSettings = null; // finally clear any active scan
+            mLastPnoScanSettings = null; // finally clear any active scan
         }
     }
 
@@ -162,14 +147,65 @@
             return false;
         }
         synchronized (mSettingsLock) {
-            if (mPendingSingleScanSettings != null
-                    || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
+            if (mLastScanSettings != null) {
                 Log.w(TAG, "A single scan is already running");
                 return false;
             }
-            mPendingSingleScanSettings = settings;
-            mPendingSingleScanEventHandler = eventHandler;
-            processPendingScans();
+
+            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
+            boolean reportFullResults = false;
+
+            for (int i = 0; i < settings.num_buckets; ++i) {
+                WifiNative.BucketSettings bucketSettings = settings.buckets[i];
+                if ((bucketSettings.report_events
+                                & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
+                    reportFullResults = true;
+                }
+                allFreqs.addChannels(bucketSettings);
+            }
+
+            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
+            if (settings.hiddenNetworks != null) {
+                int numHiddenNetworks =
+                        Math.min(settings.hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
+                for (int i = 0; i < numHiddenNetworks; i++) {
+                    hiddenNetworkSSIDSet.add(settings.hiddenNetworks[i].ssid);
+                }
+            }
+            mLastScanSettings = new LastScanSettings(
+                        mClock.getElapsedSinceBootMillis(),
+                        reportFullResults, allFreqs, eventHandler);
+
+            boolean success = false;
+            Set<Integer> freqs;
+            if (!allFreqs.isEmpty()) {
+                freqs = allFreqs.getScanFreqs();
+                success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
+                if (!success) {
+                    Log.e(TAG, "Failed to start scan, freqs=" + freqs);
+                }
+            } else {
+                // There is a scan request but no available channels could be scanned for.
+                // We regard it as a scan failure in this case.
+                Log.e(TAG, "Failed to start scan because there is no available channel to scan");
+            }
+            if (success) {
+                if (DBG) {
+                    Log.d(TAG, "Starting wifi scan for freqs=" + freqs);
+                }
+
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
+                        TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
+            } else {
+                // indicate scan failure async
+                mEventHandler.post(new Runnable() {
+                        public void run() {
+                            reportScanFailure();
+                        }
+                    });
+            }
+
             return true;
         }
     }
@@ -204,123 +240,6 @@
     private void handleScanTimeout() {
         Log.e(TAG, "Timed out waiting for scan result from wificond");
         reportScanFailure();
-        processPendingScans();
-    }
-
-    private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) {
-        return (mLastScanSettings == null || !Arrays.equals(
-                newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList));
-    }
-
-    private void processPendingScans() {
-        synchronized (mSettingsLock) {
-            // Wait for the active scan result to come back to reschedule other scans,
-            // unless if HW pno scan is running. Hw PNO scans are paused it if there
-            // are other pending scans,
-            if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) {
-                return;
-            }
-
-            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
-            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
-            final LastScanSettings newScanSettings =
-                    new LastScanSettings(mClock.getElapsedSinceBootMillis());
-
-            if (mPendingSingleScanSettings != null) {
-                boolean reportFullResults = false;
-                ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection();
-                for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) {
-                    WifiNative.BucketSettings bucketSettings =
-                            mPendingSingleScanSettings.buckets[i];
-                    if ((bucketSettings.report_events
-                                    & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
-                        reportFullResults = true;
-                    }
-                    singleScanFreqs.addChannels(bucketSettings);
-                    allFreqs.addChannels(bucketSettings);
-                }
-                newScanSettings.setSingleScan(reportFullResults, singleScanFreqs,
-                        mPendingSingleScanEventHandler);
-
-                WifiNative.HiddenNetwork[] hiddenNetworks =
-                        mPendingSingleScanSettings.hiddenNetworks;
-                if (hiddenNetworks != null) {
-                    int numHiddenNetworks =
-                            Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
-                    for (int i = 0; i < numHiddenNetworks; i++) {
-                        hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid);
-                    }
-                }
-
-                mPendingSingleScanSettings = null;
-                mPendingSingleScanEventHandler = null;
-            }
-
-            if (newScanSettings.singleScanActive) {
-                boolean success = false;
-                Set<Integer> freqs;
-                if (!allFreqs.isEmpty()) {
-                    pauseHwPnoScan();
-                    freqs = allFreqs.getScanFreqs();
-                    success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
-                    if (!success) {
-                        Log.e(TAG, "Failed to start scan, freqs=" + freqs);
-                    }
-                } else {
-                    // There is a scan request but no available channels could be scanned for.
-                    // We regard it as a scan failure in this case.
-                    Log.e(TAG, "Failed to start scan because there is "
-                            + "no available channel to scan for");
-                }
-                if (success) {
-                    // TODO handle scan timeout
-                    if (DBG) {
-                        Log.d(TAG, "Starting wifi scan for freqs=" + freqs
-                                + ", single=" + newScanSettings.singleScanActive);
-                    }
-                    mLastScanSettings = newScanSettings;
-                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
-                            TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
-                } else {
-                    // indicate scan failure async
-                    mEventHandler.post(new Runnable() {
-                            public void run() {
-                                if (newScanSettings.singleScanEventHandler != null) {
-                                    newScanSettings.singleScanEventHandler
-                                            .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
-                                }
-                            }
-                        });
-                }
-            } else if (isHwPnoScanRequired()) {
-                newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
-                boolean status;
-                // If the PNO network list has changed from the previous request, ensure that
-                // we bypass the debounce logic and restart PNO scan.
-                if (isDifferentPnoScanSettings(newScanSettings)) {
-                    status = restartHwPnoScan(mPnoSettings);
-                } else {
-                    status = startHwPnoScan(mPnoSettings);
-                }
-                if (status) {
-                    mLastScanSettings = newScanSettings;
-                } else {
-                    Log.e(TAG, "Failed to start PNO scan");
-                    // indicate scan failure async
-                    mEventHandler.post(new Runnable() {
-                        public void run() {
-                            if (mPnoEventHandler != null) {
-                                mPnoEventHandler.onPnoScanFailed();
-                            }
-                            // Clean up PNO state, we don't want to continue PNO scanning.
-                            mPnoSettings = null;
-                            mPnoEventHandler = null;
-                        }
-                    });
-                }
-            }
-        }
     }
 
     @Override
@@ -330,16 +249,13 @@
                 Log.w(TAG, "Scan failed");
                 mAlarmManager.cancel(mScanTimeoutListener);
                 reportScanFailure();
-                processPendingScans();
                 break;
             case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
                 pollLatestScanDataForPno();
-                processPendingScans();
                 break;
             case WifiMonitor.SCAN_RESULTS_EVENT:
                 mAlarmManager.cancel(mScanTimeoutListener);
                 pollLatestScanData();
-                processPendingScans();
                 break;
             default:
                 // ignore unknown event
@@ -361,21 +277,19 @@
 
     private void reportPnoScanFailure() {
         synchronized (mSettingsLock) {
-            if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) {
-                if (mLastScanSettings.pnoScanEventHandler != null) {
-                    mLastScanSettings.pnoScanEventHandler.onPnoScanFailed();
+            if (mLastPnoScanSettings != null) {
+                if (mLastPnoScanSettings.pnoScanEventHandler != null) {
+                    mLastPnoScanSettings.pnoScanEventHandler.onPnoScanFailed();
                 }
                 // Clean up PNO state, we don't want to continue PNO scanning.
-                mPnoSettings = null;
-                mPnoEventHandler = null;
-                mLastScanSettings = null;
+                mLastPnoScanSettings = null;
             }
         }
     }
 
     private void pollLatestScanDataForPno() {
         synchronized (mSettingsLock) {
-            if (mLastScanSettings == null) {
+            if (mLastPnoScanSettings == null) {
                  // got a scan before we started scanning or after scan was canceled
                 return;
             }
@@ -385,10 +299,8 @@
             for (int i = 0; i < mNativeScanResults.size(); ++i) {
                 ScanResult result = mNativeScanResults.get(i).getScanResult();
                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
-                if (timestamp_ms > mLastScanSettings.startTime) {
-                    if (mLastScanSettings.hwPnoScanActive) {
-                        hwPnoScanResults.add(result);
-                    }
+                if (timestamp_ms > mLastPnoScanSettings.startTime) {
+                    hwPnoScanResults.add(result);
                 } else {
                     numFilteredScanResults++;
                 }
@@ -398,25 +310,11 @@
                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
             }
 
-            if (mLastScanSettings.hwPnoScanActive
-                    && mLastScanSettings.pnoScanEventHandler != null) {
+            if (mLastPnoScanSettings.pnoScanEventHandler != null) {
                 ScanResult[] pnoScanResultsArray =
                         hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
-                mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
+                mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
             }
-            // On pno scan result event, we are expecting a mLastScanSettings for pno scan.
-            // However, if unlikey mLastScanSettings is for single scan, we need this part
-            // to protect from leaving WifiSingleScanStateMachine in a forever wait state.
-            if (mLastScanSettings.singleScanActive
-                    && mLastScanSettings.singleScanEventHandler != null) {
-                Log.w(TAG, "Polling pno scan result when single scan is active, reporting"
-                        + " single scan failure");
-                mLastScanSettings.singleScanEventHandler
-                        .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
-            }
-            // mLastScanSettings is for either single/batched scan or pno scan.
-            // We can safely set it to null when pno scan finishes.
-            mLastScanSettings = null;
         }
     }
 
@@ -445,8 +343,7 @@
                 ScanResult result = mNativeScanResults.get(i).getScanResult();
                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
                 if (timestamp_ms > mLastScanSettings.startTime) {
-                    if (mLastScanSettings.singleScanActive
-                            && mLastScanSettings.singleScanFreqs.containsChannel(
+                    if (mLastScanSettings.singleScanFreqs.containsChannel(
                                     result.frequency)) {
                         singleScanResults.add(result);
                     }
@@ -458,8 +355,7 @@
                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
             }
 
-            if (mLastScanSettings.singleScanActive
-                    && mLastScanSettings.singleScanEventHandler != null) {
+            if (mLastScanSettings.singleScanEventHandler != null) {
                 if (mLastScanSettings.reportSingleScanFullResults) {
                     for (ScanResult scanResult : singleScanResults) {
                         // ignore buckets scanned since there is only one bucket for a single scan
@@ -486,20 +382,11 @@
     }
 
     private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
-        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
+        return mWifiNative.startPnoScan(pnoSettings);
     }
 
     private void stopHwPnoScan() {
-        mHwPnoDebouncer.stopPnoScan();
-    }
-
-    private void pauseHwPnoScan() {
-        mHwPnoDebouncer.forceStopPnoScan();
-    }
-
-    private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) {
-        mHwPnoDebouncer.forceStopPnoScan();
-        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
+        mWifiNative.stopPnoScan();
     }
 
     /**
@@ -511,26 +398,27 @@
         return (!isConnectedPno & mHwPnoScanSupported);
     }
 
-    private boolean isHwPnoScanRequired() {
-        synchronized (mSettingsLock) {
-            if (mPnoSettings == null) return false;
-            return isHwPnoScanRequired(mPnoSettings.isConnected);
-        }
-    }
-
     @Override
     public boolean setHwPnoList(WifiNative.PnoSettings settings,
             WifiNative.PnoEventHandler eventHandler) {
         synchronized (mSettingsLock) {
-            if (mPnoSettings != null) {
+            if (mLastPnoScanSettings != null) {
                 Log.w(TAG, "Already running a PNO scan");
                 return false;
             }
-            mPnoEventHandler = eventHandler;
-            mPnoSettings = settings;
+            if (!isHwPnoScanRequired(settings.isConnected)) {
+                return false;
+            }
 
-            // For wificond based PNO, we start the scan immediately when we set pno list.
-            processPendingScans();
+            if (startHwPnoScan(settings)) {
+                mLastPnoScanSettings = new LastPnoScanSettings(
+                            mClock.getElapsedSinceBootMillis(),
+                            settings.networkList, eventHandler);
+
+            } else {
+                Log.e(TAG, "Failed to start PNO scan");
+                reportPnoScanFailure();
+            }
             return true;
         }
     }
@@ -538,12 +426,11 @@
     @Override
     public boolean resetHwPnoList() {
         synchronized (mSettingsLock) {
-            if (mPnoSettings == null) {
+            if (mLastPnoScanSettings == null) {
                 Log.w(TAG, "No PNO scan running");
                 return false;
             }
-            mPnoEventHandler = null;
-            mPnoSettings = null;
+            mLastPnoScanSettings = null;
             // For wificond based PNO, we stop the scan immediately when we reset pno list.
             stopHwPnoScan();
             return true;
@@ -591,204 +478,36 @@
     }
 
     private static class LastScanSettings {
-        public long startTime;
-
-        LastScanSettings(long startTime) {
-            this.startTime = startTime;
-        }
-
-        // Single scan settings
-        public boolean singleScanActive = false;
-        public boolean reportSingleScanFullResults;
-        public ChannelCollection singleScanFreqs;
-        public WifiNative.ScanEventHandler singleScanEventHandler;
-
-        public void setSingleScan(boolean reportSingleScanFullResults,
+        LastScanSettings(long startTime,
+                boolean reportSingleScanFullResults,
                 ChannelCollection singleScanFreqs,
                 WifiNative.ScanEventHandler singleScanEventHandler) {
-            singleScanActive = true;
+            this.startTime = startTime;
             this.reportSingleScanFullResults = reportSingleScanFullResults;
             this.singleScanFreqs = singleScanFreqs;
             this.singleScanEventHandler = singleScanEventHandler;
         }
 
-        public boolean hwPnoScanActive = false;
-        public WifiNative.PnoNetwork[] pnoNetworkList;
-        public WifiNative.PnoEventHandler pnoScanEventHandler;
+        public long startTime;
+        public boolean reportSingleScanFullResults;
+        public ChannelCollection singleScanFreqs;
+        public WifiNative.ScanEventHandler singleScanEventHandler;
 
-        public void setHwPnoScan(
+    }
+
+    private static class LastPnoScanSettings {
+        LastPnoScanSettings(long startTime,
                 WifiNative.PnoNetwork[] pnoNetworkList,
                 WifiNative.PnoEventHandler pnoScanEventHandler) {
-            hwPnoScanActive = true;
+            this.startTime = startTime;
             this.pnoNetworkList = pnoNetworkList;
             this.pnoScanEventHandler = pnoScanEventHandler;
         }
+
+        public long startTime;
+        public WifiNative.PnoNetwork[] pnoNetworkList;
+        public WifiNative.PnoEventHandler pnoScanEventHandler;
+
     }
 
-    /**
-     * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO
-     * state too often which is not handled very well by some drivers.
-     * Note: This is not thread safe!
-     */
-    public static class HwPnoDebouncer {
-        public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor";
-        private static final int MINIMUM_PNO_GAP_MS = 5 * 1000;
-
-        private final WifiNative mWifiNative;
-        private final AlarmManager mAlarmManager;
-        private final Handler mEventHandler;
-        private final Clock mClock;
-        private long mLastPnoChangeTimeStamp = -1L;
-        private boolean mExpectedPnoState = false;
-        private boolean mCurrentPnoState = false;;
-        private boolean mWaitForTimer = false;
-        private Listener mListener;
-        private WifiNative.PnoSettings mPnoSettings;
-
-        /**
-         * Interface used to indicate PNO scan notifications.
-         */
-        public interface Listener {
-            /**
-             * Used to indicate a delayed PNO scan request failure.
-             */
-            void onPnoScanFailed();
-        }
-
-        public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager,
-                Handler eventHandler, Clock clock) {
-            mWifiNative = wifiNative;
-            mAlarmManager = alarmManager;
-            mEventHandler = eventHandler;
-            mClock = clock;
-        }
-
-        /**
-         * Enable PNO state in wificond
-         */
-        private boolean startPnoScanInternal() {
-            if (mCurrentPnoState) {
-                if (DBG) Log.d(TAG, "PNO state is already enable");
-                return true;
-            }
-            if (mPnoSettings == null) {
-                Log.e(TAG, "PNO state change to enable failed, no available Pno settings");
-                return false;
-            }
-            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
-            Log.d(TAG, "Remove all networks from supplicant before starting PNO scan");
-            mWifiNative.removeAllNetworks();
-            if (mWifiNative.startPnoScan(mPnoSettings)) {
-                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable");
-                mCurrentPnoState = true;
-                return true;
-            } else {
-                Log.e(TAG, "PNO state change to enable failed");
-                mCurrentPnoState = false;
-            }
-            return false;
-        }
-
-        /**
-         * Disable PNO state in wificond
-         */
-        private boolean stopPnoScanInternal() {
-            if (!mCurrentPnoState) {
-                if (DBG) Log.d(TAG, "PNO state is already disable");
-                return true;
-            }
-            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
-            if (mWifiNative.stopPnoScan()) {
-                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable");
-                mCurrentPnoState = false;
-                return true;
-            } else {
-                Log.e(TAG, "PNO state change to disable failed");
-                mCurrentPnoState = false;
-            }
-            return false;
-        }
-
-        private final AlarmManager.OnAlarmListener mAlarmListener =
-                new AlarmManager.OnAlarmListener() {
-            public void onAlarm() {
-                if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState);
-                if (mExpectedPnoState) {
-                    if (!startPnoScanInternal()) {
-                        if (mListener != null) {
-                            mListener.onPnoScanFailed();
-                        }
-                    }
-                } else {
-                    stopPnoScanInternal();
-                }
-                mWaitForTimer = false;
-            }
-        };
-
-        /**
-         * Enable/Disable PNO state. This method will debounce PNO scan requests.
-         * @param enable boolean indicating whether PNO is being enabled or disabled.
-         */
-        private boolean setPnoState(boolean enable) {
-            boolean isSuccess = true;
-            mExpectedPnoState = enable;
-            if (!mWaitForTimer) {
-                long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp;
-                if (timeDifference >= MINIMUM_PNO_GAP_MS) {
-                    if (enable) {
-                        isSuccess = startPnoScanInternal();
-                    } else {
-                        isSuccess = stopPnoScanInternal();
-                    }
-                } else {
-                    long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference;
-                    Log.d(TAG, "Start PNO timer with delay " + alarmTimeout);
-                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            mClock.getElapsedSinceBootMillis() + alarmTimeout,
-                            PNO_DEBOUNCER_ALARM_TAG,
-                            mAlarmListener, mEventHandler);
-                    mWaitForTimer = true;
-                }
-            }
-            return isSuccess;
-        }
-
-        /**
-         * Start PNO scan
-         */
-        public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) {
-            if (DBG) Log.d(TAG, "Starting PNO scan");
-            mListener = listener;
-            mPnoSettings = pnoSettings;
-            if (!setPnoState(true)) {
-                mListener = null;
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Stop PNO scan
-         */
-        public void stopPnoScan() {
-            if (DBG) Log.d(TAG, "Stopping PNO scan");
-            setPnoState(false);
-            mListener = null;
-        }
-
-        /**
-         * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO
-         * scan immediately.
-         */
-        public void forceStopPnoScan() {
-            if (DBG) Log.d(TAG, "Force stopping Pno scan");
-            // Cancel the debounce timer and stop PNO scan.
-            if (mWaitForTimer) {
-                mAlarmManager.cancel(mAlarmListener);
-                mWaitForTimer = false;
-            }
-            stopPnoScanInternal();
-        }
-    }
 }
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
index d3c072f..4ae7d13 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -22,7 +22,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.net.NetworkScoreManager;
-import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -202,24 +201,19 @@
      * current user.
      */
     private boolean isCurrentProfile(int uid) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            int currentUser = mWifiPermissionsWrapper.getCurrentUser();
-            int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
-            if (callingUserId == currentUser) {
-                return true;
-            } else {
-                List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
-                for (UserInfo user : userProfiles) {
-                    if (user.id == callingUserId) {
-                        return true;
-                    }
+        int currentUser = mWifiPermissionsWrapper.getCurrentUser();
+        int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
+        if (callingUserId == currentUser) {
+            return true;
+        } else {
+            List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
+            for (UserInfo user : userProfiles) {
+                if (user.id == callingUserId) {
+                    return true;
                 }
             }
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
         }
+        return false;
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
index 6fde01e..84aacdf 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
@@ -108,4 +108,16 @@
         return AppGlobals.getPackageManager().checkUidPermission(
                 Manifest.permission.CHANGE_WIFI_STATE, uid);
     }
+
+    /**
+     * Determines if the caller has local mac address permission.
+     *
+     * @param uid to check the permission for
+     * @return int representation of success or denied
+     * @throws RemoteException
+     */
+    public int getLocalMacAddressPermission(int uid) throws RemoteException {
+        return AppGlobals.getPackageManager().checkUidPermission(
+                Manifest.permission.LOCAL_MAC_ADDRESS, uid);
+    }
 }
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index 11e508d..b5c5844 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -68,7 +68,9 @@
 	android.test.runner \
 	wifi-service \
 	services \
-	android.hidl.manager-V1.0-java
+	android.hidl.manager-V1.0-java \
+	android.test.base \
+	android.test.mock
 
 # These must be explicitly included because they are not normally accessible
 # from apps.
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 6dfd13a..4290ada 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi;
 
 import static com.android.server.wifi.HalDeviceManager.START_HAL_RETRY_TIMES;
-import static com.android.server.wifi.HalDeviceManager.getName;
 
 import static junit.framework.Assert.assertEquals;
 
@@ -589,6 +588,48 @@
                 TestChipV2.CHIP_MODE_ID, 1);
     }
 
+    // TestChipV3
+
+    /**
+     * Validate creation of STA interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateStaInterfaceNoInitModeTestChipV3() throws Exception {
+        // Note: we expected 2 available callbacks since we now have 2 STAs possible. So
+        // we get callback 1 after creating the first STA (since we can create another STA),
+        // and we get callback 2 after destroying the first STA (since we can create another STA -
+        // as expected).
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.STA, "sta0",
+                TestChipV3.CHIP_MODE_ID, 2);
+    }
+
+    /**
+     * Validate creation of AP interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateApInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.AP, "ap0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
+    /**
+     * Validate creation of P2P interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateP2pInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.P2P, "p2p0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
+    /**
+     * Validate creation of NAN interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateNanInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.NAN, "nan0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
     //////////////////////////////////////////////////////////////////////////////////////
     // TestChipV1 Specific Tests
     //////////////////////////////////////////////////////////////////////////////////////
@@ -637,6 +678,77 @@
     }
 
     /**
+     * Validate creation of AP interface when in STA mode with a single STA iface created.
+     * Expect a change in chip mode.
+     */
+    @Test
+    public void testCreateApWithStIfaceUpTestChipV1UsingNoHandlerListeners() throws Exception {
+        TestChipV1 chipMock = new TestChipV1();
+        chipMock.initialize();
+
+        InterfaceDestroyedListener staIdl = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener staIafrl = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+        InterfaceDestroyedListener apIdl = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener apIafrl = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock, staIdl, staIafrl, apIdl, apIafrl);
+        executeAndValidateInitializationSequence();
+
+        // Register listener & start Wi-Fi
+        mDut.registerStatusListener(mManagerStatusListenerMock, null);
+        assertTrue(mDut.start());
+        mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staIafrl, null);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.AP, apIafrl, null);
+
+        mInOrder.verify(staIafrl).onAvailableForRequest();
+        mInOrder.verify(apIafrl).onAvailableForRequest();
+
+        // Create STA Iface first.
+        IWifiStaIface staIface = mock(IWifiStaIface.class);
+        doAnswer(new GetNameAnswer("wlan0")).when(staIface).getName(
+                any(IWifiIface.getNameCallback.class));
+        doAnswer(new GetTypeAnswer(IfaceType.STA)).when(staIface).getType(
+                any(IWifiIface.getTypeCallback.class));
+        doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, staIface)).when(
+                chipMock.chip).createStaIface(any(IWifiChip.createStaIfaceCallback.class));
+        assertEquals(staIface, mDut.createStaIface(staIdl, null));
+
+        mInOrder.verify(chipMock.chip).configureChip(TestChipV1.STA_CHIP_MODE_ID);
+        mInOrder.verify(apIafrl).onAvailableForRequest();
+
+        // Now Create AP Iface.
+        IWifiApIface apIface = mock(IWifiApIface.class);
+        doAnswer(new GetNameAnswer("wlan0")).when(apIface).getName(
+                any(IWifiIface.getNameCallback.class));
+        doAnswer(new GetTypeAnswer(IfaceType.AP)).when(apIface).getType(
+                any(IWifiIface.getTypeCallback.class));
+        doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, apIface)).when(
+                chipMock.chip).createApIface(any(IWifiChip.createApIfaceCallback.class));
+        assertEquals(apIface, mDut.createApIface(apIdl, null));
+
+        mInOrder.verify(chipMock.chip).removeStaIface(getName(staIface));
+        mInOrder.verify(staIdl).onDestroyed(getName(staIface));
+        mInOrder.verify(chipMock.chip).configureChip(TestChipV1.AP_CHIP_MODE_ID);
+        mInOrder.verify(staIafrl).onAvailableForRequest();
+
+        // Stop Wi-Fi
+        mDut.stop();
+
+        mInOrder.verify(mWifiMock).stop();
+        mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+        mInOrder.verify(apIdl).onDestroyed(getName(apIface));
+
+        verifyNoMoreInteractions(mManagerStatusListenerMock, staIdl, staIafrl, apIdl, apIafrl);
+    }
+
+    /**
      * Validate creation of AP interface when in AP mode - but with no interface created. Expect
      * no change in chip mode.
      */
@@ -911,8 +1023,9 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV1() throws Exception {
-        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV1(), false,
-                TestChipV1.STA_CHIP_MODE_ID);
+        // staAvailCallbacks=0: there is no second STA so will never get available callback after
+        // first is created.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV1(), 0, TestChipV1.STA_CHIP_MODE_ID);
     }
 
     /**
@@ -1227,7 +1340,9 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV2() throws Exception {
-        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), true, TestChipV2.CHIP_MODE_ID);
+        // staAvailCallbacks=5: after every substantial change will get a callback since second
+        // STA is always available.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), 5, TestChipV2.CHIP_MODE_ID);
     }
 
     /**
@@ -1280,6 +1395,273 @@
         assertEquals(correctResults, results);
     }
 
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TestChipV3 Specific Tests
+    //////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Validate a flow sequence for test chip 3:
+     * - create STA
+     * - create P2P
+     * - request NAN: failure
+     * - create AP: should tear down P2P first
+     * - create STA: will get refused
+     * - create AP: will get refused
+     * - request P2P: failure
+     * - tear down AP
+     * - create STA
+     * - create STA: will get refused
+     * - create NAN: should tear down last created STA
+     * - create STA: will get refused
+     */
+    @Test
+    public void testInterfaceCreationFlowTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        InterfaceDestroyedListener staDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        InterfaceDestroyedListener staDestroyedListener2 = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener apDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener apAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener p2pDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener p2pAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener nanDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InOrder inOrderStaAvail = inOrder(staAvailListener);
+        InOrder inOrderApAvail = inOrder(apAvailListener);
+        InOrder inOrderP2pAvail = inOrder(p2pAvailListener);
+        InOrder inOrderNanAvail = inOrder(nanAvailListener);
+
+        // register listeners for interface availability
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
+                mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.AP, apAvailListener, mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.P2P, p2pAvailListener,
+                mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
+                mHandler);
+        mTestLooper.dispatchAll();
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // create STA
+        when(mClock.getUptimeSinceBootMillis()).thenReturn(15L);
+        IWifiIface staIface = validateInterfaceSequence(chipMock,
+                false, // chipModeValid
+                -1000, // chipModeId (only used if chipModeValid is true)
+                IfaceType.STA, // ifaceTypeToCreate
+                "sta0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                staDestroyedListener, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("STA interface wasn't created", staIface, IsNull.notNullValue());
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // create P2P
+        IWifiIface p2pIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.P2P, // ifaceTypeToCreate
+                "p2p0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                p2pDestroyedListener, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("P2P interface wasn't created", p2pIface, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+
+        // request NAN: should fail
+        IWifiIface nanIface = mDut.createNanIface(null, null);
+        collector.checkThat("NAN should not be created", nanIface, IsNull.nullValue());
+
+        // create AP: will destroy P2P
+        IWifiIface apIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.AP, // ifaceTypeToCreate
+                "ap0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                apDestroyedListener, // destroyedListener
+                null, // availableListener (already registered)
+                new InterfaceDestroyedListenerWithIfaceName("p2p0", p2pDestroyedListener)
+        );
+        collector.checkThat("AP interface wasn't created", apIface, IsNull.notNullValue());
+        verify(chipMock.chip).removeP2pIface("p2p0");
+
+        // request STA2: should fail
+        IWifiIface staIface2 = mDut.createStaIface(null, null);
+        collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
+
+        // request AP2: should fail
+        IWifiIface apIface2 = mDut.createApIface(null, null);
+        collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
+
+        // request P2P: should fail
+        p2pIface = mDut.createP2pIface(null, null);
+        collector.checkThat("P2P should not be created", p2pIface, IsNull.nullValue());
+
+        // tear down AP
+        mDut.removeIface(apIface);
+        mTestLooper.dispatchAll();
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        verify(chipMock.chip).removeApIface("ap0");
+        verify(apDestroyedListener).onDestroyed(getName(apIface));
+
+        // create STA2: using a later clock
+        when(mClock.getUptimeSinceBootMillis()).thenReturn(20L);
+        staIface2 = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.STA, // ifaceTypeToCreate
+                "sta1", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                staDestroyedListener2, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("STA 2 interface wasn't created", staIface2, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // request STA3: should fail
+        IWifiIface staIface3 = mDut.createStaIface(null, null);
+        collector.checkThat("STA3 should not be created", staIface3, IsNull.nullValue());
+
+        // create NAN: should destroy the last created STA (STA2)
+        nanIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.NAN, // ifaceTypeToCreate
+                "nan0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                nanDestroyedListener, // destroyedListener
+                null, // availableListener (already registered)
+                new InterfaceDestroyedListenerWithIfaceName(
+                        getName(staIface2), staDestroyedListener2)
+        );
+        collector.checkThat("NAN interface wasn't created", nanIface, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        verify(chipMock.chip).removeStaIface("sta1");
+        verify(staDestroyedListener2).onDestroyed(getName(staIface2));
+
+        // request STA2: should fail
+        staIface2 = mDut.createStaIface(null, null);
+        collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
+
+        verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener,
+                staDestroyedListener2, apDestroyedListener, p2pDestroyedListener,
+                nanDestroyedListener, staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener, staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener);
+    }
+
+    /**
+     * Validate P2P and NAN interactions. Expect:
+     * - STA created
+     * - NAN created
+     * - When P2P requested:
+     *   - NAN torn down
+     *   - P2P created
+     * - NAN creation refused
+     * - When P2P destroyed:
+     *   - get nan available listener
+     *   - Can create NAN when requested
+     */
+    @Test
+    public void testP2pAndNanInteractionsTestChipV3() throws Exception {
+        // staAvailCallbacks=2: only get callback (for second STA) when P2P or NAN are down.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV3(), 2, TestChipV3.CHIP_MODE_ID);
+    }
+
+    /**
+     * Validate that the getSupportedIfaceTypes API works when requesting for all chips.
+     */
+    @Test
+    public void testGetSupportedIfaceTypesAllTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        // try API
+        Set<Integer> results = mDut.getSupportedIfaceTypes();
+
+        // verify results
+        Set<Integer> correctResults = new HashSet<>();
+        correctResults.add(IfaceType.AP);
+        correctResults.add(IfaceType.STA);
+        correctResults.add(IfaceType.P2P);
+        correctResults.add(IfaceType.NAN);
+
+        assertEquals(correctResults, results);
+    }
+
+    /**
+     * Validate that the getSupportedIfaceTypes API works when requesting for a specific chip.
+     */
+    @Test
+    public void testGetSupportedIfaceTypesOneChipTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        // try API
+        Set<Integer> results = mDut.getSupportedIfaceTypes(chipMock.chip);
+
+        // verify results
+        Set<Integer> correctResults = new HashSet<>();
+        correctResults.add(IfaceType.AP);
+        correctResults.add(IfaceType.STA);
+        correctResults.add(IfaceType.P2P);
+        correctResults.add(IfaceType.NAN);
+
+        assertEquals(correctResults, results);
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////
     // utilities
     ///////////////////////////////////////////////////////////////////////////////////////
@@ -1409,7 +1791,7 @@
      * line of NAN and P2P being exclusive).
      */
     public void runP2pAndNanExclusiveInteractionsTestChip(ChipMockBase chipMock,
-            boolean duplicateStas, int onlyChipMode) throws Exception {
+            int staAvailCallbacks, int onlyChipMode) throws Exception {
         chipMock.initialize();
         mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
                 mManagerStatusListenerMock);
@@ -1496,9 +1878,9 @@
                 nanAvailListener // availableListener
         );
 
-        if (duplicateStas) {
+        if (staAvailCallbacks != 0) {
             // if there are duplicate STAs then expect an available callback for each step above
-            verify(staAvailListener, times(5)).onAvailableForRequest();
+            verify(staAvailListener, times(staAvailCallbacks)).onAvailableForRequest();
         }
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
@@ -2078,4 +2460,72 @@
                     .getAvailableModes(any(IWifiChip.getAvailableModesCallback.class));
         }
     }
+
+    // test chip configuration V3:
+    // mode:
+    //    STA + (STA || AP)
+    //    STA + (NAN || P2P)
+    private class TestChipV3 extends ChipMockBase {
+        // only mode (different number from any in other TestChips so can catch test errors)
+        static final int CHIP_MODE_ID = 7;
+
+        void initialize() throws Exception {
+            super.initialize();
+
+            // chip Id configuration
+            ArrayList<Integer> chipIds;
+            chipId = 15;
+            chipIds = new ArrayList<>();
+            chipIds.add(chipId);
+            doAnswer(new GetChipIdsAnswer(mStatusOk, chipIds)).when(mWifiMock).getChipIds(
+                    any(IWifi.getChipIdsCallback.class));
+
+            doAnswer(new GetChipAnswer(mStatusOk, chip)).when(mWifiMock).getChip(eq(15),
+                    any(IWifi.getChipCallback.class));
+
+            // initialize dummy chip modes
+            IWifiChip.ChipMode cm;
+            IWifiChip.ChipIfaceCombination cic;
+            IWifiChip.ChipIfaceCombinationLimit cicl;
+
+            //   Mode 0 (only one): 1xSTA + 1x{STA,AP}, 1xSTA + 1x{P2P,NAN}
+            availableModes = new ArrayList<>();
+            cm = new IWifiChip.ChipMode();
+            cm.id = CHIP_MODE_ID;
+
+            cic = new IWifiChip.ChipIfaceCombination();
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cic.limits.add(cicl);
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cicl.types.add(IfaceType.AP);
+            cic.limits.add(cicl);
+
+            cm.availableCombinations.add(cic);
+
+            cic = new IWifiChip.ChipIfaceCombination();
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cic.limits.add(cicl);
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.P2P);
+            cicl.types.add(IfaceType.NAN);
+            cic.limits.add(cicl);
+
+            cm.availableCombinations.add(cic);
+            availableModes.add(cm);
+
+            doAnswer(new GetAvailableModesAnswer(this)).when(chip)
+                    .getAvailableModes(any(IWifiChip.getAvailableModesCallback.class));
+        }
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/PasspointProvisioningTestUtil.java b/tests/wifitests/src/com/android/server/wifi/PasspointProvisioningTestUtil.java
new file mode 100644
index 0000000..c78135d
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/PasspointProvisioningTestUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.net.wifi.WifiSsid;
+import android.net.wifi.hotspot2.OsuProvider;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Helper for creating and populating WifiConfigurations in unit tests.
+ */
+public class PasspointProvisioningTestUtil {
+    /**
+     * These are constants used to generate predefined OsuProvider.
+     */
+    public static final WifiSsid TEST_SSID =
+            WifiSsid.createFromByteArray("TEST SSID".getBytes(StandardCharsets.UTF_8));
+    public static final String TEST_FRIENDLY_NAME = "Friendly Name";
+    public static final String TEST_SERVICE_DESCRIPTION = "Dummy Service";
+    public static final Uri TEST_SERVER_URI = Uri.parse("https://test.com");
+    public static final String TEST_NAI = "test.access.com";
+    public static final List<Integer> TEST_METHOD_LIST =
+            Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP);
+    public static final Icon TEST_ICON = Icon.createWithData(new byte[10], 0, 10);
+
+    /**
+     * Construct a {@link android.net.wifi.hotspot2.OsuProvider}.
+     * @param openOsuAP indicates if the OSU AP belongs to an open or OSEN network
+     * @return the constructed {@link android.net.wifi.hotspot2.OsuProvider}
+     */
+    public static OsuProvider generateOsuProvider(boolean openOsuAP) {
+        if (openOsuAP) {
+            return new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
+                    TEST_SERVER_URI, null, TEST_METHOD_LIST, TEST_ICON);
+        } else {
+            return new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAME, TEST_SERVICE_DESCRIPTION,
+                    TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+        }
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index 88c874e..0c35904 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wifi;
 
+import static android.net.wifi.WifiConfiguration.KeyMgmt.WPA_PSK;
 import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
@@ -32,19 +33,24 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.*;
 
+import android.app.test.TestAlarmManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.InterfaceConfiguration;
+import android.net.Uri;
 import android.net.wifi.IApInterface;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
 import android.os.INetworkManagementService;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.R;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.net.BaseNetworkObserver;
 
 import org.junit.Before;
@@ -54,7 +60,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -68,6 +73,7 @@
 
     private static final String DEFAULT_SSID = "DefaultTestSSID";
     private static final String TEST_SSID = "TestSSID";
+    private static final String TEST_PASSWORD = "TestPassword";
     private static final String TEST_COUNTRY_CODE = "TestCountry";
     private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4};
     private static final String TEST_INTERFACE_NAME = "testif0";
@@ -78,22 +84,26 @@
 
     private final WifiConfiguration mDefaultApConfig = createDefaultApConfig();
 
+    private ContentObserver mContentObserver;
+    private TestLooper mLooper;
+    private TestAlarmManager mAlarmManager;
+
     @Mock Context mContext;
-    TestLooper mLooper;
+    @Mock Resources mResources;
     @Mock WifiNative mWifiNative;
     @Mock SoftApManager.Listener mListener;
     @Mock InterfaceConfiguration mInterfaceConfiguration;
-    @Mock IBinder mApInterfaceBinder;
+    @Mock FrameworkFacade mFrameworkFacade;
     @Mock IApInterface mApInterface;
     @Mock INetworkManagementService mNmService;
     @Mock WifiApConfigStore mWifiApConfigStore;
     @Mock WifiMetrics mWifiMetrics;
-    final ArgumentCaptor<DeathRecipient> mDeathListenerCaptor =
-            ArgumentCaptor.forClass(DeathRecipient.class);
+    final ArgumentCaptor<WifiNative.WificondDeathEventHandler> mDeathListenerCaptor =
+            ArgumentCaptor.forClass(WifiNative.WificondDeathEventHandler.class);
     final ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor =
             ArgumentCaptor.forClass(BaseNetworkObserver.class);
-    final ArgumentCaptor<SoftApManager.ApInterfaceListener> mApInterfaceListenerCaptor =
-            ArgumentCaptor.forClass(SoftApManager.ApInterfaceListener.class);
+    final ArgumentCaptor<WifiNative.SoftApListener> mSoftApListenerCaptor =
+            ArgumentCaptor.forClass(WifiNative.SoftApListener.class);
 
     SoftApManager mSoftApManager;
 
@@ -103,12 +113,18 @@
         MockitoAnnotations.initMocks(this);
         mLooper = new TestLooper();
 
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(true);
-        when(mApInterface.stopHostapd()).thenReturn(true);
-        when(mApInterface.writeHostapdConfig(
-                any(), anyBoolean(), anyInt(), anyInt(), any())).thenReturn(true);
-        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.startSoftAp(any(), any())).thenReturn(true);
+        when(mWifiNative.stopSoftAp()).thenReturn(true);
+        when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(true);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+        mAlarmManager = new TestAlarmManager();
+        when(mContext.getSystemService(Context.ALARM_SERVICE))
+                .thenReturn(mAlarmManager.getAlarmManager());
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getInteger(R.integer.config_wifi_framework_soft_ap_timeout_delay))
+                .thenReturn(600000);
     }
 
     private WifiConfiguration createDefaultApConfig() {
@@ -118,14 +134,12 @@
     }
 
     private SoftApManager createSoftApManager(SoftApModeConfiguration config) throws Exception {
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(true);
-        when(mApInterface.stopHostapd()).thenReturn(true);
         if (config.getWifiConfiguration() == null) {
             when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
         }
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
@@ -158,7 +172,6 @@
         startSoftApAndVerifyEnabled(apConfig);
     }
 
-
     /**
      * Verifies startSoftAp will start with the hiddenSSID param set when it is set to true in the
      * supplied config.
@@ -174,17 +187,31 @@
         startSoftApAndVerifyEnabled(apConfig);
     }
 
+    /**
+     * Verifies startSoftAp will start with the password param set in the
+     * supplied config.
+     */
+    @Test
+    public void startSoftApWithPassphraseInConfig() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+        config.apBand = WifiConfiguration.AP_BAND_5GHZ;
+        config.SSID = TEST_SSID;
+        config.allowedKeyManagement.set(WPA_PSK);
+        config.preSharedKey = TEST_PASSWORD;
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
+        startSoftApAndVerifyEnabled(apConfig);
+    }
+
     /** Tests softap startup if default config fails to load. **/
     @Test
     public void startSoftApDefaultConfigFailedToLoad() throws Exception {
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(true);
-        when(mApInterface.stopHostapd()).thenReturn(true);
         when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
         SoftApModeConfiguration nullApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
@@ -224,13 +251,11 @@
         SoftApModeConfiguration softApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
 
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(true);
-        when(mApInterface.stopHostapd()).thenReturn(true);
         when(mWifiNative.isHalStarted()).thenReturn(true);
 
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            null,
                                                            mListener,
@@ -270,13 +295,11 @@
         SoftApModeConfiguration softApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
 
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(true);
-        when(mApInterface.stopHostapd()).thenReturn(true);
         when(mWifiNative.isHalStarted()).thenReturn(true);
 
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
@@ -308,13 +331,13 @@
      */
     @Test
     public void startSoftApApInterfaceFailedToStart() throws Exception {
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mApInterface.startHostapd(any())).thenReturn(false);
-        when(mApInterface.stopHostapd()).thenReturn(true);
+        when(mWifiNative.startSoftAp(any(), any())).thenReturn(false);
         SoftApModeConfiguration softApModeConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mDefaultApConfig);
+
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
@@ -363,7 +386,7 @@
         mSoftApManager.stop();
         mLooper.dispatchAll();
 
-        verify(mApInterface).stopHostapd();
+        verify(mWifiNative).stopSoftAp();
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -391,7 +414,7 @@
         // reset to clear verified Intents for ap state change updates
         reset(mContext);
 
-        mDeathListenerCaptor.getValue().binderDied();
+        mDeathListenerCaptor.getValue().onDeath();
         mLooper.dispatchAll();
         InOrder order = inOrder(mListener);
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
@@ -416,86 +439,181 @@
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
                 TEST_NUM_CONNECTED_CLIENTS);
         mLooper.dispatchAll();
-        assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(TEST_NUM_CONNECTED_CLIENTS,
                 apConfig.getTargetMode());
     }
 
     @Test
-    public void handlesNumAssociatedStationsWhenNotStarted() throws Exception {
-        SoftApModeConfiguration apConfig =
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
-        startSoftApAndVerifyEnabled(apConfig);
-        mSoftApManager.stop();
-        mLooper.dispatchAll();
-
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(
-                TEST_NUM_CONNECTED_CLIENTS);
-        mLooper.dispatchAll();
-        /* Verify numAssociatedStations is not updated when soft AP is not started */
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
-        verify(mWifiMetrics, never()).addSoftApNumAssociatedStationsChangedEvent(
-                TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
-    }
-
-    @Test
     public void handlesInvalidNumAssociatedStations() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
                 TEST_NUM_CONNECTED_CLIENTS);
         /* Invalid values should be ignored */
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(-1);
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(-1);
         mLooper.dispatchAll();
-        assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics, times(1)).addSoftApNumAssociatedStationsChangedEvent(
                 TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
     }
 
     @Test
-    public void resetsNumAssociatedStationsWhenStopped() throws Exception {
+    public void schedulesTimeoutTimerOnStart() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(
-                TEST_NUM_CONNECTED_CLIENTS);
-        mSoftApManager.stop();
-        mLooper.dispatchAll();
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+        // Verify timer is scheduled
+        verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
     }
 
     @Test
-    public void resetsNumAssociatedStationsOnFailure() throws Exception {
+    public void cancelsTimeoutTimerOnStop() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApManager.stop();
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void cancelsTimeoutTimerOnNewClientsConnect() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+                TEST_NUM_CONNECTED_CLIENTS);
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void schedulesTimeoutTimerWhenAllClientsDisconnect() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
-        mApInterfaceListenerCaptor.getValue().onNumAssociatedStationsChanged(
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
                 TEST_NUM_CONNECTED_CLIENTS);
-        /* Force soft AP to fail */
-        mDeathListenerCaptor.getValue().binderDied();
         mLooper.dispatchAll();
-        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
-                WifiManager.SAP_START_FAILURE_GENERAL);
+        // Verify timer is canceled at this point
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
 
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+        mLooper.dispatchAll();
+        // Verify timer is scheduled again
+        verify(mAlarmManager.getAlarmManager(), times(2)).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void stopsSoftApOnTimeoutMessage() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        mAlarmManager.dispatch(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG);
+        mLooper.dispatchAll();
+
+        verify(mWifiNative).stopSoftAp();
+    }
+
+    @Test
+    public void cancelsTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        mContentObserver.onChange(false);
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void schedulesTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+        mContentObserver.onChange(false);
+        mLooper.dispatchAll();
+
+        // Verify timer is scheduled
+        verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void doesNotScheduleTimeoutTimerOnStartWhenTimeoutIsDisabled() throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        // Verify timer is not scheduled
+        verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void doesNotScheduleTimeoutTimerWhenAllClientsDisconnectButTimeoutIsDisabled()
+            throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        // add some clients
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+                TEST_NUM_CONNECTED_CLIENTS);
+        mLooper.dispatchAll();
+        // remove all clients
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+        mLooper.dispatchAll();
+        // Verify timer is not scheduled
+        verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void unregistersSettingsObserverOnStop() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApManager.stop();
+        mLooper.dispatchAll();
+
+        verify(mFrameworkFacade).unregisterContentObserver(eq(mContext), eq(mContentObserver));
     }
 
     /** Starts soft AP and verifies that it is enabled successfully. */
     protected void startSoftApAndVerifyEnabled(
             SoftApModeConfiguration softApConfig) throws Exception {
-        String expectedSSID;
-        boolean expectedHiddenSsid;
-        InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService);
+        WifiConfiguration expectedConfig;
+        InOrder order =
+                inOrder(mListener, mApInterface, mWifiNative, mNmService);
 
         when(mWifiNative.isHalStarted()).thenReturn(false);
         when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
@@ -505,24 +623,25 @@
         WifiConfiguration config = softApConfig.getWifiConfiguration();
         if (config == null) {
             when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
-            expectedSSID = mDefaultApConfig.SSID;
-            expectedHiddenSsid = mDefaultApConfig.hiddenSSID;
+            expectedConfig = new WifiConfiguration(mDefaultApConfig);
         } else {
-            expectedSSID = config.SSID;
-            expectedHiddenSsid = config.hiddenSSID;
+            expectedConfig = new WifiConfiguration(config);
         }
 
+        ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(
+                ContentObserver.class);
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
 
         mSoftApManager.start();
         mLooper.dispatchAll();
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
-        order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0));
+        order.verify(mWifiNative).registerWificondDeathHandler(mDeathListenerCaptor.capture());
         order.verify(mNmService).registerObserver(mNetworkObserverCaptor.capture());
-        order.verify(mApInterface).writeHostapdConfig(
-                eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), eq(expectedHiddenSsid),
-                anyInt(), anyInt(), any());
-        order.verify(mApInterface).startHostapd(mApInterfaceListenerCaptor.capture());
+        ArgumentCaptor<WifiConfiguration> configCaptor =
+                ArgumentCaptor.forClass(WifiConfiguration.class);
+        order.verify(mWifiNative).startSoftAp(
+                configCaptor.capture(), mSoftApListenerCaptor.capture());
+        WifiConfigurationTestUtil.assertConfigurationEqual(expectedConfig, configCaptor.getValue());
         mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true);
         mLooper.dispatchAll();
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
@@ -535,8 +654,10 @@
         checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED,
                 WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
                 softApConfig.getTargetMode());
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.mTargetMode);
+        verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true),
+                observerCaptor.capture());
+        mContentObserver = observerCaptor.getValue();
     }
 
     /** Verifies that soft AP was not disabled. */
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
index a7bb192..0303d9a 100644
--- a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
@@ -23,15 +23,7 @@
 import static org.mockito.Matchers.anyShort;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 import android.app.test.MockAnswerUtil;
 import android.content.Context;
@@ -62,6 +54,8 @@
 import com.android.server.wifi.hotspot2.WnmData;
 import com.android.server.wifi.util.NativeUtil;
 
+import libcore.util.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -91,7 +85,8 @@
     private static final String SUPPLICANT_SSID = NETWORK_ID_TO_SSID.get(SUPPLICANT_NETWORK_ID);
     private static final int ROAM_NETWORK_ID = 4;
     private static final String BSSID = "fa:45:23:23:12:12";
-    private static final String WLAN_IFACE_NAME = "wlan0";
+    private static final String WLAN0_IFACE_NAME = "wlan0";
+    private static final String WLAN1_IFACE_NAME = "wlan1";
     private static final String P2P_IFACE_NAME = "p2p0";
     private static final String ICON_FILE_NAME  = "blahblah";
     private static final int ICON_FILE_SIZE = 72;
@@ -104,9 +99,11 @@
     @Mock Context mContext;
     @Mock WifiMonitor mWifiMonitor;
     @Mock SupplicantStaNetworkHal mSupplicantStaNetworkMock;
+    @Mock WifiNative.SupplicantDeathEventHandler mSupplicantHalDeathHandler;
     SupplicantStatus mStatusSuccess;
     SupplicantStatus mStatusFailure;
-    ISupplicant.IfaceInfo mStaIface;
+    ISupplicant.IfaceInfo mStaIface0;
+    ISupplicant.IfaceInfo mStaIface1;
     ISupplicant.IfaceInfo mP2pIface;
     ArrayList<ISupplicant.IfaceInfo> mIfaceInfoList;
     ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
@@ -143,6 +140,7 @@
 
         @Override
         protected SupplicantStaNetworkHal getStaNetworkMockable(
+                @NonNull String ifaceName,
                 ISupplicantStaNetwork iSupplicantStaNetwork) {
             return mSupplicantStaNetworkMock;
         }
@@ -153,11 +151,13 @@
         MockitoAnnotations.initMocks(this);
         mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
         mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
-        mStaIface = createIfaceInfo(IfaceType.STA, WLAN_IFACE_NAME);
+        mStaIface0 = createIfaceInfo(IfaceType.STA, WLAN0_IFACE_NAME);
+        mStaIface1 = createIfaceInfo(IfaceType.STA, WLAN1_IFACE_NAME);
         mP2pIface = createIfaceInfo(IfaceType.P2P, P2P_IFACE_NAME);
 
         mIfaceInfoList = new ArrayList<>();
-        mIfaceInfoList.add(mStaIface);
+        mIfaceInfoList.add(mStaIface0);
+        mIfaceInfoList.add(mStaIface1);
         mIfaceInfoList.add(mP2pIface);
 
         when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
@@ -216,6 +216,50 @@
         executeAndValidateInitializationSequence(false, false, false, true);
     }
 
+
+    /**
+     * Sunny day scenario for SupplicantStaIfaceHal initialization
+     * Asserts successful initialization of second interface
+     */
+    @Test
+    public void testSetupTwoInterfaces() throws Exception {
+        executeAndValidateInitializationSequence(false, false, false, false);
+        assertTrue(mDut.setupIface(WLAN1_IFACE_NAME));
+    }
+
+    /**
+     * Ensures that we do not allow operations on an interface until it's setup.
+     */
+    @Test
+    public void testEnsureOperationFailsUntilSetupInterfaces() throws Exception {
+        executeAndValidateInitializationSequence(false, false, false, false);
+
+        // Ensure that the cancel wps operation is failed because wlan1 interface is not yet setup.
+        assertFalse(mDut.cancelWps(WLAN1_IFACE_NAME));
+        verify(mISupplicantStaIfaceMock, never()).cancelWps();
+
+        // Now setup the wlan1 interface and Ensure that the cancel wps operation is successful.
+        assertTrue(mDut.setupIface(WLAN1_IFACE_NAME));
+        when(mISupplicantStaIfaceMock.cancelWps()).thenReturn(mStatusSuccess);
+        assertTrue(mDut.cancelWps(WLAN1_IFACE_NAME));
+        verify(mISupplicantStaIfaceMock).cancelWps();
+    }
+
+    /**
+     * Sunny day scenario for SupplicantStaIfaceHal interface teardown.
+     * Asserts successful initialization of second interface
+     */
+    @Test
+    public void testTeardownTwoInterfaces() throws Exception {
+        testSetupTwoInterfaces();
+        assertTrue(mDut.teardownIface(WLAN0_IFACE_NAME));
+        assertTrue(mDut.teardownIface(WLAN1_IFACE_NAME));
+
+        // Ensure that the cancel wps operation is failed because there are no interfaces setup.
+        assertFalse(mDut.cancelWps(WLAN0_IFACE_NAME));
+        verify(mISupplicantStaIfaceMock, never()).cancelWps();
+    }
+
     /**
      * Tests the loading of networks using {@link SupplicantStaNetworkHal}.
      * Fills up only the SSID field of configs and uses it as a configKey as well.
@@ -251,7 +295,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertTrue(mDut.loadNetworks(configs, extras));
+        assertTrue(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
 
         assertEquals(3, configs.size());
         assertEquals(3, extras.size());
@@ -317,7 +361,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertTrue(mDut.loadNetworks(configs, extras));
+        assertTrue(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
 
         assertEquals(2, configs.size());
         assertEquals(2, extras.size());
@@ -356,7 +400,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertFalse(mDut.loadNetworks(configs, extras));
+        assertFalse(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
     }
 
     /**
@@ -381,7 +425,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertFalse(mDut.loadNetworks(configs, extras));
+        assertFalse(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
     }
 
     /**
@@ -412,7 +456,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertTrue(mDut.loadNetworks(configs, extras));
+        assertTrue(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
         assertTrue(configs.isEmpty());
     }
 
@@ -445,7 +489,7 @@
 
         Map<String, WifiConfiguration> configs = new HashMap<>();
         SparseArray<Map<String, String>> extras = new SparseArray<>();
-        assertTrue(mDut.loadNetworks(configs, extras));
+        assertTrue(mDut.loadNetworks(WLAN0_IFACE_NAME, configs, extras));
         assertTrue(configs.isEmpty());
     }
 
@@ -469,7 +513,7 @@
         setupMocksForConnectSequence(true /*haveExistingNetwork*/);
         // Make this network different by changing SSID.
         config.SSID = "AnDifferentSSID";
-        assertTrue(mDut.connectToNetwork(config));
+        assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config));
         verify(mISupplicantStaIfaceMock).removeNetwork(SUPPLICANT_NETWORK_ID);
         verify(mISupplicantStaIfaceMock)
                 .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
@@ -483,7 +527,7 @@
         // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
         reset(mISupplicantStaIfaceMock);
         setupMocksForConnectSequence(true /*haveExistingNetwork*/);
-        assertTrue(mDut.connectToNetwork(config));
+        assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config));
         verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
         verify(mISupplicantStaIfaceMock, never())
                 .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
@@ -504,7 +548,7 @@
         assertFalse(TextUtils.equals(
                 testBssid, config.getNetworkSelectionStatus().getNetworkSelectionBSSID()));
         config.getNetworkSelectionStatus().setNetworkSelectionBSSID(testBssid);
-        assertTrue(mDut.connectToNetwork(config));
+        assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config));
         verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
         verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
         verify(mISupplicantStaIfaceMock, never())
@@ -526,7 +570,7 @@
         }).when(mISupplicantStaIfaceMock).addNetwork(
                 any(ISupplicantStaIface.addNetworkCallback.class));
 
-        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+        assertFalse(mDut.connectToNetwork(WLAN0_IFACE_NAME, createTestWifiConfiguration()));
     }
 
     /**
@@ -540,7 +584,7 @@
         when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
                 .thenReturn(false);
 
-        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+        assertFalse(mDut.connectToNetwork(WLAN0_IFACE_NAME, createTestWifiConfiguration()));
         // We should have removed the existing network once before connection and once more
         // on failure to save network configuration.
         verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
@@ -558,7 +602,7 @@
                 .when(mSupplicantStaNetworkMock).saveWifiConfiguration(
                         any(WifiConfiguration.class));
 
-        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+        assertFalse(mDut.connectToNetwork(WLAN0_IFACE_NAME, createTestWifiConfiguration()));
         // We should have removed the existing network once before connection and once more
         // on failure to save network configuration.
         verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
@@ -574,7 +618,7 @@
 
         when(mSupplicantStaNetworkMock.select()).thenReturn(false);
 
-        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+        assertFalse(mDut.connectToNetwork(WLAN0_IFACE_NAME, createTestWifiConfiguration()));
     }
 
     /**
@@ -584,7 +628,7 @@
     public void testRoamToSameNetwork() throws Exception {
         executeAndValidateInitializationSequence();
         executeAndValidateRoamSequence(true);
-        assertTrue(mDut.connectToNetwork(createTestWifiConfiguration()));
+        assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, createTestWifiConfiguration()));
     }
 
     /**
@@ -609,7 +653,7 @@
         WifiConfiguration roamingConfig = new WifiConfiguration();
         roamingConfig.networkId = connectedNetworkId;
         roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
-        assertFalse(mDut.roamToNetwork(roamingConfig));
+        assertFalse(mDut.roamToNetwork(WLAN0_IFACE_NAME, roamingConfig));
     }
 
     /**
@@ -631,7 +675,7 @@
             }
         }).when(mISupplicantStaIfaceMock).removeNetwork(anyInt());
 
-        assertTrue(mDut.removeAllNetworks());
+        assertTrue(mDut.removeAllNetworks(WLAN0_IFACE_NAME));
         verify(mISupplicantStaIfaceMock, times(NETWORK_ID_TO_SSID.size())).removeNetwork(anyInt());
     }
 
@@ -647,13 +691,13 @@
 
         // Connect to a network and verify current network is set.
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.setCurrentNetworkBssid(testBssid));
+        assertTrue(mDut.setCurrentNetworkBssid(WLAN0_IFACE_NAME, testBssid));
         verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
         reset(mSupplicantStaNetworkMock);
 
         // Remove all networks and verify current network info is resetted.
-        assertTrue(mDut.removeAllNetworks());
-        assertFalse(mDut.setCurrentNetworkBssid(testBssid));
+        assertTrue(mDut.removeAllNetworks(WLAN0_IFACE_NAME));
+        assertFalse(mDut.setCurrentNetworkBssid(WLAN0_IFACE_NAME, testBssid));
         verify(mSupplicantStaNetworkMock, never()).setBssid(eq(testBssid));
     }
 
@@ -676,7 +720,7 @@
         WifiConfiguration roamingConfig = new WifiConfiguration();
         roamingConfig.networkId = connectedNetworkId;
         roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
-        assertFalse(mDut.roamToNetwork(roamingConfig));
+        assertFalse(mDut.roamToNetwork(WLAN0_IFACE_NAME, roamingConfig));
     }
 
     /**
@@ -689,10 +733,10 @@
 
         executeAndValidateInitializationSequence();
         // Return null when not connected to the network.
-        assertTrue(mDut.getCurrentNetworkWpsNfcConfigurationToken() == null);
+        assertTrue(mDut.getCurrentNetworkWpsNfcConfigurationToken(WLAN0_IFACE_NAME) == null);
         verify(mSupplicantStaNetworkMock, never()).getWpsNfcConfigurationToken();
         executeAndValidateConnectSequence(4, false);
-        assertEquals(token, mDut.getCurrentNetworkWpsNfcConfigurationToken());
+        assertEquals(token, mDut.getCurrentNetworkWpsNfcConfigurationToken(WLAN0_IFACE_NAME));
         verify(mSupplicantStaNetworkMock).getWpsNfcConfigurationToken();
     }
 
@@ -706,10 +750,10 @@
 
         executeAndValidateInitializationSequence();
         // Fail when not connected to a network.
-        assertFalse(mDut.setCurrentNetworkBssid(bssidStr));
+        assertFalse(mDut.setCurrentNetworkBssid(WLAN0_IFACE_NAME, bssidStr));
         verify(mSupplicantStaNetworkMock, never()).setBssid(eq(bssidStr));
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.setCurrentNetworkBssid(bssidStr));
+        assertTrue(mDut.setCurrentNetworkBssid(WLAN0_IFACE_NAME, bssidStr));
         verify(mSupplicantStaNetworkMock).setBssid(eq(bssidStr));
     }
 
@@ -724,10 +768,10 @@
 
         executeAndValidateInitializationSequence();
         // Fail when not connected to a network.
-        assertFalse(mDut.sendCurrentNetworkEapIdentityResponse(identity));
+        assertFalse(mDut.sendCurrentNetworkEapIdentityResponse(WLAN0_IFACE_NAME, identity));
         verify(mSupplicantStaNetworkMock, never()).sendNetworkEapIdentityResponse(eq(identity));
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.sendCurrentNetworkEapIdentityResponse(identity));
+        assertTrue(mDut.sendCurrentNetworkEapIdentityResponse(WLAN0_IFACE_NAME, identity));
         verify(mSupplicantStaNetworkMock).sendNetworkEapIdentityResponse(eq(identity));
     }
 
@@ -742,10 +786,11 @@
         executeAndValidateInitializationSequence();
 
         // Return null when not connected to the network.
-        assertEquals(null, mDut.getCurrentNetworkEapAnonymousIdentity());
+        assertEquals(null, mDut.getCurrentNetworkEapAnonymousIdentity(WLAN0_IFACE_NAME));
         executeAndValidateConnectSequence(4, false);
         // Return anonymous identity for the current network.
-        assertEquals(anonymousIdentity, mDut.getCurrentNetworkEapAnonymousIdentity());
+        assertEquals(
+                anonymousIdentity, mDut.getCurrentNetworkEapAnonymousIdentity(WLAN0_IFACE_NAME));
     }
 
     /**
@@ -759,10 +804,10 @@
 
         executeAndValidateInitializationSequence();
         // Fail when not connected to a network.
-        assertFalse(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
+        assertFalse(mDut.sendCurrentNetworkEapSimGsmAuthResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimGsmAuthResponse(eq(params));
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
+        assertTrue(mDut.sendCurrentNetworkEapSimGsmAuthResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(eq(params));
     }
 
@@ -777,10 +822,10 @@
 
         executeAndValidateInitializationSequence();
         // Fail when not connected to a network.
-        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
+        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAuthResponse(eq(params));
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
+        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAuthResponse(eq(params));
     }
 
@@ -795,10 +840,10 @@
 
         executeAndValidateInitializationSequence();
         // Fail when not connected to a network.
-        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
+        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAutsResponse(eq(params));
         executeAndValidateConnectSequence(4, false);
-        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
+        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(WLAN0_IFACE_NAME, params));
         verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAutsResponse(eq(params));
     }
 
@@ -817,13 +862,13 @@
         executeAndValidateInitializationSequence();
 
         // This should work.
-        assertTrue(mDut.setWpsDeviceType(validDeviceTypeStr));
+        assertTrue(mDut.setWpsDeviceType(WLAN0_IFACE_NAME, validDeviceTypeStr));
         verify(mISupplicantStaIfaceMock).setWpsDeviceType(eq(expectedDeviceType));
 
         // This should not work
-        assertFalse(mDut.setWpsDeviceType(invalidDeviceType1Str));
+        assertFalse(mDut.setWpsDeviceType(WLAN0_IFACE_NAME, invalidDeviceType1Str));
         // This should not work
-        assertFalse(mDut.setWpsDeviceType(invalidDeviceType2Str));
+        assertFalse(mDut.setWpsDeviceType(WLAN0_IFACE_NAME, invalidDeviceType2Str));
     }
 
     /**
@@ -840,12 +885,12 @@
         executeAndValidateInitializationSequence();
 
         // This should work.
-        assertTrue(mDut.setWpsConfigMethods(validConfigMethodsStr));
+        assertTrue(mDut.setWpsConfigMethods(WLAN0_IFACE_NAME, validConfigMethodsStr));
         verify(mISupplicantStaIfaceMock).setWpsConfigMethods(eq(expectedConfigMethods));
 
         // This should throw an illegal argument exception.
         try {
-            assertFalse(mDut.setWpsConfigMethods(invalidConfigMethodsStr));
+            assertFalse(mDut.setWpsConfigMethods(WLAN0_IFACE_NAME, invalidConfigMethodsStr));
         } catch (IllegalArgumentException e) {
             return;
         }
@@ -868,7 +913,8 @@
                 new ISupplicantStaIfaceCallback.Hs20AnqpData());
 
         ArgumentCaptor<AnqpEvent> anqpEventCaptor = ArgumentCaptor.forClass(AnqpEvent.class);
-        verify(mWifiMonitor).broadcastAnqpDoneEvent(eq(WLAN_IFACE_NAME), anqpEventCaptor.capture());
+        verify(mWifiMonitor).broadcastAnqpDoneEvent(
+                eq(WLAN0_IFACE_NAME), anqpEventCaptor.capture());
         assertEquals(
                 ByteBufferReader.readInteger(
                         ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
@@ -890,7 +936,8 @@
                 bssid, ICON_FILE_NAME, NativeUtil.byteArrayToArrayList(iconData));
 
         ArgumentCaptor<IconEvent> iconEventCaptor = ArgumentCaptor.forClass(IconEvent.class);
-        verify(mWifiMonitor).broadcastIconDoneEvent(eq(WLAN_IFACE_NAME), iconEventCaptor.capture());
+        verify(mWifiMonitor).broadcastIconDoneEvent(
+                eq(WLAN0_IFACE_NAME), iconEventCaptor.capture());
         assertEquals(
                 ByteBufferReader.readInteger(
                         ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
@@ -913,7 +960,7 @@
                 bssid, osuMethod, HS20_URL);
 
         ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
-        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
+        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN0_IFACE_NAME), wnmDataCaptor.capture());
         assertEquals(
                 ByteBufferReader.readInteger(
                         ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
@@ -954,7 +1001,7 @@
 
         // Can't compare WifiSsid instances because they lack an equals.
         verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
-                eq(WLAN_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID),
+                eq(WLAN0_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID),
                 any(WifiSsid.class), eq(BSSID), eq(SupplicantState.INACTIVE));
     }
 
@@ -974,7 +1021,7 @@
                 NativeUtil.decodeSsid(SUPPLICANT_SSID));
 
         verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
-                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
+                eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId),
                 any(WifiSsid.class), eq(BSSID), eq(SupplicantState.ASSOCIATED));
     }
 
@@ -995,9 +1042,9 @@
                 NativeUtil.decodeSsid(SUPPLICANT_SSID));
 
         wifiMonitorInOrder.verify(mWifiMonitor).broadcastNetworkConnectionEvent(
-                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID));
+                eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID));
         wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
-                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
+                eq(WLAN0_IFACE_NAME), eq(frameworkNetworkId),
                 any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED));
     }
 
@@ -1013,12 +1060,12 @@
         mISupplicantStaIfaceCallback.onDisconnected(
                 NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
         verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
-                eq(WLAN_IFACE_NAME), eq(1), eq(reasonCode), eq(BSSID));
+                eq(WLAN0_IFACE_NAME), eq(1), eq(reasonCode), eq(BSSID));
 
         mISupplicantStaIfaceCallback.onDisconnected(
                 NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
         verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
-                eq(WLAN_IFACE_NAME), eq(0), eq(reasonCode), eq(BSSID));
+                eq(WLAN0_IFACE_NAME), eq(0), eq(reasonCode), eq(BSSID));
     }
 
     /**
@@ -1048,7 +1095,7 @@
         mISupplicantStaIfaceCallback.onDisconnected(
                 NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
 
-        verify(mWifiMonitor, times(2)).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+        verify(mWifiMonitor, times(2)).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME),
                 eq(WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD));
 
     }
@@ -1086,7 +1133,7 @@
         int reasonCode = ISupplicantStaIfaceCallback.ReasonCode.IEEE_802_1X_AUTH_FAILED;
         mISupplicantStaIfaceCallback.onDisconnected(
                 NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
-        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME),
                 eq(WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE));
     }
 
@@ -1102,7 +1149,7 @@
         mISupplicantStaIfaceCallback.onAssociationRejected(
                 NativeUtil.macAddressToByteArray(BSSID), statusCode, false);
         verify(mWifiMonitor).broadcastAssociationRejectionEvent(
-                eq(WLAN_IFACE_NAME), eq(statusCode), eq(false), eq(BSSID));
+                eq(WLAN0_IFACE_NAME), eq(statusCode), eq(false), eq(BSSID));
     }
 
     /**
@@ -1115,7 +1162,7 @@
 
         mISupplicantStaIfaceCallback.onAuthenticationTimeout(
                 NativeUtil.macAddressToByteArray(BSSID));
-        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME),
                 eq(WifiManager.ERROR_AUTH_FAILURE_TIMEOUT));
     }
 
@@ -1129,20 +1176,22 @@
 
         mISupplicantStaIfaceCallback.onBssidChanged(
                 BssidChangeReason.ASSOC_START, NativeUtil.macAddressToByteArray(BSSID));
-        verify(mWifiMonitor).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
-        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor).broadcastTargetBssidEvent(eq(WLAN0_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(
+                eq(WLAN0_IFACE_NAME), eq(BSSID));
 
         reset(mWifiMonitor);
         mISupplicantStaIfaceCallback.onBssidChanged(
                 BssidChangeReason.ASSOC_COMPLETE, NativeUtil.macAddressToByteArray(BSSID));
-        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
-        verify(mWifiMonitor).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN0_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor).broadcastAssociatedBssidEvent(eq(WLAN0_IFACE_NAME), eq(BSSID));
 
         reset(mWifiMonitor);
         mISupplicantStaIfaceCallback.onBssidChanged(
                 BssidChangeReason.DISASSOC, NativeUtil.macAddressToByteArray(BSSID));
-        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
-        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN0_IFACE_NAME), eq(BSSID));
+        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(
+                eq(WLAN0_IFACE_NAME), eq(BSSID));
     }
 
     /**
@@ -1154,7 +1203,7 @@
         assertNotNull(mISupplicantStaIfaceCallback);
 
         mISupplicantStaIfaceCallback.onEapFailure();
-        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME),
                 eq(WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE));
     }
 
@@ -1167,7 +1216,7 @@
         assertNotNull(mISupplicantStaIfaceCallback);
 
         mISupplicantStaIfaceCallback.onWpsEventSuccess();
-        verify(mWifiMonitor).broadcastWpsSuccessEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastWpsSuccessEvent(eq(WLAN0_IFACE_NAME));
     }
 
     /**
@@ -1182,7 +1231,7 @@
         short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.SECURITY_WEP_PROHIBITED;
         mISupplicantStaIfaceCallback.onWpsEventFail(
                 NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
-        verify(mWifiMonitor).broadcastWpsFailEvent(eq(WLAN_IFACE_NAME),
+        verify(mWifiMonitor).broadcastWpsFailEvent(eq(WLAN0_IFACE_NAME),
                 eq((int) cfgError), eq((int) errorInd));
     }
 
@@ -1198,7 +1247,7 @@
         short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.NO_ERROR;
         mISupplicantStaIfaceCallback.onWpsEventFail(
                 NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
-        verify(mWifiMonitor).broadcastWpsTimeoutEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastWpsTimeoutEvent(eq(WLAN0_IFACE_NAME));
     }
 
     /**
@@ -1210,7 +1259,7 @@
         assertNotNull(mISupplicantStaIfaceCallback);
 
         mISupplicantStaIfaceCallback.onWpsEventPbcOverlap();
-        verify(mWifiMonitor).broadcastWpsOverlapEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastWpsOverlapEvent(eq(WLAN0_IFACE_NAME));
     }
 
     /**
@@ -1221,11 +1270,13 @@
         executeAndValidateInitializationSequence();
         assertNotNull(mServiceManagerDeathCaptor.getValue());
         assertTrue(mDut.isInitializationComplete());
+        assertTrue(mDut.registerDeathHandler(mSupplicantHalDeathHandler));
 
         mServiceManagerDeathCaptor.getValue().serviceDied(5L);
 
         assertFalse(mDut.isInitializationComplete());
-        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN0_IFACE_NAME));
+        verify(mSupplicantHalDeathHandler).onDeath();
     }
 
     /**
@@ -1236,11 +1287,13 @@
         executeAndValidateInitializationSequence();
         assertNotNull(mSupplicantDeathCaptor.getValue());
         assertTrue(mDut.isInitializationComplete());
+        assertTrue(mDut.registerDeathHandler(mSupplicantHalDeathHandler));
 
         mSupplicantDeathCaptor.getValue().serviceDied(5L);
 
         assertFalse(mDut.isInitializationComplete());
-        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN0_IFACE_NAME));
+        verify(mSupplicantHalDeathHandler).onDeath();
     }
 
     /**
@@ -1255,7 +1308,7 @@
         mSupplicantStaIfaceDeathCaptor.getValue().serviceDied(5L);
 
         assertFalse(mDut.isInitializationComplete());
-        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN0_IFACE_NAME));
     }
 
     /**
@@ -1305,17 +1358,17 @@
                 .thenReturn(mStatusSuccess);
 
         // Fail before initialization is performed.
-        assertFalse(mDut.startWpsRegistrar(null, null));
+        assertFalse(mDut.startWpsRegistrar(WLAN0_IFACE_NAME, null, null));
 
         executeAndValidateInitializationSequence();
 
-        assertFalse(mDut.startWpsRegistrar(null, null));
+        assertFalse(mDut.startWpsRegistrar(WLAN0_IFACE_NAME, null, null));
         verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
 
-        assertFalse(mDut.startWpsRegistrar(new String(), "452233"));
+        assertFalse(mDut.startWpsRegistrar(WLAN0_IFACE_NAME, new String(), "452233"));
         verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
 
-        assertTrue(mDut.startWpsRegistrar("45:23:12:12:12:98", "562535"));
+        assertTrue(mDut.startWpsRegistrar(WLAN0_IFACE_NAME, "45:23:12:12:12:98", "562535"));
         verify(mISupplicantStaIfaceMock).startWpsRegistrar(any(byte[].class), anyString());
     }
 
@@ -1330,15 +1383,15 @@
         byte[] anyBssidBytes = {0, 0, 0, 0, 0, 0};
 
         // Fail before initialization is performed.
-        assertFalse(mDut.startWpsPbc(bssid));
+        assertFalse(mDut.startWpsPbc(WLAN0_IFACE_NAME, bssid));
         verify(mISupplicantStaIfaceMock, never()).startWpsPbc(any(byte[].class));
 
         executeAndValidateInitializationSequence();
 
-        assertTrue(mDut.startWpsPbc(bssid));
+        assertTrue(mDut.startWpsPbc(WLAN0_IFACE_NAME, bssid));
         verify(mISupplicantStaIfaceMock).startWpsPbc(eq(bssidBytes));
 
-        assertTrue(mDut.startWpsPbc(null));
+        assertTrue(mDut.startWpsPbc(WLAN0_IFACE_NAME, null));
         verify(mISupplicantStaIfaceMock).startWpsPbc(eq(anyBssidBytes));
     }
 
@@ -1359,7 +1412,7 @@
                 bssid, reasonCode, reauthDelay, HS20_URL);
 
         ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
-        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
+        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN0_IFACE_NAME), wnmDataCaptor.capture());
         assertEquals(
                 ByteBufferReader.readInteger(
                         ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
@@ -1436,7 +1489,8 @@
         // act: cause the onRegistration(...) callback to execute
         mServiceNotificationCaptor.getValue().onRegistration(ISupplicant.kInterfaceName, "", true);
 
-        assertTrue(mDut.isInitializationComplete() == shouldSucceed);
+        assertTrue(mDut.isInitializationComplete());
+        assertTrue(mDut.setupIface(WLAN0_IFACE_NAME) == shouldSucceed);
         mInOrder.verify(mISupplicantMock).linkToDeath(mSupplicantDeathCaptor.capture(),
                 anyLong());
         // verify: listInterfaces is called
@@ -1448,7 +1502,8 @@
                             any(ISupplicant.getInterfaceCallback.class));
         }
         if (causeRemoteException) {
-            mInOrder.verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(null));
+            mInOrder.verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(
+                    eq(WLAN0_IFACE_NAME));
         }
         if (!causeRemoteException && !getZeroInterfaces && !getNullInterface) {
             mInOrder.verify(mISupplicantStaIfaceMock).linkToDeath(
@@ -1566,7 +1621,7 @@
         setupMocksForConnectSequence(haveExistingNetwork);
         WifiConfiguration config = new WifiConfiguration();
         config.networkId = newFrameworkNetworkId;
-        assertTrue(mDut.connectToNetwork(config));
+        assertTrue(mDut.connectToNetwork(WLAN0_IFACE_NAME, config));
         validateConnectSequence(haveExistingNetwork, 1);
         return config;
     }
@@ -1603,7 +1658,7 @@
         WifiConfiguration roamingConfig = new WifiConfiguration();
         roamingConfig.networkId = roamNetworkId;
         roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID(roamBssid);
-        assertTrue(mDut.roamToNetwork(roamingConfig));
+        assertTrue(mDut.roamToNetwork(WLAN0_IFACE_NAME, roamingConfig));
 
         if (!sameNetwork) {
             validateConnectSequence(false, 2);
diff --git a/tests/wifitests/src/com/android/server/wifi/TestUtil.java b/tests/wifitests/src/com/android/server/wifi/TestUtil.java
index a0a1030..ab2cda5 100644
--- a/tests/wifitests/src/com/android/server/wifi/TestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/TestUtil.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 
 import java.util.ArrayList;
@@ -42,6 +43,17 @@
     }
 
     /**
+     * Send {@link WifiManager#NETWORK_STATE_CHANGED_ACTION} broadcast.
+     */
+    public static void sendNetworkStateChanged(BroadcastReceiver broadcastReceiver,
+            Context context, NetworkInfo nwInfo, WifiInfo wifiInfo) {
+        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, nwInfo);
+        intent.putExtra(WifiManager.EXTRA_WIFI_INFO, wifiInfo);
+        broadcastReceiver.onReceive(context, intent);
+    }
+
+    /**
      * Send {@link WifiManager#SCAN_RESULTS_AVAILABLE_ACTION} broadcast.
      */
     public static void sendScanResultsAvailable(BroadcastReceiver broadcastReceiver,
@@ -85,7 +97,6 @@
         }
         intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName);
         intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode);
-
         broadcastReceiver.onReceive(context, intent);
     }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java b/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
index 263cbed..6a72522 100644
--- a/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
@@ -100,9 +100,9 @@
     public void allowLowRssiIfErrorRateIsLowAndSomeDataIsMoving() throws Exception {
         mWifiInfo.setRssi(mRssiExitThreshold2GHz - 2);
         mWifiInfo.setLinkSpeed(6); // Mbps
-        mWifiInfo.txSuccessRate = 10.1; // proportional to pps
+        mWifiInfo.txSuccessRate = 2.1; // proportional to pps
         mWifiInfo.txBadRate = .5;
-        mWifiInfo.rxSuccessRate = 10.1;
+        mWifiInfo.rxSuccessRate = 2.1;
         for (int i = 0; i < 10; i++) {
             mVelocityBasedConnectedScore.updateUsingWifiInfo(mWifiInfo,
                     mClock.getWallClockMillis());
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
new file mode 100644
index 0000000..6db9cf5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import com.google.android.collect.Sets;
+
+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.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link WakeupConfigStoreData}.
+ */
+public class WakeupConfigStoreDataTest {
+
+    @Mock private WakeupConfigStoreData.DataSource<Boolean> mActiveDataSource;
+    @Mock private WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    private WakeupConfigStoreData mWakeupConfigData;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWakeupConfigData = new WakeupConfigStoreData(mActiveDataSource, mNetworkDataSource);
+    }
+
+    /**
+     * Helper function for serializing configuration data to a XML block.
+     *
+     * @param shared Flag indicating serializing shared or user configurations
+     * @return byte[] of the XML data
+     */
+    private byte[] serializeData(boolean shared) throws Exception {
+        final XmlSerializer out = new FastXmlSerializer();
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.serializeData(out, shared);
+        out.flush();
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * Helper function for parsing configuration data from a XML block.
+     *
+     * @param data XML data to parse from
+     * @param shared Flag indicating parsing of shared or user configurations
+     */
+    private void deserializeData(byte[] data, boolean shared) throws Exception {
+        final XmlPullParser in = Xml.newPullParser();
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+        in.setInput(inputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.deserializeData(in, in.getDepth(), shared);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to serialize data
+     * to the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void serializeShareDataThrowsException() throws Exception {
+        serializeData(true /* shared */);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to deserialize
+     * data from the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void deserializeShareDataThrowsException() throws Exception {
+        deserializeData(new byte[0], true /* shared */);
+    }
+
+    /**
+     * Can correctly serialize and deserialize the empty set case.
+     */
+    @Test
+    public void deserializeSerializedData_emptySet() throws Exception {
+        Set<ScanResultMatchInfo> networks = Collections.emptySet();
+        boolean isActive = false;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Can correctly serialize and deserialize a standard working case.
+     */
+    @Test
+    public void deserializeSerializedData() throws Exception {
+        ScanResultMatchInfo network1 = new ScanResultMatchInfo();
+        network1.networkSsid = "ssid 1";
+        network1.networkType = 0;
+
+        ScanResultMatchInfo network2 = new ScanResultMatchInfo();
+        network2.networkSsid = ",.23;4@, .#,%(,";
+        network2.networkType = 1;
+
+        ScanResultMatchInfo network3 = new ScanResultMatchInfo();
+        network3.networkSsid = "";
+        network3.networkType = 2;
+
+        Set<ScanResultMatchInfo> networks = Sets.newArraySet(network1, network2, network3);
+        boolean isActive = true;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Verify that reset data wipes the data sources.
+     */
+    @Test
+    public void resetDataWipesDataSources() {
+        mWakeupConfigData.resetData(false /* shared */);
+
+        verify(mActiveDataSource).setData(false);
+        verify(mNetworkDataSource).setData(eq(Collections.emptySet()));
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
index 742c520..5e45570 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -29,12 +31,18 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
 /**
  * Unit tests for {@link WakeupController}.
  */
 public class WakeupControllerTest {
 
     @Mock private Context mContext;
+    @Mock private WakeupLock mWakeupLock;
+    @Mock private WifiConfigManager mWifiConfigManager;
+    @Mock private WifiConfigStore mWifiConfigStore;
     @Mock private FrameworkFacade mFrameworkFacade;
 
     private TestLooper mLooper;
@@ -47,6 +55,11 @@
         mLooper = new TestLooper();
     }
 
+    private WakeupController newWakeupController() {
+        return new WakeupController(mContext, mLooper.getLooper(), mWakeupLock, mWifiConfigManager,
+                mWifiConfigStore, mFrameworkFacade);
+    }
+
     /**
      * Verify WakeupController is enabled when the settings toggle is true.
      */
@@ -54,8 +67,7 @@
     public void verifyEnabledWhenToggledOn() {
         when(mFrameworkFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(1);
-        mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
-                mFrameworkFacade);
+        mWakeupController = newWakeupController();
 
         assertTrue(mWakeupController.isEnabled());
     }
@@ -67,9 +79,30 @@
     public void verifyDisabledWhenToggledOff() {
         when(mFrameworkFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(0);
-        mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
-                mFrameworkFacade);
+        mWakeupController = newWakeupController();
 
         assertFalse(mWakeupController.isEnabled());
     }
+
+    /**
+     * Verify WakeupController registers its store data with the WifiConfigStore on construction.
+     */
+    @Test
+    public void registersWakeupConfigStoreData() {
+        mWakeupController = newWakeupController();
+        verify(mWifiConfigStore).registerStoreData(any(WakeupConfigStoreData.class));
+    }
+
+    /**
+     * Verify that dump calls also dump the state of the WakeupLock.
+     */
+    @Test
+    public void dumpIncludesWakeupLock() {
+        mWakeupController = newWakeupController();
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        PrintWriter writer = new PrintWriter(stream);
+        mWakeupController.dump(null, writer, null);
+
+        verify(mWakeupLock).dump(null, writer, null);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
new file mode 100644
index 0000000..7144ecf
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Unit tests for {@link WakeupLock}.
+ */
+public class WakeupLockTest {
+
+    private static final String SSID_1 = "ssid1";
+    private static final String SSID_2 = "ssid2";
+
+    @Mock private WifiConfigManager mWifiConfigManager;
+
+    private ScanResultMatchInfo mNetwork1;
+    private ScanResultMatchInfo mNetwork2;
+    private WakeupLock mWakeupLock;
+
+    /**
+     * Initialize objects before each test run.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mNetwork1 = new ScanResultMatchInfo();
+        mNetwork1.networkSsid = SSID_1;
+        mNetwork1.networkType = ScanResultMatchInfo.NETWORK_TYPE_OPEN;
+
+        mNetwork2 = new ScanResultMatchInfo();
+        mNetwork2.networkSsid = SSID_2;
+        mNetwork2.networkType = ScanResultMatchInfo.NETWORK_TYPE_EAP;
+
+        mWakeupLock = new WakeupLock(mWifiConfigManager);
+    }
+
+    /**
+     * Updates the lock enough times to evict any networks not passed in.
+     *
+     * <p>It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with
+     * the given network list. It asserts that the lock isn't empty prior to each call to update.
+     */
+    private void updateEnoughTimesToEvictWithAsserts(Collection<ScanResultMatchInfo> networks) {
+        for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) {
+            assertFalse("Lock empty after " + i + " scans", mWakeupLock.isEmpty());
+            mWakeupLock.update(networks);
+        }
+    }
+
+    /**
+     * Updates the lock enough times to evict any networks not passed in.
+     *
+     * <p>It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with
+     * the given network list. It does not make any assertions about the state of the lock.
+     */
+    private void updateEnoughTimesToEvictWithoutAsserts(Collection<ScanResultMatchInfo> networks) {
+        for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) {
+            mWakeupLock.update(networks);
+        }
+    }
+
+    /**
+     * Verify that the WakeupLock is not empty immediately after being initialized with networks.
+     */
+    @Test
+    public void verifyNotEmptyWhenInitializedWithNetworkList() {
+        mWakeupLock.initialize(Arrays.asList(mNetwork1, mNetwork2));
+        assertFalse(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Verify that the WakeupLock is empty when initialized with an empty list.
+     */
+    @Test
+    public void isEmptyWhenInitializedWithEmptyList() {
+        mWakeupLock.initialize(Collections.emptyList());
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Verify that initializing the WakeupLock clears out previous entries.
+     */
+    @Test
+    public void initializingLockClearsPreviousNetworks() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+        assertFalse(mWakeupLock.isEmpty());
+
+        mWakeupLock.initialize(Collections.emptyList());
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Updating the lock should evict scan results that haven't been seen in
+     * {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} scans.
+     */
+    @Test
+    public void updateShouldRemoveNetworksAfterConsecutiveMissedScans() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+
+        updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2));
+
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Ensure that missed scans must be consecutive in order to evict networks from lock.
+     */
+    @Test
+    public void updateWithLockedNetworkShouldResetRequiredNumberOfScans() {
+        List<ScanResultMatchInfo> lockedNetworks = Collections.singletonList(mNetwork1);
+        List<ScanResultMatchInfo> updateNetworks = Collections.singletonList(mNetwork2);
+
+        mWakeupLock.initialize(lockedNetworks);
+
+        // one update without network
+        mWakeupLock.update(updateNetworks);
+        // one update with network
+        mWakeupLock.update(lockedNetworks);
+
+        updateEnoughTimesToEvictWithAsserts(updateNetworks);
+
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Once a network is removed from the lock, it should not be reset even if it's seen again.
+     */
+    @Test
+    public void updateWithLockedNetworkAfterItIsRemovedDoesNotReset() {
+        List<ScanResultMatchInfo> lockedNetworks = Collections.singletonList(mNetwork1);
+        mWakeupLock.initialize(lockedNetworks);
+
+        updateEnoughTimesToEvictWithAsserts(Collections.emptyList());
+
+        assertTrue(mWakeupLock.isEmpty());
+        mWakeupLock.update(lockedNetworks);
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Verify that networks can be incrementally removed from the lock. Their counters should be
+     * independent.
+     */
+    @Test
+    public void networksCanBeRemovedIncrementallyFromLock() {
+        List<ScanResultMatchInfo> lockedNetworks = Arrays.asList(mNetwork1, mNetwork2);
+        mWakeupLock.initialize(lockedNetworks);
+
+        updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork1));
+        assertFalse(mWakeupLock.isEmpty());
+
+        updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2));
+        assertTrue(mWakeupLock.isEmpty());
+    }
+
+    /**
+     * Verify that initializing the lock persists the SSID list to the config store.
+     */
+    @Test
+    public void initializeShouldSaveSsidsToStore() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+        verify(mWifiConfigManager).saveToStore(eq(false));
+    }
+
+    /**
+     * Verify that update saves to store if the lock changes.
+     */
+    @Test
+    public void updateShouldOnlySaveIfLockChanges() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+        updateEnoughTimesToEvictWithoutAsserts(Collections.emptyList());
+
+        // need exactly 2 invocations: 1 for initialize, 1 for successful update
+        verify(mWifiConfigManager, times(2)).saveToStore(eq(false));
+    }
+
+    /**
+     * Verify that update does not save to store if the lock does not change.
+     */
+    @Test
+    public void updateShouldNotSaveIfLockDoesNotChange() {
+        mWakeupLock.update(Collections.singletonList(mNetwork1));
+        verify(mWifiConfigManager, never()).saveToStore(anyBoolean());
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java
deleted file mode 100644
index b123d80..0000000
--- a/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.os.UserHandle;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.Mock;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashSet;
-
-/**
- * Unit tests for {@link com.android.server.wifi.WifiCertManager}.
- */
-@SmallTest
-public class WifiCertManagerTest {
-    private static final String TAG = "WifiCertManagerTest";
-    private byte[] mConfig;
-    private String mConfigFile = "";
-
-    @Mock private Context mContext;
-    @Rule public TemporaryFolder mTempFolder = new TemporaryFolder();
-
-    public WifiCertManagerTest() {
-        mConfig = null;
-    }
-
-    @Before
-    public void setUp() {
-        try {
-            File configFile = mTempFolder.newFile();
-            mConfigFile = configFile.getAbsolutePath();
-            configFile.delete();
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to construct test", e);
-        }
-    }
-
-    /**
-     * This class is created to avoid mocking file system and KeyStore.
-     */
-    private class TestWifiCertManager extends WifiCertManager {
-        private boolean mAffiliatedUser;
-
-        public TestWifiCertManager(Context context) {
-            super(context, mConfigFile);
-            mAffiliatedUser = false;
-        }
-
-        protected String[] listClientCertsForAllUsers() {
-            String prefix = Credentials.USER_PRIVATE_KEY;
-            String mockAnswer[] = {prefix + "abc", prefix + "def", prefix + "ghi"};
-            return mockAnswer;
-        }
-
-        protected byte[] readConfigFile() {
-            return mConfig;
-        }
-
-        protected void writeConfigFile(byte[] payload) {
-            mConfig = payload;
-        }
-
-        protected boolean isAffiliatedUser() {
-            return mAffiliatedUser;
-        }
-
-        public void setAffiliatedUser(boolean value) {
-            mAffiliatedUser = value;
-        }
-    }
-
-    // TODO: b/69555027 - determine if test can be removed.  for now, disable failing test
-    public void testEmptyConfigFile() {
-        WifiCertManager certManager = new WifiCertManager(mContext, mConfigFile);
-        final String[] expected =
-                KeyStore.getInstance().list(
-                        Credentials.USER_PRIVATE_KEY, UserHandle.myUserId());
-        assertArrayEquals(expected, certManager.listClientCertsForCurrentUser());
-    }
-
-    @Test
-    public void testOperations() {
-        TestWifiCertManager certManager = new TestWifiCertManager(mContext);
-        final HashSet<String> expected1 = new HashSet<>();
-        String prefix = Credentials.USER_PRIVATE_KEY;
-        expected1.add(prefix + "abc");
-        expected1.add(prefix + "def");
-        expected1.add(prefix + "ghi");
-
-        final HashSet<String> expected2 = new HashSet<>();
-        expected2.add(prefix + "abc");
-
-        certManager.setAffiliatedUser(false);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        certManager.hideCertFromUnaffiliatedUsers("def");
-        certManager.hideCertFromUnaffiliatedUsers("ghi");
-        assertEquals(expected2,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        certManager.setAffiliatedUser(true);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        TestWifiCertManager certManager2 = new TestWifiCertManager(mContext);
-        certManager2.setAffiliatedUser(false);
-        assertEquals(expected2,
-                new HashSet<>(Arrays.asList(certManager2.listClientCertsForCurrentUser())));
-
-        certManager2.setAffiliatedUser(true);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager2.listClientCertsForCurrentUser())));
-    }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
new file mode 100644
index 0000000..0189aed
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Random;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiLinkLayerStats}.
+ */
+public class WifiLinkLayerStatsTest {
+
+    ExtendedWifiInfo mWifiInfo;
+    WifiLinkLayerStats mWifiLinkLayerStats;
+    Random mRandom = new Random();
+
+    /**
+     * Sets up for unit test
+     */
+    @Before
+    public void setUp() throws Exception {
+        mWifiInfo = new ExtendedWifiInfo();
+        mWifiLinkLayerStats = new WifiLinkLayerStats();
+    }
+
+    /**
+     * Increments the counters
+     *
+     * The values are carved up among the 4 classes (be, bk, vi, vo) so the totals come out right.
+     */
+    private void bumpCounters(WifiLinkLayerStats s, int txg, int txr, int txb, int rxg) {
+        int a = mRandom.nextInt(31);
+        int b = mRandom.nextInt(31);
+        int m0 = a & b;
+        int m1 = a & ~b;
+        int m2 = ~a & b;
+        int m3 = ~a & ~b;
+        assertEquals(-1, m0 + m1 + m2 + m3);
+
+        s.rxmpdu_be += rxg & m0;
+        s.txmpdu_be += txg & m0;
+        s.lostmpdu_be += txb & m0;
+        s.retries_be += txr & m0;
+
+        s.rxmpdu_bk += rxg & m1;
+        s.txmpdu_bk += txg & m1;
+        s.lostmpdu_bk += txb & m1;
+        s.retries_bk += txr & m1;
+
+        s.rxmpdu_vi += rxg & m2;
+        s.txmpdu_vi += txg & m2;
+        s.lostmpdu_vi += txb & m2;
+        s.retries_vi += txr & m2;
+
+        s.rxmpdu_vo += rxg & m3;
+        s.txmpdu_vo += txg & m3;
+        s.lostmpdu_vo += txb & m3;
+        s.retries_vo += txr & m3;
+    }
+
+    /**
+     *
+     * Check that average rates converge to the right values
+     *
+     * Check that the total packet counts are correct
+     *
+     */
+    @Test
+    public void checkThatAverageRatesConvergeToTheRightValuesAndTotalsAreRight() throws Exception {
+        int txg = mRandom.nextInt(1000);
+        int txr = mRandom.nextInt(100);
+        int txb = mRandom.nextInt(100);
+        int rxg = mRandom.nextInt(1000);
+        int n = 3 * 5; // Time constant is 3 seconds, 5 times time constant should get 99% there
+        for (int i = 0; i < n; i++) {
+            bumpCounters(mWifiLinkLayerStats, txg, txr, txb, rxg);
+            mWifiLinkLayerStats.timeStampInMs += 1000;
+            mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        }
+        // assertEquals(double, double, double) takes a tolerance as the third argument
+        assertEquals((double) txg, mWifiInfo.txSuccessRate, txg * 0.02);
+        assertEquals((double) txr, mWifiInfo.txRetriesRate, txr * 0.02);
+        assertEquals((double) txb, mWifiInfo.txBadRate, txb * 0.02);
+        assertEquals((double) rxg, mWifiInfo.rxSuccessRate, rxg * 0.02);
+
+        assertEquals(mWifiInfo.txSuccess, n * txg);
+        assertEquals(mWifiInfo.txRetries, n * txr);
+        assertEquals(mWifiInfo.txBad, n * txb);
+        assertEquals(mWifiInfo.rxSuccess, n * rxg);
+    }
+
+    /**
+     * A single packet in a short period of time should have small effect
+     */
+    @Test
+    public void aSinglePacketInAShortPeriodOfTimeShouldHaveSmallEffect() throws Exception {
+        bumpCounters(mWifiLinkLayerStats, 999999999, 999999999, 999999999, 99999999);
+        mWifiLinkLayerStats.timeStampInMs = 999999999;
+        mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        assertEquals(0.0, mWifiInfo.txSuccessRate, 0.0001);
+        bumpCounters(mWifiLinkLayerStats, 1, 1, 1, 1);
+        mWifiLinkLayerStats.timeStampInMs += 1;
+        mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        assertEquals(0.33, mWifiInfo.txSuccessRate, 0.01);
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
index 72a2479..9a79a23 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
@@ -20,8 +20,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -156,8 +154,10 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mWifiVendorHal.isVendorHalSupported()).thenReturn(true);
-        when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(true);
-        mWifiNative = new WifiNative("test0", mWifiVendorHal, mStaIfaceHal, mWificondControl);
+        when(mWifiVendorHal.startVendorHalSta()).thenReturn(true);
+        when(mWifiVendorHal.startVendorHalAp()).thenReturn(true);
+        mWifiNative =
+                new WifiNative(WIFI_IFACE_NAME, mWifiVendorHal, mStaIfaceHal, mWificondControl);
     }
 
     /**
@@ -482,139 +482,139 @@
     // TODO(b/28005116): Add test for the success case of getDriverStateDump().
 
     /**
-     * Verifies that setupDriverForClientMode(WIFI_IFACE_NAME) calls underlying WificondControl.
+     * Verifies that setupInterfaceForClientMode(WIFI_IFACE_NAME) calls underlying WificondControl.
      */
     @Test
     public void testSetupDriverForClientMode() {
         IClientInterface clientInterface = mock(IClientInterface.class);
-        when(mWificondControl.setupDriverForClientMode(WIFI_IFACE_NAME))
+        when(mWificondControl.setupInterfaceForClientMode(WIFI_IFACE_NAME))
                 .thenReturn(clientInterface);
 
         Pair<Integer, IClientInterface> statusAndClientInterface =
                 mWifiNative.setupForClientMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_SUCCESS == statusAndClientInterface.first);
         assertEquals(clientInterface, statusAndClientInterface.second);
-        verify(mWifiVendorHal).startVendorHal(eq(true));
-        verify(mWificondControl).setupDriverForClientMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalSta();
+        verify(mWificondControl).setupInterfaceForClientMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForClientMode(WIFI_IFACE_NAME) does not call start vendor HAL
+     * Verifies that setupInterfaceForClientMode(WIFI_IFACE_NAME) does not call start vendor HAL
      * when it is not supported and calls underlying WificondControl setup.
      */
     @Test
     public void testSetupDriverForClientModeWithNoVendorHal() {
         when(mWifiVendorHal.isVendorHalSupported()).thenReturn(false);
         IClientInterface clientInterface = mock(IClientInterface.class);
-        when(mWificondControl.setupDriverForClientMode(WIFI_IFACE_NAME))
+        when(mWificondControl.setupInterfaceForClientMode(WIFI_IFACE_NAME))
                 .thenReturn(clientInterface);
 
         Pair<Integer, IClientInterface> statusAndClientInterface =
                 mWifiNative.setupForClientMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_SUCCESS == statusAndClientInterface.first);
         assertEquals(clientInterface, statusAndClientInterface.second);
-        verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
-        verify(mWificondControl).setupDriverForClientMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal, never()).startVendorHalSta();
+        verify(mWificondControl).setupInterfaceForClientMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForClientMode(WIFI_IFACE_NAME) returns null when underlying
+     * Verifies that setupInterfaceForClientMode(WIFI_IFACE_NAME) returns null when underlying
      * WificondControl call fails.
      */
     @Test
     public void testSetupDriverForClientModeWificondError() {
-        when(mWificondControl.setupDriverForClientMode(WIFI_IFACE_NAME)).thenReturn(null);
+        when(mWificondControl.setupInterfaceForClientMode(WIFI_IFACE_NAME)).thenReturn(null);
 
         Pair<Integer, IClientInterface> statusAndClientInterface =
                 mWifiNative.setupForClientMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_FAILURE_WIFICOND == statusAndClientInterface.first);
         assertEquals(null, statusAndClientInterface.second);
-        verify(mWifiVendorHal).startVendorHal(eq(true));
-        verify(mWificondControl).setupDriverForClientMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalSta();
+        verify(mWificondControl).setupInterfaceForClientMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForClientMode(WIFI_IFACE_NAME) returns null when underlying Hal
+     * Verifies that setupInterfaceForClientMode(WIFI_IFACE_NAME) returns null when underlying Hal
      * call fails.
      */
     @Test
     public void testSetupDriverForClientModeHalError() {
-        when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
+        when(mWifiVendorHal.startVendorHalSta()).thenReturn(false);
 
         Pair<Integer, IClientInterface> statusAndClientInterface =
                 mWifiNative.setupForClientMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_FAILURE_HAL == statusAndClientInterface.first);
         assertEquals(null, statusAndClientInterface.second);
-        verify(mWifiVendorHal).startVendorHal(eq(true));
-        verify(mWificondControl, never()).setupDriverForClientMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalSta();
+        verify(mWificondControl, never()).setupInterfaceForClientMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForSoftApMode(WIFI_IFACE_NAME) calls underlying WificondControl.
+     * Verifies that setupInterfaceForSoftApMode(WIFI_IFACE_NAME) calls underlying WificondControl.
      */
     @Test
     public void testSetupDriverForSoftApMode() {
         IApInterface apInterface = mock(IApInterface.class);
-        when(mWificondControl.setupDriverForSoftApMode(WIFI_IFACE_NAME)).thenReturn(apInterface);
+        when(mWificondControl.setupInterfaceForSoftApMode(WIFI_IFACE_NAME)).thenReturn(apInterface);
 
         Pair<Integer, IApInterface> statusAndApInterface =
                 mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_SUCCESS == statusAndApInterface.first);
         assertEquals(apInterface, statusAndApInterface.second);
-        verify(mWifiVendorHal).startVendorHal(eq(false));
-        verify(mWificondControl).setupDriverForSoftApMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalAp();
+        verify(mWificondControl).setupInterfaceForSoftApMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForClientMode(WIFI_IFACE_NAME) does not call start vendor HAL when
-     * it is not supported and calls underlying WificondControl setup.
+     * Verifies that setupInterfaceForClientMode(WIFI_IFACE_NAME) does not call start vendor HAL
+     * when it is not supported and calls underlying WificondControl setup.
      */
     @Test
     public void testSetupDriverForSoftApModeWithNoVendorHal() {
         when(mWifiVendorHal.isVendorHalSupported()).thenReturn(false);
         IApInterface apInterface = mock(IApInterface.class);
-        when(mWificondControl.setupDriverForSoftApMode(WIFI_IFACE_NAME)).thenReturn(apInterface);
+        when(mWificondControl.setupInterfaceForSoftApMode(WIFI_IFACE_NAME)).thenReturn(apInterface);
 
         Pair<Integer, IApInterface> statusAndApInterface =
                 mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_SUCCESS == statusAndApInterface.first);
         assertEquals(apInterface, statusAndApInterface.second);
-        verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
-        verify(mWificondControl).setupDriverForSoftApMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal, never()).startVendorHalAp();
+        verify(mWificondControl).setupInterfaceForSoftApMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForSoftApMode(WIFI_IFACE_NAME) returns null when underlying
+     * Verifies that setupInterfaceForSoftApMode(WIFI_IFACE_NAME) returns null when underlying
      * WificondControl call fails.
      */
     @Test
     public void testSetupDriverForSoftApModeWificondError() {
-        when(mWificondControl.setupDriverForSoftApMode(WIFI_IFACE_NAME)).thenReturn(null);
+        when(mWificondControl.setupInterfaceForSoftApMode(WIFI_IFACE_NAME)).thenReturn(null);
 
         Pair<Integer, IApInterface> statusAndApInterface =
                 mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_FAILURE_WIFICOND == statusAndApInterface.first);
         assertEquals(null, statusAndApInterface.second);
 
-        verify(mWifiVendorHal).startVendorHal(eq(false));
-        verify(mWificondControl).setupDriverForSoftApMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalAp();
+        verify(mWificondControl).setupInterfaceForSoftApMode(WIFI_IFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForSoftApMode(WIFI_IFACE_NAME) returns null when underlying Hal
+     * Verifies that setupInterfaceForSoftApMode(WIFI_IFACE_NAME) returns null when underlying Hal
      * call fails.
      */
     @Test
     public void testSetupDriverForSoftApModeHalError() {
-        when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
+        when(mWifiVendorHal.startVendorHalAp()).thenReturn(false);
 
         Pair<Integer, IApInterface> statusAndApInterface =
                 mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME);
         assertTrue(WifiNative.SETUP_FAILURE_HAL == statusAndApInterface.first);
         assertEquals(null, statusAndApInterface.second);
 
-        verify(mWifiVendorHal).startVendorHal(eq(false));
-        verify(mWificondControl, never()).setupDriverForSoftApMode(WIFI_IFACE_NAME);
+        verify(mWifiVendorHal).startVendorHalAp();
+        verify(mWificondControl, never()).setupInterfaceForSoftApMode(WIFI_IFACE_NAME);
     }
 
     /**
@@ -670,10 +670,11 @@
      */
     @Test
     public void testSignalPoll() throws Exception {
-        when(mWificondControl.signalPoll()).thenReturn(SIGNAL_POLL_RESULT);
+        when(mWificondControl.signalPoll(WIFI_IFACE_NAME))
+                .thenReturn(SIGNAL_POLL_RESULT);
 
         assertEquals(SIGNAL_POLL_RESULT, mWifiNative.signalPoll());
-        verify(mWificondControl).signalPoll();
+        verify(mWificondControl).signalPoll(WIFI_IFACE_NAME);
     }
 
     /**
@@ -681,10 +682,11 @@
      */
     @Test
     public void testGetTxPacketCounters() throws Exception {
-        when(mWificondControl.getTxPacketCounters()).thenReturn(PACKET_COUNTERS_RESULT);
+        when(mWificondControl.getTxPacketCounters(WIFI_IFACE_NAME))
+                .thenReturn(PACKET_COUNTERS_RESULT);
 
         assertEquals(PACKET_COUNTERS_RESULT, mWifiNative.getTxPacketCounters());
-        verify(mWificondControl).getTxPacketCounters();
+        verify(mWificondControl).getTxPacketCounters(WIFI_IFACE_NAME);
     }
 
     /**
@@ -693,7 +695,8 @@
     @Test
     public void testScan() throws Exception {
         mWifiNative.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET);
-        verify(mWificondControl).scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET);
+        verify(mWificondControl).scan(
+                WIFI_IFACE_NAME, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET);
     }
 
     /**
@@ -702,7 +705,8 @@
     @Test
     public void testStartPnoScan() throws Exception {
         mWifiNative.startPnoScan(TEST_PNO_SETTINGS);
-        verify(mWificondControl).startPnoScan(TEST_PNO_SETTINGS);
+        verify(mWificondControl).startPnoScan(
+                WIFI_IFACE_NAME, TEST_PNO_SETTINGS);
     }
 
     /**
@@ -711,7 +715,7 @@
     @Test
     public void testStopPnoScan() throws Exception {
         mWifiNative.stopPnoScan();
-        verify(mWificondControl).stopPnoScan();
+        verify(mWificondControl).stopPnoScan(WIFI_IFACE_NAME);
     }
 
     /**
@@ -722,8 +726,8 @@
         WifiConfiguration config = mock(WifiConfiguration.class);
         mWifiNative.connectToNetwork(config);
         // connectToNetwork() should abort ongoing scan before connection.
-        verify(mWificondControl).abortScan();
-        verify(mStaIfaceHal).connectToNetwork(config);
+        verify(mWificondControl).abortScan(WIFI_IFACE_NAME);
+        verify(mStaIfaceHal).connectToNetwork(WIFI_IFACE_NAME, config);
     }
 
     /**
@@ -734,8 +738,7 @@
         WifiConfiguration config = mock(WifiConfiguration.class);
         mWifiNative.roamToNetwork(config);
         // roamToNetwork() should abort ongoing scan before connection.
-        verify(mWificondControl).abortScan();
-        verify(mStaIfaceHal).roamToNetwork(config);
+        verify(mWificondControl).abortScan(WIFI_IFACE_NAME);
+        verify(mStaIfaceHal).roamToNetwork(WIFI_IFACE_NAME, config);
     }
-
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 3d3af36..b56da5d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -613,8 +613,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Do not perform selection on 2GHz if current network is good and no 5GHz available
         testStayOrTryToSwitch(
@@ -640,8 +640,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
         testStayOrTryToSwitch(
@@ -670,8 +670,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G - 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Run Selection when the current 5Ghz network has low RSSI.
         testStayOrTryToSwitch(
@@ -695,8 +695,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Connected to a high quality 5Ghz network, so the other result is irrelevant
         testStayOrTryToSwitch(
@@ -719,8 +719,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 // Parameters for network1:
@@ -747,8 +747,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G - 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -769,9 +769,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumTxRate + 1));
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = ((double) (mStayOnNetworkMinimumTxRate + 1));
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -792,9 +791,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumRxRate + 1));
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = ((double) (mStayOnNetworkMinimumRxRate + 1));
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -815,9 +813,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumTxRate + 1));
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = ((double) (mStayOnNetworkMinimumTxRate + 1));
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
@@ -838,9 +835,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumRxRate + 1));
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = ((double) (mStayOnNetworkMinimumRxRate + 1));
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index fe2ede7..ea81d53 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -64,6 +64,8 @@
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -83,10 +85,10 @@
 
 import com.android.internal.util.AsyncChannel;
 import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback;
+import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
 import com.android.server.wifi.util.WifiAsyncChannel;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -128,6 +130,7 @@
     private Messenger mAppMessenger;
     private int mPid;
     private int mPid2 = Process.myPid();
+    private OsuProvider mOsuProvider;
 
     final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor =
             ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -165,6 +168,7 @@
     @Mock IBinder mAppBinder;
     @Mock LocalOnlyHotspotRequestInfo mRequestInfo;
     @Mock LocalOnlyHotspotRequestInfo mRequestInfo2;
+    @Mock IProvisioningCallback mProvisioningCallback;
 
     @Spy FakeWifiLog mLog;
 
@@ -280,6 +284,12 @@
         when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
         when(mWifiInjector.getWifiSettingsStore()).thenReturn(mSettingsStore);
         when(mWifiInjector.getClock()).thenReturn(mClock);
+        when(mWifiStateMachine.syncStartSubscriptionProvisioning(anyInt(),
+                any(OsuProvider.class), any(IProvisioningCallback.class), any())).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(
+                PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true);
+        // Create an OSU provider that can be provisioned via an open OSU AP
+        mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
         mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
         mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
     }
@@ -1515,6 +1525,60 @@
     }
 
     /**
+     * Verify that the call to startSubscriptionProvisioning is redirected to the Passpoint
+     * specific API startSubscriptionProvisioning when the caller has the right permissions.
+     */
+    @Test
+    public void testStartSubscriptionProvisioningWithPermission() throws Exception {
+        mWifiServiceImpl.startSubscriptionProvisioning(mOsuProvider, mProvisioningCallback);
+        verify(mWifiStateMachine).syncStartSubscriptionProvisioning(anyInt(),
+                eq(mOsuProvider), eq(mProvisioningCallback), any());
+    }
+
+    /**
+     * Verify that the call to startSubscriptionProvisioning is not directed to the Passpoint
+     * specific API startSubscriptionProvisioning when the feature is not supported.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testStartSubscriptionProvisioniningPasspointUnsupported() throws Exception {
+        when(mPackageManager.hasSystemFeature(
+                PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(false);
+        mWifiServiceImpl.startSubscriptionProvisioning(mOsuProvider, mProvisioningCallback);
+    }
+
+    /**
+     * Verify that the call to startSubscriptionProvisioning is not redirected to the Passpoint
+     * specific API startSubscriptionProvisioning when the caller provides invalid arguments
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testStartSubscriptionProvisioningWithInvalidProvider() throws Exception {
+        mWifiServiceImpl.startSubscriptionProvisioning(null, mProvisioningCallback);
+    }
+
+
+    /**
+     * Verify that the call to startSubscriptionProvisioning is not redirected to the Passpoint
+     * specific API startSubscriptionProvisioning when the caller provides invalid callback
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testStartSubscriptionProvisioningWithInvalidCallback() throws Exception {
+        mWifiServiceImpl.startSubscriptionProvisioning(mOsuProvider, null);
+    }
+
+    /**
+     * Verify that the call to startSubscriptionProvisioning is not redirected to the Passpoint
+     * specific API startSubscriptionProvisioning when the caller doesn't have NETWORK_SETTINGS
+     * permissions.
+     */
+    @Test(expected = SecurityException.class)
+    public void testStartSubscriptionProvisioningWithoutPermission() throws Exception {
+        doThrow(new SecurityException()).when(mContext)
+                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+                        eq("WifiService"));
+        mWifiServiceImpl.startSubscriptionProvisioning(mOsuProvider, mProvisioningCallback);
+    }
+
+    /**
      * Verify that a call to {@link WifiServiceImpl#restoreBackupData(byte[])} is only allowed from
      * callers with the signature only NETWORK_SETTINGS permission.
      */
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
index 2e26769..f2d6cc6 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
@@ -20,13 +20,13 @@
 import static org.mockito.Mockito.*;
 
 import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.INetworkManagementService;
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
+import android.util.Pair;
 
 import org.junit.After;
 import org.junit.Before;
@@ -56,7 +56,6 @@
     @Mock WifiNative mWifiNative;
     @Mock WifiApConfigStore mWifiApConfigStore;
     TestLooper mLooper;
-    @Mock IWificond mWificond;
     @Mock IApInterface mApInterface;
     @Mock INetworkManagementService mNMService;
     @Mock SoftApManager mSoftApManager;
@@ -76,11 +75,15 @@
         when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
         when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         mWifiStateMachinePrime = createWifiStateMachinePrime();
+
+        // creating a new WSMP cleans up any existing interfaces, check and reset expectations
+        verifyCleanupCalled();
+        reset(mWifiNative);
     }
 
     private WifiStateMachinePrime createWifiStateMachinePrime() {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+        return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+                mWifiNative, mNMService);
     }
 
     /**
@@ -103,8 +106,8 @@
      */
     private void enterSoftApActiveMode(SoftApModeConfiguration softApConfig) throws Exception {
         String fromState = mWifiStateMachinePrime.getCurrentMode();
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, mApInterface));
         when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         doAnswer(
                 new Answer<Object>() {
@@ -126,24 +129,30 @@
         mLooper.dispatchAll();
         Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState);
         if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) {
-            verify(mWificond).tearDownInterfaces();
+            verifyCleanupCalled();
         }
         assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
         verify(mSoftApManager).start();
     }
 
+    private void verifyCleanupCalled() {
+        // for now, this is a single call, but make a helper to avoid adding any additional cleanup
+        // checks
+        verify(mWifiNative).tearDown();
+    }
+
     /**
-     * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in
-     * the retrieved Wificond instance are cleaned up.
+     * Test that when a new instance of WifiStateMachinePrime is created, any existing
+     * resources in WifiNative are cleaned up.
      * Expectations:  When the new WifiStateMachinePrime instance is created a call to
-     * Wificond.tearDownInterfaces() is made.
+     * WifiNative.tearDown() is made.
      */
     @Test
-    public void testWificondExistsOnStartup() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+    public void testCleanupOnStart() throws Exception {
         WifiStateMachinePrime testWifiStateMachinePrime =
-                new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
-        verify(mWificond).tearDownInterfaces();
+                new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+                                          mWifiNative, mNMService);
+        verifyCleanupCalled();
     }
 
     /**
@@ -157,12 +166,10 @@
 
     /**
      * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state.
-     * Expectations: When going from one state to another, any interfaces that are still up are torn
-     * down.
+     * Expectations: When going from one state to another, cleanup will be called
      */
     @Test
     public void testEnterSoftApModeFromDifferentState() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         mWifiStateMachinePrime.enterClientMode();
         mLooper.dispatchAll();
         assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
@@ -179,7 +186,7 @@
         mWifiStateMachinePrime.disableWifi();
         mLooper.dispatchAll();
         verify(mSoftApManager).stop();
-        verify(mWificond).tearDownInterfaces();
+        verifyCleanupCalled();
         assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
@@ -188,8 +195,10 @@
      */
     @Test
     public void testDisableWifiFromSoftApModeState() throws Exception {
-        // Use a failure getting wificond to stay in the SoftAPModeState
-        when(mWifiInjector.makeWificond()).thenReturn(null);
+        // Use a failure getting the interface to stay in SoftApMode
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                            .thenReturn(new Pair(WifiNative.SETUP_FAILURE_HAL, null));
+
         mWifiStateMachinePrime.enterSoftAPMode(
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
         mLooper.dispatchAll();
@@ -197,7 +206,7 @@
 
         mWifiStateMachinePrime.disableWifi();
         mLooper.dispatchAll();
-        // mWificond will be null due to this test, no call to tearDownInterfaces here.
+        verifyCleanupCalled();
         assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
@@ -213,26 +222,11 @@
         mWifiStateMachinePrime.enterClientMode();
         mLooper.dispatchAll();
         verify(mSoftApManager).stop();
-        verify(mWificond).tearDownInterfaces();
+        verifyCleanupCalled();
         assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
     /**
-     * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to
-     * wificond.
-     * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in
-     * the SoftApModeState.
-     */
-    @Test
-    public void testWificondNullWhenSwitchingToApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
-        mLooper.dispatchAll();
-        assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
-    }
-
-    /**
      * Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface
      * from wificond.
      * Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should
@@ -240,8 +234,8 @@
      */
     @Test
     public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_FAILURE_HAL, null));
         mWifiStateMachinePrime.enterSoftAPMode(
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
         mLooper.dispatchAll();
@@ -255,8 +249,8 @@
      */
     @Test
     public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
         mWifiStateMachinePrime.enterSoftAPMode(
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
         mLooper.dispatchAll();
@@ -330,8 +324,8 @@
      */
     @Test
     public void testNullApModeConfigFails() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
         mWifiStateMachinePrime.enterSoftAPMode(
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
         mLooper.dispatchAll();
@@ -352,7 +346,8 @@
      */
     @Test
     public void testValidConfigIsSavedOnFailureToStart() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, null));
         when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
         WifiConfiguration config = new WifiConfiguration();
         config.SSID = "ThisIsAConfig";
@@ -365,14 +360,14 @@
     }
 
     /**
-     * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config.
+     * Test that two calls to switch to SoftAPMode in succession ends up with the correct config.
      *
      * Expectation: we should end up in SoftAPMode state configured with the second config.
      */
     @Test
     public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
+        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+                .thenReturn(new Pair(WifiNative.SETUP_SUCCESS, mApInterface));
         when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
         WifiConfiguration config1 = new WifiConfiguration();
@@ -406,12 +401,12 @@
 
     /**
      * Test that we safely disable wifi if it is already disabled.
-     * Expectations: We should not interact with wificond since we should have already cleaned up
+     * Expectations: We should not interact with WifiNative since we should have already cleaned up
      * everything.
      */
     @Test
     public void disableWifiWhenAlreadyOff() throws Exception {
-        verifyNoMoreInteractions(mWificond);
+        verifyNoMoreInteractions(mWifiNative);
         mWifiStateMachinePrime.disableWifi();
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 94589b0..e6972ea 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -63,6 +63,8 @@
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
 import android.net.wifi.WpsInfo;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.net.wifi.p2p.IWifiP2pManager;
@@ -80,7 +82,6 @@
 import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.test.TestLooper;
@@ -102,8 +103,10 @@
 import com.android.internal.util.StateMachine;
 import com.android.server.wifi.hotspot2.NetworkDetail;
 import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
 import com.android.server.wifi.p2p.WifiP2pServiceImpl;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -150,6 +153,7 @@
     private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
     private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
     private static final String OP_PACKAGE_NAME = "com.xxx";
+    private static final int TEST_UID = Process.SYSTEM_UID + 1000;
 
     private long mBinderToken;
 
@@ -334,6 +338,7 @@
     IpClient.Callback mIpClientCallback;
     PhoneStateListener mPhoneStateListener;
     NetworkRequest mDefaultNetworkRequest;
+    OsuProvider mOsuProvider;
 
     final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor =
                     ArgumentCaptor.forClass(SoftApManager.Listener.class);
@@ -352,8 +357,6 @@
     @Mock IWificond mWificond;
     @Mock IApInterface mApInterface;
     @Mock IClientInterface mClientInterface;
-    @Mock IBinder mApInterfaceBinder;
-    @Mock IBinder mClientInterfaceBinder;
     @Mock IBinder mPackageManagerBinder;
     @Mock WifiConfigManager mWifiConfigManager;
     @Mock WifiNative mWifiNative;
@@ -370,6 +373,10 @@
     @Mock ScanDetailCache mScanDetailCache;
     @Mock BaseWifiDiagnostics mWifiDiagnostics;
     @Mock ConnectivityManager mConnectivityManager;
+    @Mock IProvisioningCallback mProvisioningCallback;
+    @Mock HandlerThread mWifiServiceHandlerThread;
+    @Mock WifiPermissionsWrapper mWifiPermissionsWrapper;
+    @Mock WakeupController mWakeupController;
 
     public WifiStateMachineTest() throws Exception {
     }
@@ -413,6 +420,10 @@
         when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
         when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager);
         when(mWifiInjector.getClock()).thenReturn(mClock);
+        when(mWifiServiceHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
+        when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mWifiServiceHandlerThread);
+        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+        when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController);
 
         when(mWifiNative.setupForClientMode(WIFI_IFACE_NAME))
                 .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mClientInterface));
@@ -423,7 +434,9 @@
         when(mWifiNative.enableSupplicant()).thenReturn(true);
         when(mWifiNative.disableSupplicant()).thenReturn(true);
         when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0);
-
+        when(mWifiNative.initializeVendorHal(any(WifiNative.VendorHalDeathEventHandler.class)))
+                .thenReturn(true);
+        when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(true);
 
         mFrameworkFacade = getFrameworkFacade();
         mContext = getContext();
@@ -449,9 +462,6 @@
                 new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
                 new UserInfo(11, "managed profile", 0)));
 
-        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
-        when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
-
         doAnswer(new AnswerWithArguments() {
             public void answer(PhoneStateListener phoneStateListener, int events)
                     throws Exception {
@@ -460,7 +470,11 @@
         }).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt());
 
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
         initializeWsm();
+
+        mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
     }
 
     private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger) {
@@ -644,8 +658,8 @@
         // We start out with valid default values, break them going backwards so that
         // we test all the bailout cases.
 
-        // ClientInterface dies after creation.
-        doThrow(new RemoteException()).when(mClientInterfaceBinder).linkToDeath(any(), anyInt());
+        // wificond dies after inteface creation.
+        when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(false);
         mWsm.setSupplicantRunning(true);
         mLooper.dispatchAll();
         assertEquals("InitialState", getCurrentState().getName());
@@ -1779,6 +1793,57 @@
     }
 
     /**
+     * Verify that syncStartSubscriptionProvisioning will redirect calls with right parameters
+     * to {@link PasspointManager} with expected true being returned when in client mode.
+     */
+    @Test
+    public void syncStartSubscriptionProvisioningInClientMode() throws Exception {
+        mLooper.startAutoDispatch();
+        mWsm.syncInitialize(mWsmAsyncChannel);
+        verify(mPasspointManager).initializeProvisioner(any(Looper.class));
+        mLooper.stopAutoDispatch();
+
+        loadComponentsInStaMode();
+        when(mPasspointManager.startSubscriptionProvisioning(anyInt(),
+                any(OsuProvider.class), any(IProvisioningCallback.class))).thenReturn(true);
+        mLooper.startAutoDispatch();
+        assertEquals(true, mWsm.syncStartSubscriptionProvisioning(
+                OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mWsmAsyncChannel));
+        verify(mPasspointManager).startSubscriptionProvisioning(OTHER_USER_UID, mOsuProvider,
+                mProvisioningCallback);
+        mLooper.stopAutoDispatch();
+    }
+
+    /**
+     * Verify that syncStartSubscriptionProvisioning will be a no-op and return false before
+     * SUPPLICANT_START command is received by the WSM.
+     */
+    @Test
+    public void syncStartSubscriptionProvisioningBeforeSupplicantOrAPStart() throws Exception {
+        mLooper.startAutoDispatch();
+        assertEquals(false, mWsm.syncStartSubscriptionProvisioning(
+                OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mWsmAsyncChannel));
+        mLooper.stopAutoDispatch();
+        verify(mPasspointManager, never()).startSubscriptionProvisioning(
+                anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class));
+    }
+
+    /**
+     * Verify that syncStartSubscriptionProvisioning will be a no-op and return false when not in
+     * client mode.
+     */
+    @Test
+    public void syncStartSubscriptionProvisioningInAPMode() throws Exception {
+        loadComponentsInApMode(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+        mLooper.startAutoDispatch();
+        assertEquals(false, mWsm.syncStartSubscriptionProvisioning(
+                OTHER_USER_UID, mOsuProvider, mProvisioningCallback, mWsmAsyncChannel));
+        mLooper.stopAutoDispatch();
+        verify(mPasspointManager, never()).startSubscriptionProvisioning(
+                anyInt(), any(OsuProvider.class), any(IProvisioningCallback.class));
+    }
+
+    /**
      *  Test that we disconnect from a network if it was removed while we are in the
      *  ObtainingIpState.
      */
@@ -2058,20 +2123,19 @@
 
     @Test
     public void handleWificondDeath() throws Exception {
-        ArgumentCaptor<StateMachineDeathRecipient> deathHandlerCapturer =
-                ArgumentCaptor.forClass(StateMachineDeathRecipient.class);
+        ArgumentCaptor<WifiNative.WificondDeathEventHandler> deathHandlerCapturer =
+                ArgumentCaptor.forClass(WifiNative.WificondDeathEventHandler.class);
 
         // Trigger initialize to capture the death handler registration.
         loadComponentsInStaMode();
 
-        verify(mClientInterfaceBinder).linkToDeath(deathHandlerCapturer.capture(), anyInt());
-        StateMachineDeathRecipient deathHandler = deathHandlerCapturer.getValue();
+        verify(mWifiNative).registerWificondDeathHandler(deathHandlerCapturer.capture());
 
         mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
         mLooper.dispatchAll();
 
         // Now trigger the death notification.
-        deathHandler.binderDied();
+        deathHandlerCapturer.getValue().onDeath();
         mLooper.dispatchAll();
 
         verify(mWifiMetrics).incrementNumWificondCrashes();
@@ -2137,6 +2201,16 @@
         assertEquals(sBSSID1, wifiInfo.getBSSID());
         assertEquals(sFreq1, wifiInfo.getFrequency());
         assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+
+        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.DISCONNECTED));
+        mLooper.dispatchAll();
+
+        wifiInfo = mWsm.getWifiInfo();
+        assertEquals(null, wifiInfo.getBSSID());
+        assertEquals(WifiSsid.NONE, wifiInfo.getSSID());
+        assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId());
+        assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
     }
 
     /**
@@ -2232,56 +2306,29 @@
     }
 
     /**
-     * Test that the process uid has full wifiInfo access.
-     * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always
-     * returns a copy of WifiInfo.
-     */
-    @Test
-    public void testConnectedIdsAreVisibleFromOwnUid() throws Exception {
-        assertEquals(Process.myUid(), Binder.getCallingUid());
-        WifiInfo wifiInfo = mWsm.getWifiInfo();
-        wifiInfo.setBSSID(sBSSID);
-        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID));
-
-        connect();
-        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
-
-        assertNotEquals(wifiInfo, connectionInfo);
-        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
-        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
-    }
-
-    /**
      * Test that connected SSID and BSSID are not exposed to an app that does not have the
      * appropriate permissions.
      * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always
      * returns a copy of WifiInfo.
      */
     @Test
-    public void testConnectedIdsAreHiddenFromRandomApp() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        assertNotEquals(actualUid, fakeUid);
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+    public void testConnectedIdsAreHiddenFromAppWithoutPermission() throws Exception {
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenReturn(false);
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(false);
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
     }
 
     /**
@@ -2292,29 +2339,48 @@
      */
     @Test
     public void testConnectedIdsAreHiddenOnSecurityException() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        assertNotEquals(actualUid, fakeUid);
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenThrow(new SecurityException());
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenThrow(new SecurityException());
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
+    }
+
+    /**
+     * Test that connected SSID and BSSID are exposed to system server
+     */
+    @Test
+    public void testConnectedIdsAreVisibleFromSystemServer() throws Exception {
+        when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(true);
+
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
+
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
+        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
+        assertEquals(wifiInfo.getMacAddress(), connectionInfo.getMacAddress());
     }
 
     /**
@@ -2323,30 +2389,24 @@
      */
     @Test
     public void testConnectedIdsAreVisibleFromPermittedApp() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenReturn(true);
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(true);
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
-            assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
-            // Access to our MAC address uses a different permission, make sure it is not granted
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
+        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
+        // Access to our MAC address uses a different permission, make sure it is not granted
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress());
     }
 
     /**
@@ -2771,4 +2831,15 @@
         currentConfig.networkId = lastSelectedNetworkId - 1;
         assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
     }
+
+    /**
+     * Verify that WSM dump includes WakeupController.
+     */
+    @Test
+    public void testDumpShouldDumpWakeupController() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        PrintWriter writer = new PrintWriter(stream);
+        mWsm.dump(null, writer, null);
+        verify(mWakeupController).dump(null, writer, null);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index aa2318e..812ef03 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -77,7 +77,6 @@
 import android.net.apf.ApfCapabilities;
 import android.net.wifi.RttManager;
 import android.net.wifi.ScanResult;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
@@ -111,6 +110,7 @@
  */
 public class WifiVendorHalTest {
 
+    private static final String TEST_IFACE_NAME = "wlan0";
     WifiVendorHal mWifiVendorHal;
     private WifiStatus mWifiStatusSuccess;
     private WifiStatus mWifiStatusFailure;
@@ -195,6 +195,7 @@
                 .thenReturn(mIWifiStaIface);
         when(mHalDeviceManager.createApIface(eq(null), eq(null)))
                 .thenReturn(mIWifiApIface);
+        when(mHalDeviceManager.removeIface(any())).thenReturn(true);
         when(mHalDeviceManager.getChip(any(IWifiIface.class)))
                 .thenReturn(mIWifiChip);
         when(mHalDeviceManager.createRttController()).thenReturn(mIWifiRttController);
@@ -218,6 +219,19 @@
         when(mIWifiRttController.registerEventCallback(any(IWifiRttControllerEventCallback.class)))
                 .thenReturn(mWifiStatusSuccess);
 
+        doAnswer(new AnswerWithArguments() {
+            public void answer(IWifiIface.getNameCallback cb)
+                    throws RemoteException {
+                cb.onValues(mWifiStatusSuccess, TEST_IFACE_NAME);
+            }
+        }).when(mIWifiStaIface).getName(any(IWifiIface.getNameCallback.class));
+        doAnswer(new AnswerWithArguments() {
+            public void answer(IWifiIface.getNameCallback cb)
+                    throws RemoteException {
+                cb.onValues(mWifiStatusSuccess, TEST_IFACE_NAME);
+            }
+        }).when(mIWifiApIface).getName(any(IWifiIface.getNameCallback.class));
+
         // Create the vendor HAL object under test.
         mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mLooper.getLooper());
 
@@ -233,11 +247,11 @@
 
     /**
      * Tests the successful starting of HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalSuccessInStaMode() throws  Exception {
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -254,11 +268,11 @@
 
     /**
      * Tests the successful starting of HAL in AP mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalAp()}.
      */
     @Test
     public void testStartHalSuccessInApMode() throws Exception {
-        assertTrue(mWifiVendorHal.startVendorHal(false));
+        assertTrue(mWifiVendorHal.startVendorHalAp());
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -273,7 +287,7 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInStaMode() throws Exception {
@@ -284,7 +298,7 @@
                 return false;
             }
         }).when(mHalDeviceManager).start();
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -299,12 +313,12 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInIfaceCreationInStaMode() throws Exception {
         when(mHalDeviceManager.createStaIface(eq(null), eq(null))).thenReturn(null);
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -320,12 +334,12 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInRttControllerCreationInStaMode() throws Exception {
         when(mHalDeviceManager.createRttController()).thenReturn(null);
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -340,12 +354,12 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInChipGetInStaMode() throws Exception {
         when(mHalDeviceManager.getChip(any(IWifiIface.class))).thenReturn(null);
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -360,13 +374,13 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInStaIfaceCallbackRegistration() throws Exception {
         when(mIWifiStaIface.registerEventCallback(any(IWifiStaIfaceEventCallback.class)))
                 .thenReturn(mWifiStatusFailure);
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -381,13 +395,13 @@
 
     /**
      * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * {@link WifiVendorHal#startVendorHalSta()}.
      */
     @Test
     public void testStartHalFailureInChipCallbackRegistration() throws Exception {
         when(mIWifiChip.registerEventCallback(any(IWifiChipEventCallback.class)))
                 .thenReturn(mWifiStatusFailure);
-        assertFalse(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -402,13 +416,13 @@
     }
 
     /**
-     * Tests the failure to start HAL in STA mode using
-     * {@link WifiVendorHal#startVendorHal(boolean)}.
+     * Tests the failure to start HAL in AP mode using
+     * {@link WifiVendorHal#startVendorHalAp()}.
      */
     @Test
     public void testStartHalFailureInApMode() throws Exception {
         when(mHalDeviceManager.createApIface(eq(null), eq(null))).thenReturn(null);
-        assertFalse(mWifiVendorHal.startVendorHal(false));
+        assertFalse(mWifiVendorHal.startVendorHalAp());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
@@ -426,7 +440,7 @@
      */
     @Test
     public void testStopHalInStaMode() {
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         assertTrue(mWifiVendorHal.isHalStarted());
 
         mWifiVendorHal.stopVendorHal();
@@ -449,7 +463,7 @@
      */
     @Test
     public void testStopHalInApMode() {
-        assertTrue(mWifiVendorHal.startVendorHal(false));
+        assertTrue(mWifiVendorHal.startVendorHalAp());
         assertTrue(mWifiVendorHal.isHalStarted());
 
         mWifiVendorHal.stopVendorHal();
@@ -473,7 +487,7 @@
     public void testEnterLogging() {
         mWifiVendorHal.mLog = spy(mWifiLog);
         mWifiVendorHal.enableVerboseLogging(true);
-        mWifiVendorHal.installPacketFilter(new byte[0]);
+        mWifiVendorHal.installPacketFilter(TEST_IFACE_NAME, new byte[0]);
         verify(mWifiVendorHal.mLog).trace(eq("% filter length %"));
     }
 
@@ -483,10 +497,10 @@
     @Test
     public void testEnterSilenceWhenNotEnabled() {
         mWifiVendorHal.mLog = spy(mWifiLog);
-        mWifiVendorHal.installPacketFilter(new byte[0]);
+        mWifiVendorHal.installPacketFilter(TEST_IFACE_NAME, new byte[0]);
         mWifiVendorHal.enableVerboseLogging(true);
         mWifiVendorHal.enableVerboseLogging(false);
-        mWifiVendorHal.installPacketFilter(new byte[0]);
+        mWifiVendorHal.installPacketFilter(TEST_IFACE_NAME, new byte[0]);
         verify(mWifiVendorHal.mLog, never()).trace(eq("% filter length %"));
     }
 
@@ -498,7 +512,8 @@
         mWifiLog = spy(mWifiLog);
         mWifiVendorHal.mLog = mWifiLog;
         mWifiVendorHal.mVerboseLog = mWifiLog;
-        assertFalse(mWifiVendorHal.getBgScanCapabilities(new WifiNative.ScanCapabilities()));
+        assertFalse(mWifiVendorHal.getBgScanCapabilities(
+                TEST_IFACE_NAME, new WifiNative.ScanCapabilities()));
         verify(mWifiLog).err("% returns %");
     }
 
@@ -527,9 +542,12 @@
 
         WifiNative.ScanCapabilities result = new WifiNative.ScanCapabilities();
 
-        assertFalse(mWifiVendorHal.getBgScanCapabilities(result));  // should fail - not started
-        assertTrue(mWifiVendorHal.startVendorHalSta());           // Start the vendor hal
-        assertTrue(mWifiVendorHal.getBgScanCapabilities(result));   // should succeed
+        // should fail - not started
+        assertFalse(mWifiVendorHal.getBgScanCapabilities(TEST_IFACE_NAME, result));
+        // Start the vendor hal
+        assertTrue(mWifiVendorHal.startVendorHalSta());
+        // should succeed
+        assertTrue(mWifiVendorHal.getBgScanCapabilities(TEST_IFACE_NAME, result));
 
         assertEquals(12, result.max_scan_cache_size);
         assertEquals(34, result.max_scan_buckets);
@@ -583,7 +601,7 @@
      */
     @Test
     public void testGetSupportedFeatures() throws Exception {
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
 
         int staIfaceHidlCaps = (
                 IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN
@@ -616,7 +634,7 @@
         when(mHalDeviceManager.getSupportedIfaceTypes())
                 .thenReturn(halDeviceManagerSupportedIfaces);
 
-        assertEquals(expectedFeatureSet, mWifiVendorHal.getSupportedFeatureSet());
+        assertEquals(expectedFeatureSet, mWifiVendorHal.getSupportedFeatureSet(TEST_IFACE_NAME));
     }
 
     /**
@@ -633,13 +651,13 @@
     public void testLinkLayerStatsEnableAfterStartup() throws Exception {
         doNothing().when(mIWifiStaIface).getLinkLayerStats(any());
 
-        assertNull(mWifiVendorHal.getWifiLinkLayerStats());
+        assertNull(mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME));
         assertTrue(mWifiVendorHal.startVendorHalSta());
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        mWifiVendorHal.getWifiLinkLayerStats();
-        mWifiVendorHal.getWifiLinkLayerStats();
+        mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME);
+        mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME);
         verify(mIWifiStaIface).enableLinkLayerStatsCollection(false); // mLinkLayerStatsDebug
         verify(mIWifiStaIface, times(2)).getLinkLayerStats(any());
     }
@@ -658,7 +676,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHalAp());
         assertTrue(mWifiVendorHal.isHalStarted());
-        assertNull(mWifiVendorHal.getWifiLinkLayerStats());
+        assertNull(mWifiVendorHal.getWifiLinkLayerStats(TEST_IFACE_NAME));
 
         verify(mHalDeviceManager).start();
 
@@ -685,13 +703,13 @@
         randomizePacketStats(r, stats.iface.wmeVoPktStats);
         randomizeRadioStats(r, stats.radios);
 
-        stats.timeStampInMs = 42; // currently dropped in conversion
+        stats.timeStampInMs = r.nextLong() & 0xFFFFFFFFFFL;
 
-        String expected = numbersOnly(stats.toString());
+        String expected = numbersOnly(stats.toString() + " ");
 
         WifiLinkLayerStats converted = WifiVendorHal.frameworkFromHalLinkLayerStats(stats);
 
-        String actual = numbersOnly(converted.toString());
+        String actual = numbersOnly(converted.toString() + " ");
 
         // Do the required fixups to the both expected and actual
         expected = rmValue(expected, stats.radios.get(0).rxTimeInMs);
@@ -699,7 +717,6 @@
 
         actual = rmValue(actual, stats.radios.get(0).rxTimeInMs);
         actual = rmValue(actual, stats.radios.get(0).onTimeInMsForScan);
-        actual = actual + "42 ";
 
         // The remaining fields should agree
         assertEquals(expected, actual);
@@ -947,12 +964,15 @@
 
         when(mIWifiStaIface.setScanningMacOui(any())).thenReturn(mWifiStatusSuccess);
 
-        assertFalse(mWifiVendorHal.setScanningMacOui(oui)); // expect fail - STA not started
+        // expect fail - STA not started
+        assertFalse(mWifiVendorHal.setScanningMacOui(TEST_IFACE_NAME, oui));
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertFalse(mWifiVendorHal.setScanningMacOui(null));  // expect fail - null
-        assertFalse(mWifiVendorHal.setScanningMacOui(new byte[]{(byte) 1})); // expect fail - len
-        assertTrue(mWifiVendorHal.setScanningMacOui(oui));
-        assertTrue(mWifiVendorHal.setScanningMacOui(zzz));
+        // expect fail - null
+        assertFalse(mWifiVendorHal.setScanningMacOui(TEST_IFACE_NAME, null));
+        // expect fail - len
+        assertFalse(mWifiVendorHal.setScanningMacOui(TEST_IFACE_NAME, new byte[]{(byte) 1}));
+        assertTrue(mWifiVendorHal.setScanningMacOui(TEST_IFACE_NAME, oui));
+        assertTrue(mWifiVendorHal.setScanningMacOui(TEST_IFACE_NAME, zzz));
 
         verify(mIWifiStaIface).setScanningMacOui(eq(oui));
         verify(mIWifiStaIface).setScanningMacOui(eq(zzz));
@@ -973,7 +993,8 @@
         )).thenReturn(mWifiStatusSuccess);
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertTrue(0 == mWifiVendorHal.startSendingOffloadedPacket(slot, srcMac, kap, millis));
+        assertTrue(0 == mWifiVendorHal.startSendingOffloadedPacket(
+                TEST_IFACE_NAME, slot, srcMac, kap, millis));
 
         verify(mIWifiStaIface).startSendingKeepAlivePackets(
                 eq(slot), any(), anyShort(), any(), any(), eq(millis));
@@ -986,7 +1007,8 @@
         when(mIWifiStaIface.stopSendingKeepAlivePackets(anyInt())).thenReturn(mWifiStatusSuccess);
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertTrue(0 == mWifiVendorHal.stopSendingOffloadedPacket(slot));
+        assertTrue(0 == mWifiVendorHal.stopSendingOffloadedPacket(
+                TEST_IFACE_NAME, slot));
 
         verify(mIWifiStaIface).stopSendingKeepAlivePackets(eq(slot));
     }
@@ -1011,20 +1033,26 @@
         handler = ((cur) -> {
             breach.add(cur);
         });
-        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(hi, lo, handler)); // not started
-        assertEquals(-1, mWifiVendorHal.stopRssiMonitoring()); // not started
+        // not started
+        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, hi, lo, handler));
+        // not started
+        assertEquals(-1, mWifiVendorHal.stopRssiMonitoring(TEST_IFACE_NAME));
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
+        assertEquals(0, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, hi, lo, handler));
         int theCmdId = mWifiVendorHal.sRssiMonCmdId;
         breach.clear();
         mIWifiStaIfaceEventCallback.onRssiThresholdBreached(theCmdId, new byte[6], lower);
         assertEquals(breach.get(0), lower);
-        assertEquals(0, mWifiVendorHal.stopRssiMonitoring());
-        assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
-        assertEquals(0, mWifiVendorHal.startRssiMonitoring(med, lo, handler)); // replacing works
-        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(hi, lo, null)); // null handler fails
-        assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
-        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(lo, hi, handler)); // empty range
+        assertEquals(0, mWifiVendorHal.stopRssiMonitoring(TEST_IFACE_NAME));
+        assertEquals(0, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, hi, lo, handler));
+        // replacing works
+        assertEquals(0, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, med, lo, handler));
+        // null handler fails
+        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(
+                TEST_IFACE_NAME, hi, lo, null));
+        assertEquals(0, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, hi, lo, handler));
+        // empty range
+        assertEquals(-1, mWifiVendorHal.startRssiMonitoring(TEST_IFACE_NAME, lo, hi, handler));
     }
 
     /**
@@ -1052,11 +1080,12 @@
                 IWifiStaIface.getApfPacketFilterCapabilitiesCallback.class));
 
 
-        assertEquals(0, mWifiVendorHal.getApfCapabilities().apfVersionSupported);
+        assertEquals(0, mWifiVendorHal.getApfCapabilities(TEST_IFACE_NAME)
+                .apfVersionSupported);
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        ApfCapabilities actual = mWifiVendorHal.getApfCapabilities();
+        ApfCapabilities actual = mWifiVendorHal.getApfCapabilities(TEST_IFACE_NAME);
 
         assertEquals(myVersion, actual.apfVersionSupported);
         assertEquals(myMaxSize, actual.maximumApfProgramSize);
@@ -1078,7 +1107,7 @@
                 .thenReturn(mWifiStatusSuccess);
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertTrue(mWifiVendorHal.installPacketFilter(filter));
+        assertTrue(mWifiVendorHal.installPacketFilter(TEST_IFACE_NAME, filter));
 
         verify(mIWifiStaIface).installApfPacketFilter(eq(0), eq(expected));
     }
@@ -1095,11 +1124,12 @@
 
         assertTrue(mWifiVendorHal.startVendorHalAp());
 
-        assertFalse(mWifiVendorHal.setCountryCodeHal(null));
-        assertFalse(mWifiVendorHal.setCountryCodeHal(""));
-        assertFalse(mWifiVendorHal.setCountryCodeHal("A"));
-        assertTrue(mWifiVendorHal.setCountryCodeHal("CA")); // Only one expected to succeed
-        assertFalse(mWifiVendorHal.setCountryCodeHal("ZZZ"));
+        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, null));
+        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, ""));
+        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "A"));
+        // Only one expected to succeed
+        assertTrue(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "CA"));
+        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "ZZZ"));
 
         verify(mIWifiApIface).setCountryCode(eq(expected));
     }
@@ -1114,7 +1144,7 @@
         when(mIWifiApIface.setCountryCode(any()))
                 .thenThrow(new RemoteException("oops"));
         assertTrue(mWifiVendorHal.startVendorHalAp());
-        assertFalse(mWifiVendorHal.setCountryCodeHal("CA"));
+        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "CA"));
         assertFalse(mWifiVendorHal.isHalStarted());
         verify(mWifiLog).err(any());
     }
@@ -1231,11 +1261,11 @@
     public void testStartPktFateMonitoring() throws Exception {
         when(mIWifiStaIface.startDebugPacketFateMonitoring()).thenReturn(mWifiStatusSuccess);
 
-        assertFalse(mWifiVendorHal.startPktFateMonitoring());
+        assertFalse(mWifiVendorHal.startPktFateMonitoring(TEST_IFACE_NAME));
         verify(mIWifiStaIface, never()).startDebugPacketFateMonitoring();
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertTrue(mWifiVendorHal.startPktFateMonitoring());
+        assertTrue(mWifiVendorHal.startPktFateMonitoring(TEST_IFACE_NAME));
         verify(mIWifiStaIface).startDebugPacketFateMonitoring();
     }
 
@@ -1264,13 +1294,13 @@
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
 
         WifiNative.TxFateReport[] retrievedFates = new WifiNative.TxFateReport[1];
-        assertFalse(mWifiVendorHal.getTxPktFates(retrievedFates));
+        assertFalse(mWifiVendorHal.getTxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface, never())
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertTrue(mWifiVendorHal.getTxPktFates(retrievedFates));
+        assertTrue(mWifiVendorHal.getTxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface)
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
         assertEquals(WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED, retrievedFates[0].mFate);
@@ -1307,13 +1337,13 @@
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
 
         WifiNative.TxFateReport[] retrievedFates = new WifiNative.TxFateReport[1];
-        assertFalse(mWifiVendorHal.getTxPktFates(retrievedFates));
+        assertFalse(mWifiVendorHal.getTxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface, never())
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertTrue(mWifiVendorHal.getTxPktFates(retrievedFates));
+        assertTrue(mWifiVendorHal.getTxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface)
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
         assertEquals(WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER, retrievedFates[0].mFate);
@@ -1348,13 +1378,13 @@
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
 
         WifiNative.RxFateReport[] retrievedFates = new WifiNative.RxFateReport[1];
-        assertFalse(mWifiVendorHal.getRxPktFates(retrievedFates));
+        assertFalse(mWifiVendorHal.getRxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface, never())
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertTrue(mWifiVendorHal.getRxPktFates(retrievedFates));
+        assertTrue(mWifiVendorHal.getRxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface)
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
         assertEquals(WifiLoggerHal.RX_PKT_FATE_SUCCESS, retrievedFates[0].mFate);
@@ -1391,13 +1421,13 @@
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
 
         WifiNative.RxFateReport[] retrievedFates = new WifiNative.RxFateReport[1];
-        assertFalse(mWifiVendorHal.getRxPktFates(retrievedFates));
+        assertFalse(mWifiVendorHal.getRxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface, never())
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertTrue(mWifiVendorHal.getRxPktFates(retrievedFates));
+        assertTrue(mWifiVendorHal.getRxPktFates(TEST_IFACE_NAME, retrievedFates));
         verify(mIWifiStaIface)
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
         assertEquals(WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER, retrievedFates[0].mFate);
@@ -1413,7 +1443,7 @@
     @Test
     public void testGetTxPktFatesEmptyInputArray() throws Exception {
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertFalse(mWifiVendorHal.getTxPktFates(new WifiNative.TxFateReport[0]));
+        assertFalse(mWifiVendorHal.getTxPktFates(TEST_IFACE_NAME, new WifiNative.TxFateReport[0]));
         verify(mIWifiStaIface, never())
                 .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
     }
@@ -1424,7 +1454,7 @@
     @Test
     public void testGetRxPktFatesEmptyInputArray() throws Exception {
         assertTrue(mWifiVendorHal.startVendorHalSta());
-        assertFalse(mWifiVendorHal.getRxPktFates(new WifiNative.RxFateReport[0]));
+        assertFalse(mWifiVendorHal.getRxPktFates(TEST_IFACE_NAME, new WifiNative.RxFateReport[0]));
         verify(mIWifiStaIface, never())
                 .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
     }
@@ -1436,14 +1466,14 @@
     public void testEnableDisableNdOffload() throws Exception {
         when(mIWifiStaIface.enableNdOffload(anyBoolean())).thenReturn(mWifiStatusSuccess);
 
-        assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+        assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(TEST_IFACE_NAME, true));
         verify(mIWifiStaIface, never()).enableNdOffload(anyBoolean());
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+        assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(TEST_IFACE_NAME, true));
         verify(mIWifiStaIface).enableNdOffload(eq(true));
-        assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(false));
+        assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(TEST_IFACE_NAME, false));
         verify(mIWifiStaIface).enableNdOffload(eq(false));
     }
 
@@ -1456,7 +1486,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHalSta());
 
-        assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+        assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(TEST_IFACE_NAME, true));
         verify(mIWifiStaIface).enableNdOffload(eq(true));
     }
 
@@ -1672,8 +1702,8 @@
 
         int cmdId = mWifiVendorHal.mScan.cmdId;
 
-        mWifiVendorHal.stopBgScan();
-        mWifiVendorHal.stopBgScan(); // second call should not do anything
+        mWifiVendorHal.stopBgScan(TEST_IFACE_NAME);
+        mWifiVendorHal.stopBgScan(TEST_IFACE_NAME); // second call should not do anything
         verify(mIWifiStaIface).stopBackgroundScan(cmdId); // Should be called just once
     }
 
@@ -1690,8 +1720,8 @@
 
         int cmdId = mWifiVendorHal.mScan.cmdId;
 
-        mWifiVendorHal.pauseBgScan();
-        mWifiVendorHal.restartBgScan();
+        mWifiVendorHal.pauseBgScan(TEST_IFACE_NAME);
+        mWifiVendorHal.restartBgScan(TEST_IFACE_NAME);
         verify(mIWifiStaIface).stopBackgroundScan(cmdId); // Should be called just once
         verify(mIWifiStaIface, times(2)).startBackgroundScan(eq(cmdId), any());
     }
@@ -1854,7 +1884,7 @@
      */
     @Test
     public void testSelectTxPowerScenario() throws RemoteException {
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         // Should fail because we exposed the 1.0 IWifiChip.
         assertFalse(
                 mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
@@ -1865,7 +1895,7 @@
         mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
         when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
 
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         assertTrue(
                 mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
         verify(mIWifiChipV11).selectTxPowerScenario(
@@ -1880,7 +1910,7 @@
      */
     @Test
     public void testResetTxPowerScenario() throws RemoteException {
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         // Should fail because we exposed the 1.0 IWifiChip.
         assertFalse(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
         verify(mIWifiChipV11, never()).resetTxPowerScenario();
@@ -1890,7 +1920,7 @@
         mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
         when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess);
 
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         assertTrue(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
         verify(mIWifiChipV11).resetTxPowerScenario();
         verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
@@ -1906,13 +1936,73 @@
         mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
         when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
 
-        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertTrue(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.selectTxPowerScenario(-6));
         verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
         verify(mIWifiChipV11, never()).resetTxPowerScenario();
         mWifiVendorHal.stopVendorHal();
     }
 
+    /**
+     * Test the STA Iface creation failure due to iface name retrieval failure.
+     */
+    @Test
+    public void testCreateStaIfaceFailureInIfaceName() throws RemoteException {
+        doAnswer(new AnswerWithArguments() {
+            public void answer(IWifiIface.getNameCallback cb)
+                    throws RemoteException {
+                cb.onValues(mWifiStatusFailure, "wlan0");
+            }
+        }).when(mIWifiStaIface).getName(any(IWifiIface.getNameCallback.class));
+
+        assertTrue(mWifiVendorHal.startVendorHal());
+        assertNull(mWifiVendorHal.createStaIface(null));
+        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+    }
+
+    /**
+     * Test the STA Iface creation failure due to iface name retrieval failure.
+     */
+    @Test
+    public void testCreateApIfaceFailureInIfaceName() throws RemoteException {
+        doAnswer(new AnswerWithArguments() {
+            public void answer(IWifiIface.getNameCallback cb)
+                    throws RemoteException {
+                cb.onValues(mWifiStatusFailure, "wlan0");
+            }
+        }).when(mIWifiApIface).getName(any(IWifiIface.getNameCallback.class));
+
+        assertTrue(mWifiVendorHal.startVendorHal());
+        assertNull(mWifiVendorHal.createApIface(null));
+        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+    }
+
+    /**
+     * Test the creation and removal of STA Iface.
+     */
+    @Test
+    public void testCreateRemoveStaIface() throws RemoteException {
+        assertTrue(mWifiVendorHal.startVendorHal());
+        String ifaceName = mWifiVendorHal.createStaIface(null);
+        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        assertEquals(TEST_IFACE_NAME, ifaceName);
+        assertTrue(mWifiVendorHal.removeStaIface(ifaceName));
+        verify(mHalDeviceManager).removeIface(eq(mIWifiStaIface));
+    }
+
+    /**
+     * Test the creation and removal of Ap Iface.
+     */
+    @Test
+    public void testCreateRemoveApIface() throws RemoteException {
+        assertTrue(mWifiVendorHal.startVendorHal());
+        String ifaceName = mWifiVendorHal.createApIface(null);
+        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        assertEquals(TEST_IFACE_NAME, ifaceName);
+        assertTrue(mWifiVendorHal.removeApIface(ifaceName));
+        verify(mHalDeviceManager).removeIface(eq(mIWifiApIface));
+    }
+
     private void startBgScan(WifiNative.ScanEventHandler eventHandler) throws Exception {
         when(mIWifiStaIface.startBackgroundScan(
                 anyInt(), any(StaBackgroundScanParameters.class))).thenReturn(mWifiStatusSuccess);
@@ -1923,7 +2013,7 @@
         bucketSettings.period_ms = 16000;
         bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
         settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
-        assertTrue(mWifiVendorHal.startBgScan(settings, eventHandler));
+        assertTrue(mWifiVendorHal.startBgScan(TEST_IFACE_NAME, settings, eventHandler));
     }
 
     // Create a pair of HIDL scan result and its corresponding framework scan result for
diff --git a/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java
index 2aba969..524324a 100644
--- a/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java
@@ -16,28 +16,29 @@
 
 package com.android.server.wifi;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static android.net.wifi.WifiConfiguration.KeyMgmt.NONE;
+import static android.net.wifi.WifiConfiguration.KeyMgmt.WPA_PSK;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 import android.net.wifi.IApInterface;
+import android.net.wifi.IApInterfaceEventCallback;
 import android.net.wifi.IClientInterface;
 import android.net.wifi.IPnoScanEvent;
 import android.net.wifi.IScanEvent;
 import android.net.wifi.IWifiScannerImpl;
 import android.net.wifi.IWificond;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiScanner;
+import android.os.IBinder;
+import android.os.RemoteException;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.wifi.util.NativeUtil;
@@ -55,6 +56,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.HashSet;
@@ -69,14 +71,19 @@
     @Mock private WifiMonitor mWifiMonitor;
     @Mock private WifiMetrics mWifiMetrics;
     @Mock private IWificond mWificond;
+    @Mock private IBinder mWifiCondBinder;
     @Mock private IClientInterface mClientInterface;
     @Mock private IWifiScannerImpl mWifiScannerImpl;
     @Mock private CarrierNetworkConfig mCarrierNetworkConfig;
     @Mock private IApInterface mApInterface;
+    @Mock private WifiNative.SoftApListener mSoftApListener;
     private WificondControl mWificondControl;
     private static final String TEST_INTERFACE_NAME = "test_wlan_if";
+    private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1";
     private static final byte[] TEST_SSID =
             new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+    private static final byte[] TEST_PSK =
+            new byte[] {'T', 'e', 's', 't'};
     private static final byte[] TEST_BSSID =
             new byte[] {(byte) 0x12, (byte) 0xef, (byte) 0xa1,
                         (byte) 0x2c, (byte) 0x97, (byte) 0x8b};
@@ -153,132 +160,308 @@
         // created in specific tests
         MockitoAnnotations.initMocks(this);
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+        when(mWificond.asBinder()).thenReturn(mWifiCondBinder);
         when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
-        when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
+        when(mWificond.createClientInterface(any())).thenReturn(mClientInterface);
+        when(mWificond.createApInterface(any())).thenReturn(mApInterface);
+        when(mWificond.tearDownClientInterface(any())).thenReturn(true);
+        when(mWificond.tearDownApInterface(any())).thenReturn(true);
         when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
         when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
         when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
         mWificondControl = new WificondControl(mWifiInjector, mWifiMonitor, mCarrierNetworkConfig);
-        assertEquals(
-                mClientInterface, mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME));
+        assertEquals(mClientInterface, mWificondControl.setupInterfaceForClientMode(
+                TEST_INTERFACE_NAME));
+        verify(mWifiInjector).makeWificond();
+        verify(mWifiCondBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
     }
 
     /**
-     * Verifies that setupDriverForClientMode(TEST_INTERFACE_NAME) calls Wificond.
+     * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) calls Wificond.
      */
     @Test
-    public void testSetupDriverForClientMode() throws Exception {
+    public void testSetupInterfaceForClientMode() throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
         verify(mWificond).createClientInterface(TEST_INTERFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForClientMode(TEST_INTERFACE_NAME) calls subscribeScanEvents().
+     * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) calls subscribeScanEvents().
      */
     @Test
-    public void testSetupDriverForClientModeCallsScanEventSubscripiton() throws Exception {
+    public void testSetupInterfaceForClientModeCallsScanEventSubscripiton() throws Exception {
         verify(mWifiScannerImpl).subscribeScanEvents(any(IScanEvent.class));
     }
 
     /**
-     * Verifies that setupDriverForClientMode(TEST_INTERFACE_NAME) returns null when wificond is
+     * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) returns null when wificond is
      * not started.
      */
     @Test
-    public void testSetupDriverForClientModeErrorWhenWificondIsNotStarted() throws Exception {
+    public void testSetupInterfaceForClientModeErrorWhenWificondIsNotStarted() throws Exception {
+        // Invoke wificond death handler to clear the handle.
+        mWificondControl.binderDied();
         when(mWifiInjector.makeWificond()).thenReturn(null);
         IClientInterface returnedClientInterface =
-                mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
         assertEquals(null, returnedClientInterface);
+        verify(mWifiInjector, times(2)).makeWificond();
     }
 
     /**
-     * Verifies that setupDriverForClientMode(TEST_INTERFACE_NAME) returns null when wificond failed
-     * to setup client interface.
+     * Verifies that setupInterfaceForClientMode(TEST_INTERFACE_NAME) returns null when wificond
+     * failed to setup client interface.
      */
     @Test
-    public void testSetupDriverForClientModeErrorWhenWificondFailedToSetupInterface()
+    public void testSetupInterfaceForClientModeErrorWhenWificondFailedToSetupInterface()
             throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(null);
 
         IClientInterface returnedClientInterface =
-                mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
         assertEquals(null, returnedClientInterface);
     }
 
     /**
-     * Verifies that setupDriverForSoftApMode(TEST_INTERFACE_NAME) calls wificond.
+     * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond.
      */
     @Test
-    public void testSetupDriverForSoftApMode() throws Exception {
+    public void testTeardownClientInterface() throws Exception {
+        when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(true);
+
+        assertTrue(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME));
+        verify(mWifiScannerImpl).unsubscribeScanEvents();
+        verify(mWifiScannerImpl).unsubscribePnoScanEvents();
+        verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME);
+    }
+
+    /**
+     * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond.
+     */
+    @Test
+    public void testTeardownClientInterfaceFailDueToExceptionScannerUnsubscribe() throws Exception {
+        when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(true);
+        doThrow(new RemoteException()).when(mWifiScannerImpl).unsubscribeScanEvents();
+
+        assertFalse(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME));
+        verify(mWifiScannerImpl).unsubscribeScanEvents();
+        verify(mWifiScannerImpl, never()).unsubscribePnoScanEvents();
+        verify(mWificond, never()).tearDownClientInterface(TEST_INTERFACE_NAME);
+    }
+    /**
+     * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond.
+     */
+    @Test
+    public void testTeardownClientInterfaceErrorWhenWificondFailed() throws Exception {
+        when(mWificond.tearDownClientInterface(TEST_INTERFACE_NAME)).thenReturn(false);
+
+        assertFalse(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME));
+        verify(mWifiScannerImpl).unsubscribeScanEvents();
+        verify(mWifiScannerImpl).unsubscribePnoScanEvents();
+        verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME);
+    }
+
+    /**
+     * Verifies that the client handles are cleared after teardown.
+     */
+    @Test
+    public void testTeardownClientInterfaceClearsHandles() throws Exception {
+        testTeardownClientInterface();
+
+        assertNull(mWificondControl.signalPoll(TEST_INTERFACE_NAME));
+        verify(mClientInterface, never()).signalPoll();
+
+        assertFalse(mWificondControl.scan(
+                TEST_INTERFACE_NAME, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
+        verify(mWifiScannerImpl, never()).scan(any());
+    }
+
+    /**
+     * Verifies that setupInterfaceForSoftApMode(TEST_INTERFACE_NAME) calls wificond.
+     */
+    @Test
+    public void testSetupInterfaceForSoftApMode() throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createApInterface(TEST_INTERFACE_NAME)).thenReturn(mApInterface);
 
         IApInterface returnedApInterface =
-                mWificondControl.setupDriverForSoftApMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME);
         assertEquals(mApInterface, returnedApInterface);
+        verify(mWifiInjector).makeWificond();
+        verify(mWifiCondBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
         verify(mWificond).createApInterface(TEST_INTERFACE_NAME);
     }
 
     /**
-     * Verifies that setupDriverForSoftAp() returns null when wificond is not started.
+     * Verifies that setupInterfaceForSoftAp() returns null when wificond is not started.
      */
     @Test
-    public void testSetupDriverForSoftApModeErrorWhenWificondIsNotStarted() throws Exception {
+    public void testSetupInterfaceForSoftApModeErrorWhenWificondIsNotStarted() throws Exception {
+        // Invoke wificond death handler to clear the handle.
+        mWificondControl.binderDied();
         when(mWifiInjector.makeWificond()).thenReturn(null);
 
         IApInterface returnedApInterface =
-                mWificondControl.setupDriverForSoftApMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME);
 
         assertEquals(null, returnedApInterface);
+        verify(mWifiInjector, times(2)).makeWificond();
     }
 
     /**
-     * Verifies that setupDriverForSoftApMode(TEST_INTERFACE_NAME) returns null when wificond failed
-     * to setup AP interface.
+     * Verifies that setupInterfaceForSoftApMode(TEST_INTERFACE_NAME) returns null when wificond
+     * failed to setup AP interface.
      */
     @Test
-    public void testSetupDriverForSoftApModeErrorWhenWificondFailedToSetupInterface()
+    public void testSetupInterfaceForSoftApModeErrorWhenWificondFailedToSetupInterface()
             throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createApInterface(TEST_INTERFACE_NAME)).thenReturn(null);
 
         IApInterface returnedApInterface =
-                mWificondControl.setupDriverForSoftApMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME);
         assertEquals(null, returnedApInterface);
     }
 
     /**
+     * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond.
+     */
+    @Test
+    public void testTeardownSoftApInterface() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        when(mWificond.tearDownApInterface(TEST_INTERFACE_NAME)).thenReturn(true);
+
+        assertTrue(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME));
+        verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME);
+    }
+
+    /**
+     * Verifies that tearDownClientInterface(TEST_INTERFACE_NAME) calls Wificond.
+     */
+    @Test
+    public void testTeardownSoftApInterfaceErrorWhenWificondFailed() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        when(mWificond.tearDownApInterface(TEST_INTERFACE_NAME)).thenReturn(false);
+
+        assertFalse(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME));
+        verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME);
+    }
+
+    /**
+     * Verifies that the SoftAp handles are cleared after teardown.
+     */
+    @Test
+    public void testTeardownSoftApInterfaceClearsHandles() throws Exception {
+        testTeardownSoftApInterface();
+
+        assertFalse(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, new WifiConfiguration(), mSoftApListener));
+        verify(mApInterface, never()).writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class));
+        verify(mApInterface, never()).startHostapd(any());
+    }
+
+    /**
+     * Verifies that we can setup concurrent interfaces.
+     */
+    @Test
+    public void testSetupMulitpleInterfaces() throws Exception {
+        when(mWificond.createApInterface(TEST_INTERFACE_NAME1)).thenReturn(mApInterface);
+
+        IApInterface returnedApInterface =
+                mWificondControl.setupInterfaceForSoftApMode(TEST_INTERFACE_NAME1);
+        assertEquals(mApInterface, returnedApInterface);
+        verify(mWifiInjector).makeWificond();
+        verify(mWifiCondBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+
+        verify(mWificond).createClientInterface(TEST_INTERFACE_NAME);
+        verify(mWificond).createApInterface(TEST_INTERFACE_NAME1);
+    }
+
+    /**
+     * Verifies that we can setup concurrent interfaces.
+     */
+    @Test
+    public void testTeardownMulitpleInterfaces() throws Exception {
+        testSetupMulitpleInterfaces();
+        assertTrue(mWificondControl.tearDownClientInterface(TEST_INTERFACE_NAME));
+        assertTrue(mWificondControl.tearDownSoftApInterface(TEST_INTERFACE_NAME1));
+
+        verify(mWificond).tearDownClientInterface(TEST_INTERFACE_NAME);
+        verify(mWificond).tearDownApInterface(TEST_INTERFACE_NAME1);
+    }
+
+    /**
      * Verifies that enableSupplicant() calls wificond.
      */
     @Test
     public void testEnableSupplicant() throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
         when(mWificond.enableSupplicant()).thenReturn(true);
 
-        mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
         assertTrue(mWificondControl.enableSupplicant());
+        verify(mWifiInjector).makeWificond();
         verify(mWificond).enableSupplicant();
     }
 
     /**
+     * Verifies that enableSupplicant() returns false when there is no configured
+     * client interface.
+     */
+    @Test
+    public void testEnableSupplicantErrorWhenNoClientInterfaceConfigured() throws Exception {
+        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+        when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
+
+        // Configure client interface.
+        IClientInterface returnedClientInterface =
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
+        assertEquals(mClientInterface, returnedClientInterface);
+
+        // Tear down interfaces.
+        assertTrue(mWificondControl.tearDownInterfaces());
+
+        // Enabling supplicant should fail.
+        assertFalse(mWificondControl.enableSupplicant());
+    }
+
+    /**
      * Verifies that disableSupplicant() calls wificond.
      */
     @Test
     public void testDisableSupplicant() throws Exception {
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
         when(mWificond.disableSupplicant()).thenReturn(true);
 
-        mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
         assertTrue(mWificondControl.disableSupplicant());
+        verify(mWifiInjector).makeWificond();
         verify(mWificond).disableSupplicant();
     }
 
     /**
+     * Verifies that disableSupplicant() returns false when there is no configured
+     * client interface.
+     */
+    @Test
+    public void testDisableSupplicantErrorWhenNoClientInterfaceConfigured() throws Exception {
+        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+        when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
+
+        // Configure client interface.
+        IClientInterface returnedClientInterface =
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
+        assertEquals(mClientInterface, returnedClientInterface);
+
+        // Tear down interfaces.
+        assertTrue(mWificondControl.tearDownInterfaces());
+
+        // Disabling supplicant should fail.
+        assertFalse(mWificondControl.disableSupplicant());
+    }
+
+    /**
      * Verifies that tearDownInterfaces() calls wificond.
      */
     @Test
@@ -304,6 +487,8 @@
      */
     @Test
     public void testTearDownInterfacesErrorWhenWificondIsNotStarterd() throws Exception {
+        // Invoke wificond death handler to clear the handle.
+        mWificondControl.binderDied();
         when(mWifiInjector.makeWificond()).thenReturn(null);
         assertFalse(mWificondControl.tearDownInterfaces());
     }
@@ -316,8 +501,8 @@
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
 
-        mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
-        mWificondControl.signalPoll();
+        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
+        mWificondControl.signalPoll(TEST_INTERFACE_NAME);
         verify(mClientInterface).signalPoll();
     }
 
@@ -331,14 +516,14 @@
 
         // Configure client interface.
         IClientInterface returnedClientInterface =
-                mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
         assertEquals(mClientInterface, returnedClientInterface);
 
         // Tear down interfaces.
         assertTrue(mWificondControl.tearDownInterfaces());
 
         // Signal poll should fail.
-        assertEquals(null, mWificondControl.signalPoll());
+        assertEquals(null, mWificondControl.signalPoll(TEST_INTERFACE_NAME));
     }
 
     /**
@@ -349,8 +534,8 @@
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWificond.createClientInterface(TEST_INTERFACE_NAME)).thenReturn(mClientInterface);
 
-        mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
-        mWificondControl.getTxPacketCounters();
+        mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
+        mWificondControl.getTxPacketCounters(TEST_INTERFACE_NAME);
         verify(mClientInterface).getPacketCounters();
     }
 
@@ -365,14 +550,14 @@
 
         // Configure client interface.
         IClientInterface returnedClientInterface =
-                mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
         assertEquals(mClientInterface, returnedClientInterface);
 
         // Tear down interfaces.
         assertTrue(mWificondControl.tearDownInterfaces());
 
         // Signal poll should fail.
-        assertEquals(null, mWificondControl.getTxPacketCounters());
+        assertEquals(null, mWificondControl.getTxPacketCounters(TEST_INTERFACE_NAME));
     }
 
     /**
@@ -386,7 +571,7 @@
 
         // Configure client interface.
         IClientInterface returnedClientInterface =
-                mWificondControl.setupDriverForClientMode(TEST_INTERFACE_NAME);
+                mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME);
         assertEquals(mClientInterface, returnedClientInterface);
 
         // Tear down interfaces.
@@ -394,7 +579,8 @@
 
         // getScanResults should fail.
         assertEquals(0,
-                mWificondControl.getScanResults(WificondControl.SCAN_TYPE_SINGLE_SCAN).size());
+                mWificondControl.getScanResults(TEST_INTERFACE_NAME,
+                        WificondControl.SCAN_TYPE_SINGLE_SCAN).size());
     }
 
     /**
@@ -409,7 +595,7 @@
         when(mWifiScannerImpl.getScanResults()).thenReturn(mockScanResults);
 
         ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults(
-                WificondControl.SCAN_TYPE_SINGLE_SCAN);
+                TEST_INTERFACE_NAME, WificondControl.SCAN_TYPE_SINGLE_SCAN);
         // The test IEs {@link #TEST_INFO_ELEMENT} doesn't contained RSN IE, which means non-EAP
         // AP. So verify carrier network is not checked, since EAP is currently required for a
         // carrier network.
@@ -457,7 +643,7 @@
         when(mCarrierNetworkConfig.getCarrierName(new String(nativeScanResult.ssid)))
                 .thenReturn(carrierName);
         ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults(
-                WificondControl.SCAN_TYPE_SINGLE_SCAN);
+                TEST_INTERFACE_NAME, WificondControl.SCAN_TYPE_SINGLE_SCAN);
         assertEquals(1, returnedScanResults.size());
         // Verify returned scan result.
         ScanResult scanResult = returnedScanResults.get(0).getScanResult();
@@ -471,7 +657,7 @@
         when(mCarrierNetworkConfig.isCarrierNetwork(new String(nativeScanResult.ssid)))
                 .thenReturn(false);
         returnedScanResults = mWificondControl.getScanResults(
-                WificondControl.SCAN_TYPE_SINGLE_SCAN);
+                TEST_INTERFACE_NAME, WificondControl.SCAN_TYPE_SINGLE_SCAN);
         assertEquals(1, returnedScanResults.size());
         // Verify returned scan result.
         scanResult = returnedScanResults.get(0).getScanResult();
@@ -487,7 +673,8 @@
     @Test
     public void testScan() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
+        assertTrue(mWificondControl.scan(
+                TEST_INTERFACE_NAME, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
         verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
                 SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET)));
     }
@@ -498,7 +685,7 @@
     @Test
     public void testScanNullParameters() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.scan(null, null));
+        assertTrue(mWificondControl.scan(TEST_INTERFACE_NAME, null, null));
         verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(null, null)));
     }
 
@@ -508,7 +695,8 @@
     @Test
     public void testScanFailure() throws Exception {
         when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(false);
-        assertFalse(mWificondControl.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
+        assertFalse(mWificondControl.scan(
+                TEST_INTERFACE_NAME, SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
         verify(mWifiScannerImpl).scan(any(SingleScanSettings.class));
     }
 
@@ -518,7 +706,7 @@
     @Test
     public void testStartPnoScan() throws Exception {
         when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(true);
-        assertTrue(mWificondControl.startPnoScan(TEST_PNO_SETTINGS));
+        assertTrue(mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS));
         verify(mWifiScannerImpl).startPnoScan(argThat(new PnoScanMatcher(TEST_PNO_SETTINGS)));
     }
 
@@ -528,7 +716,7 @@
     @Test
     public void testStopPnoScan() throws Exception {
         when(mWifiScannerImpl.stopPnoScan()).thenReturn(true);
-        assertTrue(mWificondControl.stopPnoScan());
+        assertTrue(mWificondControl.stopPnoScan(TEST_INTERFACE_NAME));
         verify(mWifiScannerImpl).stopPnoScan();
     }
 
@@ -539,7 +727,7 @@
     public void testStopPnoScanFailure() throws Exception {
 
         when(mWifiScannerImpl.stopPnoScan()).thenReturn(false);
-        assertFalse(mWificondControl.stopPnoScan());
+        assertFalse(mWificondControl.stopPnoScan(TEST_INTERFACE_NAME));
         verify(mWifiScannerImpl).stopPnoScan();
     }
 
@@ -617,7 +805,7 @@
     @Test
     public void testStartPnoScanForMetrics() throws Exception {
         when(mWifiScannerImpl.startPnoScan(any(PnoSettings.class))).thenReturn(false);
-        assertFalse(mWificondControl.startPnoScan(TEST_PNO_SETTINGS));
+        assertFalse(mWificondControl.startPnoScan(TEST_INTERFACE_NAME, TEST_PNO_SETTINGS));
         verify(mWifiMetrics).incrementPnoScanStartAttempCount();
         verify(mWifiMetrics).incrementPnoScanFailedCount();
     }
@@ -627,10 +815,283 @@
      */
     @Test
     public void testAbortScan() throws Exception {
-        mWificondControl.abortScan();
+        mWificondControl.abortScan(TEST_INTERFACE_NAME);
         verify(mWifiScannerImpl).abortScan();
     }
 
+    /**
+     * Verifies successful soft ap start.
+     */
+    @Test
+    public void testStartSoftApWithPskConfig() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+        config.allowedKeyManagement.set(WPA_PSK);
+        config.preSharedKey = new String(TEST_PSK, StandardCharsets.UTF_8);
+        config.hiddenSSID = false;
+        config.apChannel = TEST_FREQUENCY;
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(true);
+        when(mApInterface.startHostapd(any())).thenReturn(true);
+
+        assertTrue(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), eq(false), eq(TEST_FREQUENCY),
+                eq(IApInterface.ENCRYPTION_TYPE_WPA), eq(TEST_PSK));
+        verify(mApInterface).startHostapd(any());
+    }
+
+    /**
+     * Verifies successful soft ap start.
+     */
+    @Test
+    public void testStartSoftApWithOpenHiddenConfig() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+        config.allowedKeyManagement.set(NONE);
+        config.hiddenSSID = true;
+        config.apChannel = TEST_FREQUENCY;
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(true);
+        when(mApInterface.startHostapd(any())).thenReturn(true);
+
+        assertTrue(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), eq(true), eq(TEST_FREQUENCY),
+                eq(IApInterface.ENCRYPTION_TYPE_NONE), eq(new byte[0]));
+        verify(mApInterface).startHostapd(any());
+    }
+
+    /**
+     * Ensures that the Ap interface callbacks are forwarded to the
+     * SoftApListener used for starting soft AP.
+     */
+    @Test
+    public void testSoftApListenerInvocation() throws Exception {
+        testSetupInterfaceForSoftApMode();
+
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(true);
+        when(mApInterface.startHostapd(any())).thenReturn(true);
+
+        final ArgumentCaptor<IApInterfaceEventCallback> apInterfaceCallbackCaptor =
+                ArgumentCaptor.forClass(IApInterfaceEventCallback.class);
+
+        assertTrue(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), anyBoolean(), anyInt(),
+                anyInt(), any(byte[].class));
+        verify(mApInterface).startHostapd(apInterfaceCallbackCaptor.capture());
+
+        int numStations = 5;
+        apInterfaceCallbackCaptor.getValue().onNumAssociatedStationsChanged(numStations);
+        verify(mSoftApListener).onNumAssociatedStationsChanged(eq(numStations));
+
+    }
+
+    /**
+     * Ensure that soft ap start fails when the interface is not setup.
+     */
+    @Test
+    public void testStartSoftApWithoutSetupInterface() throws Exception {
+        assertFalse(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, new WifiConfiguration(), mSoftApListener));
+        verify(mApInterface, never()).writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class));
+        verify(mApInterface, never()).startHostapd(any());
+    }
+
+    /**
+     * Verifies soft ap start failure.
+     */
+    @Test
+    public void testStartSoftApFailDueToWriteConfigError() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(false);
+        when(mApInterface.startHostapd(any())).thenReturn(true);
+
+        assertFalse(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), anyBoolean(), anyInt(),
+                anyInt(), any(byte[].class));
+        verify(mApInterface, never()).startHostapd(any());
+    }
+
+    /**
+     * Verifies soft ap start failure.
+     */
+    @Test
+    public void testStartSoftApFailDueToStartError() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(true);
+        when(mApInterface.startHostapd(any())).thenReturn(false);
+
+        assertFalse(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), anyBoolean(), anyInt(),
+                anyInt(), any(byte[].class));
+        verify(mApInterface).startHostapd(any());
+    }
+
+    /**
+     * Verifies soft ap start failure.
+     */
+    @Test
+    public void testStartSoftApFailDueToExceptionInStart() throws Exception {
+        testSetupInterfaceForSoftApMode();
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = new String(TEST_SSID, StandardCharsets.UTF_8);
+
+        when(mApInterface.writeHostapdConfig(
+                any(byte[].class), anyBoolean(), anyInt(), anyInt(), any(byte[].class)))
+                .thenReturn(true);
+        doThrow(new RemoteException()).when(mApInterface).startHostapd(any());
+
+        assertFalse(mWificondControl.startSoftAp(
+                TEST_INTERFACE_NAME, config, mSoftApListener));
+        verify(mApInterface).writeHostapdConfig(
+                eq(TEST_SSID), anyBoolean(), anyInt(),
+                anyInt(), any(byte[].class));
+        verify(mApInterface).startHostapd(any());
+    }
+
+    /**
+     * Verifies soft ap stop success.
+     */
+    @Test
+    public void testStopSoftAp() throws Exception {
+        testSetupInterfaceForSoftApMode();
+
+        when(mApInterface.stopHostapd()).thenReturn(true);
+
+        assertTrue(mWificondControl.stopSoftAp(TEST_INTERFACE_NAME));
+        verify(mApInterface).stopHostapd();
+    }
+
+    /**
+     * Ensure that soft ap stop fails when the interface is not setup.
+     */
+    @Test
+    public void testStopSoftApWithOutSetupInterface() throws Exception {
+        when(mApInterface.stopHostapd()).thenReturn(true);
+        assertFalse(mWificondControl.stopSoftAp(TEST_INTERFACE_NAME));
+        verify(mApInterface, never()).stopHostapd();
+    }
+
+    /**
+     * Verifies soft ap stop failure.
+     */
+    @Test
+    public void testStopSoftApFailDueToStopError() throws Exception {
+        testSetupInterfaceForSoftApMode();
+
+        when(mApInterface.stopHostapd()).thenReturn(false);
+
+        assertFalse(mWificondControl.stopSoftAp(TEST_INTERFACE_NAME));
+        verify(mApInterface).stopHostapd();
+    }
+
+    /**
+     * Verifies soft ap stop failure.
+     */
+    @Test
+    public void testStopSoftApFailDueToExceptionInStop() throws Exception {
+        testSetupInterfaceForSoftApMode();
+
+        doThrow(new RemoteException()).when(mApInterface).stopHostapd();
+
+        assertFalse(mWificondControl.stopSoftAp(TEST_INTERFACE_NAME));
+        verify(mApInterface).stopHostapd();
+    }
+
+    /**
+     * Verifies registration and invocation of wificond death handler.
+     */
+    @Test
+    public void testRegisterDeathHandler() throws Exception {
+        WifiNative.WificondDeathEventHandler handler =
+                mock(WifiNative.WificondDeathEventHandler.class);
+        assertTrue(mWificondControl.registerDeathHandler(handler));
+        mWificondControl.binderDied();
+        verify(handler).onDeath();
+    }
+
+    /**
+     * Verifies registration and invocation of 2 wificond death handlers.
+     */
+    @Test
+    public void testRegisterTwoDeathHandlers() throws Exception {
+        WifiNative.WificondDeathEventHandler handler1 =
+                mock(WifiNative.WificondDeathEventHandler.class);
+        WifiNative.WificondDeathEventHandler handler2 =
+                mock(WifiNative.WificondDeathEventHandler.class);
+        assertTrue(mWificondControl.registerDeathHandler(handler1));
+        assertFalse(mWificondControl.registerDeathHandler(handler2));
+        mWificondControl.binderDied();
+        verify(handler1).onDeath();
+        verify(handler2, never()).onDeath();
+    }
+
+    /**
+     * Verifies de-registration of wificond death handler.
+     */
+    @Test
+    public void testDeregisterDeathHandler() throws Exception {
+        WifiNative.WificondDeathEventHandler handler =
+                mock(WifiNative.WificondDeathEventHandler.class);
+        assertTrue(mWificondControl.registerDeathHandler(handler));
+        assertTrue(mWificondControl.deregisterDeathHandler());
+        mWificondControl.binderDied();
+        verify(handler, never()).onDeath();
+    }
+
+    /**
+     * Verifies handling of wificond death and ensures that all internal state is cleared and
+     * handlers are invoked.
+     */
+    @Test
+    public void testDeathHandling() throws Exception {
+        WifiNative.WificondDeathEventHandler handler =
+                mock(WifiNative.WificondDeathEventHandler.class);
+        assertTrue(mWificondControl.registerDeathHandler(handler));
+
+        testSetupInterfaceForClientMode();
+
+        mWificondControl.binderDied();
+        verify(handler).onDeath();
+
+        // The handles should be cleared after death, so these should retrieve new handles.
+        when(mWificond.enableSupplicant()).thenReturn(true);
+        assertTrue(mWificondControl.enableSupplicant());
+        verify(mWifiInjector, times(2)).makeWificond();
+        verify(mWificond).enableSupplicant();
+    }
+
     // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
     // matches the provided frequency set and ssid set.
     private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java
new file mode 100644
index 0000000..c0d319f
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/OsuNetworkConnectionTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.TestUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointProvisioner}.
+ */
+@SmallTest
+public class OsuNetworkConnectionTest {
+    private static final String TAG = "OsuNetworkConnectionTest";
+    private static final int ENABLE_LOGGING = 1;
+    private static final int DISABLE_LOGGING = 0;
+
+    private static final int TEST_NETWORK_ID = 6;
+    private static final String TEST_NAI = null;
+    private static final String TEST_NAI_OSEN = "access.test.com";
+    private static final WifiSsid TEST_SSID = WifiSsid.createFromAsciiEncoded("Test SSID");
+
+    private BroadcastReceiver mBroadcastReceiver;
+    private OsuNetworkConnection mNetworkConnection;
+    private TestLooper mLooper;
+    private Handler mHandler;
+
+    @Mock Context mContext;
+    @Mock WifiManager mWifiManager;
+    @Mock OsuNetworkConnection.Callbacks mNetworkCallbacks;
+    @Mock NetworkInfo mNwInfo;
+    @Mock WifiInfo mWifiInfo;
+    @Mock Network mCurrentNetwork;
+
+    ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor.forClass(
+            BroadcastReceiver.class);
+    ArgumentCaptor<IntentFilter> mIntentFilterCaptor = ArgumentCaptor.forClass(
+            IntentFilter.class);
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mWifiManager).when(mContext)
+                .getSystemService(eq(Context.WIFI_SERVICE));
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        when(mWifiManager.enableNetwork(TEST_NETWORK_ID, true)).thenReturn(true);
+        when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(TEST_NETWORK_ID);
+        when(mWifiManager.getCurrentNetwork()).thenReturn(mCurrentNetwork);
+        when(mWifiInfo.getNetworkId()).thenReturn(TEST_NETWORK_ID);
+        mLooper = new TestLooper();
+        mHandler = new Handler(mLooper.getLooper());
+        mNetworkConnection = new OsuNetworkConnection(mContext);
+        mNetworkConnection.enableVerboseLogging(ENABLE_LOGGING);
+    }
+
+    /**
+     * Verify that the class registers for receiving the necessary broadcast intents upon init.
+     * Verify that the initialization only occurs once even if init() is called  multiple times.
+     */
+    @Test
+    public void verifyBroadcastIntentRegistration() {
+        mNetworkConnection.init(mHandler);
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                mIntentFilterCaptor.capture(), any(), eq(mHandler));
+        verify(mWifiManager).isWifiEnabled();
+        mLooper.dispatchAll();
+        IntentFilter intentFilter = mIntentFilterCaptor.getValue();
+        assertEquals(intentFilter.countActions(), 2);
+    }
+
+    /**
+     * Verifies that onWifiEnabled() callback is invoked when the relevant intent is
+     * received and the caller is subscribed to receive the callback.
+     */
+    @Test
+    public void verifyWifiStateCallbacks() {
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        mNetworkConnection.init(mHandler);
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                any(IntentFilter.class), any(), eq(mHandler));
+        mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+        mLooper.dispatchAll();
+        mNetworkConnection.setEventCallback(mNetworkCallbacks);
+        TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext,
+                WifiManager.WIFI_STATE_ENABLED);
+        TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext,
+                WifiManager.WIFI_STATE_DISABLED);
+        mNetworkConnection.setEventCallback(null);
+        TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext,
+                WifiManager.WIFI_STATE_ENABLED);
+        TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext,
+                WifiManager.WIFI_STATE_DISABLED);
+        verify(mNetworkCallbacks, times(1)).onWifiEnabled();
+        verify(mNetworkCallbacks, times(1)).onWifiDisabled();
+    }
+
+    /**
+     * Verifies that connect() API returns false when Wifi is not enabled
+     */
+    @Test
+    public void verifyNetworkConnectionWhenWifiIsDisabled() {
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        mNetworkConnection.init(mHandler);
+        assertEquals(false, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+    }
+
+    /**
+     * Verifies that connect() API returns false when OSU AP is a part of an OSEN
+     */
+    @Test
+    public void verifyOSENUnsupported() {
+        mNetworkConnection.init(mHandler);
+        assertEquals(false, mNetworkConnection.connect(TEST_SSID, TEST_NAI_OSEN));
+    }
+
+    /**
+     * Verifies that connect() API returns false when WifiManager's addNetwork()
+     * returns an invalid network ID
+     */
+    @Test
+    public void verifyNetworkConnectionWhenAddNetworkFails() {
+        when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(-1);
+        mNetworkConnection.init(mHandler);
+        assertEquals(false, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+    }
+
+    /**
+     * Verifies that connect() API returns false when WifiManager's enableNetwork()
+     * fails for the given network ID corresponding to the OSU AP
+     */
+    @Test
+    public void verifyNetworkConnectionWhenEnableNetworkFails() {
+        when(mWifiManager.enableNetwork(TEST_NETWORK_ID, true)).thenReturn(false);
+        mNetworkConnection.init(mHandler);
+        assertEquals(false, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+        verify(mWifiManager).removeNetwork(TEST_NETWORK_ID);
+    }
+
+    /**
+     * Verifies that network state callbacks are invoked when the NETWORK_STATE_CHANGED intent
+     * is received and when WifiManager has successfully requested connection to the OSU AP.
+     */
+    @Test
+    public void verifyNetworkCallbackInvokedWhenRegistered() {
+        mNetworkConnection.init(mHandler);
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                any(IntentFilter.class), any(), eq(mHandler));
+        mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+
+        mNetworkConnection.setEventCallback(mNetworkCallbacks);
+        assertEquals(true, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+        mLooper.dispatchAll();
+
+        when(mNwInfo.getDetailedState()).thenReturn(NetworkInfo.DetailedState.CONNECTED);
+        TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
+                mNwInfo, mWifiInfo);
+        verify(mNetworkCallbacks).onConnected(mCurrentNetwork);
+
+        when(mNwInfo.getDetailedState()).thenReturn(NetworkInfo.DetailedState.DISCONNECTED);
+        TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext, mNwInfo, mWifiInfo);
+        verify(mNetworkCallbacks).onDisconnected();
+        verify(mWifiManager).removeNetwork(TEST_NETWORK_ID);
+    }
+
+    /**
+     * Verifies that the onConnected() callback is not invoked when the Network State Changed
+     * intent is called when WifiManager has successfully connected to a network that's not the
+     * OSU AP.
+     */
+    @Test
+    public void verifyNetworkDisconnectedCallbackConnectedToAnotherNetwork() {
+        when(mWifiInfo.getNetworkId()).thenReturn(TEST_NETWORK_ID + 1);
+        when(mNwInfo.getDetailedState()).thenReturn(NetworkInfo.DetailedState.CONNECTED);
+        mNetworkConnection.init(mHandler);
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                any(IntentFilter.class), any(), eq(mHandler));
+        mLooper.dispatchAll();
+
+        mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+        mNetworkConnection.setEventCallback(mNetworkCallbacks);
+        assertEquals(true, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+        TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
+                mNwInfo, mWifiInfo);
+        verify(mNetworkCallbacks, never()).onConnected(any(Network.class));
+        verify(mWifiManager).removeNetwork(TEST_NETWORK_ID);
+    }
+
+    /**
+     * Verifies that WifiManager's removeNetwork() is called when disconnectIfNeeded() is called
+     * on the OSU AP's network ID.
+     */
+    @Test
+    public void verifyNetworkTearDown() {
+        mNetworkConnection.init(mHandler);
+        assertEquals(true, mNetworkConnection.connect(TEST_SSID, TEST_NAI));
+        mNetworkConnection.disconnectIfNeeded();
+        verify(mWifiManager).removeNetwork(TEST_NETWORK_ID);
+    }
+}
+
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
index eb0f5ef..e0e6df1 100644
--- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.anyMap;
 import static org.mockito.Mockito.doThrow;
@@ -54,11 +55,14 @@
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiSsid;
+import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
+import android.os.Looper;
 import android.os.UserHandle;
+import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
 import android.util.Pair;
@@ -116,6 +120,7 @@
     private static final ANQPNetworkKey TEST_ANQP_KEY = ANQPNetworkKey.buildKey(
             TEST_SSID, TEST_BSSID, TEST_HESSID, TEST_ANQP_DOMAIN_ID);
     private static final int TEST_CREATOR_UID = 1234;
+    private static final int TEST_UID = 1500;
 
     @Mock Context mContext;
     @Mock WifiNative mWifiNative;
@@ -131,6 +136,11 @@
     @Mock WifiConfigStore mWifiConfigStore;
     @Mock PasspointConfigStoreData.DataSource mDataSource;
     @Mock WifiMetrics mWifiMetrics;
+    @Mock OsuNetworkConnection mOsuNetworkConnection;
+    @Mock PasspointProvisioner mPasspointProvisioner;
+    @Mock IProvisioningCallback mCallback;
+
+    TestLooper mLooper;
     PasspointManager mManager;
 
     /** Sets up test. */
@@ -141,6 +151,10 @@
         when(mObjectFactory.makeANQPRequestManager(any(), eq(mClock)))
                 .thenReturn(mAnqpRequestManager);
         when(mObjectFactory.makeCertificateVerifier()).thenReturn(mCertVerifier);
+        when(mObjectFactory.makeOsuNetworkConnection(any(Context.class)))
+                .thenReturn(mOsuNetworkConnection);
+        when(mObjectFactory.makePasspointProvisioner(any(Context.class),
+                any(OsuNetworkConnection.class))).thenReturn(mPasspointProvisioner);
         mManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
                 mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore, mWifiMetrics);
         ArgumentCaptor<PasspointEventHandler.Callbacks> callbacks =
@@ -153,6 +167,7 @@
                 any(WifiKeyStore.class), any(SIMAccessor.class), dataSource.capture());
         mCallbacks = callbacks.getValue();
         mDataSource = dataSource.getValue();
+        mLooper = new TestLooper();
     }
 
     /**
@@ -1365,4 +1380,18 @@
         verify(mWifiMetrics).updateSavedPasspointProfiles(
                 eq(expectedInstalledProviders), eq(expectedConnectedProviders));
     }
+    /**
+     * Verify Passpoint Manager's provisioning APIs by invoking methods in PasspointProvisioner for
+     * initiailization and provisioning a provider.
+     */
+    @Test
+    public void verifyPasspointProvisioner() {
+        mManager.initializeProvisioner(mLooper.getLooper());
+        verify(mPasspointProvisioner).init(any(Looper.class));
+        when(mPasspointProvisioner.startSubscriptionProvisioning(anyInt(), any(OsuProvider.class),
+                any(IProvisioningCallback.class))).thenReturn(true);
+        OsuProvider osuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
+        assertEquals(true,
+                mManager.startSubscriptionProvisioning(TEST_UID, osuProvider, mCallback));
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
new file mode 100644
index 0000000..4583531
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProvisionerTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.net.wifi.hotspot2.IProvisioningCallback;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointProvisioner}.
+ */
+@SmallTest
+public class PasspointProvisionerTest {
+    private static final String TAG = "PasspointProvisionerTest";
+
+    private static final int TEST_UID = 1500;
+
+    private PasspointProvisioner mPasspointProvisioner;
+    private TestLooper mLooper = new TestLooper();
+
+    @Mock Context mContext;
+    @Mock WifiManager mWifiManager;
+    @Mock IProvisioningCallback mCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        doReturn(mWifiManager).when(mContext)
+                .getSystemService(eq(Context.WIFI_SERVICE));
+        OsuNetworkConnection osuNetworkConnection = new OsuNetworkConnection(mContext);
+        mPasspointProvisioner = new PasspointProvisioner(mContext, osuNetworkConnection);
+    }
+
+    /**
+     * Verifies that initialization is required before starting subscription
+     * provisioning with a provider
+     */
+    @Test
+    public void verifyInitBeforeStartProvisioning() {
+        OsuProvider osuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
+        mPasspointProvisioner.init(mLooper.getLooper());
+        assertTrue(mPasspointProvisioner.startSubscriptionProvisioning(
+                TEST_UID, osuProvider, mCallback));
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
index 51fed83..5043c85 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
@@ -35,12 +35,11 @@
 import android.hardware.wifi.V1_0.RttType;
 import android.hardware.wifi.V1_0.WifiStatus;
 import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.MacAddress;
 import android.net.wifi.rtt.RangingRequest;
 
 import com.android.server.wifi.HalDeviceManager;
 
-import libcore.util.HexEncoding;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -118,19 +117,19 @@
 
         RttConfig rttConfig = halRequest.get(0);
         collector.checkThat("entry 0: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("000102030400".toCharArray(), false)));
+                equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray()));
         collector.checkThat("entry 0: MAC", rttConfig.type, equalTo(RttType.TWO_SIDED));
         collector.checkThat("entry 0: MAC", rttConfig.peer, equalTo(RttPeerType.AP));
 
         rttConfig = halRequest.get(1);
         collector.checkThat("entry 1: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("0A0B0C0D0E00".toCharArray(), false)));
+                equalTo(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray()));
         collector.checkThat("entry 1: MAC", rttConfig.type, equalTo(RttType.ONE_SIDED));
         collector.checkThat("entry 1: MAC", rttConfig.peer, equalTo(RttPeerType.AP));
 
         rttConfig = halRequest.get(2);
         collector.checkThat("entry 2: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("080908070605".toCharArray(), false)));
+                equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray()));
         collector.checkThat("entry 2: MAC", rttConfig.type, equalTo(RttType.TWO_SIDED));
         collector.checkThat("entry 2: MAC", rttConfig.peer, equalTo(RttPeerType.NAN));
 
@@ -164,8 +163,8 @@
     public void testRangeCancel() throws Exception {
         int cmdId = 66;
         ArrayList<byte[]> macAddresses = new ArrayList<>();
-        byte[] mac1 = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
-        byte[] mac2 = { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+        byte[] mac1 = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+        byte[] mac2 = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
         macAddresses.add(mac1);
         macAddresses.add(mac2);
 
@@ -212,7 +211,7 @@
         collector.checkThat("status", rttResult.status,
                 equalTo(RttStatus.SUCCESS));
         collector.checkThat("mac", rttResult.addr,
-                equalTo(HexEncoding.decode("05060708090A".toCharArray(), false)));
+                equalTo(MacAddress.fromString("05:06:07:08:09:0A").toByteArray()));
         collector.checkThat("distanceCm", rttResult.distanceInMm, equalTo(1500));
         collector.checkThat("timestamp", rttResult.timeStampInUs, equalTo(666L));
 
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
index 9d04580..7e71069 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
@@ -46,6 +46,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.wifi.V1_0.RttResult;
+import android.net.MacAddress;
 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
 import android.net.wifi.aware.IWifiAwareManager;
 import android.net.wifi.aware.PeerHandle;
@@ -53,6 +54,7 @@
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
 import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.ResponderConfig;
 import android.net.wifi.rtt.WifiRttManager;
 import android.os.Handler;
 import android.os.IBinder;
@@ -67,8 +69,6 @@
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
-import libcore.util.HexEncoding;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -201,6 +201,9 @@
 
     @After
     public void tearDown() throws Exception {
+        assertEquals("Binder links != unlinks to death (size)",
+                mBinderLinkToDeathCounter.mUniqueExecs.size(),
+                mBinderUnlinkToDeathCounter.mUniqueExecs.size());
         assertEquals("Binder links != unlinks to death", mBinderLinkToDeathCounter.mUniqueExecs,
                 mBinderUnlinkToDeathCounter.mUniqueExecs);
     }
@@ -253,11 +256,15 @@
     @Test
     public void testRangingFlowUsingAwarePeerHandles() throws Exception {
         RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0xA);
-        PeerHandle peerHandle = new PeerHandle(1022);
-        request.mRttPeers.add(new RangingRequest.RttPeerAware(peerHandle));
-        Map<Integer, byte[]> peerHandleToMacMap = new HashMap<>();
-        byte[] macAwarePeer = HexEncoding.decode("AABBCCDDEEFF".toCharArray(), false);
-        peerHandleToMacMap.put(1022, macAwarePeer);
+        PeerHandle peerHandle1 = new PeerHandle(1022);
+        PeerHandle peerHandle2 = new PeerHandle(1023);
+        request.mRttPeers.add(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle1));
+        request.mRttPeers.add(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle2));
+        Map<Integer, MacAddress> peerHandleToMacMap = new HashMap<>();
+        MacAddress macAwarePeer1 = MacAddress.fromString("AA:BB:CC:DD:EE:FF");
+        MacAddress macAwarePeer2 = MacAddress.fromString("BB:BB:BB:EE:EE:EE");
+        peerHandleToMacMap.put(peerHandle1.peerId, macAwarePeer1);
+        peerHandleToMacMap.put(peerHandle2.peerId, macAwarePeer2);
 
         AwareTranslatePeerHandlesToMac answer = new AwareTranslatePeerHandlesToMac(mDefaultUid,
                 peerHandleToMacMap);
@@ -274,13 +281,18 @@
         RangingRequest finalRequest = mRequestCaptor.getValue();
         assertNotEquals("Request to native is not null", null, finalRequest);
         assertEquals("Size of request", request.mRttPeers.size(), finalRequest.mRttPeers.size());
-        assertEquals("Aware peer MAC", macAwarePeer,
-                ((RangingRequest.RttPeerAware) finalRequest.mRttPeers.get(
-                        finalRequest.mRttPeers.size() - 1)).peerMacAddress);
+        assertEquals("Aware peer 1 MAC", macAwarePeer1,
+                finalRequest.mRttPeers.get(finalRequest.mRttPeers.size() - 2).macAddress);
+        assertEquals("Aware peer 2 MAC", macAwarePeer2,
+                finalRequest.mRttPeers.get(finalRequest.mRttPeers.size() - 1).macAddress);
 
-        // issue results
+        // issue results - but remove the one for peer #2
         Pair<List<RttResult>, List<RangingResult>> results =
                 RttTestUtils.getDummyRangingResults(mRequestCaptor.getValue());
+        results.first.remove(results.first.size() - 1);
+        RangingResult removed = results.second.remove(results.second.size() - 1);
+        results.second.add(
+                new RangingResult(RangingResult.STATUS_FAIL, removed.getPeerHandle(), 0, 0, 0, 0));
         mDut.onRangingResults(mIntCaptor.getValue(), results.first);
         mMockLooper.dispatchAll();
 
@@ -418,11 +430,11 @@
                         (ArrayList) mListCaptor.capture());
                 RangingRequest request0 = requests[0];
                 assertEquals(request0.mRttPeers.size(), mListCaptor.getValue().size());
-                assertArrayEquals(HexEncoding.decode("000102030400".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("00:01:02:03:04:00").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(0));
-                assertArrayEquals(HexEncoding.decode("0A0B0C0D0E00".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(1));
-                assertArrayEquals(HexEncoding.decode("080908070605".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("08:09:08:07:06:05").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(2));
             }
 
@@ -607,8 +619,12 @@
         RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0);
         Pair<List<RttResult>, List<RangingResult>> results = RttTestUtils.getDummyRangingResults(
                 request);
-        results.first.remove(0);
-        RangingResult removed = results.second.remove(0);
+        results.first.remove(2); // remove a direct AWARE request
+        RangingResult removed = results.second.remove(2);
+        results.second.add(
+                new RangingResult(RangingResult.STATUS_FAIL, removed.getMacAddress(), 0, 0, 0, 0));
+        results.first.remove(0); // remove an AP request
+        removed = results.second.remove(0);
         results.second.add(
                 new RangingResult(RangingResult.STATUS_FAIL, removed.getMacAddress(), 0, 0, 0, 0));
 
@@ -849,6 +865,125 @@
     }
 
     /**
+     * Validate that flooding the service with ranging requests will cause it to start rejecting
+     * rejects from the flooding uid. Single UID.
+     */
+    @Test
+    public void testRejectFloodingRequestsSingleUid() throws Exception {
+        runFloodRequestsTest(true);
+    }
+
+    /**
+     * Validate that flooding the service with ranging requests will cause it to start rejecting
+     * rejects from the flooding uid. WorkSource (all identical).
+     */
+    @Test
+    public void testRejectFloodingRequestsIdenticalWorksources() throws Exception {
+        runFloodRequestsTest(false);
+    }
+
+    /**
+     * Validate that flooding the service with ranging requests will cause it to start rejecting
+     * rejects from the flooding uid. WorkSource (with one constant UID but other varying UIDs -
+     * the varying UIDs should prevent the flood throttle)
+     */
+    @Test
+    public void testDontRejectFloodingRequestsVariousUids() throws Exception {
+        RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 1);
+        WorkSource ws = new WorkSource(10);
+
+        // 1. issue a request
+        mDut.startRanging(mockIbinder, mPackageName, ws, request, mockCallback);
+        mMockLooper.dispatchAll();
+
+        verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request));
+        verifyWakeupSet();
+
+        // 2. issue FLOOD LEVEL requests + 10 at various UIDs - no failure expected
+        for (int i = 0; i < RttServiceImpl.MAX_QUEUED_PER_UID + 10; ++i) {
+            WorkSource wsExtra = new WorkSource(ws);
+            wsExtra.add(11 + i);
+            mDut.startRanging(mockIbinder, mPackageName, wsExtra, request, mockCallback);
+        }
+        mMockLooper.dispatchAll();
+
+        // 3. clear queue
+        mDut.disable();
+        mMockLooper.dispatchAll();
+
+        verifyWakeupCancelled();
+        verify(mockNative).rangeCancel(eq(mIntCaptor.getValue()), any());
+        verify(mockCallback, times(RttServiceImpl.MAX_QUEUED_PER_UID + 11)).onRangingFailure(
+                RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE);
+
+        verify(mockNative, atLeastOnce()).isReady();
+        verifyNoMoreInteractions(mockNative, mockCallback, mAlarmManager.getAlarmManager());
+    }
+
+    /**
+     * Utility to run configurable tests for flooding range requests.
+     * - Execute a single request
+     * - Flood service with requests: using same ID or same WorkSource
+     * - Provide results (to clear queue) and execute another test: validate succeeds
+     */
+    private void runFloodRequestsTest(boolean useUids) throws Exception {
+        RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 1);
+        Pair<List<RttResult>, List<RangingResult>> result = RttTestUtils.getDummyRangingResults(
+                request);
+
+        WorkSource ws = new WorkSource();
+        ws.add(10);
+        ws.add(20);
+        ws.add(30);
+
+        InOrder cbInorder = inOrder(mockCallback);
+        InOrder nativeInorder = inOrder(mockNative);
+
+        // 1. issue a request
+        mDut.startRanging(mockIbinder, mPackageName, useUids ? null : ws, request, mockCallback);
+        mMockLooper.dispatchAll();
+
+        nativeInorder.verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request));
+        verifyWakeupSet();
+
+        // 2. issue FLOOD LEVEL requests + 10: should get 11 failures (10 extra + 1 original)
+        for (int i = 0; i < RttServiceImpl.MAX_QUEUED_PER_UID + 10; ++i) {
+            mDut.startRanging(mockIbinder, mPackageName, useUids ? null : ws, request,
+                    mockCallback);
+        }
+        mMockLooper.dispatchAll();
+
+        cbInorder.verify(mockCallback, times(11)).onRangingFailure(
+                RangingResultCallback.STATUS_CODE_FAIL);
+
+        // 3. provide results
+        mDut.onRangingResults(mIntCaptor.getValue(), result.first);
+        mMockLooper.dispatchAll();
+
+        cbInorder.verify(mockCallback).onRangingResults(result.second);
+        verifyWakeupCancelled();
+
+        nativeInorder.verify(mockNative).rangeRequest(mIntCaptor.capture(), eq(request));
+        verifyWakeupSet();
+
+        // 4. issue a request: don't expect a failure
+        mDut.startRanging(mockIbinder, mPackageName, useUids ? null : ws, request, mockCallback);
+        mMockLooper.dispatchAll();
+
+        // 5. clear queue
+        mDut.disable();
+        mMockLooper.dispatchAll();
+
+        verifyWakeupCancelled();
+        nativeInorder.verify(mockNative).rangeCancel(eq(mIntCaptor.getValue()), any());
+        cbInorder.verify(mockCallback, times(RttServiceImpl.MAX_QUEUED_PER_UID)).onRangingFailure(
+                RangingResultCallback.STATUS_CODE_FAIL_RTT_NOT_AVAILABLE);
+
+        verify(mockNative, atLeastOnce()).isReady();
+        verifyNoMoreInteractions(mockNative, mockCallback, mAlarmManager.getAlarmManager());
+    }
+
+    /**
      * Validate that when Wi-Fi gets disabled (HAL level) the ranging queue gets cleared.
      */
     @Test
@@ -967,9 +1102,9 @@
 
     private class AwareTranslatePeerHandlesToMac extends MockAnswerUtil.AnswerWithArguments {
         private int mExpectedUid;
-        private Map<Integer, byte[]> mPeerIdToMacMap;
+        private Map<Integer, MacAddress> mPeerIdToMacMap;
 
-        AwareTranslatePeerHandlesToMac(int expectedUid, Map<Integer, byte[]> peerIdToMacMap) {
+        AwareTranslatePeerHandlesToMac(int expectedUid, Map<Integer, MacAddress> peerIdToMacMap) {
             mExpectedUid = expectedUid;
             mPeerIdToMacMap = peerIdToMacMap;
         }
@@ -979,7 +1114,7 @@
 
             Map<Integer, byte[]> result = new HashMap<>();
             for (Integer peerId: peerIds) {
-                byte[] mac = mPeerIdToMacMap.get(peerId);
+                byte[] mac = mPeerIdToMacMap.get(peerId).toByteArray();
                 if (mac == null) {
                     continue;
                 }
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
index a014878..ec56322 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
@@ -16,17 +16,15 @@
 
 package com.android.server.wifi.rtt;
 
-import static com.android.server.wifi.util.NativeUtil.macAddressToByteArray;
-
 import android.hardware.wifi.V1_0.RttResult;
 import android.hardware.wifi.V1_0.RttStatus;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.ResponderConfig;
 import android.util.Pair;
 
-import libcore.util.HexEncoding;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -67,7 +65,7 @@
         scan1.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
         ScanResult scan2 = new ScanResult();
         scan2.BSSID = "0A:0B:0C:0D:0E:" + String.format("%02d", lastMacByte);
-        byte[] mac1 = HexEncoding.decode("080908070605".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("08:09:08:07:06:05");
 
         builder.addAccessPoint(scan1);
         builder.addAccessPoint(scan2);
@@ -92,39 +90,29 @@
         List<RangingResult> results = new ArrayList<>();
 
         if (request != null) {
-            for (RangingRequest.RttPeer peer: request.mRttPeers) {
-                RangingResult rangingResult = null;
-                byte[] overrideMac = null;
-                if (peer instanceof RangingRequest.RttPeerAp) {
+            for (ResponderConfig peer: request.mRttPeers) {
+                RangingResult rangingResult;
+                if (peer.peerHandle == null) {
                     rangingResult = new RangingResult(RangingResult.STATUS_SUCCESS,
-                            macAddressToByteArray(
-                                    ((RangingRequest.RttPeerAp) peer).scanResult.BSSID),
-                            rangeCmBase++, rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++);
-                } else if (peer instanceof RangingRequest.RttPeerAware) {
-                    RangingRequest.RttPeerAware awarePeer = (RangingRequest.RttPeerAware) peer;
-                    if (awarePeer.peerHandle != null) {
-                        rangingResult = new RangingResult(RangingResult.STATUS_SUCCESS,
-                                awarePeer.peerHandle, rangeCmBase++, rangeStdDevCmBase++,
-                                rssiBase++, rangeTimestampBase++);
-                        overrideMac = awarePeer.peerMacAddress;
-                    } else {
-                        rangingResult = new RangingResult(RangingResult.STATUS_SUCCESS,
-                                awarePeer.peerMacAddress, rangeCmBase++, rangeStdDevCmBase++,
-                                rssiBase++, rangeTimestampBase++);
-                    }
+                            peer.macAddress, rangeCmBase++, rangeStdDevCmBase++, rssiBase++,
+                            rangeTimestampBase++);
+                } else {
+                    rangingResult = new RangingResult(RangingResult.STATUS_SUCCESS,
+                            peer.peerHandle, rangeCmBase++, rangeStdDevCmBase++, rssiBase++,
+                            rangeTimestampBase++);
                 }
                 results.add(rangingResult);
-                halResults.add(getMatchingRttResult(rangingResult, overrideMac));
+                halResults.add(getMatchingRttResult(rangingResult, peer.macAddress));
             }
         } else {
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("100102030405".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("10:01:02:03:04:05"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("1A0B0C0D0E0F".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("1A:0B:0C:0D:0E:0F"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("080908070605".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("08:09:08:07:06:05"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             halResults.add(getMatchingRttResult(results.get(0), null));
             halResults.add(getMatchingRttResult(results.get(1), null));
@@ -134,12 +122,13 @@
         return new Pair<>(halResults, results);
     }
 
-    private static RttResult getMatchingRttResult(RangingResult rangingResult, byte[] overrideMac) {
+    private static RttResult getMatchingRttResult(RangingResult rangingResult,
+            MacAddress overrideMac) {
         RttResult rttResult = new RttResult();
         rttResult.status = rangingResult.getStatus() == RangingResult.STATUS_SUCCESS
                 ? RttStatus.SUCCESS : RttStatus.FAILURE;
-        System.arraycopy(overrideMac == null ? rangingResult.getMacAddress() : overrideMac, 0,
-                rttResult.addr, 0, 6);
+        System.arraycopy(overrideMac == null ? rangingResult.getMacAddress().toByteArray()
+                : overrideMac.toByteArray(), 0, rttResult.addr, 0, 6);
         rttResult.distanceInMm = rangingResult.getDistanceMm();
         rttResult.distanceSdInMm = rangingResult.getDistanceStdDevMm();
         rttResult.rssi = rangingResult.getRssi();
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
index 73ea143..15dfccc 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi.scanner;
 
 import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder;
-import static com.android.server.wifi.ScanTestUtil.assertScanDataEquals;
 import static com.android.server.wifi.ScanTestUtil.setupMockChannels;
 
 import static org.junit.Assert.*;
@@ -45,7 +44,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -104,89 +102,6 @@
     }
 
     /**
-     * Verify that we pause & resume HW PNO scan when a single scan is scheduled and invokes the
-     * OnPnoNetworkFound callback when the scan results are received.
-     */
-    @Test
-    public void pauseResumeHwDisconnectedPnoScanForSingleScan() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
-        WifiNative.ScanSettings settings = createDummyScanSettings(false);
-        ScanResults scanResults = createDummyScanResults(false);
-
-        InOrder order = inOrder(eventHandler, mWifiNative);
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        // Start single scan
-        assertTrue(mScanner.startSingleScan(settings, eventHandler));
-        // Verify that the PNO scan was paused and single scan runs successfully
-        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
-                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
-        verifyNoMoreInteractions(eventHandler);
-
-        order = inOrder(pnoEventHandler, mWifiNative);
-        // Resume PNO scan after the single scan results are received and PNO monitor debounce
-        // alarm fires.
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        // Now verify that PNO scan is resumed successfully
-        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
-        verifyNoMoreInteractions(pnoEventHandler);
-    }
-
-    /**
-     * Verify that the HW PNO delayed failure cleans up the scan settings cleanly.
-     * 1. Start Hw PNO.
-     * 2. Start Single Scan which should pause PNO scan.
-     * 3. Fail the PNO scan resume and verify that the OnPnoScanFailed callback is invoked.
-     * 4. Now restart a new PNO scan to ensure that the failure was cleanly handled.
-     */
-    @Test
-    public void delayedHwDisconnectedPnoScanFailure() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
-        WifiNative.ScanSettings settings = createDummyScanSettings(false);
-        ScanResults scanResults = createDummyScanResults(false);
-
-        InOrder order = inOrder(eventHandler, mWifiNative);
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        // Start single scan
-        assertTrue(mScanner.startSingleScan(settings, eventHandler));
-        // Verify that the PNO scan was paused and single scan runs successfully
-        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
-                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
-        verifyNoMoreInteractions(eventHandler);
-
-        // Fail the PNO resume and check that the OnPnoScanFailed callback is invoked.
-        order = inOrder(pnoEventHandler, mWifiNative);
-        when(mWifiNative.startPnoScan(any(WifiNative.PnoSettings.class))).thenReturn(false);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        order.verify(pnoEventHandler).onPnoScanFailed();
-        verifyNoMoreInteractions(pnoEventHandler);
-
-        // Add a new PNO scan request
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
-        verifyNoMoreInteractions(pnoEventHandler);
-    }
-
-    /**
      * Verify that the HW PNO scan stop failure still resets the PNO scan state.
      * 1. Start Hw PNO.
      * 2. Stop Hw PNO scan which raises a stop command to WifiNative which is failed.
@@ -205,17 +120,11 @@
         // Fail the PNO stop.
         when(mWifiNative.stopPnoScan()).thenReturn(false);
         assertTrue(mScanner.resetHwPnoList());
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
         mLooper.dispatchAll();
         verify(mWifiNative).stopPnoScan();
 
         // Add a new PNO scan request and ensure it runs successfully.
         startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
         mLooper.dispatchAll();
         InOrder order = inOrder(pnoEventHandler, mWifiNative);
         ScanResults scanResults = createDummyScanResults(false);
@@ -223,84 +132,6 @@
         verifyNoMoreInteractions(pnoEventHandler);
     }
 
-    /**
-     * Verify that the HW PNO scan is forcefully stopped (bypass debounce logic) and restarted when
-     * settings change.
-     * 1. Start Hw PNO.
-     * 2. Stop Hw PNO .
-     * 3. Now restart a new PNO scan with different settings.
-     * 4. Ensure that the stop was issued before we start again.
-     */
-    @Test
-    public void forceRestartHwDisconnectedPnoScanWhenSettingsChange() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        InOrder order = inOrder(pnoEventHandler, mWifiNative);
-
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        expectHwDisconnectedPnoScanStart(order, pnoSettings);
-
-        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
-        assertTrue(mScanner.resetHwPnoList());
-        assertTrue(mAlarmManager.isPending(
-                WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        order.verify(mWifiNative, never()).stopPnoScan();
-
-        // Now restart PNO scan with an extra network in settings.
-        pnoSettings.networkList =
-                Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1);
-        pnoSettings.networkList[pnoSettings.networkList.length - 1] =
-                createDummyPnoNetwork("ssid_pno_new");
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-
-        // This should bypass the debounce timer and stop PNO scan immediately and then start
-        // a new debounce timer for the start.
-        order.verify(mWifiNative).stopPnoScan();
-
-        // Trigger the debounce timer and ensure we start PNO scan again.
-        mAlarmManager.dispatch(WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG);
-        mLooper.dispatchAll();
-        order.verify(mWifiNative).startPnoScan(pnoSettings);
-    }
-
-    /**
-     * Verify that the HW PNO scan is not forcefully stopped (bypass debounce logic) when
-     * settings don't change.
-     * 1. Start Hw PNO.
-     * 2. Stop Hw PNO .
-     * 3. Now restart a new PNO scan with same settings.
-     * 4. Ensure that the stop was never issued.
-     */
-    @Test
-    public void noForceRestartHwDisconnectedPnoScanWhenNoSettingsChange() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        InOrder order = inOrder(pnoEventHandler, mWifiNative);
-
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        expectHwDisconnectedPnoScanStart(order, pnoSettings);
-
-        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
-        assertTrue(mScanner.resetHwPnoList());
-        assertTrue(mAlarmManager.isPending(
-                WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        order.verify(mWifiNative, never()).stopPnoScan();
-
-        // Now restart PNO scan with the same settings.
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-
-        // Trigger the debounce timer and ensure that we neither stop/start.
-        mLooper.dispatchAll();
-        order.verify(mWifiNative, never()).startPnoScan(any(WifiNative.PnoSettings.class));
-        order.verify(mWifiNative, never()).stopPnoScan();
-    }
-
     private void createScannerWithHwPnoScanSupport() {
         mResources.setBoolean(R.bool.config_wifi_background_scan_support, true);
         mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor,
@@ -389,27 +220,4 @@
         order.verify(eventHandler).onPnoNetworkFound(scanResults.getRawScanResults());
     }
 
-    /**
-     * Verify that the single scan results were delivered and that the PNO scan was paused and
-     * resumed either side of it.
-     */
-    private void expectSuccessfulSingleScanWithHwPnoEnabled(InOrder order,
-            WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScanFreqs,
-            ScanResults scanResults) {
-        // Pause PNO scan first
-        order.verify(mWifiNative).stopPnoScan();
-
-        order.verify(mWifiNative).scan(eq(expectedScanFreqs), any(Set.class));
-
-        when(mWifiNative.getPnoScanResults()).thenReturn(scanResults.getScanDetailArrayList());
-        when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
-
-        // Notify scan has finished
-        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
-        assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
-
-        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
-        assertScanDataEquals(scanResults.getScanData(), mScanner.getLatestSingleScanResults());
-    }
-
 }