Merge "[AWARE] Check dynamic location permission"
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index 51ff312..facb065 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -41,6 +41,7 @@
 import android.util.LongSparseArray;
 import android.util.MutableBoolean;
 import android.util.MutableInt;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -78,10 +79,10 @@
     public HalDeviceManager(Clock clock) {
         mClock = clock;
 
-        mInterfaceAvailableForRequestListeners.put(IfaceType.STA, new HashSet<>());
-        mInterfaceAvailableForRequestListeners.put(IfaceType.AP, new HashSet<>());
-        mInterfaceAvailableForRequestListeners.put(IfaceType.P2P, new HashSet<>());
-        mInterfaceAvailableForRequestListeners.put(IfaceType.NAN, new HashSet<>());
+        mInterfaceAvailableForRequestListeners.put(IfaceType.STA, new HashMap<>());
+        mInterfaceAvailableForRequestListeners.put(IfaceType.AP, new HashMap<>());
+        mInterfaceAvailableForRequestListeners.put(IfaceType.P2P, new HashMap<>());
+        mInterfaceAvailableForRequestListeners.put(IfaceType.NAN, new HashMap<>());
     }
 
     /* package */ void enableVerboseLogging(int verbose) {
@@ -265,10 +266,11 @@
      */
     public IWifiChip getChip(IWifiIface iface) {
         String name = getName(iface);
+        int type = getType(iface);
         if (VDBG) Log.d(TAG, "getChip: iface(name)=" + name);
 
         synchronized (mLock) {
-            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(name);
+            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(Pair.create(name, type));
             if (cacheEntry == null) {
                 Log.e(TAG, "getChip: no entry for iface(name)=" + name);
                 return null;
@@ -294,10 +296,11 @@
             @NonNull InterfaceDestroyedListener destroyedListener,
             @Nullable Handler handler) {
         String name = getName(iface);
+        int type = getType(iface);
         if (VDBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + name);
 
         synchronized (mLock) {
-            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(name);
+            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(Pair.create(name, type));
             if (cacheEntry == null) {
                 Log.e(TAG, "registerDestroyedListener: no entry for iface(name)=" + name);
                 return false;
@@ -330,12 +333,22 @@
     public void registerInterfaceAvailableForRequestListener(int ifaceType,
             @NonNull InterfaceAvailableForRequestListener listener, @Nullable Handler handler) {
         if (VDBG) {
-            Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType);
+            Log.d(TAG, "registerInterfaceAvailableForRequestListener: ifaceType=" + ifaceType
+                    + ", listener=" + listener + ", handler=" + handler);
         }
 
         synchronized (mLock) {
-            mInterfaceAvailableForRequestListeners.get(ifaceType).add(
-                    new InterfaceAvailableForRequestListenerProxy(listener, handler));
+            InterfaceAvailableForRequestListenerProxy proxy =
+                    new InterfaceAvailableForRequestListenerProxy(listener, handler);
+            if (mInterfaceAvailableForRequestListeners.get(ifaceType).containsKey(proxy)) {
+                if (VDBG) {
+                    Log.d(TAG,
+                            "registerInterfaceAvailableForRequestListener: dup listener skipped: "
+                                    + listener);
+                }
+                return;
+            }
+            mInterfaceAvailableForRequestListeners.get(ifaceType).put(proxy, null);
         }
 
         WifiChipInfo[] chipInfos = getAllChipInfo();
@@ -359,14 +372,8 @@
         }
 
         synchronized (mLock) {
-            Iterator<InterfaceAvailableForRequestListenerProxy> it =
-                    mInterfaceAvailableForRequestListeners.get(ifaceType).iterator();
-            while (it.hasNext()) {
-                if (it.next().mListener == listener) {
-                    it.remove();
-                    return;
-                }
-            }
+            mInterfaceAvailableForRequestListeners.get(ifaceType).remove(
+                    new InterfaceAvailableForRequestListenerProxy(listener, null));
         }
     }
 
@@ -412,15 +419,15 @@
     }
 
     /**
-     * Called when an interface type is possibly available for creation.
+     * Called when an interface type availability for creation is changed.
      */
     public interface InterfaceAvailableForRequestListener {
         /**
-         * Registered when an interface type could be requested. Registered with
+         * Called when an interface type availability for creation is updated. Registered with
          * registerInterfaceAvailableForRequestListener() and unregistered with
          * unregisterInterfaceAvailableForRequestListener().
          */
-        void onAvailableForRequest();
+        void onAvailabilityChanged(boolean isAvailable);
     }
 
     /**
@@ -485,7 +492,7 @@
     private IWifi mWifi;
     private final WifiEventCallback mWifiEventCallback = new WifiEventCallback();
     private final Set<ManagerStatusListenerProxy> mManagerStatusListeners = new HashSet<>();
-    private final SparseArray<Set<InterfaceAvailableForRequestListenerProxy>>
+    private final SparseArray<Map<InterfaceAvailableForRequestListenerProxy, Boolean>>
             mInterfaceAvailableForRequestListeners = new SparseArray<>();
     private final SparseArray<IWifiChipEventCallback.Stub> mDebugCallbacks = new SparseArray<>();
 
@@ -494,7 +501,8 @@
      * we need to keep a list of registered destroyed listeners. Will be validated regularly
      * in getAllChipInfoAndValidateCache().
      */
-    private final Map<String, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>();
+    private final Map<Pair<String, Integer>, InterfaceCacheEntry> mInterfaceInfoCache =
+            new HashMap<>();
 
     private class InterfaceCacheEntry {
         public IWifiChip chip;
@@ -1376,7 +1384,8 @@
                     cacheEntry.creationTime = mClock.getUptimeSinceBootMillis();
 
                     if (mDbg) Log.d(TAG, "createIfaceIfPossible: added cacheEntry=" + cacheEntry);
-                    mInterfaceInfoCache.put(cacheEntry.name, cacheEntry);
+                    mInterfaceInfoCache.put(
+                            Pair.create(cacheEntry.name, cacheEntry.type), cacheEntry);
                     return iface;
                 }
             }
@@ -1668,7 +1677,8 @@
         boolean lookupError = false;
         LongSparseArray<WifiIfaceInfo> orderedList = new LongSparseArray(interfaces.length);
         for (WifiIfaceInfo info : interfaces) {
-            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(info.name);
+            InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(
+                    Pair.create(info.name, getType(info.iface)));
             if (cacheEntry == null) {
                 Log.e(TAG,
                         "selectInterfacesToDelete: can't find cache entry with name=" + info.name);
@@ -1836,7 +1846,7 @@
             }
 
             // dispatch listeners no matter what status
-            dispatchDestroyedListeners(name);
+            dispatchDestroyedListeners(name, type);
 
             if (status != null && status.code == WifiStatusCode.SUCCESS) {
                 return true;
@@ -1876,31 +1886,40 @@
             WifiChipInfo[] chipInfos) {
         if (VDBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType);
 
-        Set<InterfaceAvailableForRequestListenerProxy> listeners =
-                mInterfaceAvailableForRequestListeners.get(ifaceType);
+        synchronized (mLock) {
+            Map<InterfaceAvailableForRequestListenerProxy, Boolean> listeners =
+                    mInterfaceAvailableForRequestListeners.get(ifaceType);
 
-        if (listeners.size() == 0) {
-            return;
-        }
+            if (listeners.size() == 0) {
+                return;
+            }
 
-        if (!isItPossibleToCreateIface(chipInfos, ifaceType)) {
-            if (VDBG) Log.d(TAG, "Creating interface type isn't possible: ifaceType=" + ifaceType);
-            return;
-        }
+            boolean isAvailable = isItPossibleToCreateIface(chipInfos, ifaceType);
 
-        if (VDBG) Log.d(TAG, "It is possible to create the interface type: ifaceType=" + ifaceType);
-        for (InterfaceAvailableForRequestListenerProxy listener : listeners) {
-            listener.trigger();
+            if (VDBG) {
+                Log.d(TAG, "Interface available for: ifaceType=" + ifaceType + " = " + isAvailable);
+            }
+            for (Map.Entry<InterfaceAvailableForRequestListenerProxy, Boolean> listenerEntry :
+                    listeners.entrySet()) {
+                if (listenerEntry.getValue() == null || listenerEntry.getValue() != isAvailable) {
+                    if (VDBG) {
+                        Log.d(TAG, "Interface available listener dispatched: ifaceType=" + ifaceType
+                                + ", listener=" + listenerEntry.getKey());
+                    }
+                    listenerEntry.getKey().triggerWithArg(isAvailable);
+                }
+                listenerEntry.setValue(isAvailable);
+            }
         }
     }
 
     // dispatch all destroyed listeners registered for the specified interface AND remove the
     // cache entry
-    private void dispatchDestroyedListeners(String name) {
+    private void dispatchDestroyedListeners(String name, int type) {
         if (VDBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + name);
 
         synchronized (mLock) {
-            InterfaceCacheEntry entry = mInterfaceInfoCache.get(name);
+            InterfaceCacheEntry entry = mInterfaceInfoCache.get(Pair.create(name, type));
             if (entry == null) {
                 Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)=" + name);
                 return;
@@ -1910,7 +1929,7 @@
                 listener.trigger();
             }
             entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
-            mInterfaceInfoCache.remove(name);
+            mInterfaceInfoCache.remove(Pair.create(name, type));
         }
     }
 
@@ -1919,7 +1938,7 @@
         if (VDBG) Log.d(TAG, "dispatchAllDestroyedListeners");
 
         synchronized (mLock) {
-            Iterator<Map.Entry<String, InterfaceCacheEntry>> it =
+            Iterator<Map.Entry<Pair<String, Integer>, InterfaceCacheEntry>> it =
                     mInterfaceInfoCache.entrySet().iterator();
             while (it.hasNext()) {
                 InterfaceCacheEntry entry = it.next().getValue();
@@ -1958,7 +1977,18 @@
             }
         }
 
-        protected abstract void action();
+        void triggerWithArg(boolean arg) {
+            if (mHandler != null) {
+                mHandler.post(() -> {
+                    actionWithArg(arg);
+                });
+            } else {
+                actionWithArg(arg);
+            }
+        }
+
+        protected void action() {}
+        protected void actionWithArg(boolean arg) {}
 
         ListenerProxy(LISTENER listener, Handler handler, String tag) {
             mListener = listener;
@@ -1990,8 +2020,8 @@
         }
 
         @Override
-        protected void action() {
-            mListener.onAvailableForRequest();
+        protected void actionWithArg(boolean isAvailable) {
+            mListener.onAvailabilityChanged(isAvailable);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java
index 8787bfd..4e84c4a 100644
--- a/service/java/com/android/server/wifi/WakeupController.java
+++ b/service/java/com/android/server/wifi/WakeupController.java
@@ -18,14 +18,22 @@
 
 import android.content.Context;
 import android.database.ContentObserver;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiScanner;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 
 /**
  * WakeupController is responsible managing Auto Wifi.
@@ -34,6 +42,8 @@
  */
 public class WakeupController {
 
+    private static final String TAG = "WakeupController";
+
     // TODO(b/69624403) propagate this to Settings
     private static final boolean USE_PLATFORM_WIFI_WAKE = false;
 
@@ -43,6 +53,34 @@
     private final ContentObserver mContentObserver;
     private final WakeupLock mWakeupLock;
     private final WifiConfigManager mWifiConfigManager;
+    private final WifiInjector mWifiInjector;
+
+    private final WifiScanner.ScanListener mScanListener = new WifiScanner.ScanListener() {
+        @Override
+        public void onPeriodChanged(int periodInMs) {
+            // no-op
+        }
+
+        @Override
+        public void onResults(WifiScanner.ScanData[] results) {
+            // TODO(easchwar) handle scan results
+        }
+
+        @Override
+        public void onFullResult(ScanResult fullScanResult) {
+            // no-op
+        }
+
+        @Override
+        public void onSuccess() {
+            // no-op
+        }
+
+        @Override
+        public void onFailure(int reason, String description) {
+            Log.e(TAG, "ScanListener onFailure: " + reason + ": " + description);
+        }
+    };
 
     /** Whether this feature is enabled in Settings. */
     private boolean mWifiWakeupEnabled;
@@ -56,12 +94,14 @@
             WakeupLock wakeupLock,
             WifiConfigManager wifiConfigManager,
             WifiConfigStore wifiConfigStore,
+            WifiInjector wifiInjector,
             FrameworkFacade frameworkFacade) {
         mContext = context;
         mHandler = new Handler(looper);
         mWakeupLock = wakeupLock;
         mWifiConfigManager = wifiConfigManager;
         mFrameworkFacade = frameworkFacade;
+        mWifiInjector = wifiInjector;
         mContentObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
@@ -88,6 +128,80 @@
     }
 
     /**
+     * Starts listening for incoming scans.
+     *
+     * <p>Should only be called upon entering ScanMode. WakeupController registers its listener with
+     * the WifiScanner. If the WakeupController is already active, then it returns early. Otherwise
+     * it performs its initialization steps and sets {@link #mIsActive} to true.
+     */
+    public void start() {
+        mWifiInjector.getWifiScanner().registerScanListener(mScanListener);
+
+        // If already active, we don't want to re-initialize the lock, so return early.
+        if (mIsActive) {
+            return;
+        }
+        setActive(true);
+
+        if (mWifiWakeupEnabled) {
+            mWakeupLock.initialize(getMostRecentSavedScanResults());
+        }
+    }
+
+    /**
+     * Stops listening for scans.
+     *
+     * <p>Should only be called upon leaving ScanMode. It deregisters the listener from
+     * WifiScanner.
+     */
+    public void stop() {
+        mWifiInjector.getWifiScanner().deregisterScanListener(mScanListener);
+    }
+
+    /** Resets the WakeupController, setting {@link #mIsActive} to false. */
+    public void reset() {
+        setActive(false);
+    }
+
+    /** Returns a list of saved networks from the last full scan. */
+    private Set<ScanResultMatchInfo> getMostRecentSavedScanResults() {
+        Set<ScanResultMatchInfo> goodSavedNetworks = getGoodSavedNetworks();
+
+        List<ScanResult> scanResults = mWifiInjector.getWifiScanner().getSingleScanResults();
+        Set<ScanResultMatchInfo> lastSeenNetworks = new HashSet<>(scanResults.size());
+        for (ScanResult scanResult : scanResults) {
+            lastSeenNetworks.add(ScanResultMatchInfo.fromScanResult(scanResult));
+        }
+
+        lastSeenNetworks.retainAll(goodSavedNetworks);
+        return lastSeenNetworks;
+    }
+
+    /** Returns a filtered list of saved networks from WifiConfigManager. */
+    private Set<ScanResultMatchInfo> getGoodSavedNetworks() {
+        List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
+
+        Set<ScanResultMatchInfo> goodSavedNetworks = new HashSet<>(savedNetworks.size());
+        for (WifiConfiguration config : savedNetworks) {
+            if (isWideAreaNetwork(config)
+                    || config.hasNoInternetAccess()
+                    || config.noInternetAccessExpected
+                    || !config.getNetworkSelectionStatus().getHasEverConnected()) {
+                continue;
+            }
+            goodSavedNetworks.add(ScanResultMatchInfo.fromWifiConfiguration(config));
+        }
+
+        Log.d(TAG, "getGoodSavedNetworks: " + goodSavedNetworks.size());
+        return goodSavedNetworks;
+    }
+
+    //TODO(b/69271702) implement WAN filtering
+    private boolean isWideAreaNetwork(WifiConfiguration wifiConfiguration) {
+        return false;
+    }
+
+    /**
      * Whether the feature is enabled in settings.
      *
      * <p>Note: This method is only used to determine whether or not to actually enable wifi. All
diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java
index 8fb61c2..ebd18cd 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -2349,8 +2349,7 @@
         Iterator<WifiConfiguration> iter = networks.iterator();
         while (iter.hasNext()) {
             WifiConfiguration config = iter.next();
-            if (!config.hiddenSSID ||
-                    config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
+            if (!config.hiddenSSID) {
                 iter.remove();
             }
         }
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 7312998..c2806d8 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -122,6 +122,7 @@
     private final Runtime mJavaRuntime;
     private final SelfRecovery mSelfRecovery;
     private final WakeupController mWakeupController;
+    private final INetworkManagementService mNwManagementService;
 
     private final boolean mUseRealLogger;
 
@@ -169,8 +170,10 @@
         mSupplicantStaIfaceHal = new SupplicantStaIfaceHal(mContext, mWifiMonitor);
         mWificondControl = new WificondControl(this, mWifiMonitor,
                 new CarrierNetworkConfig(mContext));
+        mNwManagementService = INetworkManagementService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
         mWifiNative = new WifiNative(SystemProperties.get("wifi.interface", "wlan0"),
-                mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl);
+                mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl, mNwManagementService);
         mWifiP2pMonitor = new WifiP2pMonitor(this);
         mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(mWifiP2pMonitor);
         mWifiP2pNative = new WifiP2pNative(SystemProperties.get("wifi.direct.interface", "p2p0"),
@@ -236,7 +239,7 @@
                 new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
         mWakeupController = new WakeupController(mContext,
                 mWifiStateMachineHandlerThread.getLooper(), new WakeupLock(mWifiConfigManager),
-                mWifiConfigManager, mWifiConfigStore, mFrameworkFacade);
+                mWifiConfigManager, mWifiConfigStore, this, mFrameworkFacade);
         mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
         mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
                 mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade,
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 3118761..63dc797 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -16,8 +16,10 @@
 
 package com.android.server.wifi;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.InterfaceConfiguration;
 import android.net.apf.ApfCapabilities;
 import android.net.wifi.IApInterface;
 import android.net.wifi.IClientInterface;
@@ -27,7 +29,10 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiWakeReasonAndCounts;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -35,10 +40,13 @@
 import com.android.internal.annotations.Immutable;
 import com.android.internal.util.HexDump;
 import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.net.BaseNetworkObserver;
 import com.android.server.wifi.util.FrameParser;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
@@ -47,12 +55,14 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
 
-
 /**
  * Native calls for bring up/shut down of the supplicant daemon and for
  * sending requests to the supplicant daemon
@@ -65,14 +75,19 @@
     private final SupplicantStaIfaceHal mSupplicantStaIfaceHal;
     private final WifiVendorHal mWifiVendorHal;
     private final WificondControl mWificondControl;
+    private final INetworkManagementService mNwManagementService;
 
+    // TODO(b/69426063): Remove interfaceName from constructor once WifiStateMachine switches over
+    // to the new interface management methods.
     public WifiNative(String interfaceName, WifiVendorHal vendorHal,
-                      SupplicantStaIfaceHal staIfaceHal, WificondControl condControl) {
+                      SupplicantStaIfaceHal staIfaceHal, WificondControl condControl,
+                      INetworkManagementService nwService) {
         mTAG = "WifiNative-" + interfaceName;
         mInterfaceName = interfaceName;
         mWifiVendorHal = vendorHal;
         mSupplicantStaIfaceHal = staIfaceHal;
         mWificondControl = condControl;
+        mNwManagementService = nwService;
     }
 
     public String getInterfaceName() {
@@ -158,12 +173,362 @@
      * some duplication to ease transition.
      */
     /**
+     * Meta-info about every iface that is active.
+     */
+    private static class Iface {
+        /** Type of ifaces possible */
+        public static final int IFACE_TYPE_AP = 0;
+        public static final int IFACE_TYPE_STA = 1;
+
+        @IntDef({IFACE_TYPE_AP, IFACE_TYPE_STA})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface IfaceType{}
+
+        /** Identifier allocated for the interface */
+        public final int id;
+        /** Type of the iface: STA or AP */
+        public final @IfaceType int type;
+        /** Name of the interface */
+        public String name;
+        /** External iface destroyed listener for the iface */
+        public InterfaceCallback externalListener;
+        /** Network observer registered for this interface */
+        public NetworkObserverInternal networkObserver;
+
+        Iface(int id, @Iface.IfaceType int type) {
+            this.id = id;
+            this.type = type;
+        }
+    }
+
+    /**
+     * Iface Management entity. This class maintains list of all the active ifaces.
+     */
+    private static class IfaceManager {
+        /** Integer to allocate for the next iface being created */
+        private int mNextId;
+        /** Map of the id to the iface structure */
+        private HashMap<Integer, Iface> mIfaces = new HashMap<>();
+
+        /** Allocate a new iface for the given type */
+        private Iface allocateIface(@Iface.IfaceType  int type) {
+            Iface iface = new Iface(mNextId, type);
+            mIfaces.put(mNextId, iface);
+            mNextId++;
+            return iface;
+        }
+
+        /** Remove the iface using the provided id */
+        private Iface removeIface(int id) {
+            return mIfaces.remove(id);
+        }
+
+        /** Lookup the iface using the provided id */
+        private Iface getIface(int id) {
+            return mIfaces.get(id);
+        }
+
+        /** Lookup the iface using the provided name */
+        private Iface getIface(@NonNull String ifaceName) {
+            for (Iface iface : mIfaces.values()) {
+                if (TextUtils.equals(iface.name, ifaceName)) {
+                    return iface;
+                }
+            }
+            return null;
+        }
+
+        /** Iterator to use for deleting all the ifaces while performing teardown on each of them */
+        private Iterator<Integer> getIfaceIdIter() {
+            return mIfaces.keySet().iterator();
+        }
+
+        /** Checks if there are any iface active. */
+        private boolean hasAnyIface() {
+            return !mIfaces.isEmpty();
+        }
+
+        /** Checks if there are any iface of the given type active. */
+        private boolean hasAnyIfaceOfType(@Iface.IfaceType int type) {
+            for (Iface iface : mIfaces.values()) {
+                if (iface.type == type) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /** Checks if there are any STA iface active. */
+        private boolean hasAnyStaIface() {
+            return hasAnyIfaceOfType(Iface.IFACE_TYPE_STA);
+        }
+
+        /** Checks if there are any AP iface active. */
+        private boolean hasAnyApIface() {
+            return hasAnyIfaceOfType(Iface.IFACE_TYPE_AP);
+        }
+    }
+
+    private Object mLock = new Object();
+    private final IfaceManager mIfaceMgr = new IfaceManager();
+    private HashSet<StatusListener> mStatusListeners = new HashSet<>();
+
+    /** Helper method invoked to start supplicant if there were no ifaces */
+    private boolean startHal() {
+        synchronized (mLock) {
+            if (!mIfaceMgr.hasAnyIface()) {
+                if (!mWifiVendorHal.startVendorHal()) {
+                    Log.e(mTAG, "Failed to start vendor HAL");
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /** Helper method invoked to stop HAL if there are no more ifaces */
+    private void stopHalAndWificondIfNecessary() {
+        synchronized (mLock) {
+            if (!mIfaceMgr.hasAnyIface()) {
+                if (!mWificondControl.tearDownInterfaces()) {
+                    Log.e(mTAG, "Failed to teardown ifaces from wificond");
+                }
+                mWifiVendorHal.stopVendorHal();
+            }
+        }
+    }
+
+    private static final int CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS = 100;
+    private static final int CONNECT_TO_SUPPLICANT_RETRY_TIMES = 50;
+    /**
+     * This method is called to wait for establishing connection to wpa_supplicant.
+     *
+     * @return true if connection is established, false otherwise.
+     */
+    private boolean waitForSupplicantConnection() {
+        // Start initialization if not already started.
+        if (!mSupplicantStaIfaceHal.isInitializationStarted()
+                && !mSupplicantStaIfaceHal.initialize()) {
+            return false;
+        }
+        boolean connected = false;
+        int connectTries = 0;
+        while (!connected && connectTries++ < CONNECT_TO_SUPPLICANT_RETRY_TIMES) {
+            // Check if the initialization is complete.
+            connected = mSupplicantStaIfaceHal.isInitializationComplete();
+            if (connected) {
+                break;
+            }
+            try {
+                Thread.sleep(CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS);
+            } catch (InterruptedException ignore) {
+            }
+        }
+        return connected;
+    }
+
+    /** Helper method invoked to start supplicant if there were no STA ifaces */
+    private boolean startSupplicant() {
+        synchronized (mLock) {
+            if (!mIfaceMgr.hasAnyStaIface()) {
+                if (!mWificondControl.enableSupplicant()) {
+                    Log.e(mTAG, "Failed to enable supplicant");
+                    return false;
+                }
+                if (!waitForSupplicantConnection()) {
+                    Log.e(mTAG, "Failed to connect to supplicant");
+                    return false;
+                }
+                if (!mSupplicantStaIfaceHal.registerDeathHandler(new DeathHandlerInternal())) {
+                    Log.e(mTAG, "Failed to register supplicant death handler");
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /** Helper method invoked to stop supplicant if there are no more STA ifaces */
+    private void stopSupplicantIfNecessary() {
+        synchronized (mLock) {
+            if (!mIfaceMgr.hasAnyStaIface()) {
+                if (!mSupplicantStaIfaceHal.deregisterDeathHandler()) {
+                    Log.e(mTAG, "Failed to deregister supplicant death handler");
+                }
+                if (!mWificondControl.disableSupplicant()) {
+                    Log.e(mTAG, "Failed to disable supplicant");
+                }
+            }
+        }
+    }
+
+    /** Helper method to register a network observer and return it */
+    private boolean registerNetworkObserver(@NonNull NetworkObserverInternal observer) {
+        try {
+            mNwManagementService.registerObserver(observer);
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /** Helper method to register a network observer and return it */
+    private boolean unregisterNetworkObserver(@NonNull NetworkObserverInternal observer) {
+        try {
+            mNwManagementService.unregisterObserver(observer);
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /** Helper method invoked to teardown client iface and perform necessary cleanup */
+    private void onClientInterfaceDestroyed(@NonNull Iface iface) {
+        synchronized (mLock) {
+            if (!unregisterNetworkObserver(iface.networkObserver)) {
+                Log.e(mTAG, "Failed to unregister network observer for iface=" + iface.name);
+            }
+            if (!mSupplicantStaIfaceHal.teardownIface(iface.name)) {
+                Log.e(mTAG, "Failed to teardown iface in supplicant=" + iface.name);
+            }
+            if (!mWificondControl.tearDownClientInterface(iface.name)) {
+                Log.e(mTAG, "Failed to teardown iface in wificond=" + iface.name);
+            }
+            stopSupplicantIfNecessary();
+            stopHalAndWificondIfNecessary();
+        }
+    }
+
+    /** Helper method invoked to teardown softAp iface and perform necessary cleanup */
+    private void onSoftApInterfaceDestroyed(@NonNull Iface iface) {
+        synchronized (mLock) {
+            if (!unregisterNetworkObserver(iface.networkObserver)) {
+                Log.e(mTAG, "Failed to unregister network observer for iface=" + iface.name);
+            }
+            if (!mWificondControl.stopSoftAp(iface.name)) {
+                Log.e(mTAG, "Failed to stop softap on iface=" + iface.name);
+            }
+            if (!mWificondControl.tearDownSoftApInterface(iface.name)) {
+                Log.e(mTAG, "Failed to teardown iface in wificond=" + iface.name);
+            }
+            stopHalAndWificondIfNecessary();
+        }
+    }
+
+    /** Helper method invoked to teardown iface and perform necessary cleanup */
+    private void onInterfaceDestroyed(@NonNull Iface iface) {
+        synchronized (mLock) {
+            if (iface.type == Iface.IFACE_TYPE_STA) {
+                onClientInterfaceDestroyed(iface);
+            } else if (iface.type == Iface.IFACE_TYPE_AP) {
+                onSoftApInterfaceDestroyed(iface);
+            }
+            // Invoke the external callback.
+            iface.externalListener.onDestroyed(iface.name);
+        }
+    }
+
+    /**
+     * Callback to be invoked by HalDeviceManager when an interface is destroyed.
+     */
+    private class InterfaceDestoyedListenerInternal
+            implements HalDeviceManager.InterfaceDestroyedListener {
+        /** Identifier allocated for the interface */
+        private final int mInterfaceId;
+
+        InterfaceDestoyedListenerInternal(int ifaceId) {
+            mInterfaceId = ifaceId;
+        }
+
+        @Override
+        public void onDestroyed(@NonNull String ifaceName) {
+            synchronized (mLock) {
+                final Iface iface = mIfaceMgr.removeIface(mInterfaceId);
+                if (iface == null) {
+                    Log.e(mTAG, "Received iface destroyed notification on an invalid iface="
+                            + ifaceName);
+                    return;
+                }
+                onInterfaceDestroyed(iface);
+                Log.i(mTAG, "Successfully torn down iface=" + ifaceName);
+            }
+        }
+    }
+
+    /**
+     * Common death handler for any of the lower layer daemons.
+     */
+    private class DeathHandlerInternal implements VendorHalDeathEventHandler,
+            SupplicantDeathEventHandler, WificondDeathEventHandler {
+        @Override
+        public void onDeath() {
+            synchronized (mLock) {
+                Log.i(mTAG, "One of the daemons died. Tearing down everything");
+                Iterator<Integer> ifaceIdIter = mIfaceMgr.getIfaceIdIter();
+                while (ifaceIdIter.hasNext()) {
+                    Iface iface = mIfaceMgr.getIface(ifaceIdIter.next());
+                    ifaceIdIter.remove();
+                    onInterfaceDestroyed(iface);
+                    Log.i(mTAG, "Successfully torn down iface=" + iface.name);
+                }
+                for (StatusListener listener : mStatusListeners) {
+                    listener.onStatusChanged(false);
+                }
+                // TODO(70572148): Do we need to wait to mark the system ready again?
+                for (StatusListener listener : mStatusListeners) {
+                    listener.onStatusChanged(true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Network observer to use for all interface up/down notifications.
+     */
+    private class NetworkObserverInternal extends BaseNetworkObserver {
+        /** Identifier allocated for the interface */
+        private final int mInterfaceId;
+
+        NetworkObserverInternal(int id) {
+            mInterfaceId = id;
+        }
+
+        @Override
+        public void interfaceLinkStateChanged(String ifaceName, boolean isUp) {
+            synchronized (mLock) {
+                Log.i(mTAG, "Interface link state changed=" + ifaceName + ", isUp=" + isUp);
+                final Iface iface = mIfaceMgr.getIface(mInterfaceId);
+                if (iface == null) {
+                    Log.e(mTAG, "Received iface up/down notification on an invalid iface="
+                            + ifaceName);
+                    return;
+                }
+                if (isUp) {
+                    iface.externalListener.onUp(ifaceName);
+                } else {
+                    iface.externalListener.onDown(ifaceName);
+                }
+            }
+        }
+    }
+
+    /**
      * Initialize the native modules.
      *
      * @return true on success, false otherwise.
      */
     public boolean initialize() {
-        return false;
+        synchronized (mLock) {
+            if (!mWifiVendorHal.initialize(new DeathHandlerInternal())) {
+                Log.e(mTAG, "Failed to initialize vendor HAL");
+                return false;
+            }
+            if (!mWificondControl.registerDeathHandler(new DeathHandlerInternal())) {
+                Log.e(mTAG, "Failed to initialize wificond");
+                return false;
+            }
+            return true;
+        }
     }
 
     /**
@@ -186,6 +551,7 @@
      * @param listener StatusListener listener object.
      */
     public void registerStatusListener(@NonNull StatusListener listener) {
+        mStatusListeners.add(listener);
     }
 
     /**
@@ -224,7 +590,47 @@
      * @return Returns the name of the allocated interface, will be null on failure.
      */
     public String setupInterfaceForClientMode(@NonNull InterfaceCallback interfaceCallback) {
-        return null;
+        synchronized (mLock) {
+            if (!startHal()) {
+                Log.e(mTAG, "Failed to start Hal");
+                return null;
+            }
+            if (!startSupplicant()) {
+                Log.e(mTAG, "Failed to start supplicant");
+                return null;
+            }
+            Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA);
+            if (iface == null) {
+                Log.e(mTAG, "Failed to allocate new STA iface");
+                return null;
+            }
+            iface.externalListener = interfaceCallback;
+            iface.name =
+                    mWifiVendorHal.createStaIface(new InterfaceDestoyedListenerInternal(iface.id));
+            if (TextUtils.isEmpty(iface.name)) {
+                Log.e(mTAG, "Failed to create iface in vendor HAL");
+                mIfaceMgr.removeIface(iface.id);
+                return null;
+            }
+            if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
+                Log.e(mTAG, "Failed to setup iface in wificond=" + iface.name);
+                teardownInterface(iface.name);
+                return null;
+            }
+            if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
+                Log.e(mTAG, "Failed to setup iface in supplicant=" + iface.name);
+                teardownInterface(iface.name);
+                return null;
+            }
+            iface.networkObserver = new NetworkObserverInternal(iface.id);
+            if (!registerNetworkObserver(iface.networkObserver)) {
+                Log.e(mTAG, "Failed to register network observer for iface=" + iface.name);
+                teardownInterface(iface.name);
+                return null;
+            }
+            Log.i(mTAG, "Successfully setup iface=" + iface.name);
+            return iface.name;
+        }
     }
 
     /**
@@ -237,7 +643,63 @@
      * @return Returns the name of the allocated interface, will be null on failure.
      */
     public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
-        return null;
+        synchronized (mLock) {
+            if (!startHal()) {
+                Log.e(mTAG, "Failed to start Hal");
+                return null;
+            }
+            Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);
+            if (iface == null) {
+                Log.e(mTAG, "Failed to allocate new AP iface");
+                return null;
+            }
+            iface.externalListener = interfaceCallback;
+            iface.name =
+                    mWifiVendorHal.createApIface(new InterfaceDestoyedListenerInternal(iface.id));
+            if (TextUtils.isEmpty(iface.name)) {
+                Log.e(mTAG, "Failed to create iface in vendor HAL");
+                mIfaceMgr.removeIface(iface.id);
+                return null;
+            }
+            if (mWificondControl.setupInterfaceForSoftApMode(iface.name) == null) {
+                Log.e(mTAG, "Failed to setup iface in wificond=" + iface.name);
+                teardownInterface(iface.name);
+                return null;
+            }
+            iface.networkObserver = new NetworkObserverInternal(iface.id);
+            if (!registerNetworkObserver(iface.networkObserver)) {
+                Log.e(mTAG, "Failed to register network observer for iface=" + iface.name);
+                teardownInterface(iface.name);
+                return null;
+            }
+            Log.i(mTAG, "Successfully setup iface=" + iface.name);
+            return iface.name;
+        }
+    }
+
+    /**
+     * Check if the interface is up or down.
+     *
+     * @param ifaceName Name of the interface.
+     * @return true if iface is up, false if it's down or on error.
+     */
+    public boolean isInterfaceUp(@NonNull String ifaceName) {
+        synchronized (mLock) {
+            final Iface iface = mIfaceMgr.getIface(ifaceName);
+            if (iface == null) {
+                Log.e(mTAG, "Trying to get iface state on invalid iface=" + ifaceName);
+                return false;
+            }
+            InterfaceConfiguration config = null;
+            try {
+                config = mNwManagementService.getInterfaceConfig(ifaceName);
+            } catch (RemoteException e) {
+            }
+            if (config == null) {
+                return false;
+            }
+            return config.isUp();
+        }
     }
 
     /**
@@ -249,6 +711,28 @@
      * @param ifaceName Name of the interface.
      */
     public void teardownInterface(@NonNull String ifaceName) {
+        synchronized (mLock) {
+            final Iface iface = mIfaceMgr.getIface(ifaceName);
+            if (iface == null) {
+                Log.e(mTAG, "Trying to teardown an invalid iface=" + ifaceName);
+                return;
+            }
+            // Trigger the iface removal from HAL. The rest of the cleanup will be triggered
+            // from the interface destroyed callback.
+            // TODO(b/70521011): Figure out what to do for devices with no HAL.
+            if (iface.type == Iface.IFACE_TYPE_STA) {
+                if (!mWifiVendorHal.removeStaIface(ifaceName)) {
+                    Log.e(mTAG, "Failed to remove iface in vendor HAL=" + ifaceName);
+                    return;
+                }
+            } else if (iface.type == Iface.IFACE_TYPE_AP) {
+                if (!mWifiVendorHal.removeApIface(ifaceName)) {
+                    Log.e(mTAG, "Failed to remove iface in vendor HAL=" + ifaceName);
+                    return;
+                }
+            }
+            Log.i(mTAG, "Successfully initiated teardown for iface=" + ifaceName);
+        }
     }
 
     /********************************************************
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 505a09b..4090c36 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -4657,7 +4657,14 @@
         public void enter() {
             mLastOperationMode = mOperationalMode;
             mWifiStateTracker.updateState(WifiStateTracker.SCAN_MODE);
+            mWifiInjector.getWakeupController().start();
         }
+
+        @Override
+        public void exit() {
+            mWifiInjector.getWakeupController().stop();
+        }
+
         @Override
         public boolean processMessage(Message message) {
             logStateAndMessage(message, this);
@@ -4885,6 +4892,8 @@
             // Let the system know that wifi is available in client mode.
             setWifiState(WIFI_STATE_ENABLED);
 
+            mWifiInjector.getWakeupController().reset();
+
             mNetworkInfo.setIsAvailable(true);
             if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
 
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index f1d2add..d6b568d 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -347,6 +347,24 @@
         }
     }
 
+    private class StaInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
+        private final InterfaceDestroyedListener mExternalListener;
+
+        StaInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
+            mExternalListener = externalListener;
+        }
+
+        @Override
+        public void onDestroyed(@NonNull String ifaceName) {
+            synchronized (sLock) {
+                mIWifiStaIfaces.remove(ifaceName);
+            }
+            if (mExternalListener != null) {
+                mExternalListener.onDestroyed(ifaceName);
+            }
+        }
+    }
+
     /**
      * Create a STA iface using {@link HalDeviceManager}.
      *
@@ -355,8 +373,8 @@
      */
     public String createStaIface(InterfaceDestroyedListener destroyedListener) {
         synchronized (sLock) {
-            IWifiStaIface iface;
-            iface = mHalDeviceManager.createStaIface(destroyedListener, null);
+            IWifiStaIface iface = mHalDeviceManager.createStaIface(
+                    new StaInterfaceDestroyedListenerInternal(destroyedListener), null);
             if (iface == null) {
                 mLog.err("Failed to create STA iface");
                 return stringResult(null);
@@ -416,6 +434,25 @@
         }
     }
 
+    private class ApInterfaceDestroyedListenerInternal implements InterfaceDestroyedListener {
+        private final InterfaceDestroyedListener mExternalListener;
+
+        ApInterfaceDestroyedListenerInternal(InterfaceDestroyedListener externalListener) {
+            mExternalListener = externalListener;
+        }
+
+        @Override
+        public void onDestroyed(@NonNull String ifaceName) {
+            synchronized (sLock) {
+                mIWifiApIfaces.remove(ifaceName);
+            }
+            if (mExternalListener != null) {
+                mExternalListener.onDestroyed(ifaceName);
+            }
+        }
+    }
+
+
     /**
      * Create a AP iface using {@link HalDeviceManager}.
      *
@@ -424,8 +461,8 @@
      */
     public String createApIface(InterfaceDestroyedListener destroyedListener) {
         synchronized (sLock) {
-            IWifiApIface iface;
-            iface = mHalDeviceManager.createApIface(destroyedListener, null);
+            IWifiApIface iface = mHalDeviceManager.createApIface(
+                    new ApInterfaceDestroyedListenerInternal(destroyedListener), null);
             if (iface == null) {
                 mLog.err("Failed to create AP iface");
                 return stringResult(null);
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index f8deb05..b263597 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -203,6 +203,7 @@
             String name = AWARE_INTERFACE_PREFIX + i;
             mMgr.deleteDataPathInterface(name);
         }
+        mMgr.releaseAwareInterface();
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
index dd19d7b..b9dd680 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -47,10 +47,10 @@
     private Handler mHandler;
     private WifiAwareNativeCallback mWifiAwareNativeCallback;
     private IWifiNanIface mWifiNanIface = null;
-    private InterfaceDestroyedListener mInterfaceDestroyedListener =
-            new InterfaceDestroyedListener();
+    private InterfaceDestroyedListener mInterfaceDestroyedListener;
     private InterfaceAvailableForRequestListener mInterfaceAvailableForRequestListener =
             new InterfaceAvailableForRequestListener();
+    private int mReferenceCount = 0;
 
     WifiAwareNativeManager(WifiAwareStateManager awareStateManager,
             HalDeviceManager halDeviceManager,
@@ -88,7 +88,6 @@
         if (mHalDeviceManager.isStarted()) {
             mHalDeviceManager.registerInterfaceAvailableForRequestListener(
                     IfaceType.NAN, mInterfaceAvailableForRequestListener, mHandler);
-            tryToGetAware();
         }
     }
 
@@ -104,19 +103,31 @@
     }
 
     /**
-     * Attempt to obtain the HAL NAN interface. If available then enables Aware usage.
+     * Attempt to obtain the HAL NAN interface.
      */
-    private void tryToGetAware() {
+    public void tryToGetAware() {
         synchronized (mLock) {
-            if (mDbg) Log.v(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface);
+            if (mDbg) {
+                Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount="
+                        + mReferenceCount);
+            }
 
             if (mWifiNanIface != null) {
+                mReferenceCount++;
                 return;
             }
+            if (mHalDeviceManager == null) {
+                Log.e(TAG, "tryToGetAware: mHalDeviceManager is null!?");
+                awareIsDown();
+                return;
+            }
+
+            mInterfaceDestroyedListener = new InterfaceDestroyedListener();
             IWifiNanIface iface = mHalDeviceManager.createNanIface(mInterfaceDestroyedListener,
                     mHandler);
             if (iface == null) {
-                Log.e(TAG, "Was not able to obtain an IWifiNanIface");
+                Log.e(TAG, "Was not able to obtain an IWifiNanIface (even though enabled!?)");
+                awareIsDown();
             } else {
                 if (mDbg) Log.v(TAG, "Obtained an IWifiNanIface");
 
@@ -126,44 +137,92 @@
                         Log.e(TAG, "IWifiNanIface.registerEventCallback error: " + statusString(
                                 status));
                         mHalDeviceManager.removeIface(iface);
+                        awareIsDown();
                         return;
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "IWifiNanIface.registerEventCallback exception: " + e);
-                    mHalDeviceManager.removeIface(iface);
+                    awareIsDown();
                     return;
                 }
                 mWifiNanIface = iface;
-                mWifiAwareStateManager.enableUsage();
+                mReferenceCount = 1;
             }
         }
     }
 
+    /**
+     * Release the HAL NAN interface.
+     */
+    public void releaseAware() {
+        if (mDbg) {
+            Log.d(TAG, "releaseAware: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount="
+                    + mReferenceCount);
+        }
+
+        if (mWifiNanIface == null) {
+            return;
+        }
+        if (mHalDeviceManager == null) {
+            Log.e(TAG, "releaseAware: mHalDeviceManager is null!?");
+            return;
+        }
+
+        synchronized (mLock) {
+            mReferenceCount--;
+            if (mReferenceCount != 0) {
+                return;
+            }
+            mInterfaceDestroyedListener.active = false;
+            mInterfaceDestroyedListener = null;
+            mHalDeviceManager.removeIface(mWifiNanIface);
+            mWifiNanIface = null;
+        }
+    }
+
     private void awareIsDown() {
         synchronized (mLock) {
-            if (mDbg) Log.v(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface);
-            if (mWifiNanIface != null) {
-                mWifiNanIface = null;
-                mWifiAwareStateManager.disableUsage();
+            if (mDbg) {
+                Log.d(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface + ", mReferenceCount ="
+                        + mReferenceCount);
             }
+            mWifiNanIface = null;
+            mReferenceCount = 0;
+            mWifiAwareStateManager.disableUsage();
         }
     }
 
     private class InterfaceDestroyedListener implements
             HalDeviceManager.InterfaceDestroyedListener {
+        public boolean active = true;
+
         @Override
         public void onDestroyed(@NonNull String ifaceName) {
-            if (mDbg) Log.v(TAG, "Interface was destroyed");
-            awareIsDown();
+            if (mDbg) {
+                Log.d(TAG, "Interface was destroyed: mWifiNanIface=" + mWifiNanIface + ", active="
+                        + active);
+            }
+            if (active && mWifiNanIface != null) {
+                awareIsDown();
+            } // else: we released it locally so no need to disable usage
         }
     }
 
     private class InterfaceAvailableForRequestListener implements
             HalDeviceManager.InterfaceAvailableForRequestListener {
         @Override
-        public void onAvailableForRequest() {
-            if (mDbg) Log.v(TAG, "Interface is possibly available");
-            tryToGetAware();
+        public void onAvailabilityChanged(boolean isAvailable) {
+            if (mDbg) {
+                Log.d(TAG, "Interface availability = " + isAvailable + ", mWifiNanIface="
+                        + mWifiNanIface);
+            }
+            synchronized (mLock) {
+                if (isAvailable) {
+                    mWifiAwareStateManager.enableUsage();
+                } else if (mWifiNanIface == null) { // not available could mean already have NAN
+                    mWifiAwareStateManager.disableUsage();
+                }
+            }
         }
     }
 
@@ -182,6 +241,7 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("WifiAwareNativeManager:");
         pw.println("  mWifiNanIface: " + mWifiNanIface);
+        pw.println("  mReferenceCount: " + mReferenceCount);
         mWifiAwareNativeCallback.dump(fd, pw, args);
         mHalDeviceManager.dump(fd, pw, args);
     }
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
index 1dd18ef..5ae6902 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -119,6 +119,8 @@
     private static final int COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE = 119;
     private static final int COMMAND_TYPE_RECONFIGURE = 120;
     private static final int COMMAND_TYPE_DELAYED_INITIALIZATION = 121;
+    private static final int COMMAND_TYPE_GET_AWARE = 122;
+    private static final int COMMAND_TYPE_RELEASE_AWARE = 123;
 
     private static final int RESPONSE_TYPE_ON_CONFIG_SUCCESS = 200;
     private static final int RESPONSE_TYPE_ON_CONFIG_FAIL = 201;
@@ -470,6 +472,26 @@
     }
 
     /**
+     * Place a request to get the Wi-Fi Aware interface (before which no HAL command can be
+     * executed).
+     */
+    public void getAwareInterface() {
+        Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+        msg.arg1 = COMMAND_TYPE_GET_AWARE;
+        mSm.sendMessage(msg);
+    }
+
+    /**
+     * Place a request to release the Wi-Fi Aware interface (after which no HAL command can be
+     * executed).
+     */
+    public void releaseAwareInterface() {
+        Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+        msg.arg1 = COMMAND_TYPE_RELEASE_AWARE;
+        mSm.sendMessage(msg);
+    }
+
+    /**
      * Place a request for a new client connection on the state machine queue.
      */
     public void connect(int clientId, int uid, int pid, String callingPackage,
@@ -1634,6 +1656,14 @@
                     mWifiAwareNativeManager.start(getHandler());
                     waitForResponse = false;
                     break;
+                case COMMAND_TYPE_GET_AWARE:
+                    mWifiAwareNativeManager.tryToGetAware();
+                    waitForResponse = false;
+                    break;
+                case COMMAND_TYPE_RELEASE_AWARE:
+                    mWifiAwareNativeManager.releaseAware();
+                    waitForResponse = false;
+                    break;
                 default:
                     waitForResponse = false;
                     Log.wtf(TAG, "processCommand: this isn't a COMMAND -- msg=" + msg);
@@ -1884,6 +1914,14 @@
                             "processTimeout: COMMAND_TYPE_DELAYED_INITIALIZATION - shouldn't be "
                                     + "waiting!");
                     break;
+                case COMMAND_TYPE_GET_AWARE:
+                    Log.wtf(TAG,
+                            "processTimeout: COMMAND_TYPE_GET_AWARE - shouldn't be waiting!");
+                    break;
+                case COMMAND_TYPE_RELEASE_AWARE:
+                    Log.wtf(TAG,
+                            "processTimeout: COMMAND_TYPE_RELEASE_AWARE - shouldn't be waiting!");
+                    break;
                 default:
                     Log.wtf(TAG, "processTimeout: this isn't a COMMAND -- msg=" + msg);
                     /* fall-through */
@@ -2060,6 +2098,10 @@
         boolean notificationRequired =
                 doesAnyClientNeedIdentityChangeNotifications() || notifyIdentityChange;
 
+        if (mCurrentAwareConfiguration == null) {
+            mWifiAwareNativeManager.tryToGetAware();
+        }
+
         boolean success = mWifiAwareNativeApi.enableAndConfigure(transactionId, merged,
                 notificationRequired, mCurrentAwareConfiguration == null,
                 mPowerManager.isInteractive(), mPowerManager.isDeviceIdleMode());
@@ -2290,12 +2332,16 @@
     private void enableUsageLocal() {
         if (VDBG) Log.v(TAG, "enableUsageLocal: mUsageEnabled=" + mUsageEnabled);
 
+        if (mCapabilities == null) {
+            getAwareInterface();
+            queryCapabilities();
+            releaseAwareInterface();
+        }
+
         if (mUsageEnabled) {
             return;
         }
-
         mUsageEnabled = true;
-        queryCapabilities();
         sendAwareStateChangedBroadcast(true);
 
         mAwareMetrics.recordEnableUsage();
@@ -2863,7 +2909,10 @@
 
     private void onAwareDownLocal() {
         if (VDBG) {
-            Log.v(TAG, "onAwareDown");
+            Log.v(TAG, "onAwareDown: mCurrentAwareConfiguration=" + mCurrentAwareConfiguration);
+        }
+        if (mCurrentAwareConfiguration == null) {
+            return;
         }
 
         for (int i = 0; i < mClients.size(); ++i) {
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index b5c5844..19a5efb 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -59,7 +59,7 @@
 # since neither is declared a static java library.
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	android-support-test \
-	mockito-target-minus-junit4 \
+	mockito-target-inline-minus-junit4 \
 	frameworks-base-testutils \
 	services \
 	wifi-service \
@@ -110,6 +110,7 @@
 	libunwindstack \
 	libutils \
 	libvndksupport \
+	libdexmakerjvmtiagent \
 
 ifdef WPA_SUPPLICANT_VERSION
 LOCAL_JNI_SHARED_LIBRARIES += libwpa_client
diff --git a/tests/wifitests/AndroidManifest.xml b/tests/wifitests/AndroidManifest.xml
index dcb61c7..aad2041 100644
--- a/tests/wifitests/AndroidManifest.xml
+++ b/tests/wifitests/AndroidManifest.xml
@@ -19,7 +19,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.server.wifi.test">
 
-    <application>
+    <application
+        android:debuggable="true">
         <uses-library android:name="android.test.runner" />
         <activity android:label="WifiTestDummyLabel"
                   android:name="WifiTestDummyName">
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 4290ada..3e3ce42 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -313,42 +313,51 @@
         HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
                 HalDeviceManager.InterfaceAvailableForRequestListener.class);
 
+        InOrder availInOrder = inOrder(staAvailListener, nanAvailListener);
+
         // Request STA
         IWifiIface staIface = validateInterfaceSequence(chipMock,
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
                 staAvailListener // availableListener
         );
+        availInOrder.verify(staAvailListener).onAvailabilityChanged(false);
 
         // Request NAN
         IWifiIface nanIface = validateInterfaceSequence(chipMock,
                 true, // chipModeValid
                 TestChipV1.STA_CHIP_MODE_ID, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
                 nanAvailListener // availableListener
         );
+        availInOrder.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // fiddle with the "chip" by removing the STA
-        chipMock.interfaceNames.get(IfaceType.STA).remove("sta0");
+        chipMock.interfaceNames.get(IfaceType.STA).remove("wlan0");
 
         // now try to request another NAN
         IWifiIface nanIface2 = mDut.createNanIface(nanDestroyedListener, mHandler);
+        collector.checkThat("NAN can't be created", nanIface2, IsNull.nullValue());
+
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
                 mHandler);
-        collector.checkThat("NAN can't be created", nanIface2, IsNull.nullValue());
+        mTestLooper.dispatchAll();
+
+        // extra (apparently duplicate) call since everything was cleaned-up once a cache mismatch
+        // was detected - so this is a call on a new registration
+        availInOrder.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // verify that Wi-Fi is shut-down: should also get all onDestroyed messages that are
         // registered (even if they seem out-of-sync to chip)
-        mTestLooper.dispatchAll();
         verify(mWifiMock, times(2)).stop();
         verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
         verify(staDestroyedListener).onDestroyed(getName(staIface));
@@ -384,7 +393,7 @@
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 null, // destroyedListener
@@ -399,12 +408,14 @@
                 mHandler);
         mTestLooper.dispatchAll();
 
+        verify(staAvailListener).onAvailabilityChanged(false);
+
         // remove STA interface -> should trigger callbacks
         mDut.removeIface(staIface);
         mTestLooper.dispatchAll();
 
         // verify: only a single trigger
-        verify(staAvailListener).onAvailableForRequest();
+        verify(staAvailListener).onAvailabilityChanged(true);
 
         verifyNoMoreInteractions(staAvailListener);
     }
@@ -515,8 +526,8 @@
      */
     @Test
     public void testCreateStaInterfaceNoInitModeTestChipV1() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.STA, "sta0",
-                TestChipV1.STA_CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.STA, "wlan0",
+                TestChipV1.STA_CHIP_MODE_ID, false);
     }
 
     /**
@@ -524,8 +535,8 @@
      */
     @Test
     public void testCreateApInterfaceNoInitModeTestChipV1() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.AP, "ap0",
-                TestChipV1.AP_CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.AP, "wlan0",
+                TestChipV1.AP_CHIP_MODE_ID, false);
     }
 
     /**
@@ -534,7 +545,7 @@
     @Test
     public void testCreateP2pInterfaceNoInitModeTestChipV1() throws Exception {
         runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.P2P, "p2p0",
-                TestChipV1.STA_CHIP_MODE_ID, 1);
+                TestChipV1.STA_CHIP_MODE_ID, false);
     }
 
     /**
@@ -542,8 +553,8 @@
      */
     @Test
     public void testCreateNanInterfaceNoInitModeTestChipV1() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.NAN, "nan0",
-                TestChipV1.STA_CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV1(), IfaceType.NAN, "wlan0",
+                TestChipV1.STA_CHIP_MODE_ID, false);
     }
 
     // TestChipV2
@@ -557,8 +568,8 @@
         // 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 TestChipV2(), IfaceType.STA, "sta0",
-                TestChipV2.CHIP_MODE_ID, 2);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.STA, "wlan0",
+                TestChipV2.CHIP_MODE_ID, true);
     }
 
     /**
@@ -566,8 +577,8 @@
      */
     @Test
     public void testCreateApInterfaceNoInitModeTestChipV2() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.AP, "ap0",
-                TestChipV2.CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.AP, "wlan0",
+                TestChipV2.CHIP_MODE_ID, false);
     }
 
     /**
@@ -576,7 +587,7 @@
     @Test
     public void testCreateP2pInterfaceNoInitModeTestChipV2() throws Exception {
         runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.P2P, "p2p0",
-                TestChipV2.CHIP_MODE_ID, 1);
+                TestChipV2.CHIP_MODE_ID, false);
     }
 
     /**
@@ -584,8 +595,8 @@
      */
     @Test
     public void testCreateNanInterfaceNoInitModeTestChipV2() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.NAN, "nan0",
-                TestChipV2.CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV2(), IfaceType.NAN, "wlan0",
+                TestChipV2.CHIP_MODE_ID, false);
     }
 
     // TestChipV3
@@ -599,8 +610,8 @@
         // 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);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.STA, "wlan0",
+                TestChipV3.CHIP_MODE_ID, true);
     }
 
     /**
@@ -608,8 +619,8 @@
      */
     @Test
     public void testCreateApInterfaceNoInitModeTestChipV3() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.AP, "ap0",
-                TestChipV3.CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.AP, "wlan0",
+                TestChipV3.CHIP_MODE_ID, false);
     }
 
     /**
@@ -618,7 +629,7 @@
     @Test
     public void testCreateP2pInterfaceNoInitModeTestChipV3() throws Exception {
         runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.P2P, "p2p0",
-                TestChipV3.CHIP_MODE_ID, 1);
+                TestChipV3.CHIP_MODE_ID, false);
     }
 
     /**
@@ -626,8 +637,8 @@
      */
     @Test
     public void testCreateNanInterfaceNoInitModeTestChipV3() throws Exception {
-        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.NAN, "nan0",
-                TestChipV3.CHIP_MODE_ID, 1);
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.NAN, "wlan0",
+                TestChipV3.CHIP_MODE_ID, false);
     }
 
     //////////////////////////////////////////////////////////////////////////////////////
