Merge "METRICS: histograms of scanresult sums in scan" into oc-dr1-dev
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
index 4382c81..d6009c7 100644
--- a/service/java/com/android/server/wifi/HalDeviceManager.java
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -532,6 +532,9 @@
 
     private void initializeInternal() {
         initIServiceManagerIfNecessary();
+        if (isSupportedInternal()) {
+            initIWifiIfNecessary();
+        }
     }
 
     private void teardownInternal() {
@@ -561,9 +564,7 @@
                     Log.d(TAG, "IWifi registration notification: fqName=" + fqName
                             + ", name=" + name + ", preexisting=" + preexisting);
                     synchronized (mLock) {
-                        mWifi = null; // get rid of old copy!
                         initIWifiIfNecessary();
-                        stopWifi(); // just in case
                     }
                 }
             };
@@ -673,7 +674,8 @@
                     mWifi = null;
                     return;
                 }
-                managerStatusListenerDispatch();
+                // Stopping wifi just in case. This would also trigger the status callback.
+                stopWifi();
             } catch (RemoteException e) {
                 Log.e(TAG, "Exception while operating on IWifi: " + e);
             }
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index 9c90bcf..98a5932 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -16,13 +16,16 @@
 
 package com.android.server.wifi;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.os.Environment;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -31,6 +34,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Random;
 import java.util.UUID;
@@ -50,6 +54,15 @@
     private static final int RAND_SSID_INT_MIN = 1000;
     private static final int RAND_SSID_INT_MAX = 9999;
 
+    @VisibleForTesting
+    static final int SSID_MIN_LEN = 1;
+    @VisibleForTesting
+    static final int SSID_MAX_LEN = 32;
+    @VisibleForTesting
+    static final int PSK_MIN_LEN = 8;
+    @VisibleForTesting
+    static final int PSK_MAX_LEN = 63;
+
     private WifiConfiguration mWifiApConfig = null;
 
     private ArrayList<Integer> mAllowed2GChannel = null;
@@ -224,4 +237,110 @@
         config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
         return config;
     }
+
+    /**
+     * Verify provided SSID for existence, length and conversion to bytes
+     *
+     * @param ssid String ssid name
+     * @return boolean indicating ssid met requirements
+     */
+    private static boolean validateApConfigSsid(String ssid) {
+        if (TextUtils.isEmpty(ssid)) {
+            Log.d(TAG, "SSID for softap configuration must be set.");
+            return false;
+        }
+
+        if (ssid.length() < SSID_MIN_LEN || ssid.length() > SSID_MAX_LEN) {
+            Log.d(TAG, "SSID for softap configuration string size must be at least "
+                    + SSID_MIN_LEN + " and not more than " + SSID_MAX_LEN);
+            return false;
+        }
+
+        try {
+            ssid.getBytes(StandardCharsets.UTF_8);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "softap config SSID verification failed: malformed string " + ssid);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Verify provided preSharedKey in ap config for WPA2_PSK network meets requirements.
+     */
+    private static boolean validateApConfigPreSharedKey(String preSharedKey) {
+        if (preSharedKey.length() < PSK_MIN_LEN || preSharedKey.length() > PSK_MAX_LEN) {
+            Log.d(TAG, "softap network password string size must be at least " + PSK_MIN_LEN
+                    + " and no more than " + PSK_MAX_LEN);
+            return false;
+        }
+
+        try {
+            preSharedKey.getBytes(StandardCharsets.UTF_8);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "softap network password verification failed: malformed string");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Validate a WifiConfiguration is properly configured for use by SoftApManager.
+     *
+     * This method checks the length of the SSID and for sanity between security settings (if it
+     * requires a password, was one provided?).
+     *
+     * @param apConfig {@link WifiConfiguration} to use for softap mode
+     * @return boolean true if the provided config meets the minimum set of details, false
+     * otherwise.
+     */
+    static boolean validateApWifiConfiguration(@NonNull WifiConfiguration apConfig) {
+        // first check the SSID
+        if (!validateApConfigSsid(apConfig.SSID)) {
+            // failed SSID verificiation checks
+            return false;
+        }
+
+        // now check security settings: settings app allows open and WPA2 PSK
+        if (apConfig.allowedKeyManagement == null) {
+            Log.d(TAG, "softap config key management bitset was null");
+            return false;
+        }
+
+        String preSharedKey = apConfig.preSharedKey;
+        boolean hasPreSharedKey = !TextUtils.isEmpty(preSharedKey);
+        int authType;
+
+        try {
+            authType = apConfig.getAuthType();
+        } catch (IllegalStateException e) {
+            Log.d(TAG, "Unable to get AuthType for softap config: " + e.getMessage());
+            return false;
+        }
+
+        if (authType == KeyMgmt.NONE) {
+            // open networks should not have a password
+            if (hasPreSharedKey) {
+                Log.d(TAG, "open softap network should not have a password");
+                return false;
+            }
+        } else if (authType == KeyMgmt.WPA2_PSK) {
+            // this is a config that should have a password - check that first
+            if (!hasPreSharedKey) {
+                Log.d(TAG, "softap network password must be set");
+                return false;
+            }
+
+            if (!validateApConfigPreSharedKey(preSharedKey)) {
+                // failed preSharedKey checks
+                return false;
+            }
+        } else {
+            // this is not a supported security type
+            Log.d(TAG, "softap configs must either be open or WPA2 PSK networks");
+            return false;
+        }
+
+        return true;
+    }
 }
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index c0ec8b7..973b659 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -1684,24 +1684,21 @@
     }
 
     /**
-     * Set the TX power limit.
-     * Primarily used for meeting SAR requirements during voice calls.
-     *
-     * @param powerLevelInDbm Power level to set in dBm.
-     * @return true for success; false for failure or if the HAL version does not support this API.
+     * Tx power level scenarios that can be selected.
      */
-    public boolean setTxPowerLimit(int powerLevelInDbm) {
-        return mWifiVendorHal.setTxPowerLimit(powerLevelInDbm);
-    }
+    public static final int TX_POWER_SCENARIO_NORMAL = 0;
+    public static final int TX_POWER_SCENARIO_VOICE_CALL = 1;
 
     /**
-     * Reset the TX power limit.
+     * Select one of the pre-configured TX power level scenarios or reset it back to normal.
      * Primarily used for meeting SAR requirements during voice calls.
      *
+     * @param scenario Should be one {@link #TX_POWER_SCENARIO_NORMAL} or
+     *        {@link #TX_POWER_SCENARIO_VOICE_CALL}.
      * @return true for success; false for failure or if the HAL version does not support this API.
      */
