Merge "WifiConfig corresponding to a PasspointConfiguration is not shared by default"
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index 2c16444..31124d5 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -79,6 +79,17 @@
                 contentObserver);
     }
 
+    /**
+     * Helper method for classes to unregister a ContentObserver
+     * {@see ContentResolver#unregisterContentObserver(ContentObserver)}.
+     *
+     * @param context
+     * @param contentObserver
+     */
+    public void unregisterContentObserver(Context context, ContentObserver contentObserver) {
+        context.getContentResolver().unregisterContentObserver(contentObserver);
+    }
+
     public IBinder getService(String serviceName) {
         return ServiceManager.getService(serviceName);
     }
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index ffc7113..d65f31f 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -1485,7 +1485,7 @@
             for (int type: IFACE_TYPES_BY_PRIORITY) {
                 if (chipInfo.ifaces[type].length != 0) {
                     if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType,
-                            chipInfo.ifaces[ifaceType].length != 0)) {
+                            chipInfo.ifaces)) {
                         if (DBG) {
                             Log.d(TAG, "Couldn't delete existing type " + type
                                     + " interfaces for requested type");
@@ -1515,8 +1515,7 @@
             }
 
             if (tooManyInterfaces > 0) { // may need to delete some
-                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType,
-                        chipInfo.ifaces[ifaceType].length != 0)) {
+                if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType, chipInfo.ifaces)) {
                     if (DBG) {
                         Log.d(TAG, "Would need to delete some higher priority interfaces");
                     }
@@ -1592,37 +1591,48 @@
      * Returns true if we're allowed to delete the existing interface type for the requested
      * interface type.
      *
-     * Rules:
-     * 1. Request for AP or STA will destroy any other interface (except see #4 and #5)
-     * 2. Request for P2P will destroy NAN-only
-     * 3. Request for NAN will not destroy any interface
-     * --
-     * 4. No interface will be destroyed for a requested interface of the same type
-     * 5. No interface will be destroyed if one of the requested interfaces already exists
+     * Rules - applies in order:
+     *
+     * General rules:
+     * 1. No interface will be destroyed for a requested interface of the same type
+     * 2. No interface will be destroyed if one of the requested interfaces already exists
+     * 3. If there are >1 interface of an existing type, then it is ok to destroy that type
+     *    interface
+     *
+     * Type-specific rules (but note that the general rules are appied first):
+     * 4. Request for AP or STA will destroy any other interface
+     * 5. Request for P2P will destroy NAN-only (but will destroy a second STA per #3)
+     * 6. Request for NAN will not destroy any interface (but will destroy a second STA per #3)
+
      */
     private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
-            int requestedIfaceType, boolean requestedIfaceTypeAlreadyExists) {
-        // rule 5
-        if (requestedIfaceTypeAlreadyExists) {
-            return false;
-        }
-
-        // rule 4
+            int requestedIfaceType, WifiIfaceInfo[][] currentIfaces) {
+        // rule 1
         if (existingIfaceType == requestedIfaceType) {
             return false;
         }
 
+        // rule 2
+        if (currentIfaces[requestedIfaceType].length != 0) {
+            return false;
+        }
+
         // rule 3
+        if (currentIfaces[existingIfaceType].length > 1) {
+            return true;
+        }
+
+        // rule 6
         if (requestedIfaceType == IfaceType.NAN) {
             return false;
         }
 
-        // rule 2
+        // rule 5
         if (requestedIfaceType == IfaceType.P2P) {
             return existingIfaceType == IfaceType.NAN;
         }
 
-        // rule 1, the requestIfaceType is either AP or STA
+        // rule 4, the requestIfaceType is either AP or STA
         return true;
     }
 
diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
index 59d4ab2..97f6b6f 100644
--- a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
+++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
@@ -25,8 +25,6 @@
 import com.android.internal.R;
 import com.android.server.wifi.util.TelephonyUtil;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -242,96 +240,83 @@
 
         for (ScanDetail scanDetail : scanDetails) {
             ScanResult scanResult = scanDetail.getScanResult();
-            int highestScoreOfScanResult = Integer.MIN_VALUE;
-            int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;
 
             // One ScanResult can be associated with more than one networks, hence we calculate all
             // the scores and use the highest one as the ScanResult's score.
-            List<WifiConfiguration> associatedConfigurations = null;
-            WifiConfiguration associatedConfiguration =
+            WifiConfiguration network =
                     mWifiConfigManager.getConfiguredNetworkForScanDetailAndCache(scanDetail);
 
-            if (associatedConfiguration == null) {
+            if (network == null) {
                 continue;
-            } else {
-                associatedConfigurations =
-                    new ArrayList<>(Arrays.asList(associatedConfiguration));
             }
 
-            for (WifiConfiguration network : associatedConfigurations) {
-                /**
-                 * Ignore Passpoint and Ephemeral networks. They are configured networks,
-                 * but without being persisted to the storage. They are evaluated by
-                 * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
-                 * respectively.
-                 */
-                if (network.isPasspoint() || network.isEphemeral()) {
-                    continue;
-                }
+            /**
+             * Ignore Passpoint and Ephemeral networks. They are configured networks,
+             * but without being persisted to the storage. They are evaluated by
+             * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
+             * respectively.
+             */
+            if (network.isPasspoint() || network.isEphemeral()) {
+                continue;
+            }
 
-                WifiConfiguration.NetworkSelectionStatus status =
-                        network.getNetworkSelectionStatus();
-                status.setSeenInLastQualifiedNetworkSelection(true);
+            WifiConfiguration.NetworkSelectionStatus status =
+                    network.getNetworkSelectionStatus();
+            status.setSeenInLastQualifiedNetworkSelection(true);
 
-                if (!status.isNetworkEnabled()) {
-                    continue;
-                } else if (network.BSSID != null &&  !network.BSSID.equals("any")
-                        && !network.BSSID.equals(scanResult.BSSID)) {
-                    // App has specified the only BSSID to connect for this
-                    // configuration. So only the matching ScanResult can be a candidate.
-                    localLog("Network " + WifiNetworkSelector.toNetworkString(network)
-                            + " has specified BSSID " + network.BSSID + ". Skip "
-                            + scanResult.BSSID);
-                    continue;
-                } else if (TelephonyUtil.isSimConfig(network)
-                        && !mWifiConfigManager.isSimPresent()) {
-                    // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
-                    continue;
-                }
+            if (!status.isNetworkEnabled()) {
+                continue;
+            } else if (network.BSSID != null &&  !network.BSSID.equals("any")
+                    && !network.BSSID.equals(scanResult.BSSID)) {
+                // App has specified the only BSSID to connect for this
+                // configuration. So only the matching ScanResult can be a candidate.
+                localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+                        + " has specified BSSID " + network.BSSID + ". Skip "
+                        + scanResult.BSSID);
+                continue;
+            } else if (TelephonyUtil.isSimConfig(network)
+                    && !mWifiConfigManager.isSimPresent()) {
+                // Don't select if security type is EAP SIM/AKA/AKA' when SIM is not present.
+                continue;
+            }
 
-                int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
-                        scoreHistory);
+            int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
+                    scoreHistory);
 
-                // Set candidate ScanResult for all saved networks to ensure that users can
-                // override network selection. See WifiNetworkSelector#setUserConnectChoice.
-                // TODO(b/36067705): consider alternative designs to push filtering/selecting of
-                // user connect choice networks to RecommendedNetworkEvaluator.
-                if (score > status.getCandidateScore() || (score == status.getCandidateScore()
-                        && status.getCandidate() != null
-                        && scanResult.level > status.getCandidate().level)) {
-                    mWifiConfigManager.setNetworkCandidateScanResult(
-                            network.networkId, scanResult, score);
-                }
+            // Set candidate ScanResult for all saved networks to ensure that users can
+            // override network selection. See WifiNetworkSelector#setUserConnectChoice.
+            // TODO(b/36067705): consider alternative designs to push filtering/selecting of
+            // user connect choice networks to RecommendedNetworkEvaluator.
+            if (score > status.getCandidateScore() || (score == status.getCandidateScore()
+                    && status.getCandidate() != null
+                    && scanResult.level > status.getCandidate().level)) {
+                mWifiConfigManager.setNetworkCandidateScanResult(
+                        network.networkId, scanResult, score);
+            }
 
-                // If the network is marked to use external scores, or is an open network with
-                // curate saved open networks enabled, do not consider it for network selection.
-                if (network.useExternalScores) {
-                    localLog("Network " + WifiNetworkSelector.toNetworkString(network)
-                            + " has external score.");
-                    continue;
-                }
-
-                if (score > highestScoreOfScanResult) {
-                    highestScoreOfScanResult = score;
-                    candidateIdOfScanResult = network.networkId;
-                }
+            // If the network is marked to use external scores, or is an open network with
+            // curate saved open networks enabled, do not consider it for network selection.
+            if (network.useExternalScores) {
+                localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+                        + " has external score.");
+                continue;
             }
 
             if (connectableNetworks != null) {
                 connectableNetworks.add(Pair.create(scanDetail,
-                        mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult)));
+                        mWifiConfigManager.getConfiguredNetwork(network.networkId)));
             }
 
-            if (highestScoreOfScanResult > highestScore
-                    || (highestScoreOfScanResult == highestScore
+            if (score > highestScore
+                    || (score == highestScore
                     && scanResultCandidate != null
                     && scanResult.level > scanResultCandidate.level)) {
-                highestScore = highestScoreOfScanResult;
+                highestScore = score;
                 scanResultCandidate = scanResult;
                 mWifiConfigManager.setNetworkCandidateScanResult(
-                        candidateIdOfScanResult, scanResultCandidate, highestScore);
+                        network.networkId, scanResultCandidate, highestScore);
                 // Reload the network config with the updated info.
-                candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult);
+                candidate = mWifiConfigManager.getConfiguredNetwork(network.networkId);
             }
         }
 
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 429e3c3..6d5fd27 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -23,21 +23,28 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.net.InterfaceConfiguration;
 import android.net.wifi.IApInterface;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
+import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.wifi.WifiNative.SoftApListener;
 import com.android.server.wifi.util.ApConfigUtil;
@@ -51,8 +58,15 @@
 public class SoftApManager implements ActiveModeManager {
     private static final String TAG = "SoftApManager";
 
-    private final Context mContext;
+    // Minimum limit to use for timeout delay if the value from overlay setting is too small.
+    private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000;  // 10 minutes
 
+    @VisibleForTesting
+    public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
+            + " Soft AP Send Message Timeout";
+
+    private final Context mContext;
+    private final FrameworkFacade mFrameworkFacade;
     private final WifiNative mWifiNative;
 
     private final String mCountryCode;
@@ -61,8 +75,8 @@
 
     private final Listener mListener;
 
-    private final IApInterface mApInterface;
-    private final String mApInterfaceName;
+    private IApInterface mApInterface;
+    private String mApInterfaceName;
 
     private final INetworkManagementService mNwService;
     private final WifiApConfigStore mWifiApConfigStore;
@@ -72,8 +86,9 @@
     private final int mMode;
     private WifiConfiguration mApConfig;
 
-    private int mNumAssociatedStations = 0;
-
+    /**
+     * Listener for soft AP events.
+     */
     private final SoftApListener mSoftApListener = new SoftApListener() {
         @Override
         public void onNumAssociatedStationsChanged(int numStations) {
@@ -82,7 +97,6 @@
         }
     };
 
-
     /**
      * Listener for soft AP state changes.
      */
@@ -97,23 +111,19 @@
 
     public SoftApManager(Context context,
                          Looper looper,
+                         FrameworkFacade framework,
                          WifiNative wifiNative,
                          String countryCode,
                          Listener listener,
-                         @NonNull IApInterface apInterface,
-                         @NonNull String ifaceName,
                          INetworkManagementService nms,
                          WifiApConfigStore wifiApConfigStore,
                          @NonNull SoftApModeConfiguration apConfig,
                          WifiMetrics wifiMetrics) {
-        mStateMachine = new SoftApStateMachine(looper);
-
         mContext = context;
+        mFrameworkFacade = framework;
         mWifiNative = wifiNative;
         mCountryCode = countryCode;
         mListener = listener;
-        mApInterface = apInterface;
-        mApInterfaceName = ifaceName;
         mNwService = nms;
         mWifiApConfigStore = wifiApConfigStore;
         mMode = apConfig.getTargetMode();
@@ -124,6 +134,7 @@
             mApConfig = config;
         }
         mWifiMetrics = wifiMetrics;
+        mStateMachine = new SoftApStateMachine(looper);
     }
 
     /**
@@ -141,29 +152,6 @@
     }
 
     /**
-     * Get number of stations associated with this soft AP
-     */
-    @VisibleForTesting
-    public int getNumAssociatedStations() {
-        return mNumAssociatedStations;
-    }
-
-    /**
-     * Set number of stations associated with this soft AP
-     * @param numStations Number of connected stations
-     */
-    private void setNumAssociatedStations(int numStations) {
-        if (mNumAssociatedStations == numStations) {
-            return;
-        }
-        mNumAssociatedStations = numStations;
-        Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
-
-        // TODO:(b/63906412) send it up to settings.
-        mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations, mMode);
-    }
-
-    /**
      * Update AP state.
      * @param newState new AP state
      * @param currentState current AP state
@@ -190,6 +178,17 @@
     }
 
     /**
+     * Helper function to increment the appropriate setup failure metrics.
+     */
+    private void incrementMetricsForSetupFailure(int failureReason) {
+        if (failureReason == WifiNative.SETUP_FAILURE_HAL) {
+            mWifiMetrics.incrementNumWifiOnFailureDueToHal();
+        } else if (failureReason == WifiNative.SETUP_FAILURE_WIFICOND) {
+            mWifiMetrics.incrementNumWifiOnFailureDueToWificond();
+        }
+    }
+
+    /**
      * Start a soft AP instance with the given configuration.
      * @param config AP configuration
      * @return integer result code
@@ -236,12 +235,11 @@
     }
 
     /**
-     * Teardown soft AP.
+     * Teardown soft AP and teardown the interface.
      */
     private void stopSoftAp() {
         if (!mWifiNative.stopSoftAp()) {
-            Log.d(TAG, "Soft AP stop failed");
-            return;
+            Log.e(TAG, "Soft AP stop failed");
         }
         Log.d(TAG, "Soft AP is stopped");
     }
@@ -253,6 +251,8 @@
         public static final int CMD_WIFICOND_BINDER_DEATH = 2;
         public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
         public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4;
+        public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
+        public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
 
         private final State mIdleState = new IdleState();
         private final State mStartedState = new StartedState();
@@ -300,6 +300,39 @@
             public boolean processMessage(Message message) {
                 switch (message.what) {
                     case CMD_START:
+                        // need to create our interface
+                        mApInterface = null;
+                        Pair<Integer, IApInterface> statusAndInterface =
+                                mWifiNative.setupForSoftApMode(mWifiNative.getInterfaceName());
+                        if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
+                            mApInterface = statusAndInterface.second;
+                        } else {
+                            Log.e(TAG, "setup failure when creating ap interface.");
+                            incrementMetricsForSetupFailure(statusAndInterface.first);
+                        }
+                        if (mApInterface == null) {
+                            Log.e(TAG, "Not starting softap mode without an interface.");
+                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+                                          WifiManager.WIFI_AP_STATE_DISABLED,
+                                          WifiManager.SAP_START_FAILURE_GENERAL);
+                            mWifiMetrics.incrementSoftApStartResult(
+                                    false, WifiManager.SAP_START_FAILURE_GENERAL);
+                            break;
+                        }
+                        try {
+                            mApInterfaceName = mApInterface.getInterfaceName();
+                        } catch (RemoteException e) {
+                            // Failed to get the interface name. This is not a good sign and we
+                            // should report a failure.
+                            Log.e(TAG, "Failed to get the interface name.");
+                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+                                          WifiManager.WIFI_AP_STATE_DISABLED,
+                                          WifiManager.SAP_START_FAILURE_GENERAL);
+                            mWifiMetrics.incrementSoftApStartResult(
+                                    false, WifiManager.SAP_START_FAILURE_GENERAL);
+                            break;
+                        }
+
                         // first a sanity check on the interface name.  If we failed to retrieve it,
                         // we are going to have a hard time setting up routing.
                         if (TextUtils.isEmpty(mApInterfaceName)) {
@@ -313,7 +346,6 @@
                         }
                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
                                 WifiManager.WIFI_AP_STATE_DISABLED, 0);
-                        setNumAssociatedStations(0);
                         if (!mWifiNative.registerWificondDeathHandler(mWificondDeathRecipient)) {
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                     WifiManager.WIFI_AP_STATE_ENABLING,
@@ -375,6 +407,92 @@
         private class StartedState extends State {
             private boolean mIfaceIsUp;
 
+            private int mNumAssociatedStations;
+
+            private boolean mTimeoutEnabled;
+            private int mTimeoutDelay;
+            private WakeupMessage mSoftApTimeoutMessage;
+            private SoftApTimeoutEnabledSettingObserver mSettingObserver;
+
+            /**
+            * Observer for timeout settings changes.
+            */
+            private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
+                SoftApTimeoutEnabledSettingObserver(Handler handler) {
+                    super(handler);
+                }
+
+                public void register() {
+                    mFrameworkFacade.registerContentObserver(mContext,
+                            Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
+                            true, this);
+                    mTimeoutEnabled = getValue();
+                }
+
+                public void unregister() {
+                    mFrameworkFacade.unregisterContentObserver(mContext, this);
+                }
+
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
+                            getValue() ? 1 : 0);
+                }
+
+                private boolean getValue() {
+                    boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
+                            Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
+                    return enabled;
+                }
+            }
+
+            private int getConfigSoftApTimeoutDelay() {
+                int delay = mContext.getResources().getInteger(
+                        R.integer.config_wifi_framework_soft_ap_timeout_delay);
+                if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
+                    delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
+                    Log.w(TAG, "Overriding timeout delay with minimum limit value");
+                }
+                Log.d(TAG, "Timeout delay: " + delay);
+                return delay;
+            }
+
+            private void scheduleTimeoutMessage() {
+                if (!mTimeoutEnabled) {
+                    return;
+                }
+                mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
+                Log.d(TAG, "Timeout message scheduled");
+            }
+
+            private void cancelTimeoutMessage() {
+                mSoftApTimeoutMessage.cancel();
+                Log.d(TAG, "Timeout message canceled");
+            }
+
+            /**
+             * Set number of stations associated with this soft AP
+             * @param numStations Number of connected stations
+             */
+            private void setNumAssociatedStations(int numStations) {
+                if (mNumAssociatedStations == numStations) {
+                    return;
+                }
+                mNumAssociatedStations = numStations;
+                Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
+
+                // TODO:(b/63906412) send it up to settings.
+                mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations,
+                        mMode);
+
+                if (mNumAssociatedStations == 0) {
+                    scheduleTimeoutMessage();
+                } else {
+                    cancelTimeoutMessage();
+                }
+            }
+
             private void onUpChanged(boolean isUp) {
                 if (isUp == mIfaceIsUp) {
                     return;  // no change
@@ -388,9 +506,7 @@
                 } else {
                     // TODO: handle the case where the interface was up, but goes down
                 }
-
                 mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
-                setNumAssociatedStations(0);
             }
 
             @Override
@@ -404,6 +520,30 @@
                 if (config != null) {
                     onUpChanged(config.isUp());
                 }
+
+                mTimeoutDelay = getConfigSoftApTimeoutDelay();
+                Handler handler = mStateMachine.getHandler();
+                mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
+                        SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
+                        SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
+                mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
+
+                if (mSettingObserver != null) {
+                    mSettingObserver.register();
+                }
+                Log.d(TAG, "Resetting num stations on start");
+                mNumAssociatedStations = 0;
+                scheduleTimeoutMessage();
+            }
+
+            @Override
+            public void exit() {
+                if (mSettingObserver != null) {
+                    mSettingObserver.unregister();
+                }
+                Log.d(TAG, "Resetting num stations on stop");
+                mNumAssociatedStations = 0;
+                cancelTimeoutMessage();
             }
 
             @Override
@@ -414,8 +554,22 @@
                             Log.e(TAG, "Invalid number of associated stations: " + message.arg1);
                             break;
                         }
+                        Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED");
                         setNumAssociatedStations(message.arg1);
                         break;
+                    case CMD_TIMEOUT_TOGGLE_CHANGED:
+                        boolean isEnabled = (message.arg1 == 1);
+                        if (mTimeoutEnabled == isEnabled) {
+                            break;
+                        }
+                        mTimeoutEnabled = isEnabled;
+                        if (!mTimeoutEnabled) {
+                            cancelTimeoutMessage();
+                        }
+                        if (mTimeoutEnabled && mNumAssociatedStations == 0) {
+                            scheduleTimeoutMessage();
+                        }
+                        break;
                     case CMD_INTERFACE_STATUS_CHANGED:
                         if (message.obj != mNetworkObserver) {
                             // This is from some time before the most recent configuration.
@@ -427,11 +581,21 @@
                     case CMD_START:
                         // Already started, ignore this command.
                         break;
+                    case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
+                        if (!mTimeoutEnabled) {
+                            Log.wtf(TAG, "Timeout message received while timeout is disabled."
+                                    + " Dropping.");
+                            break;
+                        }
+                        if (mNumAssociatedStations != 0) {
+                            Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
+                            break;
+                        }
+                        Log.i(TAG, "Timeout message received. Stopping soft AP.");
                     case CMD_WIFICOND_BINDER_DEATH:
                     case CMD_STOP:
                         updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                 WifiManager.WIFI_AP_STATE_ENABLED, 0);
-                        setNumAssociatedStations(0);
                         stopSoftAp();
                         if (message.what == CMD_WIFICOND_BINDER_DEATH) {
                             updateApState(WifiManager.WIFI_AP_STATE_FAILED,
diff --git a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
index 38fd1ef..bfc51f6 100644
--- a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
+++ b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
@@ -91,15 +91,14 @@
             double initialVariance = 9.0 * standardDeviation * standardDeviation;
             mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});
             mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0});
-            mLastMillis = millis;
-            return;
+        } else {
+            double dt = (millis - mLastMillis) * 0.001;
+            mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
+            setDeltaTimeSeconds(dt);
+            mFilter.predict();
+            mFilter.update(new Matrix(1, new double[]{rssi}));
         }
-        double dt = (millis - mLastMillis) * 0.001;
-        mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
-        setDeltaTimeSeconds(dt);
-        mFilter.predict();
         mLastMillis = millis;
-        mFilter.update(new Matrix(1, new double[]{rssi}));
         mFilteredRssi = mFilter.mx.get(0, 0);
         mEstimatedRateOfRssiChange = mFilter.mx.get(1, 0);
     }
diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
new file mode 100644
index 0000000..5775117
--- /dev/null
+++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.util.ArraySet;
+
+import com.android.server.wifi.WifiConfigStore.StoreData;
+import com.android.server.wifi.util.XmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Config store data for Wifi Wake.
+ */
+public class WakeupConfigStoreData implements StoreData {
+    private static final String TAG = "WakeupConfigStoreData";
+
+    private static final String XML_TAG_IS_ACTIVE = "IsActive";
+    private static final String XML_TAG_NETWORK_SECTION = "Network";
+    private static final String XML_TAG_SSID = "SSID";
+    private static final String XML_TAG_SECURITY = "Security";
+
+    private final DataSource<Boolean> mIsActiveDataSource;
+    private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    /**
+     * Interface defining a data source for the store data.
+     *
+     * @param <T> Type of data source
+     */
+    public interface DataSource<T> {
+        /**
+         * Returns the data from the data source.
+         */
+        T getData();
+
+        /**
+         * Updates the data in the data source.
+         *
+         * @param data Data retrieved from the store
+         */
+        void setData(T data);
+    }
+
+    /**
+     * Creates the config store data with its data sources.
+     *
+     * @param isActiveDataSource Data source for isActive
+     * @param networkDataSource Data source for the locked network list
+     */
+    public WakeupConfigStoreData(
+            DataSource<Boolean> isActiveDataSource,
+            DataSource<Set<ScanResultMatchInfo>> networkDataSource) {
+        mIsActiveDataSource = isActiveDataSource;
+        mNetworkDataSource = networkDataSource;
+    }
+
+    @Override
+    public void serializeData(XmlSerializer out, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Share data not supported");
+        }
+
+        XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData());
+
+        for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) {
+            writeNetwork(out, scanResultMatchInfo);
+        }
+    }
+
+    /**
+     * Writes a {@link ScanResultMatchInfo} to an XML output stream.
+     *
+     * @param out XML output stream
+     * @param scanResultMatchInfo The ScanResultMatchInfo to serizialize
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private void writeNetwork(XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo)
+            throws XmlPullParserException, IOException {
+        XmlUtil.writeNextSectionStart(out, XML_TAG_NETWORK_SECTION);
+
+        XmlUtil.writeNextValue(out, XML_TAG_SSID, scanResultMatchInfo.networkSsid);
+        XmlUtil.writeNextValue(out, XML_TAG_SECURITY, scanResultMatchInfo.networkType);
+
+        XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION);
+    }
+
+    @Override
+    public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+            throws XmlPullParserException, IOException {
+        if (shared) {
+            throw new XmlPullParserException("Shared data not supported");
+        }
+
+        boolean isActive = (Boolean) XmlUtil.readNextValueWithName(in, XML_TAG_IS_ACTIVE);
+        mIsActiveDataSource.setData(isActive);
+
+        Set<ScanResultMatchInfo> networks = new ArraySet<>();
+        while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_NETWORK_SECTION, outerTagDepth)) {
+            networks.add(parseNetwork(in, outerTagDepth + 1));
+        }
+
+        mNetworkDataSource.setData(networks);
+    }
+
+    /**
+     * Parses a {@link ScanResultMatchInfo} from an XML input stream.
+     *
+     * @param in XML input stream
+     * @param outerTagDepth XML tag depth of the containing section
+     * @return The {@link ScanResultMatchInfo}
+     * @throws IOException
+     * @throws XmlPullParserException
+     */
+    private ScanResultMatchInfo parseNetwork(XmlPullParser in, int outerTagDepth)
+            throws IOException, XmlPullParserException {
+        ScanResultMatchInfo scanResultMatchInfo = new ScanResultMatchInfo();
+        while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
+            String[] valueName = new String[1];
+            Object value = XmlUtil.readCurrentValue(in, valueName);
+            if (valueName[0] == null) {
+                throw new XmlPullParserException("Missing value name");
+            }
+            switch (valueName[0]) {
+                case XML_TAG_SSID:
+                    scanResultMatchInfo.networkSsid = (String) value;
+                    break;
+                case XML_TAG_SECURITY:
+                    scanResultMatchInfo.networkType = (int) value;
+                    break;
+                default:
+                    throw new XmlPullParserException("Unknown tag under " + TAG + ": "
+                            + valueName[0]);
+            }
+        }
+
+        return scanResultMatchInfo;
+    }
+
+    @Override
+    public void resetData(boolean shared) {
+        if (!shared) {
+            mNetworkDataSource.setData(Collections.emptySet());
+            mIsActiveDataSource.setData(false);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return TAG;
+    }
+
+    @Override
+    public boolean supportShareData() {
+        return false;
+    }
+}
diff --git a/service/java/com/android/server/wifi/WakeupController.java b/service/java/com/android/server/wifi/WakeupController.java
index a3c095a..8787bfd 100644
--- a/service/java/com/android/server/wifi/WakeupController.java
+++ b/service/java/com/android/server/wifi/WakeupController.java
@@ -24,6 +24,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * WakeupController is responsible managing Auto Wifi.
  *
@@ -38,16 +41,26 @@
     private final Handler mHandler;
     private final FrameworkFacade mFrameworkFacade;
     private final ContentObserver mContentObserver;
+    private final WakeupLock mWakeupLock;
+    private final WifiConfigManager mWifiConfigManager;
 
     /** Whether this feature is enabled in Settings. */
     private boolean mWifiWakeupEnabled;
 
+    /** Whether the WakeupController is currently active. */
+    private boolean mIsActive = false;
+
     public WakeupController(
             Context context,
             Looper looper,
+            WakeupLock wakeupLock,
+            WifiConfigManager wifiConfigManager,
+            WifiConfigStore wifiConfigStore,
             FrameworkFacade frameworkFacade) {
         mContext = context;
         mHandler = new Handler(looper);
+        mWakeupLock = wakeupLock;
+        mWifiConfigManager = wifiConfigManager;
         mFrameworkFacade = frameworkFacade;
         mContentObserver = new ContentObserver(mHandler) {
             @Override
@@ -59,6 +72,19 @@
         mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
                 Settings.Global.WIFI_WAKEUP_ENABLED), true, mContentObserver);
         mContentObserver.onChange(false /* selfChange */);
+
+        // registering the store data here has the effect of reading the persisted value of the
+        // data sources after system boot finishes
+        WakeupConfigStoreData wakeupConfigStoreData =
+                new WakeupConfigStoreData(new IsActiveDataSource(), mWakeupLock.getDataSource());
+        wifiConfigStore.registerStoreData(wakeupConfigStoreData);
+    }
+
+    private void setActive(boolean isActive) {
+        if (mIsActive != isActive) {
+            mIsActive = isActive;
+            mWifiConfigManager.saveToStore(false /* forceWrite */);
+        }
     }
 
     /**
@@ -71,4 +97,26 @@
     boolean isEnabled() {
         return mWifiWakeupEnabled;
     }
+
+    /** Dumps wakeup controller state. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Dump of WakeupController");
+        pw.println("mWifiWakeupEnabled: " + mWifiWakeupEnabled);
+        pw.println("USE_PLATFORM_WIFI_WAKE: " + USE_PLATFORM_WIFI_WAKE);
+        pw.println("mIsActive: " + mIsActive);
+        mWakeupLock.dump(fd, pw, args);
+    }
+
+    private class IsActiveDataSource implements WakeupConfigStoreData.DataSource<Boolean> {
+
+        @Override
+        public Boolean getData() {
+            return mIsActive;
+        }
+
+        @Override
+        public void setData(Boolean data) {
+            mIsActive = data;
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WakeupLock.java b/service/java/com/android/server/wifi/WakeupLock.java
index 73cda91..1fcd9f8 100644
--- a/service/java/com/android/server/wifi/WakeupLock.java
+++ b/service/java/com/android/server/wifi/WakeupLock.java
@@ -17,12 +17,16 @@
 package com.android.server.wifi;
 
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A lock to determine whether Auto Wifi can re-enable Wifi.
@@ -31,18 +35,24 @@
  */
 public class WakeupLock {
 
+    private static final String TAG = WakeupLock.class.getSimpleName();
+
     @VisibleForTesting
     static final int CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 3;
 
-    private Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>();
 
-    // TODO(easchwar) read initial value of mLockedNetworks from file
-    public WakeupLock() {
+    private final WifiConfigManager mWifiConfigManager;
+    private final Map<ScanResultMatchInfo, Integer> mLockedNetworks = new ArrayMap<>();
+
+    public WakeupLock(WifiConfigManager wifiConfigManager) {
+        mWifiConfigManager = wifiConfigManager;
     }
 
     /**
      * Initializes the WakeupLock with the given {@link ScanResultMatchInfo} list.
      *
+     * <p>This saves the wakeup lock to the store.
+     *
      * @param scanResultList list of ScanResultMatchInfos to start the lock with
      */
     public void initialize(Collection<ScanResultMatchInfo> scanResultList) {
@@ -50,17 +60,23 @@
         for (ScanResultMatchInfo scanResultMatchInfo : scanResultList) {
             mLockedNetworks.put(scanResultMatchInfo, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
         }
+
+        Log.d(TAG, "Lock initialized. Number of networks: " + mLockedNetworks.size());
+
+        mWifiConfigManager.saveToStore(false /* forceWrite */);
     }
 
     /**
      * Updates the lock with the given {@link ScanResultMatchInfo} list.
      *
      * <p>If a network in the lock is not present in the list, reduce the number of scans
-     * required to evict by one. Remove any entries in the list with 0 scans required to evict.
+     * required to evict by one. Remove any entries in the list with 0 scans required to evict. If
+     * any entries in the lock are removed, the store is updated.
      *
      * @param scanResultList list of present ScanResultMatchInfos to update the lock with
      */
     public void update(Collection<ScanResultMatchInfo> scanResultList) {
+        boolean hasChanged = false;
         Iterator<Map.Entry<ScanResultMatchInfo, Integer>> it =
                 mLockedNetworks.entrySet().iterator();
         while (it.hasNext()) {
@@ -76,9 +92,13 @@
             entry.setValue(entry.getValue() - 1);
             if (entry.getValue() <= 0) {
                 it.remove();
+                hasChanged = true;
             }
         }
-        // TODO(easchwar) write the updated list to file
+
+        if (hasChanged) {
+            mWifiConfigManager.saveToStore(false /* forceWrite */);
+        }
     }
 
     /**
@@ -87,4 +107,36 @@
     public boolean isEmpty() {
         return mLockedNetworks.isEmpty();
     }
+
+    /** Returns the data source for the WakeupLock config store data. */
+    public WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> getDataSource() {
+        return new WakeupLockDataSource();
+    }
+
+    /** Dumps wakeup lock contents. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("WakeupLock: ");
+        pw.println("Locked networks: " + mLockedNetworks.size());
+        for (Map.Entry<ScanResultMatchInfo, Integer> entry : mLockedNetworks.entrySet()) {
+            pw.println(entry.getKey() + ", scans to evict: " + entry.getValue());
+        }
+    }
+
+    private class WakeupLockDataSource
+            implements WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> {
+
+        @Override
+        public Set<ScanResultMatchInfo> getData() {
+            return mLockedNetworks.keySet();
+        }
+
+        @Override
+        public void setData(Set<ScanResultMatchInfo> data) {
+            mLockedNetworks.clear();
+            for (ScanResultMatchInfo network : data) {
+                mLockedNetworks.put(network, CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT);
+            }
+
+        }
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiCertManager.java b/service/java/com/android/server/wifi/WifiCertManager.java
deleted file mode 100644
index e180f51..0000000
--- a/service/java/com/android/server/wifi/WifiCertManager.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.app.admin.IDevicePolicyManager;
-import android.content.Context;
-import android.os.Environment;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.server.net.DelayedDiskWrite;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Manager class for affiliated Wifi certificates.
- */
-public class WifiCertManager {
-    private static final String TAG = "WifiCertManager";
-    private static final String SEP = "\n";
-
-    private final Context mContext;
-    private final Set<String> mAffiliatedUserOnlyCerts = new HashSet<String>();
-    private final String mConfigFile;
-
-    private static final String CONFIG_FILE =
-            Environment.getDataDirectory() + "/misc/wifi/affiliatedcerts.txt";
-
-    private final DelayedDiskWrite mWriter = new DelayedDiskWrite();
-
-
-    WifiCertManager(Context context) {
-        this(context, CONFIG_FILE);
-    }
-
-    WifiCertManager(Context context, String configFile) {
-        mContext = context;
-        mConfigFile = configFile;
-        final byte[] bytes = readConfigFile();
-        if (bytes == null) {
-            // Config file does not exist or empty.
-            return;
-        }
-
-        String[] keys = new String(bytes, StandardCharsets.UTF_8).split(SEP);
-        for (String key : keys) {
-            mAffiliatedUserOnlyCerts.add(key);
-        }
-
-        // Remove keys that no longer exist in KeyStore.
-        if (mAffiliatedUserOnlyCerts.retainAll(Arrays.asList(listClientCertsForAllUsers()))) {
-            writeConfig();
-        }
-    }
-
-    /** @param  key Unprefixed cert key to hide from unaffiliated users. */
-    public void hideCertFromUnaffiliatedUsers(String key) {
-        if (mAffiliatedUserOnlyCerts.add(Credentials.USER_PRIVATE_KEY + key)) {
-            writeConfig();
-        }
-    }
-
-    /** @return Prefixed cert keys that are visible to the current user. */
-    public String[] listClientCertsForCurrentUser() {
-        HashSet<String> results = new HashSet<String>();
-
-        String[] keys = listClientCertsForAllUsers();
-        if (isAffiliatedUser()) {
-            return keys;
-        }
-
-        for (String key : keys) {
-            if (!mAffiliatedUserOnlyCerts.contains(key)) {
-                results.add(key);
-            }
-        }
-        return results.toArray(new String[results.size()]);
-    }
-
-    private void writeConfig() {
-        String[] values =
-                mAffiliatedUserOnlyCerts.toArray(new String[mAffiliatedUserOnlyCerts.size()]);
-        String value = TextUtils.join(SEP, values);
-        writeConfigFile(value.getBytes(StandardCharsets.UTF_8));
-    }
-
-    protected byte[] readConfigFile() {
-        byte[] bytes = null;
-        try {
-            final File file = new File(mConfigFile);
-            final long fileSize = file.exists() ? file.length() : 0;
-            if (fileSize == 0 || fileSize >= Integer.MAX_VALUE) {
-                // Config file is empty/corrupted/non-existing.
-                return bytes;
-            }
-
-            bytes = new byte[(int) file.length()];
-            final DataInputStream stream = new DataInputStream(new FileInputStream(file));
-            stream.readFully(bytes);
-        } catch (IOException e) {
-            Log.e(TAG, "readConfigFile: failed to read " + e, e);
-        }
-        return bytes;
-    }
-
-    protected void writeConfigFile(byte[] payload) {
-        final byte[] data = payload;
-        mWriter.write(mConfigFile, new DelayedDiskWrite.Writer() {
-            public void onWriteCalled(DataOutputStream out) throws IOException {
-                out.write(data, 0, data.length);
-            }
-        });
-    }
-
-    protected String[] listClientCertsForAllUsers() {
-        return KeyStore.getInstance().list(Credentials.USER_PRIVATE_KEY, UserHandle.myUserId());
-    }
-
-    protected boolean isAffiliatedUser() {
-        IDevicePolicyManager pm = IDevicePolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
-        boolean result = false;
-        try {
-            result = pm.isAffiliatedUser();
-        } catch (Exception e) {
-            Log.e(TAG, "failed to check user affiliation", e);
-        }
-        return result;
-    }
-}
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java
index 494ce86..dc98595 100644
--- a/service/java/com/android/server/wifi/WifiController.java
+++ b/service/java/com/android/server/wifi/WifiController.java
@@ -93,6 +93,7 @@
 
     /* References to values tracked in WifiService */
     private final WifiStateMachine mWifiStateMachine;
+    private final WifiStateMachinePrime mWifiStateMachinePrime;
     private final WifiSettingsStore mSettingsStore;
     private final WifiLockManager mWifiLockManager;
 
@@ -143,11 +144,13 @@
     private EcmState mEcmState = new EcmState();
 
     WifiController(Context context, WifiStateMachine wsm, WifiSettingsStore wss,
-            WifiLockManager wifiLockManager, Looper looper, FrameworkFacade f) {
+            WifiLockManager wifiLockManager, Looper looper, FrameworkFacade f,
+            WifiStateMachinePrime wsmp) {
         super(TAG, looper);
         mFacade = f;
         mContext = context;
         mWifiStateMachine = wsm;
+        mWifiStateMachinePrime = wsmp;
         mSettingsStore = wss;
         mWifiLockManager = wifiLockManager;
 
@@ -435,7 +438,8 @@
         @Override
         public void enter() {
             mWifiStateMachine.setSupplicantRunning(false);
-            // Supplicant can't restart right away, so not the time we switched off
+            mWifiStateMachinePrime.disableWifi();
+            // Supplicant can't restart right away, so note the time we switched off
             mDisabledTimestamp = SystemClock.elapsedRealtime();
             mDeferredEnableSerialNumber++;
             mHaveDeferredEnable = false;
@@ -481,6 +485,7 @@
                         }
                         mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj,
                                 true);
+                        mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
                         transitionTo(mApEnabledState);
                     }
                     break;
@@ -565,8 +570,13 @@
                     if (msg.arg1 == 1) {
                         // remeber that we were enabled
                         mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED);
-                        deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
-                        transitionTo(mApStaDisabledState);
+                        mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj, true);
+                        mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
+                        transitionTo(mApEnabledState);
+                        // we should just go directly to ApEnabled since we will kill interfaces
+                        // from WSMP
+                        //deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
+                        //transitionTo(mApStaDisabledState);
                     }
                     break;
                 default:
@@ -630,8 +640,14 @@
                     // Before starting tethering, turn off supplicant for scan mode
                     if (msg.arg1 == 1) {
                         mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
-                        deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
-                        transitionTo(mApStaDisabledState);
+
+                        mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj, true);
+                        mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
+                        transitionTo(mApEnabledState);
+                        // we should just go directly to ApEnabled since we will kill interfaces
+                        // from WSMP
+                        //deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj));
+                        //transitionTo(mApStaDisabledState);
                     }
                     break;
                 case CMD_DEFERRED_TOGGLE:
@@ -701,22 +717,26 @@
                 case CMD_AIRPLANE_TOGGLED:
                     if (mSettingsStore.isAirplaneModeOn()) {
                         mWifiStateMachine.setHostApRunning(null, false);
+                        mWifiStateMachinePrime.disableWifi();
                         mPendingState = mApStaDisabledState;
                     }
                     break;
                 case CMD_WIFI_TOGGLED:
                     if (mSettingsStore.isWifiToggleEnabled()) {
                         mWifiStateMachine.setHostApRunning(null, false);
+                        mWifiStateMachinePrime.disableWifi();
                         mPendingState = mDeviceActiveState;
                     }
                     break;
                 case CMD_SET_AP:
                     if (msg.arg1 == 0) {
                         mWifiStateMachine.setHostApRunning(null, false);
+                        mWifiStateMachinePrime.disableWifi();
                         mPendingState = getNextWifiState();
                     }
                     break;
                 case CMD_AP_STOPPED:
+                    mWifiStateMachine.setHostApRunning(null, false);
                     if (mPendingState == null) {
                         /**
                          * Stop triggered internally, either tether notification
@@ -736,10 +756,12 @@
                 case CMD_EMERGENCY_MODE_CHANGED:
                     if (msg.arg1 == 1) {
                         mWifiStateMachine.setHostApRunning(null, false);
+                        mWifiStateMachinePrime.disableWifi();
                         mPendingState = mEcmState;
                     }
                     break;
                 case CMD_AP_START_FAILURE:
+                    mWifiStateMachine.setHostApRunning(null, false);
                     transitionTo(getNextWifiState());
                     break;
                 default:
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index f14a57f..9c9622c 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.net.NetworkKey;
 import android.net.NetworkScoreManager;
-import android.net.wifi.IApInterface;
 import android.net.wifi.IWifiScanner;
 import android.net.wifi.IWificond;
 import android.net.wifi.WifiInfo;
@@ -84,8 +83,8 @@
     private final SupplicantP2pIfaceHal mSupplicantP2pIfaceHal;
     private final WifiVendorHal mWifiVendorHal;
     private final WifiStateMachine mWifiStateMachine;
+    private final WifiStateMachinePrime mWifiStateMachinePrime;
     private final WifiSettingsStore mSettingsStore;
-    private final WifiCertManager mCertManager;
     private final OpenNetworkNotifier mOpenNetworkNotifier;
     private final WifiLockManager mLockManager;
     private final WifiController mWifiController;
@@ -225,17 +224,23 @@
                 wifiStateMachineLooper, UserManager.get(mContext),
                 this, mBackupManagerProxy, mCountryCode, mWifiNative,
                 new WrongPasswordNotifier(mContext, mFrameworkFacade));
-        mCertManager = new WifiCertManager(mContext);
+        IBinder b = mFrameworkFacade.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService networkManagementService =
+                INetworkManagementService.Stub.asInterface(b);
+        mWifiStateMachinePrime = new WifiStateMachinePrime(this, wifiStateMachineLooper,
+                mWifiNative, networkManagementService);
         mOpenNetworkNotifier = new OpenNetworkNotifier(mContext,
                 mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics,
                 mWifiConfigManager, mWifiConfigStore, mWifiStateMachine,
                 new OpenNetworkRecommender(),
                 new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
         mWakeupController = new WakeupController(mContext,
-                mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade);
+                mWifiStateMachineHandlerThread.getLooper(), new WakeupLock(mWifiConfigManager),
+                mWifiConfigManager, mWifiConfigStore, mFrameworkFacade);
         mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
         mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
-                mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
+                mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade,
+                mWifiStateMachinePrime);
         mSelfRecovery = new SelfRecovery(mWifiController, mClock);
         mWifiLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, mWifiMetrics);
         mWifiMulticastLockManager = new WifiMulticastLockManager(mWifiStateMachine,
@@ -300,12 +305,12 @@
         return mWifiStateMachine;
     }
 
-    public WifiSettingsStore getWifiSettingsStore() {
-        return mSettingsStore;
+    public WifiStateMachinePrime getWifiStateMachinePrime() {
+        return mWifiStateMachinePrime;
     }
 
-    public WifiCertManager getWifiCertManager() {
-        return mCertManager;
+    public WifiSettingsStore getWifiSettingsStore() {
+        return mSettingsStore;
     }
 
     public WifiLockManager getWifiLockManager() {
@@ -376,20 +381,15 @@
      * @param nmService NetworkManagementService allowing SoftApManager to listen for interface
      * changes
      * @param listener listener for SoftApManager
-     * @param apInterface network interface to start hostapd against
-     * @param ifaceName name of the ap interface
      * @param config SoftApModeConfiguration object holding the config and mode
      * @return an instance of SoftApManager
      */
     public SoftApManager makeSoftApManager(INetworkManagementService nmService,
                                            SoftApManager.Listener listener,
-                                           @NonNull IApInterface apInterface,
-                                           @NonNull String ifaceName,
                                            @NonNull SoftApModeConfiguration config) {
-        return new SoftApManager(mContext, mWifiServiceHandlerThread.getLooper(),
-                                 mWifiNative, mCountryCode.getCountryCode(),
-                                 listener, apInterface, ifaceName, nmService,
-                                 mWifiApConfigStore, config, mWifiMetrics);
+        return new SoftApManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
+                mFrameworkFacade, mWifiNative, mCountryCode.getCountryCode(), listener,
+                nmService, mWifiApConfigStore, config, mWifiMetrics);
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 2f1b550..3118761 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -152,6 +152,105 @@
         }
     }
 