@@ -640,7 +651,7 @@
      */
     @Test
     public void testCreateApWithStaModeUpTestChipV1() throws Exception {
-        final String name = "ap0";
+        final String name = "wlan0";
 
         TestChipV1 chipMock = new TestChipV1();
         chipMock.initialize();
@@ -666,6 +677,8 @@
         );
         collector.checkThat("allocated interface", iface, IsNull.notNullValue());
 
+        verify(iafrl).onAvailabilityChanged(false);
+
         // act: stop Wi-Fi
         mDut.stop();
         mTestLooper.dispatchAll();
@@ -707,8 +720,8 @@
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staIafrl, null);
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.AP, apIafrl, null);
 
-        mInOrder.verify(staIafrl).onAvailableForRequest();
-        mInOrder.verify(apIafrl).onAvailableForRequest();
+        mInOrder.verify(staIafrl).onAvailabilityChanged(true);
+        mInOrder.verify(apIafrl).onAvailabilityChanged(true);
 
         // Create STA Iface first.
         IWifiStaIface staIface = mock(IWifiStaIface.class);
@@ -721,7 +734,7 @@
         assertEquals(staIface, mDut.createStaIface(staIdl, null));
 
         mInOrder.verify(chipMock.chip).configureChip(TestChipV1.STA_CHIP_MODE_ID);