-    public boolean resetTxPowerLimit() {
-        return mWifiVendorHal.resetTxPowerLimit();
+    public boolean selectTxPowerScenario(int scenario) {
+        return mWifiVendorHal.selectTxPowerScenario(scenario);
     }
 
     /********************************************************
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index fe24705..89068a8 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -25,11 +25,11 @@
 import android.net.wifi.WifiInfo;
 import android.text.TextUtils;
 import android.util.LocalLog;
-import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.util.ScanResultUtil;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -61,6 +61,8 @@
     private final int mThresholdQualifiedRssi5;
     private final int mThresholdMinimumRssi24;
     private final int mThresholdMinimumRssi5;
+    private final int mStayOnNetworkMinimumTxRate;
+    private final int mStayOnNetworkMinimumRxRate;
     private final boolean mEnableAutoJoinWhenAssociated;
 
     /**
@@ -146,6 +148,18 @@
                     + " , ID: " + network.networkId);
         }
 
+        int currentRssi = wifiInfo.getRssi();
+        boolean hasQualifiedRssi =
+                (wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
+                        || (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
+        // getTxSuccessRate() and getRxSuccessRate() returns the packet rate in per 5 seconds unit.
+        boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
+                || (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
+        if (hasQualifiedRssi && hasActiveStream) {
+            localLog("Stay on current network because of good RSSI and ongoing traffic");
+            return true;
+        }
+
         // Ephemeral network is not qualified.
         if (network.ephemeral) {
             localLog("Current network is an ephemeral one.");
@@ -158,28 +172,15 @@
             return false;
         }
 
-        int currentRssi = wifiInfo.getRssi();
         if (wifiInfo.is24GHz()) {
             // 2.4GHz networks is not qualified whenever 5GHz is available
             if (is5GHzNetworkAvailable(scanDetails)) {
                 localLog("Current network is 2.4GHz. 5GHz networks available.");
                 return false;
             }
-            // When 5GHz is not available, we go through normal 2.4GHz qualification
-            if (currentRssi < mThresholdQualifiedRssi24) {
-                localLog("Current network band=2.4GHz, RSSI["
-                        + currentRssi + "]-acceptable but not qualified.");
-                return false;
-            }
-        } else if (wifiInfo.is5GHz()) {
-            // Must be 5GHz, so we always apply qualification checks
-            if (currentRssi < mThresholdQualifiedRssi5) {
-                localLog("Current network band=5GHz, RSSI["
-                        + currentRssi + "]-acceptable but not qualified.");
-                return false;
-            }
-        } else {
-            Log.e(TAG, "We're on a wifi network that's neither 2.4 or 5GHz... aliens!");
+        }
+        if (!hasQualifiedRssi) {
+            localLog("Current network RSSI[" + currentRssi + "]-acceptable but not qualified.");
             return false;
         }
 
@@ -345,9 +346,7 @@
         for (ScanDetail scanDetail : mFilteredNetworks) {
             ScanResult scanResult = scanDetail.getScanResult();
 
-            // A capability of [ESS] represents an open access point
-            // that is available for an STA to connect
-            if (scanResult.capabilities == null || !scanResult.capabilities.equals("[ESS]")) {
+            if (!ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
                 continue;
             }
 
@@ -585,5 +584,9 @@
                             R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
         mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
                             R.bool.config_wifi_framework_enable_associated_network_selection);
+        mStayOnNetworkMinimumTxRate = context.getResources().getInteger(
+                R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network);
+        mStayOnNetworkMinimumRxRate = context.getResources().getInteger(
+                R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network);
     }
 }
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index 98747a3..894d57c 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -38,6 +38,7 @@
     private static final int DUMPSYS_ENTRY_COUNT_LIMIT = 14400; // 12 hours on 3 second poll
 
     private boolean mVerboseLoggingEnabled = false;
+    private static final long FIRST_REASONABLE_WALL_CLOCK = 1490000000000L; // mid-December 2016
 
     // Cache of the last score report.
     private String mReport;
@@ -69,8 +70,10 @@
      */
     public void reset() {
         mReport = "";
-        mReportValid = false;
-        mSessionNumber++;
+        if (mReportValid) {
+            mSessionNumber++;
+            mReportValid = false;
+        }
         mConnectedScore.reset();
         mAggressiveConnectedScore.reset();
         if (mVerboseLoggingEnabled) Log.d(TAG, "reset");
@@ -112,15 +115,16 @@
 
         long millis = mConnectedScore.getMillis();
 
-        logLinkMetrics(wifiInfo);
-
         mConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
         mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis);
 
+        int s0 = mConnectedScore.generateScore();
+        int s1 = mAggressiveConnectedScore.generateScore();
+
         if (aggressiveHandover == 0) {
-            score = mConnectedScore.generateScore();
+            score = s0;
         } else {
-            score = mAggressiveConnectedScore.generateScore();
+            score = s1;
         }
 
         //sanitize boundaries
@@ -131,6 +135,8 @@
             score = 0;
         }
 
+        logLinkMetrics(wifiInfo, s0, s1);
+
         //report score
         if (score != wifiInfo.score) {
             if (mVerboseLoggingEnabled) {
@@ -157,8 +163,9 @@
     /**
      * Data logging for dumpsys
      */
-    private void logLinkMetrics(WifiInfo wifiInfo) {
+    private void logLinkMetrics(WifiInfo wifiInfo, int s0, int s1) {
         long now = mClock.getWallClockMillis();
+        if (now < FIRST_REASONABLE_WALL_CLOCK) return;
         double rssi = wifiInfo.getRssi();
         int freq = wifiInfo.getFrequency();
         int linkSpeed = wifiInfo.getLinkSpeed();
@@ -169,9 +176,10 @@
         try {
             String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now));
             String s = String.format(Locale.US, // Use US to avoid comma/decimal confusion
-                    "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f",
+                    "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d",
                     timestamp, mSessionNumber, rssi, freq, linkSpeed,
-                    txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate);
+                    txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate,
+                    s0, s1);
             mLinkMetricsHistory.add(s);
         } catch (Exception e) {
             Log.e(TAG, "format problem", e);
@@ -193,7 +201,7 @@
      * @param args unused
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx");
+        pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1");
         for (String line : mLinkMetricsHistory) {
             pw.println(line);
         }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index d222727..d6faf9b 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -780,11 +780,19 @@
         mLog.trace("setWifiEnabled package=% uid=% enable=%").c(packageName)
                 .c(Binder.getCallingUid()).c(enable).flush();
 
+        boolean isFromSettings =
+                mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid());
+
+        // If Airplane mode is enabled, only Settings is allowed to toggle Wifi
+        if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) {
+            mLog.trace("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush();
+            return false;
+        }
+
         // If SoftAp is enabled, only Settings is allowed to toggle wifi
         boolean apEnabled =
                 mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED;
-        boolean isFromSettings =
-                mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid());
+
         if (apEnabled && !isFromSettings) {
             mLog.trace("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
             return false;
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 33d8a14..2d24d20 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -733,10 +733,7 @@
     private static final int CMD_DIAGS_CONNECT_TIMEOUT                  = BASE + 252;
 
     /* Used to set the tx power limit for SAR during the start of a phone call. */
-    private static final int CMD_SET_SAR_TX_POWER_LIMIT                 = BASE + 253;
-
-    /* Used to reset the tx power limit for SAR at end of a phone call. */
-    private static final int CMD_RESET_SAR_TX_POWER_LIMIT               = BASE + 254;
+    private static final int CMD_SELECT_TX_POWER_SCENARIO               = BASE + 253;
 
     // For message logging.
     private static final Class[] sMessageClasses = {
@@ -804,7 +801,6 @@
     private final boolean mEnableChipWakeUpWhenAssociated;
     private final boolean mEnableRssiPollWhenAssociated;
     private final boolean mEnableVoiceCallSarTxPowerLimit;
-    private final int mVoiceCallSarTxPowerLimitInDbm;
 
     int mRunningBeaconCount = 0;
 
@@ -951,7 +947,7 @@
                 mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
 
         mLinkProperties = new LinkProperties();
-        mPhoneStateListener = new WifiPhoneStateListener();
+        mPhoneStateListener = new WifiPhoneStateListener(looper);
 
         mNetworkInfo.setIsAvailable(false);
         mLastBssid = null;
@@ -1051,8 +1047,6 @@
                 R.bool.config_wifi_enable_disconnection_debounce);
         mEnableVoiceCallSarTxPowerLimit = mContext.getResources().getBoolean(
                 R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit);
-        mVoiceCallSarTxPowerLimitInDbm = mContext.getResources().getInteger(
-                R.integer.config_wifi_framework_voice_call_sar_tx_power_limit_in_dbm);
         mEnableChipWakeUpWhenAssociated = true;
         mEnableRssiPollWhenAssociated = true;
 
@@ -3145,11 +3139,11 @@
     }
 
     private WifiInfo getWiFiInfoForUid(int uid) {
+        WifiInfo result = new WifiInfo(mWifiInfo);
         if (Binder.getCallingUid() == Process.myUid()) {
-            return mWifiInfo;
+            return result;
         }
 
-        WifiInfo result = new WifiInfo(mWifiInfo);
         result.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS);
 
         IBinder binder = mFacade.getService("package");
@@ -3778,13 +3772,19 @@
      * Listen for phone call state events to set/reset TX power limits for SAR requirements.
      */
     private class WifiPhoneStateListener extends PhoneStateListener {
+        WifiPhoneStateListener(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (mEnableVoiceCallSarTxPowerLimit) {
                 if (state == CALL_STATE_OFFHOOK) {
-                    sendMessage(CMD_SET_SAR_TX_POWER_LIMIT, mVoiceCallSarTxPowerLimitInDbm);
+                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+                            WifiNative.TX_POWER_SCENARIO_VOICE_CALL);
                 } else if (state == CALL_STATE_IDLE) {
-                    sendMessage(CMD_RESET_SAR_TX_POWER_LIMIT);
+                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+                            WifiNative.TX_POWER_SCENARIO_NORMAL);
                 }
             }
         }