+    /**
+     * TODO(b/69426063): NEW API Surface for interface management. This will eventually
+     * deprecate the other interface management API's above. But, for now there will be
+     * some duplication to ease transition.
+     */
+    /**
+     * Initialize the native modules.
+     *
+     * @return true on success, false otherwise.
+     */
+    public boolean initialize() {
+        return false;
+    }
+
+    /**
+     * Callback to notify when the status of one of the native daemons
+     * (wificond, wpa_supplicant & vendor HAL) changes.
+     */
+    public interface StatusListener {
+        /**
+         * @param allReady Indicates if all the native daemons are ready for operation or not.
+         */
+        void onStatusChanged(boolean allReady);
+    }
+
+    /**
+     * Register a StatusListener to get notified about any status changes from the native daemons.
+     *
+     * It is safe to re-register the same callback object - duplicates are detected and only a
+     * single copy kept.
+     *
+     * @param listener StatusListener listener object.
+     */
+    public void registerStatusListener(@NonNull StatusListener listener) {
+    }
+
+    /**
+     * Callback to notify when the associated interface is destroyed, up or down.
+     */
+    public interface InterfaceCallback {
+        /**
+         * Interface destroyed by HalDeviceManager.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onDestroyed(String ifaceName);
+
+        /**
+         * Interface is up.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onUp(String ifaceName);
+
+        /**
+         * Interface is down.
+         *
+         * @param ifaceName Name of the iface.
+         */
+        void onDown(String ifaceName);
+    }
+
+    /**
+     * Setup an interface for Client mode operations.
+     *
+     * This method configures an interface in STA mode in all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param interfaceCallback Associated callback for notifying status changes for the iface.
+     * @return Returns the name of the allocated interface, will be null on failure.
+     */
+    public String setupInterfaceForClientMode(@NonNull InterfaceCallback interfaceCallback) {
+        return null;
+    }
+
+    /**
+     * Setup an interface for Soft AP mode operations.
+     *
+     * This method configures an interface in AP mode in all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param interfaceCallback Associated callback for notifying status changes for the iface.
+     * @return Returns the name of the allocated interface, will be null on failure.
+     */
+    public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
+        return null;
+    }
+
+    /**
+     * Teardown an interface in Client/AP mode.
+     *
+     * This method tears down the associated interface from all the native daemons
+     * (wificond, wpa_supplicant & vendor HAL).
+     *
+     * @param ifaceName Name of the interface.
+     */
+    public void teardownInterface(@NonNull String ifaceName) {
+    }
+
     /********************************************************
      * Wificond operations
      ********************************************************/
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 8e6a819..2724cb5 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -97,6 +97,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.MutableInt;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -170,8 +171,6 @@
     final WifiSettingsStore mSettingsStore;
     /* Logs connection events and some general router and scan stats */
     private final WifiMetrics mWifiMetrics;
-    /* Manages affiliated certificates for current user */
-    private final WifiCertManager mCertManager;
 
     private final WifiInjector mWifiInjector;
     /* Backup/Restore Module */
@@ -201,6 +200,18 @@
     private final ConcurrentHashMap<String, Integer> mIfaceIpModes;
 
     /**
+     * One of:  {@link WifiManager#WIFI_AP_STATE_DISABLED},
+     *          {@link WifiManager#WIFI_AP_STATE_DISABLING},
+     *          {@link WifiManager#WIFI_AP_STATE_ENABLED},
+     *          {@link WifiManager#WIFI_AP_STATE_ENABLING},
+     *          {@link WifiManager#WIFI_AP_STATE_FAILED}
+     *
+     * Access/maintenance MUST be done on the wifi service thread
+     */
+    private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
+
+
+    /**
      * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death.
      *
      * @hide
@@ -433,7 +444,6 @@
         mPowerManager = mContext.getSystemService(PowerManager.class);
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        mCertManager = mWifiInjector.getWifiCertManager();
         mWifiLockManager = mWifiInjector.getWifiLockManager();
         mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
         HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
@@ -790,8 +800,7 @@
         }
 
         // If SoftAp is enabled, only Settings is allowed to toggle wifi
-        boolean apEnabled =
-                mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED;
+        boolean apEnabled = mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED;
 
         if (apEnabled && !isFromSettings) {
             mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
@@ -863,7 +872,13 @@
     public int getWifiApEnabledState() {
         enforceAccessPermission();
         mLog.info("getWifiApEnabledState uid=%").c(Binder.getCallingUid()).flush();
-        return mWifiStateMachine.syncGetWifiApState();
+
+        // hand off work to our handler thread
+        MutableInt apState = new MutableInt(WifiManager.WIFI_AP_STATE_DISABLED);
+        mClientHandler.runWithScissors(() -> {
+            apState.value = mWifiApState;
+        }, 0);
+        return apState.value;
     }
 
     /**
@@ -1024,6 +1039,8 @@
 
     /**
      * Private method to handle SoftAp state changes
+     *
+     * <p> MUST be called from the WifiStateMachine thread.
      */
     private void handleWifiApStateChange(
             int currentState, int previousState, int errorCode, String ifaceName, int mode) {
@@ -1032,6 +1049,9 @@
                 + " previousState=" + previousState + " errorCode= " + errorCode
                 + " ifaceName=" + ifaceName + " mode=" + mode);
 
+        // update the tracking ap state variable
+        mWifiApState = currentState;
+
         // check if we have a failure - since it is possible (worst case scenario where
         // WifiController and WifiStateMachine are out of sync wrt modes) to get two FAILED
         // notifications in a row, we need to handle this first.
@@ -1733,12 +1753,19 @@
     @Override
     public WifiInfo getConnectionInfo(String callingPackage) {
         enforceAccessPermission();
-        mLog.info("getConnectionInfo uid=%").c(Binder.getCallingUid()).flush();
+        int uid = Binder.getCallingUid();
+        mLog.info("getConnectionInfo uid=%").c(uid).flush();
         /*
          * Make sure we have the latest information, by sending
          * a status request to the supplicant.
          */
-        return mWifiStateMachine.syncRequestConnectionInfo(callingPackage);
+        long ident = Binder.clearCallingIdentity();
+        try {
+            WifiInfo result = mWifiStateMachine.syncRequestConnectionInfo(callingPackage, uid);
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     /**
@@ -2410,38 +2437,6 @@
         return mWifiStateMachine.getAggressiveHandover();
     }
 
-    @Override
-    public void setAllowScansWithTraffic(int enabled) {
-        enforceAccessPermission();
-        mLog.info("setAllowScansWithTraffic uid=% enabled=%")
-                .c(Binder.getCallingUid())
-                .c(enabled).flush();
-        mWifiStateMachine.setAllowScansWithTraffic(enabled);
-    }
-
-    @Override
-    public int getAllowScansWithTraffic() {
-        enforceAccessPermission();
-        mLog.info("getAllowScansWithTraffic uid=%").c(Binder.getCallingUid()).flush();
-        return mWifiStateMachine.getAllowScansWithTraffic();
-    }
-
-    @Override
-    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        enforceChangePermission();
-        mLog.info("setEnableAutoJoinWhenAssociated uid=% enabled=%")
-                .c(Binder.getCallingUid())
-                .c(enabled).flush();
-        return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled);
-    }
-
-    @Override
-    public boolean getEnableAutoJoinWhenAssociated() {
-        enforceAccessPermission();
-        mLog.info("getEnableAutoJoinWhenAssociated uid=%").c(Binder.getCallingUid()).flush();
-        return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
-    }
-
     /* Return the Wifi Connection statistics object */
     @Override
     public WifiConnectionStatistics getConnectionStatistics() {
@@ -2549,14 +2544,6 @@
         return sb.toString();
     }
 
-    public void hideCertFromUnaffiliatedUsers(String alias) {
-        mCertManager.hideCertFromUnaffiliatedUsers(alias);
-    }
-
-    public String[] listClientCertsForCurrentUser() {
-        return mCertManager.listClientCertsForCurrentUser();
-    }
-
     /**
      * Enable/disable WifiConnectivityManager at runtime
      *
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index b69d67b..505a09b 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -29,9 +29,7 @@
 import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
 import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK;
 
-import android.Manifest;
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
@@ -39,7 +37,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
@@ -60,7 +57,6 @@
 import android.net.TrafficStats;
 import android.net.dhcp.DhcpClient;
 import android.net.ip.IpClient;
-import android.net.wifi.IApInterface;
 import android.net.wifi.IClientInterface;
 import android.net.wifi.RssiPacketCountInfo;
 import android.net.wifi.ScanResult;
@@ -82,7 +78,6 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.os.BatteryStats;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -128,6 +123,7 @@
 import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
 import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
 
 import java.io.BufferedReader;
 import java.io.FileDescriptor;
@@ -184,7 +180,7 @@
     private static final String EXTRA_OSU_PROVIDER = "OsuProvider";
 
     private boolean mVerboseLoggingEnabled = false;
-
+    private final WifiPermissionsWrapper mWifiPermissionsWrapper;
     /* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
      * the corresponding BSSID.
      */
@@ -700,8 +696,6 @@
     /* Enable/Disable WifiConnectivityManager */
     static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER               = BASE + 166;
 
-    /* Enable/Disable AutoJoin when associated */
-    static final int CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED                = BASE + 167;
 
     /* Get all matching Passpoint configurations */
     static final int CMD_GET_ALL_MATCHING_CONFIGS                       = BASE + 168;
@@ -799,8 +793,6 @@
      */
     private long mSupplicantScanIntervalMs;
 
-    private boolean mEnableAutoJoinWhenAssociated;
-    private int mAlwaysEnableScansWhileAssociated;
     private final int mThresholdQualifiedRssi24;
     private final int mThresholdQualifiedRssi5;
     private final int mThresholdSaturatedRssi24;
@@ -951,6 +943,7 @@
 
         mWifiMonitor = mWifiInjector.getWifiMonitor();
         mWifiDiagnostics = mWifiInjector.makeWifiDiagnostics(mWifiNative);
+        mWifiPermissionsWrapper = mWifiInjector.getWifiPermissionsWrapper();
 
         mWifiInfo = new ExtendedWifiInfo();
         mSupplicantStateTracker =
@@ -1040,8 +1033,6 @@
                 com.android.internal.R.string.config_wifi_tcp_buffers);
 
         // Load Device configs
-        mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
-                R.bool.config_wifi_framework_enable_associated_network_selection);
         mThresholdQualifiedRssi24 = context.getResources().getInteger(
                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
         mThresholdQualifiedRssi5 = context.getResources().getInteger(
@@ -1280,26 +1271,6 @@
         // mWifiConfigManager.trimANQPCache(true);
     }
 
-    public void setAllowScansWithTraffic(int enabled) {
-        mAlwaysEnableScansWhileAssociated = enabled;
-    }
-
-    public int getAllowScansWithTraffic() {
-        return mAlwaysEnableScansWhileAssociated;
-    }
-
-    /*
-     * Dynamically turn on/off if switching networks while connected is allowd.
-     */
-    public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        sendMessage(CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED, enabled ? 1 : 0);
-        return true;
-    }
-
-    public boolean getEnableAutoJoinWhenAssociated() {
-        return mEnableAutoJoinWhenAssociated;
-    }
-
     private boolean setRandomMacOui() {
         String oui = mContext.getResources().getString(R.string.config_wifi_random_mac_oui);
         if (TextUtils.isEmpty(oui)) {
@@ -1704,9 +1675,9 @@
     /**
      * TODO: doc
      */
-    public int syncGetWifiApState() {
-        return mWifiApState.get();
-    }
+    //public int syncGetWifiApState() {
+    //    return mWifiApState.get();
+    //}
 
     /**
      * TODO: doc
@@ -1763,20 +1734,18 @@
     /**
      * Get status information for the current connection, if any.
      *
+     * @param callingPackage string indicating the calling package of the caller
+     * @param uid the calling uid
      * @return a {@link WifiInfo} object containing information about the current connection
      */
-    public WifiInfo syncRequestConnectionInfo(String callingPackage) {
-        int uid = Binder.getCallingUid();
+    public WifiInfo syncRequestConnectionInfo(String callingPackage, int uid) {
         WifiInfo result = new WifiInfo(mWifiInfo);
-        if (uid == Process.myUid()) return result;
         boolean hideBssidAndSsid = true;
         result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
 
-        IPackageManager packageManager = AppGlobals.getPackageManager();
-
         try {
-            if (packageManager.checkUidPermission(Manifest.permission.LOCAL_MAC_ADDRESS,
-                    uid) == PackageManager.PERMISSION_GRANTED) {
+            if (mWifiPermissionsWrapper.getLocalMacAddressPermission(uid)
+                    == PackageManager.PERMISSION_GRANTED) {
                 result.setMacAddress(mWifiInfo.getMacAddress());
             }
             if (mWifiPermissionsUtil.canAccessScanResults(
@@ -2311,6 +2280,7 @@
         } else {
             pw.println("mWifiConnectivityManager is not initialized");
         }
+        mWifiInjector.getWakeupController().dump(fd, pw, args);
     }
 
     public void handleUserSwitch(int userId) {
@@ -2949,6 +2919,7 @@
         // Update state
         mWifiApState.set(wifiApState);
 
+        // TODO: when this code is removed, also remove syncGetWifiApStateByName()
         if (mVerboseLoggingEnabled) log("setWifiApState: " + syncGetWifiApStateByName());
     }
 
@@ -3279,16 +3250,18 @@
                 || stateChangeResult.wifiSsid.toString().isEmpty()) && isLinkDebouncing()) {
             return state;
         }
-        // Network id is only valid when we start connecting
+        // Network id and SSID are only valid when we start connecting
         if (SupplicantState.isConnecting(state)) {
             mWifiInfo.setNetworkId(lookupFrameworkNetworkId(stateChangeResult.networkId));
+            mWifiInfo.setBSSID(stateChangeResult.BSSID);
+            mWifiInfo.setSSID(stateChangeResult.wifiSsid);
         } else {
+            // Reset parameters according to WifiInfo.reset()
             mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+            mWifiInfo.setBSSID(null);
+            mWifiInfo.setSSID(null);
         }
 
-        mWifiInfo.setBSSID(stateChangeResult.BSSID);
-        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
-
         final WifiConfiguration config = getCurrentWifiConfiguration();
         if (config != null) {
             mWifiInfo.setEphemeral(config.ephemeral);
@@ -4177,6 +4150,9 @@
             // Tearing down the client interfaces below is going to stop our supplicant.
             mWifiMonitor.stopAllMonitoring();
 
+            // stop hostapd in case it was running from SoftApMode
+            mWifiNative.stopSoftAp();
+
             mWifiNative.deregisterWificondDeathHandler();
             mWifiNative.tearDown();
         }
@@ -4323,9 +4299,12 @@
                         transitionTo(mInitialState);
                     }
                     break;
+                case CMD_START_AP:
+                    // now go directly to softap mode since we handle teardown in WSMP
+                    transitionTo(mSoftApState);
+                    break;
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
                 case CMD_STOP_AP:
                 case CMD_SET_OPERATIONAL_MODE:
                     messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
@@ -4475,10 +4454,12 @@
                         sendMessage(mBufferedScanMsg.remove());
                     break;
                 case CMD_START_AP:
-                    /* Cannot start soft AP while in client mode */
-                    loge("Failed to start soft AP with a running supplicant");
-                    setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL,
-                            null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+                //    /* Cannot start soft AP while in client mode */
+                //    loge("Failed to start soft AP with a running supplicant");
+                //    setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL,
+                //            null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+                    // now go directly to softap mode since we handle teardown in WSMP
+                    transitionTo(mSoftApState);
                     break;
                 case CMD_SET_OPERATIONAL_MODE:
                     mOperationalMode = message.arg1;
@@ -4557,15 +4538,6 @@
                 case CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER:
                     mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
                     break;
-                case CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED:
-                    final boolean allowed = (message.arg1 > 0);
-                    boolean old_state = mEnableAutoJoinWhenAssociated;
-                    mEnableAutoJoinWhenAssociated = allowed;
-                    if (!old_state && allowed && mScreenOn
-                            && getCurrentState() == mConnectedState) {
-                        mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE);
-                    }
-                    break;
                 case CMD_SELECT_TX_POWER_SCENARIO:
                     int txPowerScenario = message.arg1;
                     logd("Setting Tx power scenario to " + txPowerScenario);
@@ -4943,6 +4915,7 @@
             }
             mWifiInfo.reset();
             mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
+            setWifiState(WIFI_STATE_DISABLED);
         }
 
         @Override
@@ -6981,6 +6954,7 @@
         private String mIfaceName;
         private int mMode;
 
+        /*
         private class SoftApListener implements SoftApManager.Listener {
             @Override
             public void onStateChanged(int state, int reason) {
@@ -6993,6 +6967,7 @@
                 setWifiApState(state, reason, mIfaceName, mMode);
             }
         }
+        */
 
         @Override
         public void enter() {
@@ -7000,6 +6975,7 @@
             if (message.what != CMD_START_AP) {
                 throw new RuntimeException("Illegal transition to SoftApState: " + message);
             }
+            /*
             SoftApModeConfiguration config = (SoftApModeConfiguration) message.obj;
             mMode = config.getTargetMode();
 
@@ -7033,11 +7009,10 @@
             checkAndSetConnectivityInstance();
             mSoftApManager = mWifiInjector.makeSoftApManager(mNwService,
                                                              new SoftApListener(),
-                                                             apInterface,
-                                                             mIfaceName,
                                                              config);
             mSoftApManager.start();
             mWifiStateTracker.updateState(WifiStateTracker.SOFT_AP);
+            */
         }
 
         @Override
@@ -7056,7 +7031,8 @@
                     /* Ignore start command when it is starting/started. */
                     break;
                 case CMD_STOP_AP:
-                    mSoftApManager.stop();
+                    //mSoftApManager.stop();
+                    transitionTo(mInitialState);
                     break;
                 case CMD_START_AP_FAILURE:
                     transitionTo(mInitialState);
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
index c49b645..2d3aaba 100644
--- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -17,14 +17,11 @@
 package com.android.server.wifi;
 
 import android.annotation.NonNull;
-import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.util.Log;
 
 import com.android.internal.util.Protocol;
@@ -47,12 +44,13 @@
 
     private final WifiInjector mWifiInjector;
     private final Looper mLooper;
+    private final WifiNative mWifiNative;
     private final INetworkManagementService mNMService;
 
-    private IWificond mWificond;
-
     private Queue<SoftApModeConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>();
 
+    private String mInterfaceName;
+
     /* The base for wifi message types */
     static final int BASE = Protocol.BASE_WIFI;
 