-        mInOrder.verify(apIafrl).onAvailableForRequest();
+        mInOrder.verify(staIafrl).onAvailabilityChanged(false);
 
         // Now Create AP Iface.
         IWifiApIface apIface = mock(IWifiApIface.class);
@@ -736,7 +749,8 @@
         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();
+        mInOrder.verify(apIafrl).onAvailabilityChanged(false);
+        mInOrder.verify(staIafrl).onAvailabilityChanged(true);
 
         // Stop Wi-Fi
         mDut.stop();
@@ -754,7 +768,7 @@
      */
     @Test
     public void testCreateApWithApModeUpTestChipV1() throws Exception {
-        final String name = "ap0";
+        final String name = "wlan0";
 
         TestChipV1 chipMock = new TestChipV1();
         chipMock.initialize();
@@ -780,6 +794,8 @@
         );
         collector.checkThat("allocated interface", iface, IsNull.notNullValue());
 
+        verify(iafrl).onAvailabilityChanged(false);
+
         // act: stop Wi-Fi
         mDut.stop();
         mTestLooper.dispatchAll();
@@ -844,10 +860,8 @@
         HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
                 HalDeviceManager.InterfaceAvailableForRequestListener.class);
 
-        InOrder inOrderStaAvail = inOrder(staAvailListener);
-        InOrder inOrderApAvail = inOrder(apAvailListener);
-        InOrder inOrderP2pAvail = inOrder(p2pAvailListener);
-        InOrder inOrderNanAvail = inOrder(nanAvailListener);
+        InOrder inOrderAvail = inOrder(staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener);
 
         // register listeners for interface availability
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
@@ -859,17 +873,17 @@
                 mHandler);
         mTestLooper.dispatchAll();
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
 
         // Request STA
         IWifiIface staIface = validateInterfaceSequence(chipMock,
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
@@ -877,9 +891,7 @@
         );
         collector.checkThat("allocated STA interface", staIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
 
         // request STA2: should fail
         IWifiIface staIface2 = mDut.createStaIface(null, null);
@@ -903,14 +915,15 @@
         );
         collector.checkThat("allocated P2P interface", p2pIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // Request AP
         IWifiIface apIface = validateInterfaceSequence(chipMock,
                 true, // chipModeValid
                 TestChipV1.STA_CHIP_MODE_ID, // chipModeId
                 IfaceType.AP, // ifaceTypeToCreate
-                "ap0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.AP_CHIP_MODE_ID, // finalChipMode
                 new IWifiIface[]{staIface, p2pIface}, // tearDownList
                 apDestroyedListener, // destroyedListener
@@ -925,7 +938,8 @@
         );
         collector.checkThat("allocated AP interface", apIface, IsNull.notNullValue());
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
 
         // request AP2: should fail
         IWifiIface apIface2 = mDut.createApIface(null, null);
@@ -940,7 +954,7 @@
                 true, // chipModeValid
                 TestChipV1.AP_CHIP_MODE_ID, // chipModeId
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
@@ -951,9 +965,10 @@
         );
         collector.checkThat("allocated STA interface", staIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
 
         mTestLooper.dispatchAll();
         verify(apDestroyedListener).onDestroyed(getName(apIface));
@@ -970,7 +985,8 @@
                 null // availableListener
         );
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // Request NAN: should fail
         IWifiIface nanIface = mDut.createNanIface(nanDestroyedListener, mHandler);