@@ -3942,8 +3942,7 @@
                 case CMD_ROAM_WATCHDOG_TIMER:
                 case CMD_DISABLE_P2P_WATCHDOG_TIMER:
                 case CMD_DISABLE_EPHEMERAL_NETWORK:
-                case CMD_SET_SAR_TX_POWER_LIMIT:
-                case CMD_RESET_SAR_TX_POWER_LIMIT:
+                case CMD_SELECT_TX_POWER_SCENARIO:
                     messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
                     break;
                 case CMD_SET_SUSPEND_OPT_ENABLED:
@@ -4336,9 +4335,11 @@
             // appropriately.
             if (mEnableVoiceCallSarTxPowerLimit) {
                 if (getTelephonyManager().isOffhook()) {
-                    sendMessage(CMD_SET_SAR_TX_POWER_LIMIT, mVoiceCallSarTxPowerLimitInDbm);
-                } else if (getTelephonyManager().isIdle()) {
-                    sendMessage(CMD_RESET_SAR_TX_POWER_LIMIT);
+                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+                            WifiNative.TX_POWER_SCENARIO_VOICE_CALL);
+                } else {
+                    sendMessage(CMD_SELECT_TX_POWER_SCENARIO,
+                            WifiNative.TX_POWER_SCENARIO_NORMAL);
                 }
             }
 
@@ -4523,17 +4524,11 @@
                         mWifiConnectivityManager.forceConnectivityScan();
                     }
                     break;
-                case CMD_SET_SAR_TX_POWER_LIMIT:
-                    int txPowerLevelInDbm = message.arg1;
-                    logd("Setting Tx power limit to " + txPowerLevelInDbm);
-                    if (!mWifiNative.setTxPowerLimit(txPowerLevelInDbm)) {
-                        loge("Failed to set TX power limit");
-                    }
-                    break;
-                case CMD_RESET_SAR_TX_POWER_LIMIT:
-                    logd("Resetting Tx power limit");
-                    if (!mWifiNative.resetTxPowerLimit()) {
-                        loge("Failed to reset TX power limit");
+                case CMD_SELECT_TX_POWER_SCENARIO:
+                    int txPowerScenario = message.arg1;
+                    logd("Setting Tx power scenario to " + txPowerScenario);
+                    if (!mWifiNative.selectTxPowerScenario(txPowerScenario)) {
+                        loge("Failed to set TX power scenario");
                     }
                     break;
                 default:
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index b434f51..12674aa 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -2370,40 +2370,42 @@
         return android.hardware.wifi.V1_1.IWifiChip.castFrom(mIWifiChip);
     }
 
-    /**
-     * Set the TX power limit.
-     * Primarily used for meeting SAR requirements during voice calls.
-     *
-     * @param powerLevelInDbm Power level to set in dBm.
-     * @return true for success; false for failure or if the HAL version does not support this API.
-     */
-    public boolean setTxPowerLimit(int powerLevelInDbm) {
-        synchronized (sLock) {
-            try {
-                android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
-                if (iWifiChipV11 == null) return boolResult(false);
-                WifiStatus status = iWifiChipV11.setTxPowerLimit(powerLevelInDbm);
-                if (!ok(status)) return false;
-            } catch (RemoteException e) {
-                handleRemoteException(e);
-                return false;
-            }
-            return true;
+    private int frameworkToHalTxPowerScenario(int scenario) {
+        switch (scenario) {
+            case WifiNative.TX_POWER_SCENARIO_VOICE_CALL:
+                return android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL;
+            default:
+                throw new IllegalArgumentException("bad scenario: " + scenario);
         }
     }
 
     /**
-     * Reset the TX power limit.
+     * Select one of the pre-configured TX power level scenarios or reset it back to normal.
      * Primarily used for meeting SAR requirements during voice calls.
      *
+     * @param scenario Should be one {@link WifiNative#TX_POWER_SCENARIO_NORMAL} or
+     *        {@link WifiNative#TX_POWER_SCENARIO_VOICE_CALL}.
      * @return true for success; false for failure or if the HAL version does not support this API.
      */
-    public boolean resetTxPowerLimit() {
+    public boolean selectTxPowerScenario(int scenario) {
         synchronized (sLock) {
             try {
                 android.hardware.wifi.V1_1.IWifiChip iWifiChipV11 = getWifiChipForV1_1Mockable();
                 if (iWifiChipV11 == null) return boolResult(false);
-                WifiStatus status = iWifiChipV11.resetTxPowerLimit();
+                WifiStatus status;
+                if (scenario != WifiNative.TX_POWER_SCENARIO_NORMAL) {
+                    int halScenario;
+                    try {
+                        halScenario = frameworkToHalTxPowerScenario(scenario);
+                    } catch (IllegalArgumentException e) {
+                        mLog.err("Illegal argument for select tx power scenario")
+                                .c(e.toString()).flush();
+                        return false;
+                    }
+                    status = iWifiChipV11.selectTxPowerScenario(halScenario);
+                } else {
+                    status = iWifiChipV11.resetTxPowerScenario();
+                }
                 if (!ok(status)) return false;
             } catch (RemoteException e) {
                 handleRemoteException(e);
diff --git a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
index 211e62a..0fadd80 100644
--- a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
@@ -27,6 +27,9 @@
 import com.android.server.wifi.WifiMonitor;
 import com.android.server.wifi.WifiNative;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * WifiScanner implementation that takes advantage of the gscan HAL API
  * The gscan API is used to perform background scans and wificond is used for oneshot scans.
@@ -151,4 +154,9 @@
             return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno();
         }
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        mWificondScannerDelegate.dump(fd, pw, args);
+    }
 }
diff --git a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
index e0fb535..5281b3a 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
@@ -26,6 +26,8 @@
 import com.android.server.wifi.WifiMonitor;
 import com.android.server.wifi.WifiNative;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Comparator;
 
 /**
@@ -158,4 +160,6 @@
      * @return true if background scan needs to be started, false otherwise.
      */
     public abstract boolean shouldScheduleBackgroundScanForHwPno();
+
+    protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 }
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index af874b9..ab2a5dc 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -2156,6 +2156,9 @@
             }
             pw.println();
         }
+        if (mScannerImpl != null) {
+            mScannerImpl.dump(fd, pw, args);
+        }
     }
 
     void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
diff --git a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
index fd7fddb..84105ee 100644
--- a/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -32,6 +32,8 @@
 import com.android.server.wifi.WifiNative;
 import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -81,6 +83,7 @@
     private boolean mBackgroundScanPaused = false;
     private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY);
 