@@ -67,22 +65,17 @@
 
     WifiStateMachinePrime(WifiInjector wifiInjector,
                           Looper looper,
+                          WifiNative wifiNative,
                           INetworkManagementService nmService) {
         mWifiInjector = wifiInjector;
         mLooper = looper;
+        mWifiNative = wifiNative;
         mNMService = nmService;
 
-        // Clean up existing interfaces in wificond.
-        // This ensures that the framework and wificond are in a consistent state after a framework
-        // restart.
-        try {
-            mWificond = mWifiInjector.makeWificond();
-            if (mWificond != null) {
-                mWificond.tearDownInterfaces();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "wificond died during framework startup");
-        }
+        mInterfaceName = mWifiNative.getInterfaceName();
+
+        // make sure we do not have leftover state in the event of a restart
+        mWifiNative.tearDown();
     }
 
     /**
@@ -211,23 +204,14 @@
             return HANDLED;
         }
 
-        private void tearDownInterfaces() {
-            if (mWificond != null) {
-                try {
-                    mWificond.tearDownInterfaces();
-                } catch (RemoteException e) {
-                    // There is very little we can do here
-                    Log.e(TAG, "Failed to tear down interfaces via wificond");
-                }
-                mWificond = null;
-            }
-            return;
+        private void cleanup() {
+            mWifiNative.disableSupplicant();
+            mWifiNative.tearDown();
         }
 
         class ClientModeState extends State {
             @Override
             public void enter() {
-                mWificond = mWifiInjector.makeWificond();
             }
 
             @Override
@@ -240,7 +224,7 @@
 
             @Override
             public void exit() {
-                tearDownInterfaces();
+                cleanup();
             }
         }
 
@@ -266,33 +250,18 @@
         }
 
         class SoftAPModeState extends State {
-            IApInterface mApInterface = null;
-            String mIfaceName = null;
 
             @Override
             public void enter() {
+                // For now - need to clean up from other mode management in WSM
+                cleanup();
+
                 final Message message = mModeStateMachine.getCurrentMessage();
                 if (message.what != ModeStateMachine.CMD_START_SOFT_AP_MODE) {
                     Log.d(TAG, "Entering SoftAPMode (idle)");
                     return;
                 }
 
-                // Continue with setup since we are changing modes
-                mApInterface = null;
-
-                try {
-                    mWificond = mWifiInjector.makeWificond();
-                    mApInterface = mWificond.createApInterface(
-                            mWifiInjector.getWifiNative().getInterfaceName());
-                    mIfaceName = mApInterface.getInterfaceName();
-                } catch (RemoteException e) {
-                    initializationFailed(
-                            "Could not get IApInterface instance or its name from wificond");
-                    return;
-                } catch (NullPointerException e) {
-                    initializationFailed("wificond failure when initializing softap mode");
-                    return;
-                }
                 mModeStateMachine.transitionTo(mSoftAPModeActiveState);
             }
 
@@ -310,9 +279,8 @@
                         // not in active state, nothing to stop.
                         break;
                     case CMD_START_AP_FAILURE:
-                        // remove the saved config for the start attempt
-                        mApConfigQueue.poll();
-                        Log.e(TAG, "Failed to start SoftApMode.  Wait for next mode command.");
+                        // with interface management in softapmanager, no setup failures can be seen
+                        // here
                         break;
                     case CMD_AP_STOPPED:
                         Log.d(TAG, "SoftApModeActiveState stopped.  Wait for next mode command.");
@@ -325,15 +293,9 @@
 
             @Override
             public void exit() {
-                tearDownInterfaces();
-            }
-
-            protected IApInterface getInterface() {
-                return mApInterface;
-            }
-
-            protected String getInterfaceName() {
-                return mIfaceName;
+                // while in transition, cleanup is done on entering states.  in the future, each
+                // mode will clean up their own state on exit
+                //cleanup();
             }
 
             private void initializationFailed(String message) {
@@ -345,8 +307,9 @@
         class WifiDisabledState extends State {
             @Override
             public void enter() {
-                // make sure everything is torn down
                 Log.d(TAG, "Entering WifiDisabledState");
+                // make sure everything is torn down
+                cleanup();
             }
 
             @Override
@@ -415,9 +378,8 @@
                     config = null;
                 }
                 this.mActiveModeManager = mWifiInjector.makeSoftApManager(mNMService,
-                        new SoftApListener(), ((SoftAPModeState) mSoftAPModeState).getInterface(),
-                        ((SoftAPModeState) mSoftAPModeState).getInterfaceName(), softApModeConfig);
-                mActiveModeManager.start();
+                        new SoftApListener(), softApModeConfig);
+                this.mActiveModeManager.start();
             }
 
             @Override
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
index 421d9ac..62cc3ec 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -329,8 +329,8 @@
             enforceNetworkStackPermission();
         }
 
-        if (message != null
-                && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
+        if (message != null && message.length
+                > mStateManager.getCharacteristics().getMaxServiceSpecificInfoLength()) {
             throw new IllegalArgumentException(
                     "Message length longer than supported by device characteristics");
         }
diff --git a/service/java/com/android/server/wifi/rtt/RttNative.java b/service/java/com/android/server/wifi/rtt/RttNative.java
index e920310..e085352 100644
--- a/service/java/com/android/server/wifi/rtt/RttNative.java
+++ b/service/java/com/android/server/wifi/rtt/RttNative.java
@@ -206,11 +206,8 @@
         for (ResponderConfig responder: request.mRttPeers) {
             RttConfig config = new RttConfig();
 
-            if (responder.macAddress.length != config.addr.length) {
-                Log.e(TAG, "Invalid configuration: unexpected BSSID length -- " + responder);
-                continue;
-            }
-            System.arraycopy(responder.macAddress, 0, config.addr, 0, config.addr.length);
+            System.arraycopy(responder.macAddress.toByteArray(), 0, config.addr, 0,
+                    config.addr.length);
 
             try {
                 config.type = responder.supports80211mc ? RttType.TWO_SIDED : RttType.ONE_SIDED;
diff --git a/service/java/com/android/server/wifi/rtt/RttService.java b/service/java/com/android/server/wifi/rtt/RttService.java
index 5c2cec1..afffbee 100644
--- a/service/java/com/android/server/wifi/rtt/RttService.java
+++ b/service/java/com/android/server/wifi/rtt/RttService.java
@@ -43,14 +43,14 @@
 
     @Override
     public void onStart() {
-        Log.i(TAG, "Registering " + Context.WIFI_RTT2_SERVICE);
-        publishBinderService(Context.WIFI_RTT2_SERVICE, mImpl);
+        Log.i(TAG, "Registering " + Context.WIFI_RTT_RANGING_SERVICE);
+        publishBinderService(Context.WIFI_RTT_RANGING_SERVICE, mImpl);
     }
 
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
-            Log.i(TAG, "Starting " + Context.WIFI_RTT2_SERVICE);
+            Log.i(TAG, "Starting " + Context.WIFI_RTT_RANGING_SERVICE);
 
             WifiInjector wifiInjector = WifiInjector.getInstance();
             if (wifiInjector == null) {
diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
index ff722ae..3417f74 100644
--- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
+++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.hardware.wifi.V1_0.RttResult;
 import android.hardware.wifi.V1_0.RttStatus;
+import android.net.MacAddress;
 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
 import android.net.wifi.aware.IWifiAwareManager;
 import android.net.wifi.rtt.IRttCallback;
@@ -48,8 +49,6 @@
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
-import libcore.util.HexEncoding;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -350,11 +349,7 @@
         private void cancelRanging(RttRequestInfo rri) {
             ArrayList<byte[]> macAddresses = new ArrayList<>();
             for (ResponderConfig peer : rri.request.mRttPeers) {
-                if (peer.macAddress.length != 6) {
-                    Log.e(TAG, "Invalid configuration: unexpected BSSID length -- " + peer);
-                    continue;
-                }
-                macAddresses.add(peer.macAddress);
+                macAddresses.add(peer.macAddress.toByteArray());
             }
 
             mRttNative.rangeCancel(rri.cmdId, macAddresses);
@@ -472,7 +467,7 @@
                 try {
                     callback.onRangingFailure(RangingResultCallback.STATUS_CODE_FAIL);
                 } catch (RemoteException e) {
-                    Log.e(TAG,  "RttServiceSynchronized.queueRangingRequest: spamming, callback "
+                    Log.e(TAG, "RttServiceSynchronized.queueRangingRequest: spamming, callback "
                             + "failed -- " + e);
                 }
                 return;
@@ -609,7 +604,7 @@
         /**
          * Perform pre-execution throttling checks:
          * - If all uids in ws are in background then check last execution and block if request is
-         *   more frequent than permitted
+         * more frequent than permitted
          * - If executing (i.e. permitted) then update execution time
          *
          * Returns true to permit execution, false to abort it.
@@ -735,9 +730,10 @@
                         + ", peerIdToMacMap=" + peerIdToMacMap);
             }
 
-            for (ResponderConfig rttPeer: request.request.mRttPeers) {
+            for (ResponderConfig rttPeer : request.request.mRttPeers) {
                 if (rttPeer.peerHandle != null && rttPeer.macAddress == null) {
-                    rttPeer.macAddress = peerIdToMacMap.get(rttPeer.peerHandle.peerId);
+                    rttPeer.macAddress = MacAddress.fromBytes(
+                            peerIdToMacMap.get(rttPeer.peerHandle.peerId));
                 }
             }
 
@@ -799,20 +795,18 @@
          */
         private List<RangingResult> postProcessResults(RangingRequest request,
                 List<RttResult> results) {
-            Map<String, RttResult> resultEntries = new HashMap<>();
-            for (RttResult result: results) {
-                resultEntries.put(new String(HexEncoding.encode(result.addr)), result);
+            Map<MacAddress, RttResult> resultEntries = new HashMap<>();
+            for (RttResult result : results) {
+                resultEntries.put(MacAddress.fromBytes(result.addr), result);
             }
 
             List<RangingResult> finalResults = new ArrayList<>(request.mRttPeers.size());
 
-            for (ResponderConfig peer: request.mRttPeers) {
-                RttResult resultForRequest = resultEntries.get(
-                        new String(HexEncoding.encode(peer.macAddress)));
+            for (ResponderConfig peer : request.mRttPeers) {
+                RttResult resultForRequest = resultEntries.get(peer.macAddress);
                 if (resultForRequest == null) {
                     if (VDBG) {
-                        Log.v(TAG, "postProcessResults: missing=" + new String(
-                                HexEncoding.encode(peer.macAddress)));
+                        Log.v(TAG, "postProcessResults: missing=" + peer.macAddress);
                     }
                     if (peer.peerHandle == null) {
                         finalResults.add(
diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
index 72a7a27..9afe061 100644
--- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -35,7 +35,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -66,28 +65,16 @@
 
     private final Object mSettingsLock = new Object();
 
-    // Next scan settings to apply when the previous scan completes
-    private WifiNative.ScanSettings mPendingSingleScanSettings = null;
-    private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null;
-
     private ArrayList<ScanDetail> mNativeScanResults;
     private WifiScanner.ScanData mLatestSingleScanResult =
             new WifiScanner.ScanData(0, 0, new ScanResult[0]);
 
-    // Settings for the currently running scan, null if no scan active
+    // Settings for the currently running single scan, null if no scan active
     private LastScanSettings mLastScanSettings = null;
+    // Settings for the currently running pno scan, null if no scan active
+    private LastPnoScanSettings mLastPnoScanSettings = null;
 
-    // Pno related info.
-    private WifiNative.PnoSettings mPnoSettings = null;
-    private WifiNative.PnoEventHandler mPnoEventHandler;
     private final boolean mHwPnoScanSupported;
-    private final HwPnoDebouncer mHwPnoDebouncer;
-    private final HwPnoDebouncer.Listener mHwPnoDebouncerListener = new HwPnoDebouncer.Listener() {
-        public void onPnoScanFailed() {
-            Log.e(TAG, "Pno scan failure received");
-            reportPnoScanFailure();
-        }
-    };
 
     /**
      * Duration to wait before timing out a scan.
@@ -114,7 +101,6 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mEventHandler = new Handler(looper, this);
         mClock = clock;
-        mHwPnoDebouncer = new HwPnoDebouncer(mWifiNative, mAlarmManager, mEventHandler, mClock);
 
         // Check if the device supports HW PNO scans.
         mHwPnoScanSupported = mContext.getResources().getBoolean(
@@ -131,10 +117,9 @@
     @Override
     public void cleanup() {
         synchronized (mSettingsLock) {
-            mPendingSingleScanSettings = null;
-            mPendingSingleScanEventHandler = null;
             stopHwPnoScan();
             mLastScanSettings = null; // finally clear any active scan
+            mLastPnoScanSettings = null; // finally clear any active scan
         }
     }
 
@@ -162,14 +147,65 @@
             return false;
         }
         synchronized (mSettingsLock) {
-            if (mPendingSingleScanSettings != null
-                    || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
+            if (mLastScanSettings != null) {
                 Log.w(TAG, "A single scan is already running");
                 return false;
             }
-            mPendingSingleScanSettings = settings;
-            mPendingSingleScanEventHandler = eventHandler;
-            processPendingScans();
+
+            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
+            boolean reportFullResults = false;
+
+            for (int i = 0; i < settings.num_buckets; ++i) {
+                WifiNative.BucketSettings bucketSettings = settings.buckets[i];
+                if ((bucketSettings.report_events
+                                & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
+                    reportFullResults = true;
+                }
+                allFreqs.addChannels(bucketSettings);
+            }
+
+            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
+            if (settings.hiddenNetworks != null) {
+                int numHiddenNetworks =
+                        Math.min(settings.hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
+                for (int i = 0; i < numHiddenNetworks; i++) {
+                    hiddenNetworkSSIDSet.add(settings.hiddenNetworks[i].ssid);
+                }
+            }
+            mLastScanSettings = new LastScanSettings(
+                        mClock.getElapsedSinceBootMillis(),
+                        reportFullResults, allFreqs, eventHandler);
+
+            boolean success = false;
+            Set<Integer> freqs;
+            if (!allFreqs.isEmpty()) {
+                freqs = allFreqs.getScanFreqs();
+                success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
+                if (!success) {
+                    Log.e(TAG, "Failed to start scan, freqs=" + freqs);
+                }
+            } else {
+                // There is a scan request but no available channels could be scanned for.
+                // We regard it as a scan failure in this case.
+                Log.e(TAG, "Failed to start scan because there is no available channel to scan");
+            }
+            if (success) {
+                if (DBG) {
+                    Log.d(TAG, "Starting wifi scan for freqs=" + freqs);
+                }
+
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
+                        TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
+            } else {
+                // indicate scan failure async
+                mEventHandler.post(new Runnable() {
+                        public void run() {
+                            reportScanFailure();
+                        }
+                    });
+            }
+
             return true;
         }
     }
@@ -204,123 +240,6 @@
     private void handleScanTimeout() {
         Log.e(TAG, "Timed out waiting for scan result from wificond");
         reportScanFailure();
-        processPendingScans();
-    }
-
-    private boolean isDifferentPnoScanSettings(LastScanSettings newScanSettings) {
-        return (mLastScanSettings == null || !Arrays.equals(
-                newScanSettings.pnoNetworkList, mLastScanSettings.pnoNetworkList));
-    }
-
-    private void processPendingScans() {
-        synchronized (mSettingsLock) {
-            // Wait for the active scan result to come back to reschedule other scans,
-            // unless if HW pno scan is running. Hw PNO scans are paused it if there
-            // are other pending scans,
-            if (mLastScanSettings != null && !mLastScanSettings.hwPnoScanActive) {
-                return;
-            }
-
-            ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
-            Set<String> hiddenNetworkSSIDSet = new HashSet<>();
-            final LastScanSettings newScanSettings =
-                    new LastScanSettings(mClock.getElapsedSinceBootMillis());
-
-            if (mPendingSingleScanSettings != null) {
-                boolean reportFullResults = false;
-                ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection();
-                for (int i = 0; i < mPendingSingleScanSettings.num_buckets; ++i) {
-                    WifiNative.BucketSettings bucketSettings =
-                            mPendingSingleScanSettings.buckets[i];
-                    if ((bucketSettings.report_events
-                                    & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) {
-                        reportFullResults = true;
-                    }
-                    singleScanFreqs.addChannels(bucketSettings);
-                    allFreqs.addChannels(bucketSettings);
-                }
-                newScanSettings.setSingleScan(reportFullResults, singleScanFreqs,
-                        mPendingSingleScanEventHandler);
-
-                WifiNative.HiddenNetwork[] hiddenNetworks =
-                        mPendingSingleScanSettings.hiddenNetworks;
-                if (hiddenNetworks != null) {
-                    int numHiddenNetworks =
-                            Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
-                    for (int i = 0; i < numHiddenNetworks; i++) {
-                        hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid);
-                    }
-                }
-
-                mPendingSingleScanSettings = null;
-                mPendingSingleScanEventHandler = null;
-            }
-
-            if (newScanSettings.singleScanActive) {
-                boolean success = false;
-                Set<Integer> freqs;
-                if (!allFreqs.isEmpty()) {
-                    pauseHwPnoScan();
-                    freqs = allFreqs.getScanFreqs();
-                    success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
-                    if (!success) {
-                        Log.e(TAG, "Failed to start scan, freqs=" + freqs);
-                    }
-                } else {
-                    // There is a scan request but no available channels could be scanned for.
-                    // We regard it as a scan failure in this case.
-                    Log.e(TAG, "Failed to start scan because there is "
-                            + "no available channel to scan for");
-                }
-                if (success) {
-                    // TODO handle scan timeout
-                    if (DBG) {
-                        Log.d(TAG, "Starting wifi scan for freqs=" + freqs
-                                + ", single=" + newScanSettings.singleScanActive);
-                    }
-                    mLastScanSettings = newScanSettings;
-                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS,
-                            TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler);
-                } else {
-                    // indicate scan failure async
-                    mEventHandler.post(new Runnable() {
-                            public void run() {
-                                if (newScanSettings.singleScanEventHandler != null) {
-                                    newScanSettings.singleScanEventHandler
-                                            .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
-                                }
-                            }
-                        });
-                }
-            } else if (isHwPnoScanRequired()) {
-                newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler);
-                boolean status;
-                // If the PNO network list has changed from the previous request, ensure that
-                // we bypass the debounce logic and restart PNO scan.
-                if (isDifferentPnoScanSettings(newScanSettings)) {
-                    status = restartHwPnoScan(mPnoSettings);
-                } else {
-                    status = startHwPnoScan(mPnoSettings);
-                }
-                if (status) {
-                    mLastScanSettings = newScanSettings;
-                } else {
-                    Log.e(TAG, "Failed to start PNO scan");
-                    // indicate scan failure async
-                    mEventHandler.post(new Runnable() {
-                        public void run() {
-                            if (mPnoEventHandler != null) {
-                                mPnoEventHandler.onPnoScanFailed();
-                            }
-                            // Clean up PNO state, we don't want to continue PNO scanning.
-                            mPnoSettings = null;
-                            mPnoEventHandler = null;
-                        }
-                    });
-                }
-            }
-        }
     }
 
     @Override
@@ -330,16 +249,13 @@
                 Log.w(TAG, "Scan failed");
                 mAlarmManager.cancel(mScanTimeoutListener);
                 reportScanFailure();
-                processPendingScans();
                 break;
             case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
                 pollLatestScanDataForPno();
-                processPendingScans();
                 break;
             case WifiMonitor.SCAN_RESULTS_EVENT:
                 mAlarmManager.cancel(mScanTimeoutListener);
                 pollLatestScanData();
-                processPendingScans();
                 break;
             default:
                 // ignore unknown event
@@ -361,21 +277,19 @@
 
     private void reportPnoScanFailure() {
         synchronized (mSettingsLock) {
-            if (mLastScanSettings != null && mLastScanSettings.hwPnoScanActive) {
-                if (mLastScanSettings.pnoScanEventHandler != null) {
-                    mLastScanSettings.pnoScanEventHandler.onPnoScanFailed();
+            if (mLastPnoScanSettings != null) {
+                if (mLastPnoScanSettings.pnoScanEventHandler != null) {
+                    mLastPnoScanSettings.pnoScanEventHandler.onPnoScanFailed();
                 }
                 // Clean up PNO state, we don't want to continue PNO scanning.
-                mPnoSettings = null;
-                mPnoEventHandler = null;
-                mLastScanSettings = null;
+                mLastPnoScanSettings = null;
             }
         }
     }
 
     private void pollLatestScanDataForPno() {
         synchronized (mSettingsLock) {
-            if (mLastScanSettings == null) {
+            if (mLastPnoScanSettings == null) {
                  // got a scan before we started scanning or after scan was canceled
                 return;
             }
@@ -385,10 +299,8 @@
             for (int i = 0; i < mNativeScanResults.size(); ++i) {
                 ScanResult result = mNativeScanResults.get(i).getScanResult();
                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
-                if (timestamp_ms > mLastScanSettings.startTime) {
-                    if (mLastScanSettings.hwPnoScanActive) {
-                        hwPnoScanResults.add(result);
-                    }
+                if (timestamp_ms > mLastPnoScanSettings.startTime) {
+                    hwPnoScanResults.add(result);
                 } else {
                     numFilteredScanResults++;
                 }
@@ -398,25 +310,11 @@
                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
             }
 
-            if (mLastScanSettings.hwPnoScanActive
-                    && mLastScanSettings.pnoScanEventHandler != null) {
+            if (mLastPnoScanSettings.pnoScanEventHandler != null) {
                 ScanResult[] pnoScanResultsArray =
                         hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
-                mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
+                mLastPnoScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
             }
-            // On pno scan result event, we are expecting a mLastScanSettings for pno scan.
-            // However, if unlikey mLastScanSettings is for single scan, we need this part
-            // to protect from leaving WifiSingleScanStateMachine in a forever wait state.
-            if (mLastScanSettings.singleScanActive
-                    && mLastScanSettings.singleScanEventHandler != null) {
-                Log.w(TAG, "Polling pno scan result when single scan is active, reporting"
-                        + " single scan failure");
-                mLastScanSettings.singleScanEventHandler
-                        .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
-            }
-            // mLastScanSettings is for either single/batched scan or pno scan.
-            // We can safely set it to null when pno scan finishes.
-            mLastScanSettings = null;
         }
     }
 
@@ -445,8 +343,7 @@
                 ScanResult result = mNativeScanResults.get(i).getScanResult();
                 long timestamp_ms = result.timestamp / 1000; // convert us -> ms
                 if (timestamp_ms > mLastScanSettings.startTime) {
-                    if (mLastScanSettings.singleScanActive
-                            && mLastScanSettings.singleScanFreqs.containsChannel(
+                    if (mLastScanSettings.singleScanFreqs.containsChannel(
                                     result.frequency)) {
                         singleScanResults.add(result);
                     }
@@ -458,8 +355,7 @@
                 Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
             }
 
-            if (mLastScanSettings.singleScanActive
-                    && mLastScanSettings.singleScanEventHandler != null) {
+            if (mLastScanSettings.singleScanEventHandler != null) {
                 if (mLastScanSettings.reportSingleScanFullResults) {
                     for (ScanResult scanResult : singleScanResults) {
                         // ignore buckets scanned since there is only one bucket for a single scan
@@ -486,20 +382,11 @@
     }
 
     private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
-        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
+        return mWifiNative.startPnoScan(pnoSettings);
     }
 
     private void stopHwPnoScan() {
-        mHwPnoDebouncer.stopPnoScan();
-    }
-
-    private void pauseHwPnoScan() {
-        mHwPnoDebouncer.forceStopPnoScan();
-    }
-
-    private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) {
-        mHwPnoDebouncer.forceStopPnoScan();
-        return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
+        mWifiNative.stopPnoScan();
     }
 
     /**
@@ -511,26 +398,27 @@
         return (!isConnectedPno & mHwPnoScanSupported);
     }
 
-    private boolean isHwPnoScanRequired() {
-        synchronized (mSettingsLock) {
-            if (mPnoSettings == null) return false;
-            return isHwPnoScanRequired(mPnoSettings.isConnected);
-        }
-    }
-
     @Override
     public boolean setHwPnoList(WifiNative.PnoSettings settings,
             WifiNative.PnoEventHandler eventHandler) {
         synchronized (mSettingsLock) {
-            if (mPnoSettings != null) {
+            if (mLastPnoScanSettings != null) {
                 Log.w(TAG, "Already running a PNO scan");
                 return false;
             }
-            mPnoEventHandler = eventHandler;
-            mPnoSettings = settings;
+            if (!isHwPnoScanRequired(settings.isConnected)) {
+                return false;
+            }
 
-            // For wificond based PNO, we start the scan immediately when we set pno list.
-            processPendingScans();
+            if (startHwPnoScan(settings)) {
+                mLastPnoScanSettings = new LastPnoScanSettings(
+                            mClock.getElapsedSinceBootMillis(),
+                            settings.networkList, eventHandler);
+
+            } else {
+                Log.e(TAG, "Failed to start PNO scan");
+                reportPnoScanFailure();
+            }
             return true;
         }
     }
@@ -538,12 +426,11 @@
     @Override
     public boolean resetHwPnoList() {
         synchronized (mSettingsLock) {
-            if (mPnoSettings == null) {
+            if (mLastPnoScanSettings == null) {
                 Log.w(TAG, "No PNO scan running");
                 return false;
             }
-            mPnoEventHandler = null;
-            mPnoSettings = null;
+            mLastPnoScanSettings = null;
             // For wificond based PNO, we stop the scan immediately when we reset pno list.
             stopHwPnoScan();
             return true;
@@ -591,204 +478,36 @@
     }
 
     private static class LastScanSettings {
-        public long startTime;
-
-        LastScanSettings(long startTime) {
-            this.startTime = startTime;
-        }
-
-        // Single scan settings
-        public boolean singleScanActive = false;
-        public boolean reportSingleScanFullResults;
-        public ChannelCollection singleScanFreqs;
-        public WifiNative.ScanEventHandler singleScanEventHandler;
-
-        public void setSingleScan(boolean reportSingleScanFullResults,
+        LastScanSettings(long startTime,
+                boolean reportSingleScanFullResults,
                 ChannelCollection singleScanFreqs,
                 WifiNative.ScanEventHandler singleScanEventHandler) {
-            singleScanActive = true;
+            this.startTime = startTime;
             this.reportSingleScanFullResults = reportSingleScanFullResults;
             this.singleScanFreqs = singleScanFreqs;
             this.singleScanEventHandler = singleScanEventHandler;
         }
 
-        public boolean hwPnoScanActive = false;
-        public WifiNative.PnoNetwork[] pnoNetworkList;
-        public WifiNative.PnoEventHandler pnoScanEventHandler;
+        public long startTime;
+        public boolean reportSingleScanFullResults;
+        public ChannelCollection singleScanFreqs;
+        public WifiNative.ScanEventHandler singleScanEventHandler;
 
-        public void setHwPnoScan(
+    }
+
+    private static class LastPnoScanSettings {
+        LastPnoScanSettings(long startTime,
                 WifiNative.PnoNetwork[] pnoNetworkList,
                 WifiNative.PnoEventHandler pnoScanEventHandler) {
-            hwPnoScanActive = true;
+            this.startTime = startTime;
             this.pnoNetworkList = pnoNetworkList;
             this.pnoScanEventHandler = pnoScanEventHandler;
         }
+
+        public long startTime;
+        public WifiNative.PnoNetwork[] pnoNetworkList;
+        public WifiNative.PnoEventHandler pnoScanEventHandler;
+
     }
 
-    /**
-     * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO
-     * state too often which is not handled very well by some drivers.
-     * Note: This is not thread safe!
-     */
-    public static class HwPnoDebouncer {
-        public static final String PNO_DEBOUNCER_ALARM_TAG = TAG + "Pno Monitor";
-        private static final int MINIMUM_PNO_GAP_MS = 5 * 1000;
-
-        private final WifiNative mWifiNative;
-        private final AlarmManager mAlarmManager;
-        private final Handler mEventHandler;
-        private final Clock mClock;
-        private long mLastPnoChangeTimeStamp = -1L;
-        private boolean mExpectedPnoState = false;
-        private boolean mCurrentPnoState = false;;
-        private boolean mWaitForTimer = false;
-        private Listener mListener;
-        private WifiNative.PnoSettings mPnoSettings;
-
-        /**
-         * Interface used to indicate PNO scan notifications.
-         */
-        public interface Listener {
-            /**
-             * Used to indicate a delayed PNO scan request failure.
-             */
-            void onPnoScanFailed();
-        }
-
-        public HwPnoDebouncer(WifiNative wifiNative, AlarmManager alarmManager,
-                Handler eventHandler, Clock clock) {
-            mWifiNative = wifiNative;
-            mAlarmManager = alarmManager;
-            mEventHandler = eventHandler;
-            mClock = clock;
-        }
-
-        /**
-         * Enable PNO state in wificond
-         */
-        private boolean startPnoScanInternal() {
-            if (mCurrentPnoState) {
-                if (DBG) Log.d(TAG, "PNO state is already enable");
-                return true;
-            }
-            if (mPnoSettings == null) {
-                Log.e(TAG, "PNO state change to enable failed, no available Pno settings");
-                return false;
-            }
-            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
-            Log.d(TAG, "Remove all networks from supplicant before starting PNO scan");
-            mWifiNative.removeAllNetworks();
-            if (mWifiNative.startPnoScan(mPnoSettings)) {
-                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable");
-                mCurrentPnoState = true;
-                return true;
-            } else {
-                Log.e(TAG, "PNO state change to enable failed");
-                mCurrentPnoState = false;
-            }
-            return false;
-        }
-
-        /**
-         * Disable PNO state in wificond
-         */
-        private boolean stopPnoScanInternal() {
-            if (!mCurrentPnoState) {
-                if (DBG) Log.d(TAG, "PNO state is already disable");
-                return true;
-            }
-            mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
-            if (mWifiNative.stopPnoScan()) {
-                Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable");
-                mCurrentPnoState = false;
-                return true;
-            } else {
-                Log.e(TAG, "PNO state change to disable failed");
-                mCurrentPnoState = false;
-            }
-            return false;
-        }
-
-        private final AlarmManager.OnAlarmListener mAlarmListener =
-                new AlarmManager.OnAlarmListener() {
-            public void onAlarm() {
-                if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState);
-                if (mExpectedPnoState) {
-                    if (!startPnoScanInternal()) {
-                        if (mListener != null) {
-                            mListener.onPnoScanFailed();
-                        }
-                    }
-                } else {
-                    stopPnoScanInternal();
-                }
-                mWaitForTimer = false;
-            }
-        };
-
-        /**
-         * Enable/Disable PNO state. This method will debounce PNO scan requests.
-         * @param enable boolean indicating whether PNO is being enabled or disabled.
-         */
-        private boolean setPnoState(boolean enable) {
-            boolean isSuccess = true;
-            mExpectedPnoState = enable;
-            if (!mWaitForTimer) {
-                long timeDifference = mClock.getElapsedSinceBootMillis() - mLastPnoChangeTimeStamp;
-                if (timeDifference >= MINIMUM_PNO_GAP_MS) {
-                    if (enable) {
-                        isSuccess = startPnoScanInternal();
-                    } else {
-                        isSuccess = stopPnoScanInternal();
-                    }
-                } else {
-                    long alarmTimeout = MINIMUM_PNO_GAP_MS - timeDifference;
-                    Log.d(TAG, "Start PNO timer with delay " + alarmTimeout);
-                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            mClock.getElapsedSinceBootMillis() + alarmTimeout,
-                            PNO_DEBOUNCER_ALARM_TAG,
-                            mAlarmListener, mEventHandler);
-                    mWaitForTimer = true;
-                }
-            }
-            return isSuccess;
-        }
-
-        /**
-         * Start PNO scan
-         */
-        public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) {
-            if (DBG) Log.d(TAG, "Starting PNO scan");
-            mListener = listener;
-            mPnoSettings = pnoSettings;
-            if (!setPnoState(true)) {
-                mListener = null;
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Stop PNO scan
-         */
-        public void stopPnoScan() {
-            if (DBG) Log.d(TAG, "Stopping PNO scan");
-            setPnoState(false);
-            mListener = null;
-        }
-
-        /**
-         * Force stop PNO scanning. This method will bypass the debounce logic and stop PNO
-         * scan immediately.
-         */
-        public void forceStopPnoScan() {
-            if (DBG) Log.d(TAG, "Force stopping Pno scan");
-            // Cancel the debounce timer and stop PNO scan.
-            if (mWaitForTimer) {
-                mAlarmManager.cancel(mAlarmListener);
-                mWaitForTimer = false;
-            }
-            stopPnoScanInternal();
-        }
-    }
 }
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
index d3c072f..4ae7d13 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -22,7 +22,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.net.NetworkScoreManager;
-import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -202,24 +201,19 @@
      * current user.
      */
     private boolean isCurrentProfile(int uid) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            int currentUser = mWifiPermissionsWrapper.getCurrentUser();
-            int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
-            if (callingUserId == currentUser) {
-                return true;
-            } else {
-                List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
-                for (UserInfo user : userProfiles) {
-                    if (user.id == callingUserId) {
-                        return true;
-                    }
+        int currentUser = mWifiPermissionsWrapper.getCurrentUser();
+        int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
+        if (callingUserId == currentUser) {
+            return true;
+        } else {
+            List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
+            for (UserInfo user : userProfiles) {
+                if (user.id == callingUserId) {
+                    return true;
                 }
             }
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
         }
+        return false;
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
index 6fde01e..84aacdf 100644
--- a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
@@ -108,4 +108,16 @@
         return AppGlobals.getPackageManager().checkUidPermission(
                 Manifest.permission.CHANGE_WIFI_STATE, uid);
     }
+
+    /**
+     * Determines if the caller has local mac address permission.
+     *
+     * @param uid to check the permission for
+     * @return int representation of success or denied
+     * @throws RemoteException
+     */
+    public int getLocalMacAddressPermission(int uid) throws RemoteException {
+        return AppGlobals.getPackageManager().checkUidPermission(
+                Manifest.permission.LOCAL_MAC_ADDRESS, uid);
+    }
 }
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index 11e508d..b5c5844 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -68,7 +68,9 @@
 	android.test.runner \
 	wifi-service \
 	services \
-	android.hidl.manager-V1.0-java
+	android.hidl.manager-V1.0-java \
+	android.test.base \
+	android.test.mock
 
 # These must be explicitly included because they are not normally accessible
 # from apps.
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 18a1f8f..4290ada 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi;
 
 import static com.android.server.wifi.HalDeviceManager.START_HAL_RETRY_TIMES;
-import static com.android.server.wifi.HalDeviceManager.getName;
 
 import static junit.framework.Assert.assertEquals;
 
@@ -589,6 +588,48 @@
                 TestChipV2.CHIP_MODE_ID, 1);
     }
 
+    // TestChipV3
+
+    /**
+     * Validate creation of STA interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateStaInterfaceNoInitModeTestChipV3() throws Exception {
+        // Note: we expected 2 available callbacks since we now have 2 STAs possible. So
+        // we get callback 1 after creating the first STA (since we can create another STA),
+        // and we get callback 2 after destroying the first STA (since we can create another STA -
+        // as expected).
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.STA, "sta0",
+                TestChipV3.CHIP_MODE_ID, 2);
+    }
+
+    /**
+     * Validate creation of AP interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateApInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.AP, "ap0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
+    /**
+     * Validate creation of P2P interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateP2pInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.P2P, "p2p0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
+    /**
+     * Validate creation of NAN interface from blank start-up. The remove interface.
+     */
+    @Test
+    public void testCreateNanInterfaceNoInitModeTestChipV3() throws Exception {
+        runCreateSingleXxxInterfaceNoInitMode(new TestChipV3(), IfaceType.NAN, "nan0",
+                TestChipV3.CHIP_MODE_ID, 1);
+    }
+
     //////////////////////////////////////////////////////////////////////////////////////
     // TestChipV1 Specific Tests
     //////////////////////////////////////////////////////////////////////////////////////
@@ -982,8 +1023,9 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV1() throws Exception {
-        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV1(), false,
-                TestChipV1.STA_CHIP_MODE_ID);
+        // staAvailCallbacks=0: there is no second STA so will never get available callback after
+        // first is created.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV1(), 0, TestChipV1.STA_CHIP_MODE_ID);
     }
 
     /**
@@ -1298,7 +1340,9 @@
      */
     @Test
     public void testP2pAndNanInteractionsTestChipV2() throws Exception {
-        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), true, TestChipV2.CHIP_MODE_ID);
+        // staAvailCallbacks=5: after every substantial change will get a callback since second
+        // STA is always available.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV2(), 5, TestChipV2.CHIP_MODE_ID);
     }
 
     /**
@@ -1351,6 +1395,273 @@
         assertEquals(correctResults, results);
     }
 
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TestChipV3 Specific Tests
+    //////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Validate a flow sequence for test chip 3:
+     * - create STA
+     * - create P2P
+     * - request NAN: failure
+     * - create AP: should tear down P2P first
+     * - create STA: will get refused
+     * - create AP: will get refused
+     * - request P2P: failure
+     * - tear down AP
+     * - create STA
+     * - create STA: will get refused
+     * - create NAN: should tear down last created STA
+     * - create STA: will get refused
+     */
+    @Test
+    public void testInterfaceCreationFlowTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        InterfaceDestroyedListener staDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        InterfaceDestroyedListener staDestroyedListener2 = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener apDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener apAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener p2pDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener p2pAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InterfaceDestroyedListener nanDestroyedListener = mock(
+                InterfaceDestroyedListener.class);
+        HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
+                HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+        InOrder inOrderStaAvail = inOrder(staAvailListener);
+        InOrder inOrderApAvail = inOrder(apAvailListener);
+        InOrder inOrderP2pAvail = inOrder(p2pAvailListener);
+        InOrder inOrderNanAvail = inOrder(nanAvailListener);
+
+        // register listeners for interface availability
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
+                mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.AP, apAvailListener, mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.P2P, p2pAvailListener,
+                mHandler);
+        mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
+                mHandler);
+        mTestLooper.dispatchAll();
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // create STA
+        when(mClock.getUptimeSinceBootMillis()).thenReturn(15L);
+        IWifiIface staIface = validateInterfaceSequence(chipMock,
+                false, // chipModeValid
+                -1000, // chipModeId (only used if chipModeValid is true)
+                IfaceType.STA, // ifaceTypeToCreate
+                "sta0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                staDestroyedListener, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("STA interface wasn't created", staIface, IsNull.notNullValue());
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // create P2P
+        IWifiIface p2pIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.P2P, // ifaceTypeToCreate
+                "p2p0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                p2pDestroyedListener, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("P2P interface wasn't created", p2pIface, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+
+        // request NAN: should fail
+        IWifiIface nanIface = mDut.createNanIface(null, null);
+        collector.checkThat("NAN should not be created", nanIface, IsNull.nullValue());
+
+        // create AP: will destroy P2P
+        IWifiIface apIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.AP, // ifaceTypeToCreate
+                "ap0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                apDestroyedListener, // destroyedListener
+                null, // availableListener (already registered)
+                new InterfaceDestroyedListenerWithIfaceName("p2p0", p2pDestroyedListener)
+        );
+        collector.checkThat("AP interface wasn't created", apIface, IsNull.notNullValue());
+        verify(chipMock.chip).removeP2pIface("p2p0");
+
+        // request STA2: should fail
+        IWifiIface staIface2 = mDut.createStaIface(null, null);
+        collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
+
+        // request AP2: should fail
+        IWifiIface apIface2 = mDut.createApIface(null, null);
+        collector.checkThat("AP2 should not be created", apIface2, IsNull.nullValue());
+
+        // request P2P: should fail
+        p2pIface = mDut.createP2pIface(null, null);
+        collector.checkThat("P2P should not be created", p2pIface, IsNull.nullValue());
+
+        // tear down AP
+        mDut.removeIface(apIface);
+        mTestLooper.dispatchAll();
+
+        inOrderStaAvail.verify(staAvailListener).onAvailableForRequest();
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+        verify(chipMock.chip).removeApIface("ap0");
+        verify(apDestroyedListener).onDestroyed(getName(apIface));
+
+        // create STA2: using a later clock
+        when(mClock.getUptimeSinceBootMillis()).thenReturn(20L);
+        staIface2 = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.STA, // ifaceTypeToCreate
+                "sta1", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                staDestroyedListener2, // destroyedListener
+                null // availableListener (already registered)
+        );
+        collector.checkThat("STA 2 interface wasn't created", staIface2, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        inOrderNanAvail.verify(nanAvailListener).onAvailableForRequest();
+
+        // request STA3: should fail
+        IWifiIface staIface3 = mDut.createStaIface(null, null);
+        collector.checkThat("STA3 should not be created", staIface3, IsNull.nullValue());
+
+        // create NAN: should destroy the last created STA (STA2)
+        nanIface = validateInterfaceSequence(chipMock,
+                true, // chipModeValid
+                TestChipV3.CHIP_MODE_ID, // chipModeId
+                IfaceType.NAN, // ifaceTypeToCreate
+                "nan0", // ifaceName
+                TestChipV3.CHIP_MODE_ID, // finalChipMode
+                null, // tearDownList
+                nanDestroyedListener, // destroyedListener
+                null, // availableListener (already registered)
+                new InterfaceDestroyedListenerWithIfaceName(
+                        getName(staIface2), staDestroyedListener2)
+        );
+        collector.checkThat("NAN interface wasn't created", nanIface, IsNull.notNullValue());
+
+        inOrderApAvail.verify(apAvailListener).onAvailableForRequest();
+        inOrderP2pAvail.verify(p2pAvailListener).onAvailableForRequest();
+        verify(chipMock.chip).removeStaIface("sta1");
+        verify(staDestroyedListener2).onDestroyed(getName(staIface2));
+
+        // request STA2: should fail
+        staIface2 = mDut.createStaIface(null, null);
+        collector.checkThat("STA2 should not be created", staIface2, IsNull.nullValue());
+
+        verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener,
+                staDestroyedListener2, apDestroyedListener, p2pDestroyedListener,
+                nanDestroyedListener, staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener, staAvailListener, apAvailListener, p2pAvailListener,
+                nanAvailListener);
+    }
+
+    /**
+     * Validate P2P and NAN interactions. Expect:
+     * - STA created
+     * - NAN created
+     * - When P2P requested:
+     *   - NAN torn down
+     *   - P2P created
+     * - NAN creation refused
+     * - When P2P destroyed:
+     *   - get nan available listener
+     *   - Can create NAN when requested
+     */
+    @Test
+    public void testP2pAndNanInteractionsTestChipV3() throws Exception {
+        // staAvailCallbacks=2: only get callback (for second STA) when P2P or NAN are down.
+        runP2pAndNanExclusiveInteractionsTestChip(new TestChipV3(), 2, TestChipV3.CHIP_MODE_ID);
+    }
+
+    /**
+     * Validate that the getSupportedIfaceTypes API works when requesting for all chips.
+     */
+    @Test
+    public void testGetSupportedIfaceTypesAllTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        // try API
+        Set<Integer> results = mDut.getSupportedIfaceTypes();
+
+        // verify results
+        Set<Integer> correctResults = new HashSet<>();
+        correctResults.add(IfaceType.AP);
+        correctResults.add(IfaceType.STA);
+        correctResults.add(IfaceType.P2P);
+        correctResults.add(IfaceType.NAN);
+
+        assertEquals(correctResults, results);
+    }
+
+    /**
+     * Validate that the getSupportedIfaceTypes API works when requesting for a specific chip.
+     */
+    @Test
+    public void testGetSupportedIfaceTypesOneChipTestChipV3() throws Exception {
+        TestChipV3 chipMock = new TestChipV3();
+        chipMock.initialize();
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+                mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+        executeAndValidateStartupSequence();
+
+        // try API
+        Set<Integer> results = mDut.getSupportedIfaceTypes(chipMock.chip);
+
+        // verify results
+        Set<Integer> correctResults = new HashSet<>();
+        correctResults.add(IfaceType.AP);
+        correctResults.add(IfaceType.STA);
+        correctResults.add(IfaceType.P2P);
+        correctResults.add(IfaceType.NAN);
+
+        assertEquals(correctResults, results);
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////
     // utilities
     ///////////////////////////////////////////////////////////////////////////////////////
@@ -1480,7 +1791,7 @@
      * line of NAN and P2P being exclusive).
      */
     public void runP2pAndNanExclusiveInteractionsTestChip(ChipMockBase chipMock,
-            boolean duplicateStas, int onlyChipMode) throws Exception {
+            int staAvailCallbacks, int onlyChipMode) throws Exception {
         chipMock.initialize();
         mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
                 mManagerStatusListenerMock);