@@ -982,9 +998,8 @@
         mDut.removeIface(p2pIface);
         mTestLooper.dispatchAll();
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
         verify(chipMock.chip, times(2)).removeP2pIface("p2p0");
         verify(p2pDestroyedListener2).onDestroyed(getName(p2pIface));
 
@@ -993,7 +1008,7 @@
                 true, // chipModeValid
                 TestChipV1.STA_CHIP_MODE_ID, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
@@ -1001,8 +1016,7 @@
         );
         collector.checkThat("allocated NAN interface", nanIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
                 staDestroyedListener2, apDestroyedListener, apAvailListener, p2pDestroyedListener,
@@ -1023,9 +1037,7 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV1() throws Exception {
-        // 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);
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV1(), TestChipV1.STA_CHIP_MODE_ID);
     }
 
     /**
@@ -1054,7 +1066,7 @@
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV1.STA_CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener1, // destroyedListener
@@ -1062,6 +1074,8 @@
         );
         collector.checkThat("STA created", staIface1, IsNull.notNullValue());
 
+        verify(staAvailListener1).onAvailabilityChanged(false);
+
         // get STA interface again
         IWifiIface staIface2 = mDut.createStaIface(staDestroyedListener2, mHandler);
         collector.checkThat("STA created", staIface2, IsNull.nullValue());
@@ -1170,10 +1184,8 @@
         HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
                 HalDeviceManager.InterfaceAvailableForRequestListener.class);
 
-        InOrder inOrderStaAvail = inOrder(staAvailListener);
-        InOrder inOrderApAvail = inOrder(apAvailListener);
-        InOrder inOrderP2pAvail = inOrder(p2pAvailListener);
-        InOrder inOrderNanAvail = inOrder(nanAvailListener);
+        InOrder inOrderAvail = inOrder(staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener);
 
         // register listeners for interface availability
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
@@ -1185,10 +1197,10 @@
                 mHandler);
         mTestLooper.dispatchAll();
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
 
         // create STA
         when(mClock.getUptimeSinceBootMillis()).thenReturn(15L);
@@ -1196,7 +1208,7 @@
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV2.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
@@ -1204,11 +1216,6 @@
         );
         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
@@ -1222,8 +1229,8 @@
         );
         collector.checkThat("P2P interface wasn't created", p2pIface, IsNull.notNullValue());
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // request NAN: should fail
         IWifiIface nanIface = mDut.createNanIface(null, null);
@@ -1234,7 +1241,7 @@
                 true, // chipModeValid
                 TestChipV2.CHIP_MODE_ID, // chipModeId
                 IfaceType.AP, // ifaceTypeToCreate
-                "ap0", // ifaceName
+                "wlan1", // ifaceName
                 TestChipV2.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 apDestroyedListener, // destroyedListener
@@ -1242,6 +1249,9 @@
         );
         collector.checkThat("AP interface wasn't created", apIface, IsNull.notNullValue());
 
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
+
         // request STA2: should fail
         IWifiIface staIface2 = mDut.createStaIface(null, null);
         collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
@@ -1254,9 +1264,9 @@
         mDut.removeIface(apIface);
         mTestLooper.dispatchAll();
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        verify(chipMock.chip).removeApIface("ap0");
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
+        verify(chipMock.chip).removeApIface("wlan1");
         verify(apDestroyedListener).onDestroyed(getName(apIface));
 
         // create STA2: using a later clock
@@ -1265,7 +1275,7 @@
                 true, // chipModeValid
                 TestChipV2.CHIP_MODE_ID, // chipModeId
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta1", // ifaceName
+                "wlan1", // ifaceName
                 TestChipV2.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener2, // destroyedListener
@@ -1273,7 +1283,7 @@
         );
         collector.checkThat("STA 2 interface wasn't created", staIface2, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
 
         // request STA3: should fail
         IWifiIface staIface3 = mDut.createStaIface(null, null);
@@ -1284,7 +1294,7 @@
                 true, // chipModeValid
                 TestChipV2.CHIP_MODE_ID, // chipModeId
                 IfaceType.AP, // ifaceTypeToCreate
-                "ap0", // ifaceName
+                "wlan1", // ifaceName
                 TestChipV2.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 apDestroyedListener, // destroyedListener
@@ -1295,12 +1305,14 @@
         );
         collector.checkThat("AP interface wasn't created", apIface, IsNull.notNullValue());
 
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(false);
+
         // tear down P2P
         mDut.removeIface(p2pIface);
         mTestLooper.dispatchAll();
 
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
         verify(chipMock.chip).removeP2pIface("p2p0");
         verify(p2pDestroyedListener).onDestroyed(getName(p2pIface));
 
@@ -1309,7 +1321,7 @@
                 true, // chipModeValid
                 TestChipV2.CHIP_MODE_ID, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV2.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
@@ -1317,7 +1329,7 @@
         );
         collector.checkThat("NAN interface wasn't created", nanIface, IsNull.notNullValue());
 
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener,
                 staDestroyedListener2, apDestroyedListener, p2pDestroyedListener,
@@ -1340,9 +1352,7 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV2() throws Exception {
-        // staAvailCallbacks=5: after every substantial change will get a callback since second
-        // STA is always available.
-        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), 5, TestChipV2.CHIP_MODE_ID);
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), TestChipV2.CHIP_MODE_ID);
     }
 
     /**
@@ -1445,10 +1455,8 @@
         HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
                 HalDeviceManager.InterfaceAvailableForRequestListener.class);
 
-        InOrder inOrderStaAvail = inOrder(staAvailListener);
-        InOrder inOrderApAvail = inOrder(apAvailListener);
-        InOrder inOrderP2pAvail = inOrder(p2pAvailListener);
-        InOrder inOrderNanAvail = inOrder(nanAvailListener);
+        InOrder inOrderAvail = inOrder(staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener);
 
         // register listeners for interface availability
         mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
@@ -1460,10 +1468,10 @@
                 mHandler);
         mTestLooper.dispatchAll();
 
-        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
 
         // create STA
         when(mClock.getUptimeSinceBootMillis()).thenReturn(15L);
@@ -1471,7 +1479,7 @@
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV3.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
@@ -1479,11 +1487,6 @@
         );
         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
@@ -1497,7 +1500,9 @@
         );
         collector.checkThat("P2P interface wasn't created", p2pIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(false);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // request NAN: should fail
         IWifiIface nanIface = mDut.createNanIface(null, null);
@@ -1508,7 +1513,7 @@
                 true, // chipModeValid
                 TestChipV3.CHIP_MODE_ID, // chipModeId
                 IfaceType.AP, // ifaceTypeToCreate
-                "ap0", // ifaceName
+                "wlan1", // ifaceName
                 TestChipV3.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 apDestroyedListener, // destroyedListener
@@ -1518,6 +1523,8 @@
         collector.checkThat("AP interface wasn't created", apIface, IsNull.notNullValue());
         verify(chipMock.chip).removeP2pIface("p2p0");
 
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(false);
+
         // request STA2: should fail
         IWifiIface staIface2 = mDut.createStaIface(null, null);
         collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
@@ -1534,11 +1541,11 @@
         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");
+        inOrderAvail.verify(apAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(p2pAvailListener).onAvailabilityChanged(true);
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(true);
+        verify(chipMock.chip).removeApIface("wlan1");
         verify(apDestroyedListener).onDestroyed(getName(apIface));
 
         // create STA2: using a later clock
@@ -1547,7 +1554,7 @@
                 true, // chipModeValid
                 TestChipV3.CHIP_MODE_ID, // chipModeId
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta1", // ifaceName
+                "wlan1", // ifaceName
                 TestChipV3.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener2, // destroyedListener
@@ -1555,9 +1562,7 @@
         );
         collector.checkThat("STA 2 interface wasn't created", staIface2, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        inOrderAvail.verify(staAvailListener).onAvailabilityChanged(false);
 
         // request STA3: should fail
         IWifiIface staIface3 = mDut.createStaIface(null, null);
@@ -1568,7 +1573,7 @@
                 true, // chipModeValid
                 TestChipV3.CHIP_MODE_ID, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 TestChipV3.CHIP_MODE_ID, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
@@ -1578,9 +1583,8 @@
         );
         collector.checkThat("NAN interface wasn't created", nanIface, IsNull.notNullValue());
 
-        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
-        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
-        verify(chipMock.chip).removeStaIface("sta1");
+        inOrderAvail.verify(nanAvailListener).onAvailabilityChanged(false);
+        verify(chipMock.chip).removeStaIface("wlan1");
         verify(staDestroyedListener2).onDestroyed(getName(staIface2));
 
         // request STA2: should fail
@@ -1608,8 +1612,7 @@
      */
     @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);
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV3(), TestChipV3.CHIP_MODE_ID);
     }
 
     /**
@@ -1725,7 +1728,7 @@
     }
 
     private void runCreateSingleXxxInterfaceNoInitMode(ChipMockBase chipMock, int ifaceTypeToCreate,
-            String ifaceName, int finalChipMode, int expectedAvailableCalls) throws Exception {
+            String ifaceName, int finalChipMode, boolean multipleIfaceSupport) throws Exception {
         chipMock.initialize();
         mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
                 mManagerStatusListenerMock);
@@ -1737,6 +1740,8 @@
         HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
                 HalDeviceManager.InterfaceAvailableForRequestListener.class);
 
+        InOrder availInOrder = inOrder(iafrl);
+
         IWifiIface iface = validateInterfaceSequence(chipMock,
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
@@ -1748,6 +1753,7 @@
                 iafrl // availableListener
         );
         collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+        availInOrder.verify(iafrl).onAvailabilityChanged(multipleIfaceSupport);
 
         // act: remove interface
         mDut.removeIface(iface);
@@ -1770,7 +1776,9 @@
         }
 
         verify(idl).onDestroyed(ifaceName);
-        verify(iafrl, times(expectedAvailableCalls)).onAvailableForRequest();
+        if (!multipleIfaceSupport) {
+            availInOrder.verify(iafrl).onAvailabilityChanged(true);
+        }
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
     }
@@ -1791,7 +1799,7 @@
      * line of NAN and P2P being exclusive).
      */
     public void runP2pAndNanExclusiveInteractionsTestChip(ChipMockBase chipMock,
-            int staAvailCallbacks, int onlyChipMode) throws Exception {
+            int onlyChipMode) throws Exception {
         chipMock.initialize();
         mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
                 mManagerStatusListenerMock);