+    private ArrayList<ScanDetail> mNativeScanResults;
     private WifiScanner.ScanData mLatestSingleScanResult =
             new WifiScanner.ScanData(0, 0, new ScanResult[0]);
 
@@ -535,11 +538,11 @@
                  // got a scan before we started scanning or after scan was canceled
                 return;
             }
-            ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
+            mNativeScanResults = mWifiNative.getScanResults();
             List<ScanResult> hwPnoScanResults = new ArrayList<>();
             int numFilteredScanResults = 0;
-            for (int i = 0; i < nativeResults.size(); ++i) {
-                ScanResult result = nativeResults.get(i).getScanResult();
+            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) {
@@ -556,11 +559,8 @@
 
             if (mLastScanSettings.hwPnoScanActive
                     && mLastScanSettings.pnoScanEventHandler != null) {
-                ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()];
-                for (int i = 0; i < pnoScanResultsArray.length; ++i) {
-                    ScanResult result = nativeResults.get(i).getScanResult();
-                    pnoScanResultsArray[i] = hwPnoScanResults.get(i);
-                }
+                ScanResult[] pnoScanResultsArray =
+                        hwPnoScanResults.toArray(new ScanResult[hwPnoScanResults.size()]);
                 mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
             }
             // On pno scan result event, we are expecting a mLastScanSettings for pno scan.
@@ -598,12 +598,12 @@
             }
 
             if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
-            ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
+            mNativeScanResults = mWifiNative.getScanResults();
             List<ScanResult> singleScanResults = new ArrayList<>();
             List<ScanResult> backgroundScanResults = new ArrayList<>();
             int numFilteredScanResults = 0;
-            for (int i = 0; i < nativeResults.size(); ++i) {
-                ScanResult result = nativeResults.get(i).getScanResult();
+            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.backgroundScanActive) {
@@ -772,6 +772,40 @@
         return false;
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (mSettingsLock) {
+            pw.println("Latest native scan results:");
+            if (mNativeScanResults != null && mNativeScanResults.size() != 0) {
+                long nowMs = mClock.getElapsedSinceBootMillis();
+                pw.println("    BSSID              Frequency  RSSI  Age(sec)   SSID "
+                        + "                                Flags");
+                for (ScanDetail scanDetail : mNativeScanResults) {
+                    ScanResult r = scanDetail.getScanResult();
+                    long timeStampMs = r.timestamp / 1000;
+                    String age;
+                    if (timeStampMs <= 0) {
+                        age = "___?___";
+                    } else if (nowMs < timeStampMs) {
+                        age = "  0.000";
+                    } else if (timeStampMs < nowMs - 1000000) {
+                        age = ">1000.0";
+                    } else {
+                        age = String.format("%3.3f", (nowMs - timeStampMs) / 1000.0);
+                    }
+                    String ssid = r.SSID == null ? "" : r.SSID;
+                    pw.printf("  %17s  %9d  %5d   %7s    %-32s  %s\n",
+                              r.BSSID,
+                              r.frequency,
+                              r.level,
+                              age,
+                              String.format("%1.32s", ssid),
+                              r.capabilities);
+                }
+            }
+        }
+    }
+
     private static class LastScanSettings {
         public long startTime;
 
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
index 3e203a6..7a25e17 100644
--- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -120,6 +120,9 @@
                 anyLong())).thenReturn(true);
         when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
                 any(IServiceNotification.Stub.class))).thenReturn(true);