@@ -1567,9 +1878,9 @@
                 nanAvailListener // availableListener
         );
 
-        if (duplicateStas) {
+        if (staAvailCallbacks != 0) {
             // if there are duplicate STAs then expect an available callback for each step above
-            verify(staAvailListener, times(5)).onAvailableForRequest();
+            verify(staAvailListener, times(staAvailCallbacks)).onAvailableForRequest();
         }
 
         verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
@@ -2149,4 +2460,72 @@
                     .getAvailableModes(any(IWifiChip.getAvailableModesCallback.class));
         }
     }
+
+    // test chip configuration V3:
+    // mode:
+    //    STA + (STA || AP)
+    //    STA + (NAN || P2P)
+    private class TestChipV3 extends ChipMockBase {
+        // only mode (different number from any in other TestChips so can catch test errors)
+        static final int CHIP_MODE_ID = 7;
+
+        void initialize() throws Exception {
+            super.initialize();
+
+            // chip Id configuration
+            ArrayList<Integer> chipIds;
+            chipId = 15;
+            chipIds = new ArrayList<>();
+            chipIds.add(chipId);
+            doAnswer(new GetChipIdsAnswer(mStatusOk, chipIds)).when(mWifiMock).getChipIds(
+                    any(IWifi.getChipIdsCallback.class));
+
+            doAnswer(new GetChipAnswer(mStatusOk, chip)).when(mWifiMock).getChip(eq(15),
+                    any(IWifi.getChipCallback.class));
+
+            // initialize dummy chip modes
+            IWifiChip.ChipMode cm;
+            IWifiChip.ChipIfaceCombination cic;
+            IWifiChip.ChipIfaceCombinationLimit cicl;
+
+            //   Mode 0 (only one): 1xSTA + 1x{STA,AP}, 1xSTA + 1x{P2P,NAN}
+            availableModes = new ArrayList<>();
+            cm = new IWifiChip.ChipMode();
+            cm.id = CHIP_MODE_ID;
+
+            cic = new IWifiChip.ChipIfaceCombination();
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cic.limits.add(cicl);
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cicl.types.add(IfaceType.AP);
+            cic.limits.add(cicl);
+
+            cm.availableCombinations.add(cic);
+
+            cic = new IWifiChip.ChipIfaceCombination();
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.STA);
+            cic.limits.add(cicl);
+
+            cicl = new IWifiChip.ChipIfaceCombinationLimit();
+            cicl.maxIfaces = 1;
+            cicl.types.add(IfaceType.P2P);
+            cicl.types.add(IfaceType.NAN);
+            cic.limits.add(cicl);
+
+            cm.availableCombinations.add(cic);
+            availableModes.add(cm);
+
+            doAnswer(new GetAvailableModesAnswer(this)).when(chip)
+                    .getAvailableModes(any(IWifiChip.getAvailableModesCallback.class));
+        }
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index cda9cf5..93db347 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -33,17 +33,26 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.*;
 
+import android.app.test.TestAlarmManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.InterfaceConfiguration;
+import android.net.Uri;
 import android.net.wifi.IApInterface;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.INetworkManagementService;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
 
+import com.android.internal.R;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.net.BaseNetworkObserver;
 
 import org.junit.Before;
@@ -77,11 +86,16 @@
 
     private final WifiConfiguration mDefaultApConfig = createDefaultApConfig();
 
+    private ContentObserver mContentObserver;
+    private TestLooper mLooper;
+    private TestAlarmManager mAlarmManager;
+
     @Mock Context mContext;
-    TestLooper mLooper;
+    @Mock Resources mResources;
     @Mock WifiNative mWifiNative;
     @Mock SoftApManager.Listener mListener;
     @Mock InterfaceConfiguration mInterfaceConfiguration;
+    @Mock FrameworkFacade mFrameworkFacade;
     @Mock IApInterface mApInterface;
     @Mock INetworkManagementService mNmService;
     @Mock WifiApConfigStore mWifiApConfigStore;
@@ -104,6 +118,15 @@
         when(mWifiNative.startSoftAp(any(), any())).thenReturn(true);
         when(mWifiNative.stopSoftAp()).thenReturn(true);
         when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(true);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+        mAlarmManager = new TestAlarmManager();
+        when(mContext.getSystemService(Context.ALARM_SERVICE))
+                .thenReturn(mAlarmManager.getAlarmManager());
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getInteger(R.integer.config_wifi_framework_soft_ap_timeout_delay))
+                .thenReturn(600000);
     }
 
     private WifiConfiguration createDefaultApConfig() {
@@ -118,11 +141,10 @@
         }
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
-                                                           mApInterface,
-                                                           TEST_INTERFACE_NAME,
                                                            mNmService,
                                                            mWifiApConfigStore,
                                                            config,
@@ -184,16 +206,21 @@
     /** Tests softap startup if default config fails to load. **/
     @Test
     public void startSoftApDefaultConfigFailedToLoad() throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+
         when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
         SoftApModeConfiguration nullApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
-                                                           mApInterface,
-                                                           TEST_INTERFACE_NAME,
                                                            mNmService,
                                                            mWifiApConfigStore,
                                                            nullApConfig,