@@ -1810,31 +1818,38 @@
 
         InterfaceDestroyedListener p2pDestroyedListener = mock(
                 InterfaceDestroyedListener.class);
-        HalDeviceManager.InterfaceAvailableForRequestListener p2pAvailListener = null;
+
+        InOrder availInOrder = inOrder(staAvailListener, nanAvailListener);
 
         // Request STA
         IWifiIface staIface = validateInterfaceSequence(chipMock,
                 false, // chipModeValid
                 -1000, // chipModeId (only used if chipModeValid is true)
                 IfaceType.STA, // ifaceTypeToCreate
-                "sta0", // ifaceName
+                "wlan0", // ifaceName
                 onlyChipMode, // finalChipMode
                 null, // tearDownList
                 staDestroyedListener, // destroyedListener
                 staAvailListener // availableListener
         );
+        availInOrder.verify(staAvailListener).onAvailabilityChanged(
+                chipMock.chipMockId == CHIP_MOCK_V2 || chipMock.chipMockId == CHIP_MOCK_V3);
 
         // Request NAN
         IWifiIface nanIface = validateInterfaceSequence(chipMock,
                 true, // chipModeValid
                 onlyChipMode, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 onlyChipMode, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
                 nanAvailListener // availableListener
         );
+        if (chipMock.chipMockId == CHIP_MOCK_V3) {
+            availInOrder.verify(staAvailListener).onAvailabilityChanged(false);
+        }
+        availInOrder.verify(nanAvailListener).onAvailabilityChanged(false);
 
         // Request P2P
         IWifiIface p2pIface = validateInterfaceSequence(chipMock,
@@ -1845,7 +1860,7 @@
                 onlyChipMode, // finalChipMode
                 new IWifiIface[]{nanIface}, // tearDownList
                 p2pDestroyedListener, // destroyedListener
-                p2pAvailListener, // availableListener
+                null, // availableListener
                 // destroyedInterfacesDestroyedListeners...
                 new InterfaceDestroyedListenerWithIfaceName(
                         getName(nanIface), nanDestroyedListener)
@@ -1864,24 +1879,26 @@
 
         mTestLooper.dispatchAll();
         verify(p2pDestroyedListener).onDestroyed(getName(p2pIface));
-        verify(nanAvailListener).onAvailableForRequest();
+        if (chipMock.chipMockId == CHIP_MOCK_V3) {
+            availInOrder.verify(staAvailListener).onAvailabilityChanged(true);
+        }
+        availInOrder.verify(nanAvailListener).onAvailabilityChanged(true);
 
         // Request NAN: expect success now
         nanIface = validateInterfaceSequence(chipMock,
                 true, // chipModeValid
                 onlyChipMode, // chipModeId
                 IfaceType.NAN, // ifaceTypeToCreate
-                "nan0", // ifaceName
+                "wlan0", // ifaceName
                 onlyChipMode, // finalChipMode
                 null, // tearDownList
                 nanDestroyedListener, // destroyedListener
                 nanAvailListener // availableListener
         );
-
-        if (staAvailCallbacks != 0) {
-            // if there are duplicate STAs then expect an available callback for each step above
-            verify(staAvailListener, times(staAvailCallbacks)).onAvailableForRequest();
+        if (chipMock.chipMockId == CHIP_MOCK_V3) {
+            availInOrder.verify(staAvailListener).onAvailabilityChanged(false);
         }
+        availInOrder.verify(nanAvailListener).onAvailabilityChanged(false);
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
                 nanDestroyedListener, nanAvailListener, p2pDestroyedListener);
@@ -2285,7 +2302,13 @@
 
     // chip configuration
 
+    private static final int CHIP_MOCK_V1 = 0;
+    private static final int CHIP_MOCK_V2 = 1;
+    private static final int CHIP_MOCK_V3 = 2;
+
     private class ChipMockBase {
+        public int chipMockId;
+
         public IWifiChip chip;
         public int chipId;
         public boolean chipModeValid = false;
@@ -2353,6 +2376,8 @@
         void initialize() throws Exception {
             super.initialize();
 
+            chipMockId = CHIP_MOCK_V1;
+
             // chip Id configuration
             ArrayList<Integer> chipIds;
             chipId = 10;
@@ -2414,6 +2439,8 @@
         void initialize() throws Exception {
             super.initialize();
 
+            chipMockId = CHIP_MOCK_V2;
+
             // chip Id configuration
             ArrayList<Integer> chipIds;
             chipId = 12;
@@ -2472,6 +2499,8 @@
         void initialize() throws Exception {
             super.initialize();
 
+            chipMockId = CHIP_MOCK_V3;
+
             // chip Id configuration
             ArrayList<Integer> chipIds;
             chipId = 15;
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
index 5e45570..afbd87a 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
@@ -19,20 +19,32 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiScanner;
 import android.os.test.TestLooper;
 import android.provider.Settings;
 
+import com.android.server.wifi.util.ScanResultUtil;
+
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 /**
  * Unit tests for {@link WakeupController}.
@@ -41,8 +53,10 @@
 
     @Mock private Context mContext;
     @Mock private WakeupLock mWakeupLock;
-    @Mock private WifiConfigManager mWifiConfigManager;
     @Mock private WifiConfigStore mWifiConfigStore;
+    @Mock private WifiInjector mWifiInjector;
+    @Mock private WifiScanner mWifiScanner;
+    @Mock private WifiConfigManager mWifiConfigManager;
     @Mock private FrameworkFacade mFrameworkFacade;
 
     private TestLooper mLooper;
@@ -52,12 +66,30 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
         mLooper = new TestLooper();
     }
 
-    private WakeupController newWakeupController() {
-        return new WakeupController(mContext, mLooper.getLooper(), mWakeupLock, mWifiConfigManager,
-                mWifiConfigStore, mFrameworkFacade);
+    /** Initializes the wakeupcontroller in the given `enabled` state. */
+    private void initializeWakeupController(boolean enabled) {
+        int settingsValue = enabled ? 1 : 0;
+        when(mFrameworkFacade.getIntegerSetting(mContext,
+                Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(settingsValue);
+        mWakeupController = new WakeupController(mContext,
+                mLooper.getLooper(),
+                mWakeupLock,
+                mWifiConfigManager,
+                mWifiConfigStore,
+                mWifiInjector,
+                mFrameworkFacade);
+    }
+
+    private ScanResult createOpenScanResult(String ssid) {
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = ssid;
+        scanResult.capabilities = "";
+        return scanResult;
     }
 
     /**
@@ -65,9 +97,7 @@
      */
     @Test
     public void verifyEnabledWhenToggledOn() {
-        when(mFrameworkFacade.getIntegerSetting(mContext,
-                Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(1);
-        mWakeupController = newWakeupController();
+        initializeWakeupController(true /* enabled */);
 
         assertTrue(mWakeupController.isEnabled());
     }
@@ -77,9 +107,7 @@
      */
     @Test
     public void verifyDisabledWhenToggledOff() {
-        when(mFrameworkFacade.getIntegerSetting(mContext,
-                Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(0);
-        mWakeupController = newWakeupController();
+        initializeWakeupController(false /* enabled */);
 
         assertFalse(mWakeupController.isEnabled());
     }
@@ -89,7 +117,7 @@
      */
     @Test
     public void registersWakeupConfigStoreData() {
-        mWakeupController = newWakeupController();
+        initializeWakeupController(true /* enabled */);
         verify(mWifiConfigStore).registerStoreData(any(WakeupConfigStoreData.class));
     }
 
@@ -98,11 +126,121 @@
      */
     @Test
     public void dumpIncludesWakeupLock() {
-        mWakeupController = newWakeupController();
+        initializeWakeupController(true /* enabled */);
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         PrintWriter writer = new PrintWriter(stream);
         mWakeupController.dump(null, writer, null);
 
         verify(mWakeupLock).dump(null, writer, null);
     }
+
+    /**
+     * Verify that start initializes the wakeup lock.
+     */
+    @Test
+    public void startInitializesWakeupLock() {
+        initializeWakeupController(true /* enabled */);
+        mWakeupController.start();
+        verify(mWakeupLock).initialize(any());
+    }
+
+    /**
+     * Verify that start does not initialize the wakeup lock when feature is disabled.
+     */
+    @Test
+    public void startDoesNotInitializeWakeupLockWhenDisabled() {
+        initializeWakeupController(false /* enabled */);
+        mWakeupController.start();
+        verify(mWakeupLock, never()).initialize(any());
+    }
+
+    /**
+     * Verify that start does not re-initialize the wakeup lock if the controller is already active.
+     */
+    @Test
+    public void startDoesNotInitializeWakeupLockIfAlreadyActive() {
+        initializeWakeupController(true /* enabled */);
+        InOrder inOrder = Mockito.inOrder(mWakeupLock);
+
+        mWakeupController.start();
+        inOrder.verify(mWakeupLock).initialize(any());
+
+        mWakeupController.stop();
+        mWakeupController.start();
+        inOrder.verify(mWakeupLock, never()).initialize(any());
+    }
+
+    /**
+     * Verify that start registers the scan listener on the wifi scanner.
+     */
+    @Test
+    public void startRegistersScanListener() {
+        initializeWakeupController(true /* enabled */);
+        mWakeupController.start();
+        verify(mWifiScanner).registerScanListener(any());
+    }
+
+    /**
+     * Verify that stop deregisters the scan listener from the wifi scanner.
+     */
+    @Test
+    public void stopDeresgistersScanListener() {
+        initializeWakeupController(true /* enabled */);
+        mWakeupController.start();
+        mWakeupController.stop();
+        verify(mWifiScanner).deregisterScanListener(any());
+    }
+
+    /**
+     * Verify that reset sets active to false.
+     *
+     * <p>This is accomplished by initiating another call to start and verifying that the wakeup
+     * lock is re-initialized.
+     */
+    @Test
+    public void resetSetsActiveToFalse() {
+        initializeWakeupController(true /* enabled */);
+        InOrder inOrder = Mockito.inOrder(mWakeupLock);
+
+        mWakeupController.start();
+        inOrder.verify(mWakeupLock).initialize(any());
+
+        mWakeupController.stop();
+        mWakeupController.reset();
+        mWakeupController.start();
+        inOrder.verify(mWakeupLock).initialize(any());
+    }
+
+    /**
+     * Verify that the wakeup lock is initialized with the intersection of ScanResults and saved
+     * networks.
+     */
+    @Test
+    public void startInitializesWakeupLockWithSavedScanResults() {
+        String ssid1 = "ssid 1";
+        String ssid2 = "ssid 2";
+        String quotedSsid = ScanResultUtil.createQuotedSSID(ssid1);
+
+        // saved configs
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(quotedSsid);
+        openNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
+        WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+        wepNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
+        when(mWifiConfigManager.getSavedNetworks())
+                .thenReturn(Arrays.asList(openNetwork, wepNetwork));
+
+        // scan results from most recent scan
+        ScanResult savedScanResult = createOpenScanResult(ssid1);
+        ScanResult unsavedScanResult = createOpenScanResult(ssid2);
+        when(mWifiScanner.getSingleScanResults())
+                .thenReturn(Arrays.asList(savedScanResult, unsavedScanResult));
+
+        // intersection of most recent scan + saved configs
+        Collection<ScanResultMatchInfo> expectedMatchInfos =
+                Collections.singleton(ScanResultMatchInfo.fromScanResult(savedScanResult));
+
+        initializeWakeupController(true /* enabled */);
+        mWakeupController.start();
+        verify(mWakeupLock).initialize(eq(expectedMatchInfos));
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index a2c989a..5d6b14d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -2880,11 +2880,6 @@
         verifyAddNetworkToWifiConfigManager(network2);
         verifyAddNetworkToWifiConfigManager(network3);
 
-        // Enable all of them.
-        assertTrue(mWifiConfigManager.enableNetwork(network1.networkId, false, TEST_CREATOR_UID));
-        assertTrue(mWifiConfigManager.enableNetwork(network2.networkId, false, TEST_CREATOR_UID));
-        assertTrue(mWifiConfigManager.enableNetwork(network3.networkId, false, TEST_CREATOR_UID));
-
         // Now set scan results in 2 of them to set the corresponding
         // {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} field.
         assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
@@ -2903,15 +2898,6 @@
         assertEquals(network3.SSID, hiddenNetworks.get(0).ssid);
         assertEquals(network1.SSID, hiddenNetworks.get(1).ssid);
         assertEquals(network2.SSID, hiddenNetworks.get(2).ssid);
-
-        // Now permanently disable |network3|. This should remove network 3 from the list.
-        assertTrue(mWifiConfigManager.disableNetwork(network3.networkId, TEST_CREATOR_UID));
-
-        // Retrieve the hidden network list again & verify the order of the networks returned.
-        hiddenNetworks = mWifiConfigManager.retrieveHiddenNetworkList();
-        assertEquals(2, hiddenNetworks.size());
-        assertEquals(network1.SSID, hiddenNetworks.get(0).ssid);
-        assertEquals(network2.SSID, hiddenNetworks.get(1).ssid);
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
new file mode 100644
index 0000000..fe08fe6
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+import android.app.test.MockAnswerUtil;
+import android.net.InterfaceConfiguration;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.net.BaseNetworkObserver;
+import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener;
+import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler;
+import com.android.server.wifi.WifiNative.VendorHalDeathEventHandler;
+import com.android.server.wifi.WifiNative.WificondDeathEventHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for the interface management operations in
+ * {@link com.android.server.wifi.WifiNative}.
+ */
+@SmallTest
+public class WifiNativeInterfaceManagementTest {
+    private static final String IFACE_NAME_0 = "mockWlan0";
+    private static final String IFACE_NAME_1 = "mockWlan1";
+
+    @Mock private WifiVendorHal mWifiVendorHal;
+    @Mock private WificondControl mWificondControl;
+    @Mock private SupplicantStaIfaceHal mSupplicantStaIfaceHal;
+    @Mock private INetworkManagementService mNwManagementService;
+
+    @Mock private WifiNative.StatusListener mStatusListener;
+    @Mock private WifiNative.InterfaceCallback mIfaceCallback0;
+    @Mock private WifiNative.InterfaceCallback mIfaceCallback1;
+
+    private ArgumentCaptor<VendorHalDeathEventHandler> mWifiVendorHalDeathHandlerCaptor =
+            ArgumentCaptor.forClass(VendorHalDeathEventHandler.class);
+    private ArgumentCaptor<WificondDeathEventHandler> mWificondDeathHandlerCaptor =
+            ArgumentCaptor.forClass(WificondDeathEventHandler.class);
+    private ArgumentCaptor<SupplicantDeathEventHandler> mSupplicantDeathHandlerCaptor =
+            ArgumentCaptor.forClass(SupplicantDeathEventHandler.class);
+    private ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor0 =
+            ArgumentCaptor.forClass(BaseNetworkObserver.class);
+    private ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor1 =
+            ArgumentCaptor.forClass(BaseNetworkObserver.class);
+    private ArgumentCaptor<InterfaceDestroyedListener> mIfaceDestroyedListenerCaptor0 =
+            ArgumentCaptor.forClass(InterfaceDestroyedListener.class);
+    private ArgumentCaptor<InterfaceDestroyedListener> mIfaceDestroyedListenerCaptor1 =
+            ArgumentCaptor.forClass(InterfaceDestroyedListener.class);
+
+    private WifiNative mWifiNative;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        // Setup mocks for the positive single interface cases, individual tests can modify the
+        // mocks for negative or multi-interface tests.
+        when(mWifiVendorHal.initialize(mWifiVendorHalDeathHandlerCaptor.capture()))
+            .thenReturn(true);
+        when(mWifiVendorHal.startVendorHal()).thenReturn(true);
+        when(mWifiVendorHal.createStaIface(any())).thenReturn(IFACE_NAME_0);
+        when(mWifiVendorHal.createApIface(any())).thenReturn(IFACE_NAME_0);
+        when(mWifiVendorHal.removeStaIface(any())).thenReturn(true);
+        when(mWifiVendorHal.removeApIface(any())).thenReturn(true);
+
+        when(mWificondControl.registerDeathHandler(mWificondDeathHandlerCaptor.capture()))
+            .thenReturn(true);
+        when(mWificondControl.enableSupplicant()).thenReturn(true);
+        when(mWificondControl.disableSupplicant()).thenReturn(true);
+        when(mWificondControl.setupInterfaceForClientMode(any()))
+            .thenReturn(mock(IClientInterface.class));
+        when(mWificondControl.setupInterfaceForSoftApMode(any()))
+            .thenReturn(mock(IApInterface.class));
+        when(mWificondControl.tearDownClientInterface(any())).thenReturn(true);
+        when(mWificondControl.tearDownSoftApInterface(any())).thenReturn(true);
+        when(mWificondControl.tearDownInterfaces()).thenReturn(true);
+
+        when(mSupplicantStaIfaceHal.registerDeathHandler(mSupplicantDeathHandlerCaptor.capture()))
+            .thenReturn(true);
+        when(mSupplicantStaIfaceHal.deregisterDeathHandler()).thenReturn(true);
+        when(mSupplicantStaIfaceHal.initialize()).thenReturn(true);
+        when(mSupplicantStaIfaceHal.isInitializationStarted()).thenReturn(false);
+        when(mSupplicantStaIfaceHal.isInitializationComplete()).thenReturn(true);
+        when(mSupplicantStaIfaceHal.setupIface(any())).thenReturn(true);
+        when(mSupplicantStaIfaceHal.teardownIface(any())).thenReturn(true);
+
+        mWifiNative = new WifiNative(
+                IFACE_NAME_0, mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl,
+                mNwManagementService);
+        mWifiNative.initialize();
+        mWifiNative.registerStatusListener(mStatusListener);
+
+        verify(mWifiVendorHal).initialize(any());
+        verify(mWificondControl).registerDeathHandler(any());
+    }
+
+    /**
+     * Verifies the setup of a single client interface.
+     */
+    @Test
+    public void testSetupClientInterface() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a single softAp interface.
+     */
+    @Test
+    public void testSetupSoftApInterface() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a single client interface.
+     */
+    @Test
+    public void testSetupAndTeardownClientInterface() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateTeardownClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a single client interface.
+     */
+    @Test
+    public void testSetupAndTeardownSoftApInterface() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateTeardownSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a client & softAp interface.
+     */
+    @Test
+    public void testSetupAndTeardownClientAndSoftApInterface_Seq1() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateSetupSoftApInterface(
+                true, false, IFACE_NAME_1, mIfaceCallback1, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+        executeAndValidateTeardownClientInterface(false, true, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        executeAndValidateTeardownSoftApInterface(false, false, IFACE_NAME_1, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a client & softAp interface.
+     */
+    @Test
+    public void testSetupAndTeardownClientAndSoftApInterface_Seq2() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateSetupSoftApInterface(
+                true, false, IFACE_NAME_1, mIfaceCallback1, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+        executeAndValidateTeardownSoftApInterface(true, false, IFACE_NAME_1, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+        executeAndValidateTeardownClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a client & softAp interface.
+     */
+    @Test
+    public void testSetupAndTeardownClientAndSoftApInterface_Seq3() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateSetupClientInterface(
+                false, true, IFACE_NAME_1, mIfaceCallback1, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+        executeAndValidateTeardownSoftApInterface(true, false, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        executeAndValidateTeardownClientInterface(false, false, IFACE_NAME_1, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup & teardown of a client & softAp interface.
+     */
+    @Test
+    public void testSetupAndTeardownClientAndSoftApInterface_Seq4() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        executeAndValidateSetupClientInterface(
+                false, true, IFACE_NAME_1, mIfaceCallback1, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+        executeAndValidateTeardownClientInterface(false, true, IFACE_NAME_1, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+        executeAndValidateTeardownSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mIfaceDestroyedListenerCaptor0.getValue(), mNetworkObserverCaptor0.getValue());
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and then a SoftAp interface which would
+     * destroy the Client interface. This is what would happen on older devices which do not
+     * support concurrent interfaces.
+     */
+    @Test
+    public void testSetupClientAndSoftApInterfaceCausesClientInterfaceTeardown() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+
+        // Trigger the STA interface teardown when AP interface is created.
+        // The iface name will remain the same.
+        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+            public String answer(InterfaceDestroyedListener destroyedListener) {
+                mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
+                return IFACE_NAME_0;
+            }
+        }).when(mWifiVendorHal).createApIface(any());
+
+        assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1));
+
+        validateOnDestroyedClientInterface(
+                false, true, IFACE_NAME_0, mIfaceCallback0, mNetworkObserverCaptor0.getValue());
+        validateSetupSoftApInterface(
+                true, false, IFACE_NAME_0, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+
+        // Execute a teardown of the interface to ensure that the new iface removal works.
+        executeAndValidateTeardownSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and then a SoftAp interface which would
+     * destroy the Client interface. This is what would happen on older devices which do not
+     * support concurrent interfaces.
+     */
+    @Test
+    public void testSetupSoftApAndClientInterfaceCausesSoftApInterfaceTeardown() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+
+        // Trigger the AP interface teardown when STA interface is created.
+        // The iface name will remain the same.
+        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+            public String answer(InterfaceDestroyedListener destroyedListener) {
+                mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
+                return IFACE_NAME_0;
+            }
+        }).when(mWifiVendorHal).createStaIface(any());
+
+        assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForClientMode(mIfaceCallback1));
+
+        validateOnDestroyedSoftApInterface(
+                true, false, IFACE_NAME_0, mIfaceCallback0, mNetworkObserverCaptor0.getValue());
+        validateSetupClientInterface(
+                false, true, IFACE_NAME_0, mIfaceDestroyedListenerCaptor1,
+                mNetworkObserverCaptor1);
+
+        // Execute a teardown of the interface to ensure that the new iface removal works.
+        executeAndValidateTeardownClientInterface(false, false, IFACE_NAME_0, mIfaceCallback1,
+                mIfaceDestroyedListenerCaptor1.getValue(), mNetworkObserverCaptor1.getValue());
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and trigger an interface down event.
+     */
+    @Test
+    public void testSetupClientInterfaceAndTriggerInterfaceDown() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+
+        mNetworkObserverCaptor0.getValue().interfaceLinkStateChanged(IFACE_NAME_0, false);
+        verify(mIfaceCallback0).onDown(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and trigger an interface up event.
+     */
+    @Test
+    public void testSetupClientInterfaceAndTriggerInterfaceUp() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+
+        mNetworkObserverCaptor0.getValue().interfaceLinkStateChanged(IFACE_NAME_0, true);
+        verify(mIfaceCallback0).onUp(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and wificond death handling.
+     */
+    @Test
+    public void testSetupClientInterfaceAndWicondDied() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        // Trigger wificond death
+        mWificondDeathHandlerCaptor.getValue().onDeath();
+
+        validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mNetworkObserverCaptor0.getValue());
+
+        verify(mStatusListener).onStatusChanged(false);
+        verify(mStatusListener).onStatusChanged(true);
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a soft ap interface and vendor HAL death handling.
+     */
+    @Test
+    public void testSetupSoftApInterfaceAndVendorHalDied() throws Exception {
+        executeAndValidateSetupSoftApInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        // Trigger vendor HAL death
+        mWifiVendorHalDeathHandlerCaptor.getValue().onDeath();
+
+        validateOnDestroyedSoftApInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mNetworkObserverCaptor0.getValue());
+
+        verify(mStatusListener).onStatusChanged(false);
+        verify(mStatusListener).onStatusChanged(true);
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the setup of a client interface and supplicant HAL death handling.
+     */
+    @Test
+    public void testSetupClientInterfaceAndVendorHalDied() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+        // Trigger wificond death
+        mSupplicantDeathHandlerCaptor.getValue().onDeath();
+
+        validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mNetworkObserverCaptor0.getValue());
+
+        verify(mStatusListener).onStatusChanged(false);
+        verify(mStatusListener).onStatusChanged(true);
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInStartHal() throws Exception {
+        when(mWifiVendorHal.startVendorHal()).thenReturn(false);
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInStartSupplicant() throws Exception {
+        when(mWificondControl.enableSupplicant()).thenReturn(false);
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+        inOrder.verify(mWificondControl).enableSupplicant();
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInHalCreateStaIface() throws Exception {
+        when(mWifiVendorHal.createStaIface(any())).thenReturn(null);
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+        inOrder.verify(mWificondControl).enableSupplicant();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted();
+        inOrder.verify(mSupplicantStaIfaceHal).initialize();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete();
+        inOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any());
+        inOrder.verify(mWifiVendorHal).createStaIface(any());
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInWificondSetupInterfaceForClientMode()
+            throws Exception {
+        when(mWificondControl.setupInterfaceForClientMode(any())).thenReturn(null);
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+        inOrder.verify(mWificondControl).enableSupplicant();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted();
+        inOrder.verify(mSupplicantStaIfaceHal).initialize();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete();
+        inOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any());
+        inOrder.verify(mWifiVendorHal).createStaIface(mIfaceDestroyedListenerCaptor0.capture());
+        inOrder.verify(mWificondControl).setupInterfaceForClientMode(any());
+        inOrder.verify(mWifiVendorHal).removeStaIface(any());
+
+        // Trigger the HAL interface destroyed callback to verify the whole removal sequence.
+        mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
+        validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                null);
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInSupplicantSetupIface() throws Exception {
+        when(mSupplicantStaIfaceHal.setupIface(any())).thenReturn(false);
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+        inOrder.verify(mWificondControl).enableSupplicant();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted();
+        inOrder.verify(mSupplicantStaIfaceHal).initialize();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete();
+        inOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any());
+        inOrder.verify(mWifiVendorHal).createStaIface(mIfaceDestroyedListenerCaptor0.capture());
+        inOrder.verify(mWificondControl).setupInterfaceForClientMode(any());
+        inOrder.verify(mSupplicantStaIfaceHal).setupIface(any());
+        inOrder.verify(mWifiVendorHal).removeStaIface(any());
+
+        // Trigger the HAL interface destroyed callback to verify the whole removal sequence.
+        mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
+        validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                null);
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies failure handling in setup of a client interface.
+     */
+    @Test
+    public void testSetupClientInterfaceFailureInNetworkObserverRegister() throws Exception {
+        doThrow(new RemoteException()).when(mNwManagementService).registerObserver(any());
+        assertNull(mWifiNative.setupInterfaceForClientMode(mIfaceCallback0));
+
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+        inOrder.verify(mWifiVendorHal).startVendorHal();
+        inOrder.verify(mWificondControl).enableSupplicant();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted();
+        inOrder.verify(mSupplicantStaIfaceHal).initialize();
+        inOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete();
+        inOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any());
+        inOrder.verify(mWifiVendorHal).createStaIface(mIfaceDestroyedListenerCaptor0.capture());
+        inOrder.verify(mWificondControl).setupInterfaceForClientMode(any());
+        inOrder.verify(mSupplicantStaIfaceHal).setupIface(any());
+        inOrder.verify(mNwManagementService).registerObserver(mNetworkObserverCaptor0.capture());
+        inOrder.verify(mWifiVendorHal).removeStaIface(any());
+
+        // Trigger the HAL interface destroyed callback to verify the whole removal sequence.
+        mIfaceDestroyedListenerCaptor0.getValue().onDestroyed(IFACE_NAME_0);
+        validateOnDestroyedClientInterface(false, false, IFACE_NAME_0, mIfaceCallback0,
+                mNetworkObserverCaptor0.getValue());
+
+        // To test if the failure is handled cleanly, invoke teardown and ensure that
+        // none of the mocks are used because the iface does not exist in the internal
+        // database.
+        mWifiNative.teardownInterface(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    /**
+     * Verifies the interface state query API.
+     */
+    @Test
+    public void testIsInterfaceUp() throws Exception {
+        executeAndValidateSetupClientInterface(
+                false, false, IFACE_NAME_0, mIfaceCallback0, mIfaceDestroyedListenerCaptor0,
+                mNetworkObserverCaptor0);
+
+        InterfaceConfiguration config = new InterfaceConfiguration();
+        when(mNwManagementService.getInterfaceConfig(IFACE_NAME_0)).thenReturn(config);
+
+        config.setInterfaceUp();
+        assertTrue(mWifiNative.isInterfaceUp(IFACE_NAME_0));
+
+        config.setInterfaceDown();
+        assertFalse(mWifiNative.isInterfaceUp(IFACE_NAME_0));
+
+        when(mNwManagementService.getInterfaceConfig(IFACE_NAME_0)).thenReturn(null);
+        assertFalse(mWifiNative.isInterfaceUp(IFACE_NAME_0));
+
+        verify(mNwManagementService, times(3)).getInterfaceConfig(IFACE_NAME_0);
+
+        verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, mIfaceCallback0, mIfaceCallback1);
+    }
+
+    private void executeAndValidateSetupClientInterface(
+            boolean existingStaIface, boolean existingApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            ArgumentCaptor<InterfaceDestroyedListener> destroyedListenerCaptor,
+            ArgumentCaptor<BaseNetworkObserver> networkObserverCaptor) throws Exception {
+        when(mWifiVendorHal.createStaIface(any())).thenReturn(ifaceName);
+        assertEquals(ifaceName, mWifiNative.setupInterfaceForClientMode(callback));
+
+        validateSetupClientInterface(
+                existingStaIface, existingApIface, ifaceName, destroyedListenerCaptor,
+                networkObserverCaptor);
+    }
+
+    private void validateSetupClientInterface(
+            boolean existingStaIface, boolean existingApIface,
+            String ifaceName, ArgumentCaptor<InterfaceDestroyedListener> destroyedListenerCaptor,
+            ArgumentCaptor<BaseNetworkObserver> networkObserverCaptor) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService);
+
+        if (!existingStaIface && !existingApIface) {
+            inOrder.verify(mWifiVendorHal).startVendorHal();
+        }
+        if (!existingStaIface) {
+            inOrder.verify(mWificondControl).enableSupplicant();
+            inOrder.verify(mSupplicantStaIfaceHal).isInitializationStarted();
+            inOrder.verify(mSupplicantStaIfaceHal).initialize();
+            inOrder.verify(mSupplicantStaIfaceHal).isInitializationComplete();
+            inOrder.verify(mSupplicantStaIfaceHal).registerDeathHandler(any());
+        }
+        inOrder.verify(mWifiVendorHal).createStaIface(destroyedListenerCaptor.capture());
+        inOrder.verify(mWificondControl).setupInterfaceForClientMode(ifaceName);
+        inOrder.verify(mSupplicantStaIfaceHal).setupIface(ifaceName);
+        inOrder.verify(mNwManagementService).registerObserver(networkObserverCaptor.capture());
+    }
+
+    private void executeAndValidateTeardownClientInterface(
+            boolean anyOtherStaIface, boolean anyOtherApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            InterfaceDestroyedListener destroyedListener,
+            BaseNetworkObserver networkObserver) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, callback);
+
+        mWifiNative.teardownInterface(ifaceName);
+
+        inOrder.verify(mWifiVendorHal).removeStaIface(ifaceName);
+
+        // Now trigger the HalDeviceManager destroy callback to initiate the rest of the teardown.
+        destroyedListener.onDestroyed(ifaceName);
+
+        validateOnDestroyedClientInterface(
+                anyOtherStaIface, anyOtherApIface, ifaceName, callback, networkObserver);
+    }
+
+    private void validateOnDestroyedClientInterface(
+            boolean anyOtherStaIface, boolean anyOtherApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            BaseNetworkObserver networkObserver) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal,
+                mNwManagementService, callback);
+
+        inOrder.verify(mNwManagementService).unregisterObserver(networkObserver);
+        inOrder.verify(mSupplicantStaIfaceHal).teardownIface(ifaceName);
+        inOrder.verify(mWificondControl).tearDownClientInterface(ifaceName);
+
+        if (!anyOtherStaIface) {
+            inOrder.verify(mSupplicantStaIfaceHal).deregisterDeathHandler();
+            inOrder.verify(mWificondControl).disableSupplicant();
+        }
+        if (!anyOtherStaIface && !anyOtherApIface) {
+            inOrder.verify(mWificondControl).tearDownInterfaces();
+            inOrder.verify(mWifiVendorHal).stopVendorHal();
+        }
+        inOrder.verify(callback).onDestroyed(ifaceName);
+    }
+
+    private void executeAndValidateSetupSoftApInterface(
+            boolean existingStaIface, boolean existingApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            ArgumentCaptor<InterfaceDestroyedListener> destroyedListenerCaptor,
+            ArgumentCaptor<BaseNetworkObserver> networkObserverCaptor) throws Exception {
+        when(mWifiVendorHal.createApIface(any())).thenReturn(ifaceName);
+        assertEquals(ifaceName, mWifiNative.setupInterfaceForSoftApMode(callback));
+
+        validateSetupSoftApInterface(
+                existingStaIface, existingApIface, ifaceName, destroyedListenerCaptor,
+                networkObserverCaptor);
+    }
+
+    private void validateSetupSoftApInterface(
+            boolean existingStaIface, boolean existingApIface,
+            String ifaceName, ArgumentCaptor<InterfaceDestroyedListener> destroyedListenerCaptor,
+            ArgumentCaptor<BaseNetworkObserver> networkObserverCaptor) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mNwManagementService);
+
+        if (!existingStaIface && !existingApIface) {
+            inOrder.verify(mWifiVendorHal).startVendorHal();
+        }
+        inOrder.verify(mWifiVendorHal).createApIface(destroyedListenerCaptor.capture());
+        inOrder.verify(mWificondControl).setupInterfaceForSoftApMode(ifaceName);
+        inOrder.verify(mNwManagementService).registerObserver(networkObserverCaptor.capture());
+    }
+
+    private void executeAndValidateTeardownSoftApInterface(
+            boolean anyOtherStaIface, boolean anyOtherApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            InterfaceDestroyedListener destroyedListener,
+            BaseNetworkObserver networkObserver) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mNwManagementService, callback);
+
+        mWifiNative.teardownInterface(ifaceName);
+
+        inOrder.verify(mWifiVendorHal).removeApIface(ifaceName);
+
+        // Now trigger the HalDeviceManager destroy callback to initiate the rest of the teardown.
+        destroyedListener.onDestroyed(ifaceName);
+
+        validateOnDestroyedSoftApInterface(
+                anyOtherStaIface, anyOtherApIface, ifaceName, callback, networkObserver);
+    }
+
+    private void validateOnDestroyedSoftApInterface(
+            boolean anyOtherStaIface, boolean anyOtherApIface,
+            String ifaceName, @Mock WifiNative.InterfaceCallback callback,
+            BaseNetworkObserver networkObserver) throws Exception {
+        InOrder inOrder = inOrder(mWifiVendorHal, mWificondControl, mNwManagementService, callback);
+
+        inOrder.verify(mNwManagementService).unregisterObserver(networkObserver);
+        inOrder.verify(mWificondControl).stopSoftAp(ifaceName);
+        inOrder.verify(mWificondControl).tearDownSoftApInterface(ifaceName);
+
+        if (!anyOtherStaIface && !anyOtherApIface) {
+            inOrder.verify(mWificondControl).tearDownInterfaces();
+            inOrder.verify(mWifiVendorHal).stopVendorHal();
+        }
+        inOrder.verify(callback).onDestroyed(ifaceName);
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
index 9a79a23..4c2b6f7 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
@@ -28,6 +28,7 @@
 import android.net.wifi.IApInterface;
 import android.net.wifi.IClientInterface;
 import android.net.wifi.WifiConfiguration;
+import android.os.INetworkManagementService;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
 
@@ -148,6 +149,7 @@
     @Mock private WifiVendorHal mWifiVendorHal;
     @Mock private WificondControl mWificondControl;
     @Mock private SupplicantStaIfaceHal mStaIfaceHal;
+    @Mock private INetworkManagementService mNwService;
     private WifiNative mWifiNative;
 
     @Before
@@ -156,8 +158,8 @@
         when(mWifiVendorHal.isVendorHalSupported()).thenReturn(true);
         when(mWifiVendorHal.startVendorHalSta()).thenReturn(true);
         when(mWifiVendorHal.startVendorHalAp()).thenReturn(true);
-        mWifiNative =
-                new WifiNative(WIFI_IFACE_NAME, mWifiVendorHal, mStaIfaceHal, mWificondControl);
+        mWifiNative = new WifiNative(
+                WIFI_IFACE_NAME, mWifiVendorHal, mStaIfaceHal, mWificondControl, mNwService);
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index d0a482e..52281df 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -1177,7 +1177,7 @@
                 IFACE_IP_MODE_LOCAL_ONLY);
 
         mLooper.dispatchAll();
-        verifyNoMoreInteractions(mHandler);
+        verify(mHandler, never()).handleMessage(any(Message.class));
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 5208c74..7e2a17b 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -2766,4 +2766,38 @@
         mWsm.dump(null, writer, null);
         verify(mWakeupController).dump(null, writer, null);
     }
+
+    /**
+     * Verify that entering and exiting scan mode state correctly triggers Wakeup lifecycle events.
+     */
+    @Test
+    public void verifyScanModeStateTransitionsTriggerWakeupLifecycleEvents() throws Exception {
+        loadComponentsInStaMode();
+
+        // enter scan mode
+        mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
+        mLooper.dispatchAll();
+
+        // exit scan mode
+        mWsm.setOperationalMode(WifiStateMachine.DISABLED_MODE);
+        mLooper.dispatchAll();
+
+        InOrder inOrder = inOrder(mWakeupController);
+        inOrder.verify(mWakeupController).start();
+        inOrder.verify(mWakeupController).stop();
+    }
+
+    /**
+     * Verify that entering connect mode state resets WakeupController events.
+     */
+    @Test
+    public void verifyConnectModeStateTransitionsTriggerWakeupLifecycleEvents() throws Exception {
+        loadComponentsInStaMode();
+
+        //  enter connect mode
+        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+        mLooper.dispatchAll();
+
+        verify(mWakeupController).reset();
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 812ef03..9830b91 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -88,6 +88,7 @@
 import android.util.Pair;
 
 import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener;
 import com.android.server.wifi.util.NativeUtil;
 
 import org.junit.Before;
@@ -191,9 +192,9 @@
                 mHalDeviceManagerStatusCallbacks.onStatusChanged();
             }
         }).when(mHalDeviceManager).stop();
-        when(mHalDeviceManager.createStaIface(eq(null), eq(null)))
+        when(mHalDeviceManager.createStaIface(any(), eq(null)))
                 .thenReturn(mIWifiStaIface);
-        when(mHalDeviceManager.createApIface(eq(null), eq(null)))
+        when(mHalDeviceManager.createApIface(any(), eq(null)))
                 .thenReturn(mIWifiApIface);
         when(mHalDeviceManager.removeIface(any())).thenReturn(true);
         when(mHalDeviceManager.getChip(any(IWifiIface.class)))
@@ -255,7 +256,7 @@
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).getChip(eq(mIWifiStaIface));
         verify(mHalDeviceManager).createRttController();
         verify(mHalDeviceManager).isReady();
@@ -263,7 +264,7 @@
         verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
         verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
     }
 
     /**
@@ -276,12 +277,12 @@
         assertTrue(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createApIface(any(), eq(null));
         verify(mHalDeviceManager).getChip(eq(mIWifiApIface));
         verify(mHalDeviceManager).isReady();
         verify(mHalDeviceManager).isStarted();
 
-        verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createStaIface(any(), eq(null));
         verify(mHalDeviceManager, never()).createRttController();
     }
 
@@ -303,8 +304,8 @@
 
         verify(mHalDeviceManager).start();
 
-        verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createStaIface(any(), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
         verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
         verify(mHalDeviceManager, never()).createRttController();
         verify(mIWifiStaIface, never())
@@ -317,15 +318,15 @@
      */
     @Test
     public void testStartHalFailureInIfaceCreationInStaMode() throws Exception {
-        when(mHalDeviceManager.createStaIface(eq(null), eq(null))).thenReturn(null);
+        when(mHalDeviceManager.createStaIface(any(), eq(null))).thenReturn(null);
         assertFalse(mWifiVendorHal.startVendorHalSta());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).stop();
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
         verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
         verify(mHalDeviceManager, never()).createRttController();
         verify(mIWifiStaIface, never())