+        when(mServiceManagerMock.getTransport(
+                eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
+                .thenReturn(IServiceManager.Transport.HWBINDER);
         when(mWifiMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(
                 true);
         when(mWifiMock.registerEventCallback(any(IWifiEventCallback.class))).thenReturn(mStatusOk);
@@ -164,6 +167,22 @@
     }
 
     /**
+     * Test the service manager notification coming in after
+     * {@link HalDeviceManager#initIWifiIfNecessary()} is already invoked as a part of
+     * {@link HalDeviceManager#initialize()}.
+     */
+    @Test
+    public void testServiceRegisterationAfterInitialize() throws Exception {
+        mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+        executeAndValidateInitializationSequence();
+
+        // This should now be ignored since IWifi is already non-null.
+        mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+
+        verifyNoMoreInteractions(mManagerStatusListenerMock, mWifiMock, mServiceManagerMock);
+    }
+
+    /**
      * Validate that multiple callback registrations are called and that duplicate ones are
      * only called once.
      */
@@ -221,7 +240,7 @@
 
         // verify: service and callback calls
         mInOrder.verify(mWifiMock).start();
-        mInOrder.verify(mManagerStatusListenerMock, times(3)).onStatusChanged();
+        mInOrder.verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
 
         verifyNoMoreInteractions(mManagerStatusListenerMock);
     }
@@ -1058,17 +1077,14 @@
      */
     @Test
     public void testIsSupportedTrue() throws Exception {
-        when(mServiceManagerMock.getTransport(
-                eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
-                .thenReturn(IServiceManager.Transport.HWBINDER);
         mInOrder = inOrder(mServiceManagerMock, mWifiMock);
         executeAndValidateInitializationSequence();
         assertTrue(mDut.isSupported());
     }
 
     /**
-     * Validate that isSupported() returns true when IServiceManager finds the vendor HAL daemon in
-     * the VINTF.
+     * Validate that isSupported() returns false when IServiceManager does not find the vendor HAL
+     * daemon in the VINTF.
      */
     @Test
     public void testIsSupportedFalse() throws Exception {
@@ -1076,7 +1092,7 @@
                 eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
                 .thenReturn(IServiceManager.Transport.EMPTY);
         mInOrder = inOrder(mServiceManagerMock, mWifiMock);
-        executeAndValidateInitializationSequence();
+        executeAndValidateInitializationSequence(false);
         assertFalse(mDut.isSupported());
     }
 
@@ -1088,6 +1104,10 @@
     }
 
     private void executeAndValidateInitializationSequence() throws Exception {
+        executeAndValidateInitializationSequence(true);
+    }
+
+    private void executeAndValidateInitializationSequence(boolean isSupported) throws Exception {
         // act:
         mDut.initialize();
 
@@ -1097,13 +1117,20 @@
         mInOrder.verify(mServiceManagerMock).registerForNotifications(eq(IWifi.kInterfaceName),
                 eq(""), mServiceNotificationCaptor.capture());
 
-        // act: get the service started (which happens even when service was already up)
-        mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+        // The service should already be up at this point.
+        mInOrder.verify(mServiceManagerMock).getTransport(eq(IWifi.kInterfaceName),
+                eq(HalDeviceManager.HAL_INSTANCE_NAME));
 
-        // verify: wifi initialization sequence
-        mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
-        mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
-        collector.checkThat("isReady is true", mDut.isReady(), equalTo(true));
+        // verify: wifi initialization sequence if vendor HAL is supported.
+        if (isSupported) {
+            mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+            mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+            // verify: onStop called as a part of initialize.
+            mInOrder.verify(mWifiMock).stop();
+            collector.checkThat("isReady is true", mDut.isReady(), equalTo(true));
+        } else {
+            collector.checkThat("isReady is false", mDut.isReady(), equalTo(false));
+        }
     }
 
     private void executeAndValidateStartupSequence()throws Exception {
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index 2d3b066..02064d8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wifi;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
 
 import java.io.File;
 import java.lang.reflect.Method;
+import java.util.Random;
 
 /**
  * Unit tests for {@link com.android.server.wifi.WifiApConfigStore}.
@@ -50,12 +52,15 @@
     private static final String TEST_DEFAULT_AP_SSID = "TestAP";
     private static final String TEST_CONFIGURED_AP_SSID = "ConfiguredAP";
     private static final String TEST_DEFAULT_HOTSPOT_SSID = "TestShare";
+    private static final String TEST_DEFAULT_HOTSPOT_PSK = "TestPassword";
     private static final int RAND_SSID_INT_MIN = 1000;
     private static final int RAND_SSID_INT_MAX = 9999;
+    private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
 
     @Mock Context mContext;
     @Mock BackupManagerProxy mBackupManagerProxy;
     File mApConfigFile;
+    Random mRandom;
 
     @Before
     public void setUp() throws Exception {
@@ -73,6 +78,8 @@
         resources.setString(R.string.wifi_localhotspot_configure_ssid_default,
                             TEST_DEFAULT_HOTSPOT_SSID);
         when(mContext.getResources()).thenReturn(resources);
+
+        mRandom = new Random();
     }
 
     @After
@@ -195,6 +202,17 @@
     }
 
     /**
+     * Verify a proper WifiConfiguration is generate by getDefaultApConfiguration().
+     */
+    @Test
+    public void getDefaultApConfigurationIsValid() {
+        WifiApConfigStore store = new WifiApConfigStore(
+                mContext, mBackupManagerProxy, mApConfigFile.getPath());
+        WifiConfiguration config = store.getApConfiguration();
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
      * Verify a proper local only hotspot config is generated when called properly with the valid
      * context.
      */
@@ -204,5 +222,147 @@
         verifyDefaultApConfig(config, TEST_DEFAULT_HOTSPOT_SSID);
         // The LOHS config should also have a specific network id set - check that as well.
         assertEquals(WifiConfiguration.LOCAL_ONLY_NETWORK_ID, config.networkId);
+
+        // verify that the config passes the validateApWifiConfiguration check
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
+     * Helper method to generate random SSIDs.
+     *
+     * Note: this method has limited use as a random SSID generator.  The characters used in this
+     * method do no not cover all valid inputs.
+     * @param length number of characters to generate for the name
+     * @return String generated string of random characters
+     */
+    private String generateRandomString(int length) {
+
+        StringBuilder stringBuilder = new StringBuilder(length);
+        int index = -1;
+        while (stringBuilder.length() < length) {
+            index = mRandom.nextInt(TEST_CHAR_SET_AS_STRING.length());
+            stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index));
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Verify the SSID checks in validateApWifiConfiguration.
+     *
+     * Cases to check and verify they trigger failed verification:
+     * null WifiConfiguration.SSID
+     * empty WifiConfiguration.SSID
+     * invalid WifiConfiguaration.SSID length
+     *
+     * Additionally check a valid SSID with a random (within valid ranges) length.
+     */
+    @Test
+    public void testSsidVerificationInValidateApWifiConfigurationCheck() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = null;
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+        config.SSID = "";
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+        // check a string that is too large
+        config.SSID = generateRandomString(WifiApConfigStore.SSID_MAX_LEN + 1);
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // now check a valid SSID with a random length
+        config.SSID = generateRandomString(mRandom.nextInt(WifiApConfigStore.SSID_MAX_LEN + 1));
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
+     * Verify the Open network checks in validateApWifiConfiguration.
+     *
+     * If the configured network is open, it should not have a password set.
+     *
+     * Additionally verify a valid open network passes verification.
+     */
+    @Test
+    public void testOpenNetworkConfigInValidateApWifiConfigurationCheck() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        config.preSharedKey = TEST_DEFAULT_HOTSPOT_PSK;
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // open networks should not have a password set
+        config.preSharedKey = null;
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+        config.preSharedKey = "";
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
+     * Verify the WPA2_PSK network checks in validateApWifiConfiguration.
+     *
+     * If the configured network is configured with a preSharedKey, verify that the passwork is set
+     * and it meets length requirements.
+     */
+    @Test
+    public void testWpa2PskNetworkConfigInValidateApWifiConfigurationCheck() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        config.preSharedKey = null;
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+        config.preSharedKey = "";
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // test too short
+        config.preSharedKey =
+                generateRandomString(WifiApConfigStore.PSK_MIN_LEN - 1);
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // test too long
+        config.preSharedKey =
+                generateRandomString(WifiApConfigStore.PSK_MAX_LEN + 1);
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // explicitly test min length
+        config.preSharedKey =
+            generateRandomString(WifiApConfigStore.PSK_MIN_LEN);
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // explicitly test max length
+        config.preSharedKey =
+                generateRandomString(WifiApConfigStore.PSK_MAX_LEN);
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+
+        // test random (valid length)
+        int maxLen = WifiApConfigStore.PSK_MAX_LEN;
+        int minLen = WifiApConfigStore.PSK_MIN_LEN;
+        config.preSharedKey =
+                generateRandomString(mRandom.nextInt(maxLen - minLen) + minLen);
+        assertTrue(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
+     * Verify an invalid AuthType setting (that would trigger an IllegalStateException)
+     * returns false when triggered in the validateApWifiConfiguration.
+     */
+    @Test
+    public void testInvalidAuthTypeInValidateApWifiConfigurationCheck() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
+    }
+
+    /**
+     * Verify an unsupported authType returns false for validateApWifiConfigurationCheck.
+     */
+    @Test
+    public void testUnsupportedAuthTypeInValidateApWifiConfigurationCheck() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = TEST_DEFAULT_HOTSPOT_SSID;
+
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        assertFalse(WifiApConfigStore.validateApWifiConfiguration(config));
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index fee9033..4965a35 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -76,6 +76,10 @@
                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
         mThresholdQualifiedRssi5G = mResource.getInteger(
                 R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+        mStayOnNetworkMinimumTxRate = mResource.getInteger(
+                R.integer.config_wifi_framework_min_tx_rate_for_staying_on_network);
+        mStayOnNetworkMinimumRxRate = mResource.getInteger(
+                R.integer.config_wifi_framework_min_rx_rate_for_staying_on_network);
         mThresholdSaturatedRssi2G = mResource.getInteger(
                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
         mThresholdSaturatedRssi5G = mResource.getInteger(
@@ -149,6 +153,8 @@
     private int mThresholdMinimumRssi5G;
     private int mThresholdQualifiedRssi2G;
     private int mThresholdQualifiedRssi5G;
+    private int mStayOnNetworkMinimumTxRate;
+    private int mStayOnNetworkMinimumRxRate;
     private int mThresholdSaturatedRssi2G;
     private int mThresholdSaturatedRssi5G;
 
@@ -172,6 +178,12 @@
                 R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
                 .thenReturn(-85);
         when(mResource.getInteger(
+                R.integer.config_wifi_framework_max_tx_rate_for_full_scan))
+                .thenReturn(8);
+        when(mResource.getInteger(
+                R.integer.config_wifi_framework_max_rx_rate_for_full_scan))
+                .thenReturn(8);
+        when(mResource.getInteger(
                 R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
                 .thenReturn(-57);
         when(mResource.getInteger(
@@ -615,9 +627,19 @@
      */
     @Test
     public void test2GhzQualifiedNo5GhzAvailable() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
         // Do not perform selection on 2GHz if current network is good and no 5GHz available
-        testStayOrSwitch(mThresholdQualifiedRssi2G, false,
-                mThresholdQualifiedRssi2G + 10, false, false);
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                false /* not open network */,
+                // Should not try to switch.
+                false);
     }
 
     /**
@@ -632,24 +654,24 @@
      */
     @Test
     public void test2GhzHighQuality5GhzAvailable() {
-        // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
-        testStayOrSwitch(mThresholdQualifiedRssi2G, false,
-                mThresholdQualifiedRssi5G - 1, true, true);
-    }
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
 
-    /**
-     * Wifi network selector performs network selection on 2Ghz networks if the current network
-     * is not of high quality.
-     *
-     * WifiStateMachine is under connected state and 2.4GHz test1 is connected.
-     *
-     * Expected behavior: network selection is performed
-     */
-    @Test
-    public void test2GhzNotQualifiedOther2GhzAvailable() {
-        // Run Selection on 2Ghz networks when not qualified even if no 5GHz available
-        testStayOrSwitch(mThresholdQualifiedRssi2G - 1, false,
-                mThresholdQualifiedRssi2G - 10, false, true);
+        // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
+        testStayOrTryToSwitch(
+                // Parameters for network1:
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                false /* not open network */,
+                // Parameters for network2:
+                mThresholdQualifiedRssi5G + 1 /* rssi */,
+                true /* a 5G network */,
+                false /* not open network */,
+                // Should try to switch.
+                true);
     }
 
     /**
@@ -662,10 +684,19 @@
      */
     @Test
     public void test5GhzNotQualifiedLowRssi() {
-        // Run Selection when the current 5Ghz network has low RSSI, regardless of what may
-        // be available. The second scan result is irrelevant.
-        testStayOrSwitch(mThresholdQualifiedRssi5G - 1, true,
-                mThresholdQualifiedRssi2G - 1, false, true);
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G - 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+        // Run Selection when the current 5Ghz network has low RSSI.
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+                true /* a 5G network */,
+                false /* not open network */,
+                // Should try to switch.
+                true);
     }
 
     /**
@@ -678,9 +709,162 @@
      */
     @Test
     public void test5GhzQualified() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
         // Connected to a high quality 5Ghz network, so the other result is irrelevant
-        testStayOrSwitch(mThresholdQualifiedRssi5G, true,
-                mThresholdQualifiedRssi5G + 10, true, false);
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+                true /* a 5G network */,
+                false /* not open network */,
+                // Should not try to switch.
+                false);
+    }
+
+    /**
+     * New network selection is performed if the currently connected network
+     * band is 2G and there is no sign of streaming traffic.
+     *
+     * Expected behavior: Network Selector perform network selection after connected
+     * to the first one.
+     */
+    @Test
+    public void band2GNetworkIsNotSufficientWhenNoOngoingTrafficAnd5GhzAvailable() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+        testStayOrTryToSwitch(
+                // Parameters for network1:
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                false /* not open network */,
+                // Parameters for network2:
+                mThresholdQualifiedRssi5G + 1 /* rssi */,
+                true /* a 5G network */,
+                false /* not open network */,
+                // Should try to switch.
+                true);
+    }
+
+    /**
+     * New network selection is performed if the currently connected network
+     * band is 2G with bad rssi.
+     *
+     * Expected behavior: Network Selector perform network selection after connected
+     * to the first one.
+     */
+    @Test
+    public void band2GNetworkIsNotSufficientWithBadRssi() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G - 1);
+        // No streaming traffic.
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                false /* not open network */,
+                // Should try to switch.
+                true);
+    }
+
+    /**
+     * New network selection is not performed if the currently connected 2G network
+     * has good Rssi and sign of streaming tx traffic.
+     *
+     * Expected behavior: Network selector does not perform network selection.
+     */
+    @Test
+    public void band2GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+        // Streaming traffic
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
+                (double) (mStayOnNetworkMinimumTxRate + 1));
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                true /* open network */,
+                // Should not try to switch.
+                false);
+    }
+
+    /**
+     * New network selection is not performed if the currently connected 2G network
+     * has good Rssi and sign of streaming rx traffic.
+     *
+     * Expected behavior: Network selector does not perform network selection.
+     */
+    @Test
+    public void band2GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
+        // Streaming traffic
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
+                (double) (mStayOnNetworkMinimumRxRate + 1));
+
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
+                false /* not a 5G network */,
+                true /* open network */,
+                // Should not try to switch.
+                false);
+    }
+
+    /**
+     * New network selection is not performed if the currently connected 5G network
+     * has good Rssi and sign of streaming tx traffic.
+     *
+     * Expected behavior: Network selector does not perform network selection.
+     */
+    @Test
+    public void band5GNetworkIsSufficientWhenOnGoingTxTrafficCombinedWithGoodRssi() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+        // Streaming traffic
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
+                (double) (mStayOnNetworkMinimumTxRate + 1));
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+                true /* a 5G network */,
+                true /* open network */,
+                // Should not try to switch.
+                false);
+    }
+
+    /**
+     * New network selection is not performed if the currently connected 5G network
+     * has good Rssi and sign of streaming rx traffic.
+     *
+     * Expected behavior: Network selector does not perform network selection.
+     */
+    @Test
+    public void band5GNetworkIsSufficientWhenOnGoingRxTrafficCombinedWithGoodRssi() {
+        // Rssi after connected.
+        when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
+        // Streaming traffic
+        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
+        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
+                (double) (mStayOnNetworkMinimumRxRate + 1));
+
+        testStayOrTryToSwitch(
+                mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
+                true /* a 5G network */,
+                true /* open network */,
+                // Should not try to switch.
+                false);
     }
 
     /**
@@ -690,45 +874,78 @@
      * It sets up two networks, connects to the first, and then ensures that
      * both are available in the scan results for the NetworkSelector.
      */