@@ -217,6 +244,324 @@
     }
 
     /**
+     * Test that failure to create the SoftApInterface increments the corresponding metrics and
+     * proper state updates are sent out.
+     */
+    @Test
+    public void testSetupForSoftApModeHalFailureIncrementsMetrics() throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(TEST_INTERFACE_NAME))
+                .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
+
+        SoftApModeConfiguration config = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+
+        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+        SoftApModeConfiguration nullApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           TEST_COUNTRY_CODE,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           nullApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
+                nullApConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
+    }
+
+    /**
+     * Test that failure to create the SoftApInterface due to a wificond error increments
+     * the corresponding metrics and proper state updates are sent out.
+     */
+    @Test
+    public void testSetupForSoftApModeWificondFailureIncrementsMetrics() throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(TEST_INTERFACE_NAME))
+                .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
+
+        SoftApModeConfiguration config = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+
+        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+        SoftApModeConfiguration nullApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           TEST_COUNTRY_CODE,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           nullApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
+                nullApConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
+    }
+
+    /**
+     * Test that failure to retrieve the SoftApInterface increments the corresponding metrics
+     * and proper state updates are sent out.
+     */
+    @Test
+    public void testSetupForSoftApModeNullApInterfaceFailureIncrementsMetrics() throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, null));
+
+        SoftApModeConfiguration config = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+
+        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+        SoftApModeConfiguration nullApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           TEST_COUNTRY_CODE,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           nullApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
+                nullApConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+    }
+
+    /**
+     * Test that an empty SoftApInterface name is detected as a failure and increments the
+     * corresponding metrics and proper state updates are sent out.
+     */
+    @Test
+    public void testSetupForSoftApModeEmptyInterfaceNameFailureIncrementsMetrics()
+            throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn("");
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
+        SoftApModeConfiguration config = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+
+        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+        SoftApModeConfiguration nullApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           TEST_COUNTRY_CODE,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           nullApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, "",
+                nullApConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+    }
+
+    /**
+     * Test that catching a RemoteException when retrieving the SoftApInterface name
+     * is detected as a failure and increments the corresponding metrics and proper
+     * state updates are sent out.
+     */
+    @Test
+    public void testSetupForSoftApModeInterfaceNameRemoteExceptionFailureIncrementsMetrics()
+            throws Exception {
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        doThrow(new RemoteException()).when(mApInterface).getInterfaceName();
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
+        SoftApModeConfiguration config = new SoftApModeConfiguration(
+                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+
+        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+        SoftApModeConfiguration nullApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           TEST_COUNTRY_CODE,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           nullApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        checkApStateChangedBroadcast(intentCaptor.getValue(), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_DISABLED, WifiManager.SAP_START_FAILURE_GENERAL, null,
+                nullApConfig.getTargetMode());
+
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+    }
+
+
+    /**
+     * Tests that the generic error is propagated and properly reported when starting softap and we
+     * cannot register a wificond death handler.
+     */
+    @Test
+    public void startSoftApFailRegisterWificondDeathHandlerGeneralError() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+        config.apBand = WifiConfiguration.AP_BAND_5GHZ;
+        config.SSID = TEST_SSID;
+        SoftApModeConfiguration softApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
+
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
+        when(mWifiNative.registerWificondDeathHandler(any())).thenReturn(false);
+
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           null,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           softApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        List<Intent> capturedIntents = intentCaptor.getAllValues();
+        checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING,
+                WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
+                softApConfig.getTargetMode());
+        checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_ENABLING, WifiManager.SAP_START_FAILURE_GENERAL, TEST_INTERFACE_NAME,
+                softApConfig.getTargetMode());
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+    }
+
+    /**
+     * Tests that the generic error is propagated and properly reported when starting softap and we
+     * catch a RemoteException when attempting to register a NetworkObserver.
+     */
+    @Test
+    public void startSoftApFailNetworkObserverRemoteExceptionGeneralError() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+        config.apBand = WifiConfiguration.AP_BAND_5GHZ;
+        config.SSID = TEST_SSID;
+        SoftApModeConfiguration softApConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
+
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
+        doThrow(new RemoteException()).when(mNmService).registerObserver(any());
+
+        SoftApManager newSoftApManager = new SoftApManager(mContext,
+                                                           mLooper.getLooper(),
+                                                           mFrameworkFacade,
+                                                           mWifiNative,
+                                                           null,
+                                                           mListener,
+                                                           mNmService,
+                                                           mWifiApConfigStore,
+                                                           softApConfig,
+                                                           mWifiMetrics);
+        mLooper.dispatchAll();
+        newSoftApManager.start();
+        mLooper.dispatchAll();
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(),
+                eq(UserHandle.ALL));
+
+        List<Intent> capturedIntents = intentCaptor.getAllValues();
+        checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING,
+                WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
+                softApConfig.getTargetMode());
+        checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_FAILED,
+                WIFI_AP_STATE_ENABLING, WifiManager.SAP_START_FAILURE_GENERAL, TEST_INTERFACE_NAME,
+                softApConfig.getTargetMode());
+        // we do once on enter of idle, and another after the failure
+        verify(mWifiNative, times(2)).deregisterWificondDeathHandler();
+        verify(mNmService).unregisterObserver(any());
+        verify(mWifiMetrics).incrementSoftApStartResult(false,
+                WifiManager.SAP_START_FAILURE_GENERAL);
+    }
+
+    /**
      * Tests that the generic error is propagated and properly reported when starting softap and the
      * specified channel cannot be used.
      */
@@ -228,15 +573,19 @@
         SoftApModeConfiguration softApConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
 
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
         when(mWifiNative.isHalStarted()).thenReturn(true);
 
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            null,
                                                            mListener,
-                                                           mApInterface,
-                                                           TEST_INTERFACE_NAME,
                                                            mNmService,
                                                            mWifiApConfigStore,
                                                            softApConfig,
@@ -273,13 +622,18 @@
 
         when(mWifiNative.isHalStarted()).thenReturn(true);
 
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
+
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
-                                                           mApInterface,
-                                                           TEST_INTERFACE_NAME,
                                                            mNmService,
                                                            mWifiApConfigStore,
                                                            softApConfig,
@@ -307,15 +661,19 @@
     @Test
     public void startSoftApApInterfaceFailedToStart() throws Exception {
         when(mWifiNative.startSoftAp(any(), any())).thenReturn(false);
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
         SoftApModeConfiguration softApModeConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mDefaultApConfig);
+
         SoftApManager newSoftApManager = new SoftApManager(mContext,
                                                            mLooper.getLooper(),
+                                                           mFrameworkFacade,
                                                            mWifiNative,
                                                            TEST_COUNTRY_CODE,
                                                            mListener,
-                                                           mApInterface,
-                                                           TEST_INTERFACE_NAME,
                                                            mNmService,
                                                            mWifiApConfigStore,
                                                            softApModeConfig,
@@ -415,30 +773,11 @@
         mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
                 TEST_NUM_CONNECTED_CLIENTS);
         mLooper.dispatchAll();
-        assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(TEST_NUM_CONNECTED_CLIENTS,
                 apConfig.getTargetMode());
     }
 
     @Test
-    public void handlesNumAssociatedStationsWhenNotStarted() throws Exception {
-        SoftApModeConfiguration apConfig =
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
-        startSoftApAndVerifyEnabled(apConfig);
-        mSoftApManager.stop();
-        mLooper.dispatchAll();
-
-        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
-                TEST_NUM_CONNECTED_CLIENTS);
-        mLooper.dispatchAll();
-        /* Verify numAssociatedStations is not updated when soft AP is not started */
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
-        verify(mWifiMetrics, never()).addSoftApNumAssociatedStationsChangedEvent(
-                TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
-    }
-
-    @Test
     public void handlesInvalidNumAssociatedStations() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
@@ -449,41 +788,155 @@
         /* Invalid values should be ignored */
         mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(-1);
         mLooper.dispatchAll();
-        assertEquals(TEST_NUM_CONNECTED_CLIENTS, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics, times(1)).addSoftApNumAssociatedStationsChangedEvent(
                 TEST_NUM_CONNECTED_CLIENTS, apConfig.getTargetMode());
     }
 
     @Test
-    public void resetsNumAssociatedStationsWhenStopped() throws Exception {
+    public void schedulesTimeoutTimerOnStart() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
-        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
-                TEST_NUM_CONNECTED_CLIENTS);
-        mSoftApManager.stop();
-        mLooper.dispatchAll();
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+        // Verify timer is scheduled
+        verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
     }
 
     @Test
-    public void resetsNumAssociatedStationsOnFailure() throws Exception {
+    public void cancelsTimeoutTimerOnStop() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApManager.stop();
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void cancelsTimeoutTimerOnNewClientsConnect() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+                TEST_NUM_CONNECTED_CLIENTS);
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void schedulesTimeoutTimerWhenAllClientsDisconnect() throws Exception {
         SoftApModeConfiguration apConfig =
                 new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
         startSoftApAndVerifyEnabled(apConfig);
 
         mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
                 TEST_NUM_CONNECTED_CLIENTS);
-        /* Force soft AP to fail */
-        mDeathListenerCaptor.getValue().onDeath();
         mLooper.dispatchAll();
-        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
-                WifiManager.SAP_START_FAILURE_GENERAL);
+        // Verify timer is canceled at this point
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
 
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
-        verify(mWifiMetrics).addSoftApUpChangedEvent(false, apConfig.getTargetMode());
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+        mLooper.dispatchAll();
+        // Verify timer is scheduled again
+        verify(mAlarmManager.getAlarmManager(), times(2)).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void stopsSoftApOnTimeoutMessage() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        mAlarmManager.dispatch(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG);
+        mLooper.dispatchAll();
+
+        verify(mWifiNative).stopSoftAp();
+    }
+
+    @Test
+    public void cancelsTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        mContentObserver.onChange(false);
+        mLooper.dispatchAll();
+
+        // Verify timer is canceled
+        verify(mAlarmManager.getAlarmManager()).cancel(any(WakeupMessage.class));
+    }
+
+    @Test
+    public void schedulesTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1);
+        mContentObserver.onChange(false);
+        mLooper.dispatchAll();
+
+        // Verify timer is scheduled
+        verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void doesNotScheduleTimeoutTimerOnStartWhenTimeoutIsDisabled() throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+
+        // Verify timer is not scheduled
+        verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void doesNotScheduleTimeoutTimerWhenAllClientsDisconnectButTimeoutIsDisabled()
+            throws Exception {
+        // start with timeout toggle disabled
+        when(mFrameworkFacade.getIntegerSetting(
+                mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(0);
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        // add some clients
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(
+                TEST_NUM_CONNECTED_CLIENTS);
+        mLooper.dispatchAll();
+        // remove all clients
+        mSoftApListenerCaptor.getValue().onNumAssociatedStationsChanged(0);
+        mLooper.dispatchAll();
+        // Verify timer is not scheduled
+        verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(),
+                eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG), any(), any());
+    }
+
+    @Test
+    public void unregistersSettingsObserverOnStop() throws Exception {
+        SoftApModeConfiguration apConfig =
+                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null);
+        startSoftApAndVerifyEnabled(apConfig);
+        mSoftApManager.stop();
+        mLooper.dispatchAll();
+
+        verify(mFrameworkFacade).unregisterContentObserver(eq(mContext), eq(mContentObserver));
     }
 
     /** Starts soft AP and verifies that it is enabled successfully. */
@@ -506,8 +959,15 @@
             expectedConfig = new WifiConfiguration(config);
         }
 
+        ArgumentCaptor<ContentObserver> observerCaptor = ArgumentCaptor.forClass(
+                ContentObserver.class);
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
 
+        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+        when(mWifiNative.setupForSoftApMode(eq(TEST_INTERFACE_NAME)))
+                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+
         mSoftApManager.start();
         mLooper.dispatchAll();
         order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
@@ -530,8 +990,10 @@
         checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED,
                 WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, TEST_INTERFACE_NAME,
                 softApConfig.getTargetMode());
-        assertEquals(0, mSoftApManager.getNumAssociatedStations());
         verify(mWifiMetrics).addSoftApUpChangedEvent(true, softApConfig.mTargetMode);
+        verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(true),
+                observerCaptor.capture());
+        mContentObserver = observerCaptor.getValue();
     }
 
     /** Verifies that soft AP was not disabled. */
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
new file mode 100644
index 0000000..6db9cf5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupConfigStoreDataTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link WakeupConfigStoreData}.
+ */
+public class WakeupConfigStoreDataTest {
+
+    @Mock private WakeupConfigStoreData.DataSource<Boolean> mActiveDataSource;
+    @Mock private WakeupConfigStoreData.DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
+
+    private WakeupConfigStoreData mWakeupConfigData;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWakeupConfigData = new WakeupConfigStoreData(mActiveDataSource, mNetworkDataSource);
+    }
+
+    /**
+     * Helper function for serializing configuration data to a XML block.
+     *
+     * @param shared Flag indicating serializing shared or user configurations
+     * @return byte[] of the XML data
+     */
+    private byte[] serializeData(boolean shared) throws Exception {
+        final XmlSerializer out = new FastXmlSerializer();
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.serializeData(out, shared);
+        out.flush();
+        return outputStream.toByteArray();
+    }
+
+    /**
+     * Helper function for parsing configuration data from a XML block.
+     *
+     * @param data XML data to parse from
+     * @param shared Flag indicating parsing of shared or user configurations
+     */
+    private void deserializeData(byte[] data, boolean shared) throws Exception {
+        final XmlPullParser in = Xml.newPullParser();
+        final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+        in.setInput(inputStream, StandardCharsets.UTF_8.name());
+        mWakeupConfigData.deserializeData(in, in.getDepth(), shared);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to serialize data
+     * to the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void serializeShareDataThrowsException() throws Exception {
+        serializeData(true /* shared */);
+    }
+
+    /**
+     * Verify that a XmlPullParserException will be thrown when attempting to deserialize
+     * data from the share store.
+     */
+    @Test(expected = XmlPullParserException.class)
+    public void deserializeShareDataThrowsException() throws Exception {
+        deserializeData(new byte[0], true /* shared */);
+    }
+
+    /**
+     * Can correctly serialize and deserialize the empty set case.
+     */
+    @Test
+    public void deserializeSerializedData_emptySet() throws Exception {
+        Set<ScanResultMatchInfo> networks = Collections.emptySet();
+        boolean isActive = false;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Can correctly serialize and deserialize a standard working case.
+     */
+    @Test
+    public void deserializeSerializedData() throws Exception {
+        ScanResultMatchInfo network1 = new ScanResultMatchInfo();
+        network1.networkSsid = "ssid 1";
+        network1.networkType = 0;
+
+        ScanResultMatchInfo network2 = new ScanResultMatchInfo();
+        network2.networkSsid = ",.23;4@, .#,%(,";
+        network2.networkType = 1;
+
+        ScanResultMatchInfo network3 = new ScanResultMatchInfo();
+        network3.networkSsid = "";
+        network3.networkType = 2;
+
+        Set<ScanResultMatchInfo> networks = Sets.newArraySet(network1, network2, network3);
+        boolean isActive = true;
+
+        when(mActiveDataSource.getData()).thenReturn(isActive);
+        when(mNetworkDataSource.getData()).thenReturn(networks);
+
+        byte[] bytes = serializeData(false /* shared */);
+        deserializeData(bytes, false /* shared */);
+
+        verify(mActiveDataSource).setData(eq(isActive));
+        verify(mNetworkDataSource).setData(eq(networks));
+    }
+
+    /**
+     * Verify that reset data wipes the data sources.
+     */
+    @Test
+    public void resetDataWipesDataSources() {
+        mWakeupConfigData.resetData(false /* shared */);
+
+        verify(mActiveDataSource).setData(false);
+        verify(mNetworkDataSource).setData(eq(Collections.emptySet()));
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
index 742c520..5e45570 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -29,12 +31,18 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
 /**
  * Unit tests for {@link WakeupController}.
  */
 public class WakeupControllerTest {
 
     @Mock private Context mContext;
+    @Mock private WakeupLock mWakeupLock;
+    @Mock private WifiConfigManager mWifiConfigManager;
+    @Mock private WifiConfigStore mWifiConfigStore;
     @Mock private FrameworkFacade mFrameworkFacade;
 
     private TestLooper mLooper;
@@ -47,6 +55,11 @@
         mLooper = new TestLooper();
     }
 
+    private WakeupController newWakeupController() {
+        return new WakeupController(mContext, mLooper.getLooper(), mWakeupLock, mWifiConfigManager,
+                mWifiConfigStore, mFrameworkFacade);
+    }
+
     /**
      * Verify WakeupController is enabled when the settings toggle is true.
      */
@@ -54,8 +67,7 @@
     public void verifyEnabledWhenToggledOn() {
         when(mFrameworkFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(1);
-        mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
-                mFrameworkFacade);
+        mWakeupController = newWakeupController();
 
         assertTrue(mWakeupController.isEnabled());
     }
@@ -67,9 +79,30 @@
     public void verifyDisabledWhenToggledOff() {
         when(mFrameworkFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(0);
-        mWakeupController = new WakeupController(mContext, mLooper.getLooper(),
-                mFrameworkFacade);
+        mWakeupController = newWakeupController();
 
         assertFalse(mWakeupController.isEnabled());
     }
+
+    /**
+     * Verify WakeupController registers its store data with the WifiConfigStore on construction.
+     */
+    @Test
+    public void registersWakeupConfigStoreData() {
+        mWakeupController = newWakeupController();
+        verify(mWifiConfigStore).registerStoreData(any(WakeupConfigStoreData.class));
+    }
+
+    /**
+     * Verify that dump calls also dump the state of the WakeupLock.
+     */
+    @Test
+    public void dumpIncludesWakeupLock() {
+        mWakeupController = newWakeupController();
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        PrintWriter writer = new PrintWriter(stream);
+        mWakeupController.dump(null, writer, null);
+
+        verify(mWakeupLock).dump(null, writer, null);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
index bad9e3a..7144ecf 100644
--- a/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WakeupLockTest.java
@@ -18,9 +18,13 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -35,6 +39,8 @@
     private static final String SSID_1 = "ssid1";
     private static final String SSID_2 = "ssid2";
 
+    @Mock private WifiConfigManager mWifiConfigManager;
+
     private ScanResultMatchInfo mNetwork1;
     private ScanResultMatchInfo mNetwork2;
     private WakeupLock mWakeupLock;
@@ -44,6 +50,8 @@
      */
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         mNetwork1 = new ScanResultMatchInfo();
         mNetwork1.networkSsid = SSID_1;
         mNetwork1.networkType = ScanResultMatchInfo.NETWORK_TYPE_OPEN;
@@ -52,7 +60,7 @@
         mNetwork2.networkSsid = SSID_2;
         mNetwork2.networkType = ScanResultMatchInfo.NETWORK_TYPE_EAP;
 
-        mWakeupLock = new WakeupLock();
+        mWakeupLock = new WakeupLock(mWifiConfigManager);
     }
 
     /**
@@ -69,6 +77,18 @@
     }
 
     /**
+     * Updates the lock enough times to evict any networks not passed in.
+     *
+     * <p>It calls update {@link WakeupLock#CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT} times with
+     * the given network list. It does not make any assertions about the state of the lock.
+     */
+    private void updateEnoughTimesToEvictWithoutAsserts(Collection<ScanResultMatchInfo> networks) {
+        for (int i = 0; i < WakeupLock.CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT; i++) {
+            mWakeupLock.update(networks);
+        }
+    }
+
+    /**
      * Verify that the WakeupLock is not empty immediately after being initialized with networks.
      */
     @Test
@@ -161,4 +181,34 @@
         updateEnoughTimesToEvictWithAsserts(Collections.singletonList(mNetwork2));
         assertTrue(mWakeupLock.isEmpty());
     }
+
+    /**
+     * Verify that initializing the lock persists the SSID list to the config store.
+     */
+    @Test
+    public void initializeShouldSaveSsidsToStore() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+        verify(mWifiConfigManager).saveToStore(eq(false));
+    }
+
+    /**
+     * Verify that update saves to store if the lock changes.
+     */
+    @Test
+    public void updateShouldOnlySaveIfLockChanges() {
+        mWakeupLock.initialize(Collections.singletonList(mNetwork1));
+        updateEnoughTimesToEvictWithoutAsserts(Collections.emptyList());
+
+        // need exactly 2 invocations: 1 for initialize, 1 for successful update
+        verify(mWifiConfigManager, times(2)).saveToStore(eq(false));
+    }
+
+    /**
+     * Verify that update does not save to store if the lock does not change.
+     */
+    @Test
+    public void updateShouldNotSaveIfLockDoesNotChange() {
+        mWakeupLock.update(Collections.singletonList(mNetwork1));
+        verify(mWifiConfigManager, never()).saveToStore(anyBoolean());
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java
deleted file mode 100644
index b123d80..0000000
--- a/tests/wifitests/src/com/android/server/wifi/WifiCertManagerTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-import android.os.UserHandle;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.Mock;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashSet;
-
-/**
- * Unit tests for {@link com.android.server.wifi.WifiCertManager}.
- */
-@SmallTest
-public class WifiCertManagerTest {
-    private static final String TAG = "WifiCertManagerTest";
-    private byte[] mConfig;
-    private String mConfigFile = "";
-
-    @Mock private Context mContext;
-    @Rule public TemporaryFolder mTempFolder = new TemporaryFolder();
-
-    public WifiCertManagerTest() {
-        mConfig = null;
-    }
-
-    @Before
-    public void setUp() {
-        try {
-            File configFile = mTempFolder.newFile();
-            mConfigFile = configFile.getAbsolutePath();
-            configFile.delete();
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to construct test", e);
-        }
-    }
-
-    /**
-     * This class is created to avoid mocking file system and KeyStore.
-     */
-    private class TestWifiCertManager extends WifiCertManager {
-        private boolean mAffiliatedUser;
-
-        public TestWifiCertManager(Context context) {
-            super(context, mConfigFile);
-            mAffiliatedUser = false;
-        }
-
-        protected String[] listClientCertsForAllUsers() {
-            String prefix = Credentials.USER_PRIVATE_KEY;
-            String mockAnswer[] = {prefix + "abc", prefix + "def", prefix + "ghi"};
-            return mockAnswer;
-        }
-
-        protected byte[] readConfigFile() {
-            return mConfig;
-        }
-
-        protected void writeConfigFile(byte[] payload) {
-            mConfig = payload;
-        }
-
-        protected boolean isAffiliatedUser() {
-            return mAffiliatedUser;
-        }
-
-        public void setAffiliatedUser(boolean value) {
-            mAffiliatedUser = value;
-        }
-    }
-
-    // TODO: b/69555027 - determine if test can be removed.  for now, disable failing test
-    public void testEmptyConfigFile() {
-        WifiCertManager certManager = new WifiCertManager(mContext, mConfigFile);
-        final String[] expected =
-                KeyStore.getInstance().list(
-                        Credentials.USER_PRIVATE_KEY, UserHandle.myUserId());
-        assertArrayEquals(expected, certManager.listClientCertsForCurrentUser());
-    }
-
-    @Test
-    public void testOperations() {
-        TestWifiCertManager certManager = new TestWifiCertManager(mContext);
-        final HashSet<String> expected1 = new HashSet<>();
-        String prefix = Credentials.USER_PRIVATE_KEY;
-        expected1.add(prefix + "abc");
-        expected1.add(prefix + "def");
-        expected1.add(prefix + "ghi");
-
-        final HashSet<String> expected2 = new HashSet<>();
-        expected2.add(prefix + "abc");
-
-        certManager.setAffiliatedUser(false);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        certManager.hideCertFromUnaffiliatedUsers("def");
-        certManager.hideCertFromUnaffiliatedUsers("ghi");
-        assertEquals(expected2,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        certManager.setAffiliatedUser(true);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager.listClientCertsForCurrentUser())));
-
-        TestWifiCertManager certManager2 = new TestWifiCertManager(mContext);
-        certManager2.setAffiliatedUser(false);
-        assertEquals(expected2,
-                new HashSet<>(Arrays.asList(certManager2.listClientCertsForCurrentUser())));
-
-        certManager2.setAffiliatedUser(true);
-        assertEquals(expected1,
-                new HashSet<>(Arrays.asList(certManager2.listClientCertsForCurrentUser())));
-    }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
index c7b6180..9d5054e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
@@ -88,6 +88,7 @@
     @Mock FrameworkFacade mFacade;
     @Mock WifiSettingsStore mSettingsStore;
     @Mock WifiStateMachine mWifiStateMachine;
+    @Mock WifiStateMachinePrime mWifiStateMachinePrime;
     @Mock WifiLockManager mWifiLockManager;
     @Mock ContentResolver mContentResolver;
 
@@ -110,7 +111,8 @@
                 ArgumentCaptor.forClass(ContentObserver.class);
 
         mWifiController = new WifiController(mContext, mWifiStateMachine,
-                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade);
+                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade,
+                mWifiStateMachinePrime);
         verify(mFacade, times(3)).registerContentObserver(eq(mContext), any(Uri.class), eq(false),
                 observerCaptor.capture());
 
@@ -367,7 +369,8 @@
         when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
 
         mWifiController = new WifiController(mContext, mWifiStateMachine,
-                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade);
+                mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade,
+                mWifiStateMachinePrime);
 
         mWifiController.start();
         mLooper.dispatchAll();