@@ -343,12 +344,12 @@
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).createRttController();
         verify(mHalDeviceManager).stop();
         verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
         verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
     }
 
@@ -363,13 +364,13 @@
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).createRttController();
         verify(mHalDeviceManager).getChip(any(IWifiIface.class));
         verify(mHalDeviceManager).stop();
         verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
     }
 
     /**
@@ -384,13 +385,13 @@
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).stop();
         verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
 
         verify(mHalDeviceManager, never()).createRttController();
         verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
     }
 
     /**
@@ -405,14 +406,14 @@
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).createRttController();
         verify(mHalDeviceManager).getChip(any(IWifiIface.class));
         verify(mHalDeviceManager).stop();
         verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
         verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
     }
 
     /**
@@ -421,15 +422,15 @@
      */
     @Test
     public void testStartHalFailureInApMode() throws Exception {
-        when(mHalDeviceManager.createApIface(eq(null), eq(null))).thenReturn(null);
+        when(mHalDeviceManager.createApIface(any(), eq(null))).thenReturn(null);
         assertFalse(mWifiVendorHal.startVendorHalAp());
         assertFalse(mWifiVendorHal.isHalStarted());
 
         verify(mHalDeviceManager).start();
-        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createApIface(any(), eq(null));
         verify(mHalDeviceManager).stop();
 
-        verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createStaIface(any(), eq(null));
         verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
         verify(mHalDeviceManager, never()).createRttController();
     }