-    private void testStayOrSwitch(int levelNetwork1, boolean is5GHzNetwork1,
-                int levelNetwork2, boolean is5GHzNetwork2, boolean shouldSelect) {
-        // Create an updated ScanDetails that includes a new network
+    private void testStayOrTryToSwitch(
+            int rssiNetwork1, boolean is5GHzNetwork1, boolean isOpenNetwork1,
+            int rssiNetwork2, boolean is5GHzNetwork2, boolean isOpenNetwork2,
+            boolean shouldSelect) {
         String[] ssids = {"\"test1\"", "\"test2\""};
         String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
-        int[] freqs = new int[2];
-        freqs[0] = is5GHzNetwork1 ? 5180 : 2437;
-        freqs[1] = is5GHzNetwork2 ? 5180 : 2437;
-        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
-        int[] levels = {levelNetwork1, levelNetwork2};
-        int[] securities = {SECURITY_PSK, SECURITY_PSK};
+        int[] freqs = {is5GHzNetwork1 ? 5180 : 2437, is5GHzNetwork2 ? 5180 : 2437};
+        String[] caps = {isOpenNetwork1 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]",
+                         isOpenNetwork2 ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"};
+        int[] levels = {rssiNetwork1, rssiNetwork2};
+        int[] securities = {isOpenNetwork1 ? SECURITY_NONE : SECURITY_PSK,
+                            isOpenNetwork2 ? SECURITY_NONE : SECURITY_PSK};
+        testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect);
+    }
 
+    /**
+     * This is a meta-test that given one scan results, will
+     * determine whether or not network selection should be performed.
+     *
+     * It sets up two networks, connects to the first, and then ensures that
+     * the scan results for the NetworkSelector.
+     */
+    private void testStayOrTryToSwitch(
+            int rssi, boolean is5GHz, boolean isOpenNetwork,
+            boolean shouldSelect) {
+        String[] ssids = {"\"test1\""};
+        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+        int[] freqs = {is5GHz ? 5180 : 2437};
+        String[] caps = {isOpenNetwork ? "[ESS]" : "[WPA2-EAP-CCMP][ESS]"};
+        int[] levels = {rssi};
+        int[] securities = {isOpenNetwork ? SECURITY_NONE : SECURITY_PSK};
+        testStayOrTryToSwitchImpl(ssids, bssids, freqs, caps, levels, securities, shouldSelect);
+    }
+
+    private void testStayOrTryToSwitchImpl(String[] ssids, String[] bssids, int[] freqs,
+            String[] caps, int[] levels, int[] securities,
+            boolean shouldSelect) {
         // Make a network selection to connect to test1.
         ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
                 WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
                         freqs, caps, levels, securities, mWifiConfigManager, mClock);
         List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
         HashSet<String> blacklist = new HashSet<String>();