@@ -457,11 +460,13 @@
     public void testRestartWifiStackDoesNotExitAPMode() throws Exception {
         mWifiController.obtainMessage(CMD_SET_AP, 1).sendToTarget();
         mLooper.dispatchAll();
+        verify(mWifiStateMachinePrime).enterSoftAPMode(any());
         assertEquals("ApEnabledState", getCurrentState().getName());
 
         reset(mWifiStateMachine);
         mWifiController.sendMessage(CMD_RESTART_WIFI);
         mLooper.dispatchAll();
         verifyZeroInteractions(mWifiStateMachine);
+        verify(mWifiStateMachinePrime, never()).disableWifi();
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index ea81d53..d0a482e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -31,6 +31,7 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
 import static android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
@@ -45,6 +46,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -370,10 +372,11 @@
     /**
      * Verify that wifi can be enabled by a caller with WIFI_STATE_CHANGE permission when wifi is
      * off (no hotspot, no airplane mode).
+     *
+     * Note: hotspot is disabled by default
      */
     @Test
     public void testSetWifiEnabledSuccess() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
         when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
         assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
@@ -385,7 +388,6 @@
      */
     @Test
     public void testSetWifiEnabledNoToggle() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(false);
         assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
         verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
@@ -402,7 +404,6 @@
                                                 eq("WifiService"));
         when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
         mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
-        verify(mWifiStateMachine, never()).syncGetWifiApState();
     }
 
     /**
@@ -441,7 +442,17 @@
      */
     @Test
     public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
+        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+        mWifiServiceImpl.checkAndStartWifi();
+
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                (IntentFilter) argThat(new IntentFilterMatcher()));
+
+        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+                WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_ENABLING, SAP_START_FAILURE_GENERAL,
+                WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
         when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
         when(mContext.checkPermission(
                 eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt()))
@@ -456,7 +467,17 @@
      */
     @Test
     public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
+        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+        mWifiServiceImpl.checkAndStartWifi();
+
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                (IntentFilter) argThat(new IntentFilterMatcher()));
+
+        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+                WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_ENABLING, SAP_START_FAILURE_GENERAL,
+                WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
         when(mContext.checkPermission(
                 eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt()))
                         .thenReturn(PackageManager.PERMISSION_DENIED);
@@ -472,7 +493,6 @@
      */
     @Test
     public void testSetWifiDisabledSuccess() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
         assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
         verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
@@ -483,7 +503,6 @@
      */
     @Test
     public void testSetWifiDisabledNoToggle() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false);
         assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
         verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
@@ -495,7 +514,6 @@
      */
     @Test(expected = SecurityException.class)
     public void testSetWifiDisabledWithoutPermission() throws Exception {
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         doThrow(new SecurityException()).when(mContext)
                 .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
                                                 eq("WifiService"));
@@ -560,6 +578,58 @@
     }
 
     /**
+     * Ensure we return the proper variable for the softap state after getting an AP state change
+     * broadcast.
+     */
+    @Test
+    public void testGetWifiApEnabled() {
+        // set up WifiServiceImpl with a live thread for testing
+        HandlerThread serviceHandlerThread = new HandlerThread("ServiceHandlerThreadForTest");
+        serviceHandlerThread.start();
+        when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(serviceHandlerThread);
+        mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
+        mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
+
+        // ap should be disabled when wifi hasn't been started
+        assertEquals(WifiManager.WIFI_AP_STATE_DISABLED, mWifiServiceImpl.getWifiApEnabledState());
+
+        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+        mWifiServiceImpl.checkAndStartWifi();
+        mLooper.dispatchAll();
+
+        // ap should be disabled initially
+        assertEquals(WifiManager.WIFI_AP_STATE_DISABLED, mWifiServiceImpl.getWifiApEnabledState());
+
+        // send an ap state change to verify WifiServiceImpl is updated
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+                (IntentFilter) argThat(new IntentFilterMatcher()));
+
+        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL,
+                WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+        mLooper.dispatchAll();
+
+        assertEquals(WifiManager.WIFI_AP_STATE_FAILED, mWifiServiceImpl.getWifiApEnabledState());
+    }
+
+    /**
+     * Ensure we do not allow unpermitted callers to get the wifi ap state.
+     */
+    @Test
+    public void testGetWifiApEnabledPermissionDenied() {
+        // we should not be able to get the state
+        doThrow(new SecurityException()).when(mContext)
+                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.ACCESS_WIFI_STATE),
+                                                eq("WifiService"));
+
+        try {
+            mWifiServiceImpl.getWifiApEnabledState();
+            fail("expected SecurityException");
+        } catch (SecurityException expected) { }
+    }
+
+    /**
      * Make sure we do not start wifi if System services have to be restarted to decrypt the device.
      */
     @Test
@@ -591,7 +661,6 @@
         when(mSettingsStore.handleWifiToggled(true)).thenReturn(true);
         when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
         when(mWifiStateMachine.syncGetWifiState()).thenReturn(WIFI_STATE_DISABLED);
-        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
         when(mContext.getPackageName()).thenReturn(ANDROID_SYSTEM_PACKAGE);
         mWifiServiceImpl.checkAndStartWifi();
         verify(mWifiController).start();
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
index 2e26769..0c5db44 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
@@ -19,8 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.*;
 
-import android.net.wifi.IApInterface;
-import android.net.wifi.IWificond;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.INetworkManagementService;
@@ -56,8 +54,6 @@
     @Mock WifiNative mWifiNative;
     @Mock WifiApConfigStore mWifiApConfigStore;
     TestLooper mLooper;
-    @Mock IWificond mWificond;
-    @Mock IApInterface mApInterface;
     @Mock INetworkManagementService mNMService;
     @Mock SoftApManager mSoftApManager;
     SoftApManager.Listener mSoftApListener;
@@ -76,11 +72,15 @@
         when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
         when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         mWifiStateMachinePrime = createWifiStateMachinePrime();
+
+        // creating a new WSMP cleans up any existing interfaces, check and reset expectations
+        verifyCleanupCalled();
+        reset(mWifiNative);
     }
 
     private WifiStateMachinePrime createWifiStateMachinePrime() {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+        return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+                mWifiNative, mNMService);
     }
 
     /**
@@ -103,47 +103,46 @@
      */
     private void enterSoftApActiveMode(SoftApModeConfiguration softApConfig) throws Exception {
         String fromState = mWifiStateMachinePrime.getCurrentMode();
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
-        when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         doAnswer(
                 new Answer<Object>() {
                     public SoftApManager answer(InvocationOnMock invocation) {
                         Object[] args = invocation.getArguments();
                         assertEquals(mNMService, (INetworkManagementService) args[0]);
                         mSoftApListener = (SoftApManager.Listener) args[1];
-                        assertEquals(mApInterface, (IApInterface) args[2]);
-                        assertEquals(WIFI_IFACE_NAME, (String) args[3]);
-                        assertEquals(softApConfig, (SoftApModeConfiguration) args[4]);
+                        assertEquals(softApConfig, (SoftApModeConfiguration) args[2]);
                         return mSoftApManager;
                     }
                 }).when(mWifiInjector).makeSoftApManager(any(INetworkManagementService.class),
                                                          any(SoftApManager.Listener.class),
-                                                         any(IApInterface.class),
-                                                         anyString(),
                                                          any());
         mWifiStateMachinePrime.enterSoftAPMode(softApConfig);
         mLooper.dispatchAll();
         Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState);
         if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) {
-            verify(mWificond).tearDownInterfaces();
+            verifyCleanupCalled();
         }
         assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
         verify(mSoftApManager).start();
     }
 
+    private void verifyCleanupCalled() {
+        // for now, this is a single call, but make a helper to avoid adding any additional cleanup
+        // checks
+        verify(mWifiNative, atLeastOnce()).tearDown();
+    }
+
     /**
-     * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in
-     * the retrieved Wificond instance are cleaned up.
+     * Test that when a new instance of WifiStateMachinePrime is created, any existing
+     * resources in WifiNative are cleaned up.
      * Expectations:  When the new WifiStateMachinePrime instance is created a call to
-     * Wificond.tearDownInterfaces() is made.
+     * WifiNative.tearDown() is made.
      */
     @Test
-    public void testWificondExistsOnStartup() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+    public void testCleanupOnStart() throws Exception {
         WifiStateMachinePrime testWifiStateMachinePrime =
-                new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
-        verify(mWificond).tearDownInterfaces();
+                new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(),
+                                          mWifiNative, mNMService);
+        verifyCleanupCalled();
     }
 
     /**
@@ -157,12 +156,10 @@
 
     /**
      * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state.
-     * Expectations: When going from one state to another, any interfaces that are still up are torn
-     * down.
+     * Expectations: When going from one state to another, cleanup will be called
      */
     @Test
     public void testEnterSoftApModeFromDifferentState() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         mWifiStateMachinePrime.enterClientMode();
         mLooper.dispatchAll();
         assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
@@ -179,7 +176,7 @@
         mWifiStateMachinePrime.disableWifi();
         mLooper.dispatchAll();
         verify(mSoftApManager).stop();
-        verify(mWificond).tearDownInterfaces();
+        verifyCleanupCalled();
         assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
@@ -188,16 +185,15 @@
      */
     @Test
     public void testDisableWifiFromSoftApModeState() throws Exception {
-        // Use a failure getting wificond to stay in the SoftAPModeState
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
+        enterSoftApActiveMode();
+        // now inject failure through the SoftApManager.Listener
+        mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
         mLooper.dispatchAll();
         assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
 
         mWifiStateMachinePrime.disableWifi();
         mLooper.dispatchAll();
-        // mWificond will be null due to this test, no call to tearDownInterfaces here.
+        verifyCleanupCalled();
         assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
@@ -213,54 +209,24 @@
         mWifiStateMachinePrime.enterClientMode();
         mLooper.dispatchAll();
         verify(mSoftApManager).stop();
-        verify(mWificond).tearDownInterfaces();
+        verifyCleanupCalled();
         assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
     }
 
     /**
-     * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to
-     * wificond.
-     * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in
-     * the SoftApModeState.
-     */
-    @Test
-    public void testWificondNullWhenSwitchingToApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
-        mLooper.dispatchAll();
-        assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
-    }
-
-    /**
-     * Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface
-     * from wificond.
-     * Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should
-     * remain in the SoftApModeState.
-     */
-    @Test
-    public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
-        mLooper.dispatchAll();
-        assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
-    }
-
-    /**
      * Test that we do enter the SoftApModeActiveState if we are already in the SoftApModeState.
      * Expectations: We should exit the current SoftApModeState and re-enter before successfully
      * entering the SoftApModeActiveState.
      */
     @Test
     public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
+        enterSoftApActiveMode();
+        // now inject failure through the SoftApManager.Listener
+        mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
         mLooper.dispatchAll();
         assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+        // clear the first call to start SoftApManager
+        reset(mSoftApManager);
 
         enterSoftApActiveMode();
     }
@@ -324,56 +290,12 @@
     }
 
     /**
-     * Test that the proper config is used if a prior attempt fails without using the config.
-     * Expectations: A call to start softap with a null config fails, but a second call has a set
-     * config - this second call should use the correct config.
-     */
-    @Test
-    public void testNullApModeConfigFails() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(null);
-        mWifiStateMachinePrime.enterSoftAPMode(
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null));
-        mLooper.dispatchAll();
-        assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
-        WifiConfiguration config = new WifiConfiguration();
-        config.SSID = "ThisIsAConfig";
-        SoftApModeConfiguration softApConfig =
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
-        enterSoftApActiveMode(softApConfig);
-    }
-
-    /**
-     * Test that a failed call to start softap with a valid config does not persist the ap
-     * configuration to the WifiApConfigStore.
-     *
-     * Expectations: A call to start SoftAPMode with a config should not write out the config if we
-     * did not create a SoftApManager.
-     */
-    @Test
-    public void testValidConfigIsSavedOnFailureToStart() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(null);
-        when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
-        WifiConfiguration config = new WifiConfiguration();
-        config.SSID = "ThisIsAConfig";
-        SoftApModeConfiguration softApConfig =
-                new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, config);
-        mWifiStateMachinePrime.enterSoftAPMode(softApConfig);
-        mLooper.dispatchAll();
-        assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
-        verify(mWifiApConfigStore, never()).setApConfiguration(eq(config));
-    }
-
-    /**
-     * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config.
+     * Test that two calls to switch to SoftAPMode in succession ends up with the correct config.
      *
      * Expectation: we should end up in SoftAPMode state configured with the second config.
      */
     @Test
     public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception {
-        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
-        when(mWificond.createApInterface(WIFI_IFACE_NAME)).thenReturn(mApInterface);
-        when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
         WifiConfiguration config1 = new WifiConfiguration();
         config1.SSID = "ThisIsAConfig";
@@ -386,14 +308,10 @@
 
         when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
                                              any(SoftApManager.Listener.class),
-                                             any(IApInterface.class),
-                                             anyString(),
                                              eq(softApConfig1)))
                 .thenReturn(mSoftApManager);
         when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
                                              any(SoftApManager.Listener.class),
-                                             any(IApInterface.class),
-                                             anyString(),
                                              eq(softApConfig2)))
                 .thenReturn(mSoftApManager);
 
@@ -406,12 +324,12 @@
 
     /**
      * Test that we safely disable wifi if it is already disabled.
-     * Expectations: We should not interact with wificond since we should have already cleaned up
+     * Expectations: We should not interact with WifiNative since we should have already cleaned up
      * everything.
      */
     @Test
     public void disableWifiWhenAlreadyOff() throws Exception {
-        verifyNoMoreInteractions(mWificond);
+        verifyNoMoreInteractions(mWifiNative);
         mWifiStateMachinePrime.disableWifi();
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index ad5aa43..5208c74 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -16,18 +16,6 @@
 
 package com.android.server.wifi;
 
-import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-
-import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -52,7 +40,6 @@
 import android.net.NetworkRequest;
 import android.net.dhcp.DhcpClient;
 import android.net.ip.IpClient;
-import android.net.wifi.IApInterface;
 import android.net.wifi.IClientInterface;
 import android.net.wifi.IWificond;
 import android.net.wifi.ScanResult;
@@ -106,6 +93,7 @@
 import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
 import com.android.server.wifi.p2p.WifiP2pServiceImpl;
 import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -152,6 +140,7 @@
     private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
     private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
     private static final String OP_PACKAGE_NAME = "com.xxx";
+    private static final int TEST_UID = Process.SYSTEM_UID + 1000;
 
     private long mBinderToken;
 
@@ -353,7 +342,6 @@
     @Mock PropertyService mPropertyService;
     @Mock BuildProperties mBuildProperties;
     @Mock IWificond mWificond;
-    @Mock IApInterface mApInterface;
     @Mock IClientInterface mClientInterface;
     @Mock IBinder mPackageManagerBinder;
     @Mock WifiConfigManager mWifiConfigManager;
@@ -373,6 +361,8 @@
     @Mock ConnectivityManager mConnectivityManager;
     @Mock IProvisioningCallback mProvisioningCallback;
     @Mock HandlerThread mWifiServiceHandlerThread;
+    @Mock WifiPermissionsWrapper mWifiPermissionsWrapper;
+    @Mock WakeupController mWakeupController;
 
     public WifiStateMachineTest() throws Exception {
     }
@@ -403,11 +393,10 @@
         when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
         when(mWifiInjector.makeWifiConnectivityManager(any(WifiInfo.class), anyBoolean()))
                 .thenReturn(mWifiConnectivityManager);
-        when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
-                                             mSoftApManagerListenerCaptor.capture(),
-                                             any(IApInterface.class), anyString(),
-                                             any(SoftApModeConfiguration.class)))
-                .thenReturn(mSoftApManager);
+        //when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
+        //                                     mSoftApManagerListenerCaptor.capture(),
+        //                                     any(SoftApModeConfiguration.class)))
+        //        .thenReturn(mSoftApManager);
         when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager);
         when(mWifiInjector.getWifiStateTracker()).thenReturn(mWifiStateTracker);
         when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor);
@@ -418,12 +407,14 @@
         when(mWifiInjector.getClock()).thenReturn(mClock);
         when(mWifiServiceHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
         when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mWifiServiceHandlerThread);
+        when(mWifiInjector.getWifiPermissionsWrapper()).thenReturn(mWifiPermissionsWrapper);
+        when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController);
 
         when(mWifiNative.setupForClientMode(WIFI_IFACE_NAME))
                 .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mClientInterface));
-        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
-                .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
-        when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
+        //when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
+        //        .thenReturn(Pair.create(WifiNative.SETUP_SUCCESS, mApInterface));
+        //when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
         when(mWifiNative.enableSupplicant()).thenReturn(true);
         when(mWifiNative.disableSupplicant()).thenReturn(true);
@@ -464,6 +455,8 @@
         }).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt());
 
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
         initializeWsm();
 
         mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
@@ -570,21 +563,6 @@
         assertEquals("DisconnectedState", getCurrentState().getName());
     }
 
-    private void checkApStateChangedBroadcast(Intent intent, int expectedCurrentState,
-            int expectedPrevState, int expectedErrorCode, String expectedIfaceName,
-            int expectedMode) {
-        int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
-        int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
-        int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, HOTSPOT_NO_ERROR);
-        String ifaceName = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
-        int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
-        assertEquals(expectedCurrentState, currentState);
-        assertEquals(expectedPrevState, prevState);
-        assertEquals(expectedErrorCode, errorCode);
-        assertEquals(expectedIfaceName, ifaceName);
-        assertEquals(expectedMode, mode);
-    }
-
     private void loadComponentsInApMode(int mode) throws Exception {
         SoftApModeConfiguration config = new SoftApModeConfiguration(mode, new WifiConfiguration());
         mWsm.setHostApRunning(config, true);
@@ -592,20 +570,8 @@
 
         assertEquals("SoftApState", getCurrentState().getName());
 
-        verify(mWifiNative).setupForSoftApMode(WIFI_IFACE_NAME);
-        verify(mSoftApManager).start();
-
-        // get the SoftApManager.Listener and trigger some updates
-        SoftApManager.Listener listener = mSoftApManagerListenerCaptor.getValue();
-        listener.onStateChanged(WIFI_AP_STATE_ENABLING, 0);
-        assertEquals(WIFI_AP_STATE_ENABLING, mWsm.syncGetWifiApState());
-        listener.onStateChanged(WIFI_AP_STATE_ENABLED, 0);
-        assertEquals(WIFI_AP_STATE_ENABLED, mWsm.syncGetWifiApState());
-        listener.onStateChanged(WIFI_AP_STATE_DISABLING, 0);
-        assertEquals(WIFI_AP_STATE_DISABLING, mWsm.syncGetWifiApState());
-        // note, this will trigger a mode change when TestLooper is dispatched
-        listener.onStateChanged(WIFI_AP_STATE_DISABLED, 0);
-        assertEquals(WIFI_AP_STATE_DISABLED, mWsm.syncGetWifiApState());
+        verify(mWifiNative, never()).setupForSoftApMode(WIFI_IFACE_NAME);
+        verify(mSoftApManager, never()).start();
     }
 
     private void setupMockWpsPbc() throws Exception {
@@ -2193,6 +2159,16 @@
         assertEquals(sBSSID1, wifiInfo.getBSSID());
         assertEquals(sFreq1, wifiInfo.getFrequency());
         assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+
+        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+                new StateChangeResult(0, sWifiSsid, sBSSID1, SupplicantState.DISCONNECTED));
+        mLooper.dispatchAll();
+
+        wifiInfo = mWsm.getWifiInfo();
+        assertEquals(null, wifiInfo.getBSSID());
+        assertEquals(WifiSsid.NONE, wifiInfo.getSSID());
+        assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiInfo.getNetworkId());
+        assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
     }
 
     /**
@@ -2288,56 +2264,29 @@
     }
 
     /**
-     * Test that the process uid has full wifiInfo access.
-     * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always
-     * returns a copy of WifiInfo.
-     */
-    @Test
-    public void testConnectedIdsAreVisibleFromOwnUid() throws Exception {
-        assertEquals(Process.myUid(), Binder.getCallingUid());
-        WifiInfo wifiInfo = mWsm.getWifiInfo();
-        wifiInfo.setBSSID(sBSSID);
-        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID));
-
-        connect();
-        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
-
-        assertNotEquals(wifiInfo, connectionInfo);
-        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
-        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
-    }
-
-    /**
      * Test that connected SSID and BSSID are not exposed to an app that does not have the
      * appropriate permissions.
      * Also tests that {@link WifiStateMachine#syncRequestConnectionInfo(String)} always
      * returns a copy of WifiInfo.
      */
     @Test