@@ -448,13 +449,13 @@
 
         verify(mHalDeviceManager).start();
         verify(mHalDeviceManager).stop();
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         verify(mHalDeviceManager).getChip(eq(mIWifiStaIface));
         verify(mHalDeviceManager).createRttController();
         verify(mHalDeviceManager, times(2)).isReady();
         verify(mHalDeviceManager, times(2)).isStarted();
 
-        verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createApIface(any(), eq(null));
     }
 
     /**
@@ -471,16 +472,78 @@
 
         verify(mHalDeviceManager).start();
         verify(mHalDeviceManager).stop();
-        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createApIface(any(), eq(null));
         verify(mHalDeviceManager).getChip(eq(mIWifiApIface));
         verify(mHalDeviceManager, times(2)).isReady();
         verify(mHalDeviceManager, times(2)).isStarted();
 
-        verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager, never()).createStaIface(any(), eq(null));
         verify(mHalDeviceManager, never()).createRttController();
     }
 
     /**
+     * Tests the handling of interface destroyed callback from HalDeviceManager.
+     */
+    @Test
+    public void testStaInterfaceDestroyedHandling() throws  Exception {
+        ArgumentCaptor<InterfaceDestroyedListener> internalListenerCaptor =
+                ArgumentCaptor.forClass(InterfaceDestroyedListener.class);
+        InterfaceDestroyedListener externalLister = mock(InterfaceDestroyedListener.class);
+
+        assertTrue(mWifiVendorHal.startVendorHal());
+        assertNotNull(mWifiVendorHal.createStaIface(externalLister));
+        assertTrue(mWifiVendorHal.isHalStarted());
+
+        verify(mHalDeviceManager).start();
+        verify(mHalDeviceManager).createStaIface(internalListenerCaptor.capture(), eq(null));
+        verify(mHalDeviceManager).getChip(eq(mIWifiStaIface));
+        verify(mHalDeviceManager).createRttController();
+        verify(mHalDeviceManager).isReady();
+        verify(mHalDeviceManager).isStarted();
+        verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+        verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
+
+        // Now trigger the interface destroyed callback from HalDeviceManager and ensure the
+        // external listener is invoked and iface removed from internal database.
+        internalListenerCaptor.getValue().onDestroyed(TEST_IFACE_NAME);
+        verify(externalLister).onDestroyed(TEST_IFACE_NAME);
+
+        // This should fail now, since the interface was already destroyed.
+        assertFalse(mWifiVendorHal.removeStaIface(TEST_IFACE_NAME));
+        verify(mHalDeviceManager, never()).removeIface(any());
+    }
+
+    /**
+     * Tests the handling of interface destroyed callback from HalDeviceManager.
+     */
+    @Test
+    public void testApInterfaceDestroyedHandling() throws  Exception {
+        ArgumentCaptor<InterfaceDestroyedListener> internalListenerCaptor =
+                ArgumentCaptor.forClass(InterfaceDestroyedListener.class);
+        InterfaceDestroyedListener externalLister = mock(InterfaceDestroyedListener.class);
+
+        assertTrue(mWifiVendorHal.startVendorHal());
+        assertNotNull(mWifiVendorHal.createApIface(externalLister));
+        assertTrue(mWifiVendorHal.isHalStarted());
+
+        verify(mHalDeviceManager).start();
+        verify(mHalDeviceManager).createApIface(internalListenerCaptor.capture(), eq(null));
+        verify(mHalDeviceManager).getChip(eq(mIWifiApIface));
+        verify(mHalDeviceManager).isReady();
+        verify(mHalDeviceManager).isStarted();
+        verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
+
+        // Now trigger the interface destroyed callback from HalDeviceManager and ensure the
+        // external listener is invoked and iface removed from internal database.
+        internalListenerCaptor.getValue().onDestroyed(TEST_IFACE_NAME);
+        verify(externalLister).onDestroyed(TEST_IFACE_NAME);
+
+        // This should fail now, since the interface was already destroyed.
+        assertFalse(mWifiVendorHal.removeApIface(TEST_IFACE_NAME));
+        verify(mHalDeviceManager, never()).removeIface(any());
+    }
+
+    /**
      * Test that enter logs when verbose logging is enabled
      */
     @Test