-        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
-                blacklist, mWifiInfo, false, true, false);
+        // DummyNetworkEvaluator always return the first network in the scan results
+        // for connection, so this should connect to the first network.
+        WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(
+                scanDetails,
+                blacklist, mWifiInfo, false, true, true);
+        assertNotNull("Result should be not null", candidate);
+        WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+                scanDetails.get(0).getScanResult(), candidate);
 
         when(mWifiInfo.getNetworkId()).thenReturn(0);
         when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
-        when(mWifiInfo.is24GHz()).thenReturn(!is5GHzNetwork1);
-        when(mWifiInfo.is5GHz()).thenReturn(is5GHzNetwork1);
-        when(mWifiInfo.getRssi()).thenReturn(levels[0]);
+        when(mWifiInfo.is24GHz()).thenReturn(!ScanResult.is5GHz(freqs[0]));
+        when(mWifiInfo.is5GHz()).thenReturn(ScanResult.is5GHz(freqs[0]));
+
         when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
                 + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
 
         candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo,
                 true, false, false);
 
-        if (!shouldSelect) {
-            assertEquals("Expect null configuration", null, candidate);
-        } else {
+        // DummyNetworkEvaluator always return the first network in the scan results
+        // for connection, so if nework selection is performed, the first network should
+        // be returned as candidate.
+        if (shouldSelect) {
             assertNotNull("Result should be not null", candidate);
-            ScanResult expectedResult = scanDetails.get(0).getScanResult();
             WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
-                    expectedResult, candidate);
+                    scanDetails.get(0).getScanResult(), candidate);
+        } else {
+            assertEquals("Expect null configuration", null, candidate);
         }
     }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index 3b11d28..055050d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -362,6 +362,7 @@
     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));
         verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
     }
@@ -386,12 +387,39 @@
         doThrow(new SecurityException()).when(mContext)
                 .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
                                                 eq("WifiService"));
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
         mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
         verify(mWifiStateMachine, never()).syncGetWifiApState();
     }
 
     /**
      * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
+     * are in airplane mode.
+     */
+    @Test
+    public void testSetWifiEnabledFromNetworkSettingsHolderWhenInAirplaneMode() throws Exception {
+        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
+        verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+    }
+
+    /**
+     * Verify that a caller without the NETWORK_SETTINGS permission can't enable wifi
+     * if we are in airplane mode.
+     */
+    @Test
+    public void testSetWifiEnabledFromAppFailsWhenInAirplaneMode() throws Exception {
+        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+        assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+        verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
+    }
+
+    /**
+     * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
      * are in softap mode.
      */
     @Test
@@ -399,6 +427,7 @@
         when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
         when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
         assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
         verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
     }
@@ -410,6 +439,7 @@
     public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
         when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
         when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
         assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
         verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
         verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 10d6460..4dd0621 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -1902,7 +1902,7 @@
      * setting/resetting Tx power limit.
      */
     @Test