-    public void testConnectedIdsAreHiddenFromRandomApp() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        assertNotEquals(actualUid, fakeUid);
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+    public void testConnectedIdsAreHiddenFromAppWithoutPermission() throws Exception {
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenReturn(false);
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(false);
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
     }
 
     /**
@@ -2348,29 +2297,48 @@
      */
     @Test
     public void testConnectedIdsAreHiddenOnSecurityException() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        assertNotEquals(actualUid, fakeUid);
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenThrow(new SecurityException());
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenThrow(new SecurityException());
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(WifiSsid.NONE, connectionInfo.getSSID());
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getBSSID());
+    }
+
+    /**
+     * Test that connected SSID and BSSID are exposed to system server
+     */
+    @Test
+    public void testConnectedIdsAreVisibleFromSystemServer() throws Exception {
+        when(mWifiPermissionsWrapper.getLocalMacAddressPermission(anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(true);
+
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
+
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
+        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
+        assertEquals(wifiInfo.getMacAddress(), connectionInfo.getMacAddress());
     }
 
     /**
@@ -2379,30 +2347,24 @@
      */
     @Test
     public void testConnectedIdsAreVisibleFromPermittedApp() throws Exception {
-        int actualUid = Binder.getCallingUid();
-        int fakeUid = Process.myUid() + 100000;
-        BinderUtil.setUid(fakeUid);
-        try {
-            WifiInfo wifiInfo = mWsm.getWifiInfo();
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
 
-            // Get into a connected state, with known BSSID and SSID
-            connect();
-            assertEquals(sBSSID, wifiInfo.getBSSID());
-            assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
+        // Get into a connected state, with known BSSID and SSID
+        connect();
+        assertEquals(sBSSID, wifiInfo.getBSSID());
+        assertEquals(sWifiSsid, wifiInfo.getWifiSsid());
 
-            when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(fakeUid), anyInt()))
-                    .thenReturn(true);
+        when(mWifiPermissionsUtil.canAccessScanResults(anyString(), eq(TEST_UID), anyInt()))
+                .thenReturn(true);
 
-            WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName());
+        WifiInfo connectionInfo = mWsm.syncRequestConnectionInfo(mContext.getOpPackageName(),
+                TEST_UID);
 
-            assertNotEquals(wifiInfo, connectionInfo);
-            assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
-            assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
-            // Access to our MAC address uses a different permission, make sure it is not granted
-            assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress());
-        } finally {
-            BinderUtil.setUid(actualUid);
-        }
+        assertNotEquals(wifiInfo, connectionInfo);
+        assertEquals(wifiInfo.getSSID(), connectionInfo.getSSID());
+        assertEquals(wifiInfo.getBSSID(), connectionInfo.getBSSID());
+        // Access to our MAC address uses a different permission, make sure it is not granted
+        assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, connectionInfo.getMacAddress());
     }
 
     /**
@@ -2477,40 +2439,6 @@
     }
 
     /**
-     * Test that failure to start HAL in AP mode increments the corresponding metrics.
-     */
-    @Test
-    public void testSetupForSoftApModeHalFailureIncrementsMetrics() throws Exception {
-        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
-                .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_HAL, null));
-
-        SoftApModeConfiguration config = new SoftApModeConfiguration(
-                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
-        mWsm.setHostApRunning(config, true);
-        mLooper.dispatchAll();
-
-        verify(mWifiNative).setupForSoftApMode(WIFI_IFACE_NAME);
-        verify(mWifiMetrics).incrementNumWifiOnFailureDueToHal();
-    }
-
-    /**
-     * Test that failure to start HAL in AP mode increments the corresponding metrics.
-     */
-    @Test
-    public void testSetupForSoftApModeWificondFailureIncrementsMetrics() throws Exception {
-        when(mWifiNative.setupForSoftApMode(WIFI_IFACE_NAME))
-                .thenReturn(Pair.create(WifiNative.SETUP_FAILURE_WIFICOND, null));
-
-        SoftApModeConfiguration config = new SoftApModeConfiguration(
-                WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
-        mWsm.setHostApRunning(config, true);
-        mLooper.dispatchAll();
-
-        verify(mWifiNative).setupForSoftApMode(WIFI_IFACE_NAME);
-        verify(mWifiMetrics).incrementNumWifiOnFailureDueToWificond();
-    }
-
-    /**
      * Test that failure to start HAL in client mode increments the corresponding metrics.
      */
     @Test
@@ -2827,4 +2755,15 @@
         currentConfig.networkId = lastSelectedNetworkId - 1;
         assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
     }
+
+    /**
+     * Verify that WSM dump includes WakeupController.
+     */
+    @Test
+    public void testDumpShouldDumpWakeupController() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        PrintWriter writer = new PrintWriter(stream);
+        mWsm.dump(null, writer, null);
+        verify(mWakeupController).dump(null, writer, null);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
index 51fed83..5043c85 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttNativeTest.java
@@ -35,12 +35,11 @@
 import android.hardware.wifi.V1_0.RttType;
 import android.hardware.wifi.V1_0.WifiStatus;
 import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.MacAddress;
 import android.net.wifi.rtt.RangingRequest;
 
 import com.android.server.wifi.HalDeviceManager;
 
-import libcore.util.HexEncoding;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -118,19 +117,19 @@
 
         RttConfig rttConfig = halRequest.get(0);
         collector.checkThat("entry 0: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("000102030400".toCharArray(), false)));
+                equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray()));
         collector.checkThat("entry 0: MAC", rttConfig.type, equalTo(RttType.TWO_SIDED));
         collector.checkThat("entry 0: MAC", rttConfig.peer, equalTo(RttPeerType.AP));
 
         rttConfig = halRequest.get(1);
         collector.checkThat("entry 1: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("0A0B0C0D0E00".toCharArray(), false)));
+                equalTo(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray()));
         collector.checkThat("entry 1: MAC", rttConfig.type, equalTo(RttType.ONE_SIDED));
         collector.checkThat("entry 1: MAC", rttConfig.peer, equalTo(RttPeerType.AP));
 
         rttConfig = halRequest.get(2);
         collector.checkThat("entry 2: MAC", rttConfig.addr,
-                equalTo(HexEncoding.decode("080908070605".toCharArray(), false)));
+                equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray()));
         collector.checkThat("entry 2: MAC", rttConfig.type, equalTo(RttType.TWO_SIDED));
         collector.checkThat("entry 2: MAC", rttConfig.peer, equalTo(RttPeerType.NAN));
 
@@ -164,8 +163,8 @@
     public void testRangeCancel() throws Exception {
         int cmdId = 66;
         ArrayList<byte[]> macAddresses = new ArrayList<>();
-        byte[] mac1 = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
-        byte[] mac2 = { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+        byte[] mac1 = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+        byte[] mac2 = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
         macAddresses.add(mac1);
         macAddresses.add(mac2);
 
@@ -212,7 +211,7 @@
         collector.checkThat("status", rttResult.status,
                 equalTo(RttStatus.SUCCESS));
         collector.checkThat("mac", rttResult.addr,
-                equalTo(HexEncoding.decode("05060708090A".toCharArray(), false)));
+                equalTo(MacAddress.fromString("05:06:07:08:09:0A").toByteArray()));
         collector.checkThat("distanceCm", rttResult.distanceInMm, equalTo(1500));
         collector.checkThat("timestamp", rttResult.timeStampInUs, equalTo(666L));
 
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
index ee8c15f..7e71069 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java
@@ -46,6 +46,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.wifi.V1_0.RttResult;
+import android.net.MacAddress;
 import android.net.wifi.aware.IWifiAwareMacAddressProvider;
 import android.net.wifi.aware.IWifiAwareManager;
 import android.net.wifi.aware.PeerHandle;
@@ -68,8 +69,6 @@
 import com.android.server.wifi.Clock;
 import com.android.server.wifi.util.WifiPermissionsUtil;
 
-import libcore.util.HexEncoding;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -261,9 +260,9 @@
         PeerHandle peerHandle2 = new PeerHandle(1023);
         request.mRttPeers.add(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle1));
         request.mRttPeers.add(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle2));
-        Map<Integer, byte[]> peerHandleToMacMap = new HashMap<>();
-        byte[] macAwarePeer1 = HexEncoding.decode("AABBCCDDEEFF".toCharArray(), false);
-        byte[] macAwarePeer2 = HexEncoding.decode("BBBBBBEEEEEE".toCharArray(), false);
+        Map<Integer, MacAddress> peerHandleToMacMap = new HashMap<>();
+        MacAddress macAwarePeer1 = MacAddress.fromString("AA:BB:CC:DD:EE:FF");
+        MacAddress macAwarePeer2 = MacAddress.fromString("BB:BB:BB:EE:EE:EE");
         peerHandleToMacMap.put(peerHandle1.peerId, macAwarePeer1);
         peerHandleToMacMap.put(peerHandle2.peerId, macAwarePeer2);
 
@@ -431,11 +430,11 @@
                         (ArrayList) mListCaptor.capture());
                 RangingRequest request0 = requests[0];
                 assertEquals(request0.mRttPeers.size(), mListCaptor.getValue().size());
-                assertArrayEquals(HexEncoding.decode("000102030400".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("00:01:02:03:04:00").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(0));
-                assertArrayEquals(HexEncoding.decode("0A0B0C0D0E00".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(1));
-                assertArrayEquals(HexEncoding.decode("080908070605".toCharArray(), false),
+                assertArrayEquals(MacAddress.fromString("08:09:08:07:06:05").toByteArray(),
                         (byte[]) mListCaptor.getValue().get(2));
             }
 
@@ -1103,9 +1102,9 @@
 
     private class AwareTranslatePeerHandlesToMac extends MockAnswerUtil.AnswerWithArguments {
         private int mExpectedUid;
-        private Map<Integer, byte[]> mPeerIdToMacMap;
+        private Map<Integer, MacAddress> mPeerIdToMacMap;
 
-        AwareTranslatePeerHandlesToMac(int expectedUid, Map<Integer, byte[]> peerIdToMacMap) {
+        AwareTranslatePeerHandlesToMac(int expectedUid, Map<Integer, MacAddress> peerIdToMacMap) {
             mExpectedUid = expectedUid;
             mPeerIdToMacMap = peerIdToMacMap;
         }
@@ -1115,7 +1114,7 @@
 
             Map<Integer, byte[]> result = new HashMap<>();
             for (Integer peerId: peerIds) {
-                byte[] mac = mPeerIdToMacMap.get(peerId);
+                byte[] mac = mPeerIdToMacMap.get(peerId).toByteArray();
                 if (mac == null) {
                     continue;
                 }
diff --git a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
index db3cd17..ec56322 100644
--- a/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
+++ b/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java
@@ -18,14 +18,13 @@
 
 import android.hardware.wifi.V1_0.RttResult;
 import android.hardware.wifi.V1_0.RttStatus;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
 import android.net.wifi.rtt.ResponderConfig;
 import android.util.Pair;
 
-import libcore.util.HexEncoding;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -66,7 +65,7 @@
         scan1.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
         ScanResult scan2 = new ScanResult();
         scan2.BSSID = "0A:0B:0C:0D:0E:" + String.format("%02d", lastMacByte);
-        byte[] mac1 = HexEncoding.decode("080908070605".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("08:09:08:07:06:05");
 
         builder.addAccessPoint(scan1);
         builder.addAccessPoint(scan2);
@@ -107,13 +106,13 @@
             }
         } else {
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("100102030405".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("10:01:02:03:04:05"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("1A0B0C0D0E0F".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("1A:0B:0C:0D:0E:0F"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             results.add(new RangingResult(RangingResult.STATUS_SUCCESS,
-                    HexEncoding.decode("080908070605".toCharArray(), false), rangeCmBase++,
+                    MacAddress.fromString("08:09:08:07:06:05"), rangeCmBase++,
                     rangeStdDevCmBase++, rssiBase++, rangeTimestampBase++));
             halResults.add(getMatchingRttResult(results.get(0), null));
             halResults.add(getMatchingRttResult(results.get(1), null));
@@ -123,12 +122,13 @@
         return new Pair<>(halResults, results);
     }
 
-    private static RttResult getMatchingRttResult(RangingResult rangingResult, byte[] overrideMac) {
+    private static RttResult getMatchingRttResult(RangingResult rangingResult,
+            MacAddress overrideMac) {
         RttResult rttResult = new RttResult();
         rttResult.status = rangingResult.getStatus() == RangingResult.STATUS_SUCCESS
                 ? RttStatus.SUCCESS : RttStatus.FAILURE;
-        System.arraycopy(overrideMac == null ? rangingResult.getMacAddress() : overrideMac, 0,
-                rttResult.addr, 0, 6);
+        System.arraycopy(overrideMac == null ? rangingResult.getMacAddress().toByteArray()
+                : overrideMac.toByteArray(), 0, rttResult.addr, 0, 6);
         rttResult.distanceInMm = rangingResult.getDistanceMm();
         rttResult.distanceSdInMm = rangingResult.getDistanceStdDevMm();
         rttResult.rssi = rangingResult.getRssi();
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
index 4ffc66d..5b797e8 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -16,12 +16,35 @@
 
 package com.android.server.wifi.scanner;
 
-import static com.android.server.wifi.ScanTestUtil.*;
+import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder;
+import static com.android.server.wifi.ScanTestUtil.assertNativePnoSettingsEquals;
+import static com.android.server.wifi.ScanTestUtil.assertNativeScanSettingsEquals;
+import static com.android.server.wifi.ScanTestUtil.assertScanDatasEquals;
+import static com.android.server.wifi.ScanTestUtil.assertScanResultsEquals;
+import static com.android.server.wifi.ScanTestUtil.channelsToSpec;
+import static com.android.server.wifi.ScanTestUtil.computeSingleScanNativeSettings;
+import static com.android.server.wifi.ScanTestUtil.createRequest;
+import static com.android.server.wifi.ScanTestUtil.createSingleScanNativeSettingsForChannels;
 import static com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine
         .CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import android.app.test.MockAnswerUtil.AnswerWithArguments;
 import android.app.test.TestAlarmManager;
@@ -63,11 +86,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
-import org.mockito.compat.CapturingMatcher;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -161,14 +184,35 @@
         return messageCaptor.getValue();
     }
 
+    private static class ConditionalMessageCaptor implements ArgumentMatcher<Message> {
+        private Message mLastValue;
+        private final int mWhat;
+
+        private ConditionalMessageCaptor(int what) {
+            mWhat = what;
+        }
+
+        public Message getLastValue() {
+            assertNotNull("Nothing captured yet", mLastValue);
+
+            return mLastValue;
+        }
+
+        public boolean matches(Message message) {
+            boolean isMatch = message.what == mWhat;
+
+            if (isMatch) {
+                mLastValue = message;
+            }
+
+            return isMatch;
+        }
+    }
+
     private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler,
             final int what) {
-        CapturingMatcher<Message> messageMatcher = new CapturingMatcher<Message>() {
-            public boolean matchesObject(Object argument) {
-                Message message = (Message) argument;
-                return message.what == what;
-            }
-        };
+        ConditionalMessageCaptor messageMatcher = new ConditionalMessageCaptor(what);
+
         order.verify(handler).handleMessage(argThat(messageMatcher));
         return messageMatcher.getLastValue();
     }
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
index 73ea143..15dfccc 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
@@ -17,7 +17,6 @@
 package com.android.server.wifi.scanner;
 
 import static com.android.server.wifi.ScanTestUtil.NativeScanSettingsBuilder;
-import static com.android.server.wifi.ScanTestUtil.assertScanDataEquals;
 import static com.android.server.wifi.ScanTestUtil.setupMockChannels;
 
 import static org.junit.Assert.*;
@@ -45,7 +44,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -104,89 +102,6 @@
     }
 
     /**
-     * Verify that we pause & resume HW PNO scan when a single scan is scheduled and invokes the
-     * OnPnoNetworkFound callback when the scan results are received.
-     */
-    @Test
-    public void pauseResumeHwDisconnectedPnoScanForSingleScan() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
-        WifiNative.ScanSettings settings = createDummyScanSettings(false);
-        ScanResults scanResults = createDummyScanResults(false);
-
-        InOrder order = inOrder(eventHandler, mWifiNative);
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        // Start single scan
-        assertTrue(mScanner.startSingleScan(settings, eventHandler));
-        // Verify that the PNO scan was paused and single scan runs successfully
-        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
-                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
-        verifyNoMoreInteractions(eventHandler);
-
-        order = inOrder(pnoEventHandler, mWifiNative);
-        // Resume PNO scan after the single scan results are received and PNO monitor debounce
-        // alarm fires.
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        // Now verify that PNO scan is resumed successfully
-        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
-        verifyNoMoreInteractions(pnoEventHandler);
-    }
-
-    /**
-     * Verify that the HW PNO delayed failure cleans up the scan settings cleanly.
-     * 1. Start Hw PNO.
-     * 2. Start Single Scan which should pause PNO scan.
-     * 3. Fail the PNO scan resume and verify that the OnPnoScanFailed callback is invoked.
-     * 4. Now restart a new PNO scan to ensure that the failure was cleanly handled.
-     */
-    @Test
-    public void delayedHwDisconnectedPnoScanFailure() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
-        WifiNative.ScanSettings settings = createDummyScanSettings(false);
-        ScanResults scanResults = createDummyScanResults(false);
-
-        InOrder order = inOrder(eventHandler, mWifiNative);
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        // Start single scan
-        assertTrue(mScanner.startSingleScan(settings, eventHandler));
-        // Verify that the PNO scan was paused and single scan runs successfully
-        expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
-                expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
-        verifyNoMoreInteractions(eventHandler);
-
-        // Fail the PNO resume and check that the OnPnoScanFailed callback is invoked.
-        order = inOrder(pnoEventHandler, mWifiNative);
-        when(mWifiNative.startPnoScan(any(WifiNative.PnoSettings.class))).thenReturn(false);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        order.verify(pnoEventHandler).onPnoScanFailed();
-        verifyNoMoreInteractions(pnoEventHandler);
-
-        // Add a new PNO scan request
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
-        expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
-        verifyNoMoreInteractions(pnoEventHandler);
-    }
-
-    /**
      * Verify that the HW PNO scan stop failure still resets the PNO scan state.
      * 1. Start Hw PNO.
      * 2. Stop Hw PNO scan which raises a stop command to WifiNative which is failed.
@@ -205,17 +120,11 @@
         // Fail the PNO stop.
         when(mWifiNative.stopPnoScan()).thenReturn(false);
         assertTrue(mScanner.resetHwPnoList());
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
         mLooper.dispatchAll();
         verify(mWifiNative).stopPnoScan();
 
         // Add a new PNO scan request and ensure it runs successfully.
         startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        assertTrue("dispatch pno monitor alarm",
-                mAlarmManager.dispatch(
-                        WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
         mLooper.dispatchAll();
         InOrder order = inOrder(pnoEventHandler, mWifiNative);
         ScanResults scanResults = createDummyScanResults(false);
@@ -223,84 +132,6 @@
         verifyNoMoreInteractions(pnoEventHandler);
     }
 
-    /**
-     * Verify that the HW PNO scan is forcefully stopped (bypass debounce logic) and restarted when
-     * settings change.
-     * 1. Start Hw PNO.
-     * 2. Stop Hw PNO .
-     * 3. Now restart a new PNO scan with different settings.
-     * 4. Ensure that the stop was issued before we start again.
-     */
-    @Test
-    public void forceRestartHwDisconnectedPnoScanWhenSettingsChange() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        InOrder order = inOrder(pnoEventHandler, mWifiNative);
-
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        expectHwDisconnectedPnoScanStart(order, pnoSettings);
-
-        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
-        assertTrue(mScanner.resetHwPnoList());
-        assertTrue(mAlarmManager.isPending(
-                WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        order.verify(mWifiNative, never()).stopPnoScan();
-
-        // Now restart PNO scan with an extra network in settings.
-        pnoSettings.networkList =
-                Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1);
-        pnoSettings.networkList[pnoSettings.networkList.length - 1] =
-                createDummyPnoNetwork("ssid_pno_new");
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-
-        // This should bypass the debounce timer and stop PNO scan immediately and then start
-        // a new debounce timer for the start.
-        order.verify(mWifiNative).stopPnoScan();
-
-        // Trigger the debounce timer and ensure we start PNO scan again.
-        mAlarmManager.dispatch(WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG);
-        mLooper.dispatchAll();
-        order.verify(mWifiNative).startPnoScan(pnoSettings);
-    }
-
-    /**
-     * Verify that the HW PNO scan is not forcefully stopped (bypass debounce logic) when
-     * settings don't change.
-     * 1. Start Hw PNO.
-     * 2. Stop Hw PNO .
-     * 3. Now restart a new PNO scan with same settings.
-     * 4. Ensure that the stop was never issued.
-     */
-    @Test
-    public void noForceRestartHwDisconnectedPnoScanWhenNoSettingsChange() {
-        createScannerWithHwPnoScanSupport();
-
-        WifiNative.PnoEventHandler pnoEventHandler = mock(WifiNative.PnoEventHandler.class);
-        WifiNative.PnoSettings pnoSettings = createDummyPnoSettings(false);
-        InOrder order = inOrder(pnoEventHandler, mWifiNative);
-
-        // Start PNO scan
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-        expectHwDisconnectedPnoScanStart(order, pnoSettings);
-
-        // Stop PNO now. This should trigger the debounce timer and not stop PNO.
-        assertTrue(mScanner.resetHwPnoList());
-        assertTrue(mAlarmManager.isPending(
-                WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
-        order.verify(mWifiNative, never()).stopPnoScan();
-
-        // Now restart PNO scan with the same settings.
-        startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
-
-        // Trigger the debounce timer and ensure that we neither stop/start.
-        mLooper.dispatchAll();
-        order.verify(mWifiNative, never()).startPnoScan(any(WifiNative.PnoSettings.class));
-        order.verify(mWifiNative, never()).stopPnoScan();
-    }
-
     private void createScannerWithHwPnoScanSupport() {
         mResources.setBoolean(R.bool.config_wifi_background_scan_support, true);
         mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor,
@@ -389,27 +220,4 @@
         order.verify(eventHandler).onPnoNetworkFound(scanResults.getRawScanResults());
     }
 
-    /**
-     * Verify that the single scan results were delivered and that the PNO scan was paused and
-     * resumed either side of it.
-     */
-    private void expectSuccessfulSingleScanWithHwPnoEnabled(InOrder order,
-            WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScanFreqs,
-            ScanResults scanResults) {
-        // Pause PNO scan first
-        order.verify(mWifiNative).stopPnoScan();
-
-        order.verify(mWifiNative).scan(eq(expectedScanFreqs), any(Set.class));
-
-        when(mWifiNative.getPnoScanResults()).thenReturn(scanResults.getScanDetailArrayList());
-        when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
-
-        // Notify scan has finished
-        mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
-        assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
-
-        order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
-        assertScanDataEquals(scanResults.getScanData(), mScanner.getLatestSingleScanResults());
-    }
-
 }