@@ -1957,7 +2020,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNull(mWifiVendorHal.createStaIface(null));
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
     }
 
     /**
@@ -1974,7 +2037,7 @@
 
         assertTrue(mWifiVendorHal.startVendorHal());
         assertNull(mWifiVendorHal.createApIface(null));
-        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createApIface(any(), eq(null));
     }
 
     /**
@@ -1984,7 +2047,7 @@
     public void testCreateRemoveStaIface() throws RemoteException {
         assertTrue(mWifiVendorHal.startVendorHal());
         String ifaceName = mWifiVendorHal.createStaIface(null);
-        verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createStaIface(any(), eq(null));
         assertEquals(TEST_IFACE_NAME, ifaceName);
         assertTrue(mWifiVendorHal.removeStaIface(ifaceName));
         verify(mHalDeviceManager).removeIface(eq(mIWifiStaIface));
@@ -1997,7 +2060,7 @@
     public void testCreateRemoveApIface() throws RemoteException {
         assertTrue(mWifiVendorHal.startVendorHal());
         String ifaceName = mWifiVendorHal.createApIface(null);
-        verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+        verify(mHalDeviceManager).createApIface(any(), eq(null));
         assertEquals(TEST_IFACE_NAME, ifaceName);
         assertTrue(mWifiVendorHal.removeApIface(ifaceName));
         verify(mHalDeviceManager).removeIface(eq(mIWifiApIface));
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
index 122d6e6..56515ee 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
@@ -16,11 +16,12 @@
 
 package com.android.server.wifi.aware;
 
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
@@ -78,86 +79,165 @@
         mDut.start(mHandlerMock);
 
         mInOrder = inOrder(mWifiAwareStateManagerMock, mHalDeviceManager);
+
+        // validate (and capture) that register manage status callback
+        mInOrder.verify(mHalDeviceManager).initialize();
+        mInOrder.verify(mHalDeviceManager).registerStatusListener(
+                mManagerStatusListenerCaptor.capture(), any());
     }
 
     /**
-     * Test the control flow of the manager:
+     * Test the control flow of the manager when Aware isn't being actively used:
+     *
      * 1. onStatusChange (ready/started)
-     * 2. null NAN iface
-     * 3. onAvailableForRequest
-     * 4. non-null NAN iface -> enableUsage
-     * 5. onStatusChange (!started) -> disableUsage
-     * 6. onStatusChange (ready/started)
-     * 7. non-null NAN iface -> enableUsage
-     * 8. onDestroyed -> disableUsage
-     * 9. onStatusChange (!started)
+     * 2. on available -> enableUsage
+     * 3. onStatusChange (!started) -> disableUsage
+     * 4. onStatusChange (ready/started) + available -> enableUsage
+     * 5. on not available -> disableUsage
+     *
+     * --> no interface creation at any point!
      */
     @Test
-    public void testControlFlow() {
-        // configure HalDeviceManager as ready/wifi started
+    public void testControlFlowWithoutInterface() {
+        // configure HalDeviceManager as ready/wifi started (and to return an interface if
+        // requested)
         when(mHalDeviceManager.isReady()).thenReturn(true);
         when(mHalDeviceManager.isStarted()).thenReturn(true);
+        when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(mWifiNanIfaceMock);
 
-        // validate (and capture) that register manage status callback
-        mInOrder.verify(mHalDeviceManager).registerStatusListener(
-                mManagerStatusListenerCaptor.capture(), any());
-
-        // 1 & 2 onStatusChange (ready/started): validate that trying to get a NAN interface
-        // (make sure gets a NULL)
-        when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(null);
-
+        // 1. onStatusChange (ready/started)
         mManagerStatusListenerCaptor.getValue().onStatusChanged();
         mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
                 eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any(Handler.class));
-        mAvailListenerCaptor.getValue().onAvailableForRequest();
 
-        mInOrder.verify(mHalDeviceManager).createNanIface(
-                mDestroyedListenerCaptor.capture(), any());
-        collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
-
-        // 3 & 4 onAvailableForRequest + non-null return value: validate that enables usage
-        when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(mWifiNanIfaceMock);
-
-        mAvailListenerCaptor.getValue().onAvailableForRequest();
-
-        mInOrder.verify(mHalDeviceManager).createNanIface(
-                mDestroyedListenerCaptor.capture(), any());
+        // 2. NAN is available -> enableUsage
+        mAvailListenerCaptor.getValue().onAvailabilityChanged(true);
         mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
-        collector.checkThat("non-null interface", mDut.getWifiNanIface(),
-                equalTo(mWifiNanIfaceMock));
 
-        // 5 onStatusChange (!started): disable usage
+        // 3. onStatusChange (not ready) -> disableUsage
         when(mHalDeviceManager.isStarted()).thenReturn(false);
         mManagerStatusListenerCaptor.getValue().onStatusChanged();
 
         mInOrder.verify(mWifiAwareStateManagerMock).disableUsage();
-        collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
 
-        // 6 & 7 onStatusChange (ready/started) + non-null NAN interface: enable usage
+        // 4. onStatusChange (ready/started) + available -> enableUsage
         when(mHalDeviceManager.isStarted()).thenReturn(true);
         mManagerStatusListenerCaptor.getValue().onStatusChanged();
 
         mManagerStatusListenerCaptor.getValue().onStatusChanged();
         mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
                 eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any(Handler.class));
-        mAvailListenerCaptor.getValue().onAvailableForRequest();
+        mAvailListenerCaptor.getValue().onAvailabilityChanged(true);
 
-        mInOrder.verify(mHalDeviceManager).createNanIface(
-                mDestroyedListenerCaptor.capture(), any());
         mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
-        collector.checkThat("non-null interface", mDut.getWifiNanIface(),
-                equalTo(mWifiNanIfaceMock));
 
-        // 8 onDestroyed: disable usage
-        mDestroyedListenerCaptor.getValue().onDestroyed(new String());
+        // 5. not available -> disableUsage
+        mAvailListenerCaptor.getValue().onAvailabilityChanged(false);
 
         mInOrder.verify(mWifiAwareStateManagerMock).disableUsage();
-        collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
 
-        // 9 onStatusChange (!started): nothing more happens
-        when(mHalDeviceManager.isStarted()).thenReturn(false);
+        mInOrder.verify(mHalDeviceManager, never()).createNanIface(any(), any());
+        verifyNoMoreInteractions(mWifiAwareStateManagerMock);
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
+    }
+
+    /**
+     * Test the control flow (and reference counting) of the manager when Aware is actively used and
+     * reference counted (i.e. irregular requests/releases).
+     */
+    @Test
+    public void testReferenceCounting() {
+        // configure HalDeviceManager as ready/wifi started (and to return an interface if
+        // requested)
+        when(mHalDeviceManager.isReady()).thenReturn(true);
+        when(mHalDeviceManager.isStarted()).thenReturn(true);
+        when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(mWifiNanIfaceMock);
+
+        // 1. onStatusChange (ready/started)
         mManagerStatusListenerCaptor.getValue().onStatusChanged();
+        mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
+                eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any(Handler.class));
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
 
+        mAvailListenerCaptor.getValue().onAvailabilityChanged(true);
+        mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
+
+        // 2. request (interface obtained)
+        mDut.tryToGetAware();
+        mInOrder.verify(mHalDeviceManager).createNanIface(mDestroyedListenerCaptor.capture(),
+                any());
+        assertEquals("Interface mismatch", mWifiNanIfaceMock, mDut.getWifiNanIface());
+
+        // 3. release (interface released)
+        mDut.releaseAware();
+        mInOrder.verify(mHalDeviceManager).removeIface(mWifiNanIfaceMock);
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
+
+        mDestroyedListenerCaptor.getValue().onDestroyed("nan0");
+
+        // 4. request (interface obtained)
+        mDut.tryToGetAware();
+        mInOrder.verify(mHalDeviceManager).createNanIface(mDestroyedListenerCaptor.capture(),
+                any());
+        assertEquals("Interface mismatch", mWifiNanIfaceMock, mDut.getWifiNanIface());
+
+        // 5. request (nop - already have interface)
+        mDut.tryToGetAware();
+        assertEquals("Interface mismatch", mWifiNanIfaceMock, mDut.getWifiNanIface());
+
+        // 6. release (nop - reference counting requests)
+        mDut.releaseAware();
+        assertEquals("Interface mismatch", mWifiNanIfaceMock, mDut.getWifiNanIface());
+
+        // 7. release (interface released)
+        mDut.releaseAware();
+        mInOrder.verify(mHalDeviceManager).removeIface(mWifiNanIfaceMock);
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
+
+        mDestroyedListenerCaptor.getValue().onDestroyed("nan0");
+
+        mInOrder.verify(mHalDeviceManager, never()).createNanIface(any(), any());
+        mInOrder.verify(mHalDeviceManager, never()).removeIface(any());
+        verifyNoMoreInteractions(mWifiAwareStateManagerMock);
+    }
+
+    /**
+     * Test the control flow when the interface gets deleted due to external
+     */
+    @Test
+    public void testRequestFlowWithAsyncDeletes() {
+        // configure HalDeviceManager as ready/wifi started (and to return an interface if
+        // requested)
+        when(mHalDeviceManager.isReady()).thenReturn(true);
+        when(mHalDeviceManager.isStarted()).thenReturn(true);
+        when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(mWifiNanIfaceMock);
+
+        // 1. onStatusChange (ready/started)
+        mManagerStatusListenerCaptor.getValue().onStatusChanged();
+        mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
+                eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any(Handler.class));
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
+
+        mAvailListenerCaptor.getValue().onAvailabilityChanged(true);
+        mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
+
+        // 2. request (interface obtained)
+        mDut.tryToGetAware();
+        mInOrder.verify(mHalDeviceManager).createNanIface(mDestroyedListenerCaptor.capture(),
+                any());
+        assertEquals("Interface mismatch", mWifiNanIfaceMock, mDut.getWifiNanIface());
+
+        // 3. interface gets destroyed
+        mDestroyedListenerCaptor.getValue().onDestroyed("nan0");
+
+        mInOrder.verify(mWifiAwareStateManagerMock).disableUsage();
+        assertNull("Interface non-null!", mDut.getWifiNanIface());
+
+        // 4. a release doesn't do much
+        mDut.releaseAware();
+
+        mInOrder.verify(mHalDeviceManager, never()).createNanIface(any(), any());
+        mInOrder.verify(mHalDeviceManager, never()).removeIface(any());
         verifyNoMoreInteractions(mWifiAwareStateManagerMock);
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
index fb11789..765d868 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
@@ -3069,13 +3069,16 @@
 
         mDut.enableUsage();
         mMockLooper.dispatchAll();
+        inOrder.verify(mMockNativeManager).tryToGetAware();
         inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
         mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
         mMockLooper.dispatchAll();
+        inOrder.verify(mMockNativeManager).releaseAware();
 
         // (1) connect
         mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
         mMockLooper.dispatchAll();
+        inOrder.verify(mMockNativeManager).tryToGetAware();
         inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
                 eq(configRequest), eq(false), eq(true), eq(true), eq(false));
         mDut.onConfigSuccessResponse(transactionId.getValue());