-    public void testVoiceCallSar_disabledTxPowerLimit_WifiOn() throws Exception {
+    public void testVoiceCallSar_disabledTxPowerScenario_WifiOn() throws Exception {
         loadComponentsInStaMode();
         mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
         assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
@@ -1915,7 +1915,7 @@
      * setting/resetting Tx power limit.
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimit_WifiOn() throws Exception {
+    public void testVoiceCallSar_enabledTxPowerScenario_WifiOn() throws Exception {
         mResources.setBoolean(
                 R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
         initializeWsm();
@@ -1933,16 +1933,13 @@
      * {@link TelephonyManager#CALL_STATE_OFFHOOK}.
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimitCallStateOffHook_WhenWifiTurnedOn()
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiTurnedOn()
             throws Exception {
-        int powerLevelInDbm = -45;
-        mResources.setInteger(
-                R.integer.config_wifi_framework_voice_call_sar_tx_power_limit_in_dbm,
-                powerLevelInDbm);
         mResources.setBoolean(
                 R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
         initializeWsm();
 
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
         when(mTelephonyManager.isOffhook()).thenReturn(true);
 
         loadComponentsInStaMode();
@@ -1950,7 +1947,7 @@
         assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
         assertEquals("DisconnectedState", getCurrentState().getName());
         assertNotNull(mPhoneStateListener);
-        verify(mWifiNative).setTxPowerLimit(eq(powerLevelInDbm));
+        verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
     }
 
     /**
@@ -1959,12 +1956,13 @@
      * {@link TelephonyManager#CALL_STATE_IDLE}.
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimitCallStateIdle_WhenWifiTurnedOn()
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateIdle_WhenWifiTurnedOn()
             throws Exception {
         mResources.setBoolean(
                 R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
         initializeWsm();
 
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
         when(mTelephonyManager.isIdle()).thenReturn(true);
 
         loadComponentsInStaMode();
@@ -1972,7 +1970,30 @@
         assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
         assertEquals("DisconnectedState", getCurrentState().getName());
         assertNotNull(mPhoneStateListener);
-        verify(mWifiNative).resetTxPowerLimit();
+    }
+
+    /**
+     * Test that we do register the telephony call state listener on devices which do support
+     * setting/resetting Tx power limit and set the tx power level if we're in state
+     * {@link TelephonyManager#CALL_STATE_OFFHOOK}. This test checks if the
+     * {@link WifiNative#selectTxPowerScenario(int)} failure is handled correctly.
+     */
+    @Test
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiTurnedOn_Fails()
+            throws Exception {
+        mResources.setBoolean(
+                R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
+        initializeWsm();
+
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(false);
+        when(mTelephonyManager.isOffhook()).thenReturn(true);
+
+        loadComponentsInStaMode();
+        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+        assertEquals("DisconnectedState", getCurrentState().getName());
+        assertNotNull(mPhoneStateListener);
+        verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
     }
 
     /**
@@ -1981,16 +2002,14 @@
      * {@link TelephonyManager#CALL_STATE_OFFHOOK}.
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimitCallStateOffHook_WhenWifiOn() throws Exception {
-        int powerLevelInDbm = -45;
-        mResources.setInteger(
-                R.integer.config_wifi_framework_voice_call_sar_tx_power_limit_in_dbm,
-                powerLevelInDbm);
-        testVoiceCallSar_enabledTxPowerLimit_WifiOn();
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiOn()
+            throws Exception {
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+        testVoiceCallSar_enabledTxPowerScenario_WifiOn();
 
         mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
         mLooper.dispatchAll();
-        verify(mWifiNative).setTxPowerLimit(eq(powerLevelInDbm));
+        verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
     }
 
     /**
@@ -1999,12 +2018,31 @@
      * {@link TelephonyManager#CALL_STATE_IDLE}.
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimitCallStateIdle_WhenWifiOn() throws Exception {
-        testVoiceCallSar_enabledTxPowerLimit_WifiOn();
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateIdle_WhenWifiOn() throws Exception {
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(true);
+        testVoiceCallSar_enabledTxPowerScenario_WifiOn();
 
         mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
         mLooper.dispatchAll();
-        verify(mWifiNative).resetTxPowerLimit();
+        verify(mWifiNative, atLeastOnce())
+                .selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_NORMAL));
+    }
+
+    /**
+     * Test that we invoke the corresponding WifiNative method when
+     * {@link PhoneStateListener#onCallStateChanged(int, String)} is invoked with state
+     * {@link TelephonyManager#CALL_STATE_OFFHOOK}. This test checks if the
+     * {@link WifiNative#selectTxPowerScenario(int)} failure is handled correctly.
+     */
+    @Test
+    public void testVoiceCallSar_enabledTxPowerScenarioCallStateOffHook_WhenWifiOn_Fails()
+            throws Exception {
+        when(mWifiNative.selectTxPowerScenario(anyInt())).thenReturn(false);
+        testVoiceCallSar_enabledTxPowerScenario_WifiOn();
+
+        mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
+        mLooper.dispatchAll();
+        verify(mWifiNative).selectTxPowerScenario(eq(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
     }
 
     /**
@@ -2014,18 +2052,18 @@
      * wifi is off (state machine is not in SupplicantStarted state).
      */
     @Test
-    public void testVoiceCallSar_enabledTxPowerLimitCallState_WhenWifiOff() throws Exception {
+    public void testVoiceCallSar_enabledTxPowerScenarioCallState_WhenWifiOff() throws Exception {
         mResources.setBoolean(
                 R.bool.config_wifi_framework_enable_voice_call_sar_tx_power_limit, true);
         initializeWsm();
 
         mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, "");
         mLooper.dispatchAll();
-        verify(mWifiNative, never()).setTxPowerLimit(anyInt());
+        verify(mWifiNative, never()).selectTxPowerScenario(anyInt());
 
         mPhoneStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, "");
         mLooper.dispatchAll();
-        verify(mWifiNative, never()).resetTxPowerLimit();
+        verify(mWifiNative, never()).selectTxPowerScenario(anyInt());
     }
 
     /**
@@ -2103,4 +2141,20 @@
         currentConfig.networkId = lastSelectedNetworkId - 1;
         assertFalse(mWsm.shouldEvaluateWhetherToSendExplicitlySelected(currentConfig));
     }
+
+    /**
+     * Test that {@link WifiStateMachine#syncRequestConnectionInfo()} always returns a copy of
+     * WifiInfo.
+     */
+    @Test
+    public void testSyncRequestConnectionInfoDoesNotReturnLocalReference() {
+        WifiInfo wifiInfo = mWsm.getWifiInfo();
+        wifiInfo.setBSSID(sBSSID);
+        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(sSSID));
+
+        WifiInfo syncWifiInfo = mWsm.syncRequestConnectionInfo();
+        assertEquals(wifiInfo.getSSID(), syncWifiInfo.getSSID());
+        assertEquals(wifiInfo.getBSSID(), syncWifiInfo.getBSSID());
+        assertFalse(wifiInfo == syncWifiInfo);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 3f2f389..84de9d2 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -1844,48 +1844,67 @@
     }
 
     /**
-     * Test the new setTxPowerLimit HIDL method invocation. This should return failure if the
+     * Test the new selectTxPowerScenario HIDL method invocation. This should return failure if the
      * HAL service is exposing the 1.0 interface.
      */
     @Test
-    public void testSetTxPowerLimit() throws RemoteException {
-        int powerLevelInDbm = -45;
-
+    public void testSelectTxPowerScenario() throws RemoteException {
         assertTrue(mWifiVendorHal.startVendorHal(true));
         // Should fail because we exposed the 1.0 IWifiChip.
-        assertFalse(mWifiVendorHal.setTxPowerLimit(powerLevelInDbm));
-        verify(mIWifiChipV11, never()).setTxPowerLimit(eq(powerLevelInDbm));
+        assertFalse(
+                mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+        verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
         mWifiVendorHal.stopVendorHal();
 
         // Now expose the 1.1 IWifiChip.
         mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
-        when(mIWifiChipV11.setTxPowerLimit(anyInt())).thenReturn(mWifiStatusSuccess);
+        when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
 
         assertTrue(mWifiVendorHal.startVendorHal(true));
-        assertTrue(mWifiVendorHal.setTxPowerLimit(powerLevelInDbm));
-        verify(mIWifiChipV11).setTxPowerLimit(eq(powerLevelInDbm));
+        assertTrue(
+                mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_VOICE_CALL));
+        verify(mIWifiChipV11).selectTxPowerScenario(
+                eq(android.hardware.wifi.V1_1.IWifiChip.TxPowerScenario.VOICE_CALL));
+        verify(mIWifiChipV11, never()).resetTxPowerScenario();
         mWifiVendorHal.stopVendorHal();
     }
 
     /**
-     * Test the new setTxPowerLimit HIDL method invocation. This should return failure if the
+     * Test the new resetTxPowerScenario HIDL method invocation. This should return failure if the
      * HAL service is exposing the 1.0 interface.
      */
     @Test
-    public void testResetTxPowerLimit() throws RemoteException {
+    public void testResetTxPowerScenario() throws RemoteException {
         assertTrue(mWifiVendorHal.startVendorHal(true));
         // Should fail because we exposed the 1.0 IWifiChip.
-        assertFalse(mWifiVendorHal.resetTxPowerLimit());
-        verify(mIWifiChipV11, never()).resetTxPowerLimit();
+        assertFalse(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
+        verify(mIWifiChipV11, never()).resetTxPowerScenario();
         mWifiVendorHal.stopVendorHal();
 
         // Now expose the 1.1 IWifiChip.
         mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
-        when(mIWifiChipV11.resetTxPowerLimit()).thenReturn(mWifiStatusSuccess);
+        when(mIWifiChipV11.resetTxPowerScenario()).thenReturn(mWifiStatusSuccess);
 
         assertTrue(mWifiVendorHal.startVendorHal(true));
-        assertTrue(mWifiVendorHal.resetTxPowerLimit());
-        verify(mIWifiChipV11).resetTxPowerLimit();
+        assertTrue(mWifiVendorHal.selectTxPowerScenario(WifiNative.TX_POWER_SCENARIO_NORMAL));
+        verify(mIWifiChipV11).resetTxPowerScenario();
+        verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
+        mWifiVendorHal.stopVendorHal();
+    }
+
+    /**
+     * Test the new selectTxPowerScenario HIDL method invocation with a bad scenario index.
+     */
+    @Test
+    public void testInvalidSelectTxPowerScenario() throws RemoteException {
+        // Expose the 1.1 IWifiChip.
+        mWifiVendorHal = new WifiVendorHalSpyV1_1(mHalDeviceManager, mLooper.getLooper());
+        when(mIWifiChipV11.selectTxPowerScenario(anyInt())).thenReturn(mWifiStatusSuccess);
+
+        assertTrue(mWifiVendorHal.startVendorHal(true));
+        assertFalse(mWifiVendorHal.selectTxPowerScenario(-6));
+        verify(mIWifiChipV11, never()).selectTxPowerScenario(anyInt());
+        verify(mIWifiChipV11, never()).resetTxPowerScenario();
         mWifiVendorHal.stopVendorHal();
     }
 
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
index 3a5f6b9..ed7c582 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
@@ -37,8 +37,12 @@
 import org.junit.Test;
 import org.mockito.InOrder;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * Unit tests for {@link com.android.server.wifi.scanner.WificondScannerImpl}.
@@ -554,6 +558,28 @@
     }
 
     /**
+     * Test that dump() of WificondScannerImpl dumps native scan results.
+     */
+    @Test
+    public void dumpContainsNativeScanResults() {
+        assertDumpContainsRequestLog("Latest native scan results:");
+    }
+
+    private void assertDumpContainsRequestLog(String log) {
+        String objectDump = dumpObject();
+        Pattern logLineRegex = Pattern.compile(".*" + log + ".*");
+        assertTrue("dump did not contain log = " + log + "\n " + objectDump + "\n",
+                logLineRegex.matcher(objectDump).find());
+    }
+
+    private String dumpObject() {
+        StringWriter stringWriter = new StringWriter();
+        mScanner.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+                new String[0]);
+        return stringWriter.toString();
+    }
+
+    /**
      * Run a test with the given settings where all native scans succeed
      * This will execute expectedPeriods.length scan periods by first
      * starting the scan settings and then dispatching the scan period alarm to start the