Add Hotspot WPA3 Settings

- Add WPA3 SAE security types
  - "WPA3-Personal"
  - "WPA2/WPA3-Personal"

- Verify valid WPA3 password

- Enable QR code scanner for Hotspot WPA3

- Screenshot
  https://screenshot.googleplex.com/B6u54wh8w35Xnyf
  https://screenshot.googleplex.com/8hWHHUTb6UaS9vB

Bug: 167968488
Test:
- Manual Test
- atest WifiTetherSecurityPreferenceControllerTest
- atest WifiUtilsTest

Change-Id: I155ed0bfd187d27ba864f9fb1b78d08c437740f3
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index f8df920..bf757d1 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -223,6 +223,10 @@
     <!-- Security types for wireless tether -->
     <string-array name="wifi_tether_security">
         <!-- Do not translate. -->
+        <item>@string/wifi_security_sae</item>
+        <!-- Do not translate. -->
+        <item>@string/wifi_security_psk_sae</item>
+        <!-- Do not translate. -->
         <item>@string/wifi_security_wpa2</item>
         <!-- Do not translate. -->
         <item>@string/wifi_security_none</item>
@@ -231,6 +235,10 @@
     <!-- Values for security type for wireless tether -->
     <string-array name="wifi_tether_security_values" translatable="false">
         <!-- Do not translate. -->
+        <item>3</item>
+        <!-- Do not translate. -->
+        <item>2</item>
+        <!-- Do not translate. -->
         <item>1</item>
         <!-- Do not translate. -->
         <item>0</item>
diff --git a/src/com/android/settings/AllInOneTetherSettings.java b/src/com/android/settings/AllInOneTetherSettings.java
index 82e3206..5442ed7 100644
--- a/src/com/android/settings/AllInOneTetherSettings.java
+++ b/src/com/android/settings/AllInOneTetherSettings.java
@@ -355,7 +355,7 @@
     @Override
     public void onTetherConfigUpdated(AbstractPreferenceController controller) {
         final SoftApConfiguration config = buildNewConfig();
-        mPasswordPreferenceController.updateVisibility(config.getSecurityType());
+        mPasswordPreferenceController.setSecurityType(config.getSecurityType());
         mWifiManager.setSoftApConfiguration(config);
 
         if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
diff --git a/src/com/android/settings/wifi/WifiUtils.java b/src/com/android/settings/wifi/WifiUtils.java
index 06f74d5..d05260a 100644
--- a/src/com/android/settings/wifi/WifiUtils.java
+++ b/src/com/android/settings/wifi/WifiUtils.java
@@ -56,12 +56,12 @@
     }
 
     /**
-     * Check if the WPA2-PSK hotspot password is valid.
+     * Check if the hotspot password is valid.
      */
-    public static boolean isHotspotWpa2PasswordValid(String password) {
+    public static boolean isHotspotPasswordValid(String password, int securityType) {
         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
         try {
-            configBuilder.setPassphrase(password, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+            configBuilder.setPassphrase(password, securityType);
         } catch (IllegalArgumentException e) {
             return false;
         }
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 2957e1f..abf5bec 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -301,7 +301,11 @@
 
         final String ssid = removeFirstAndLastDoubleQuotes(softApConfiguration.getSsid());
         String security;
-        if (softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
+        final int securityType = softApConfiguration.getSecurityType();
+        if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) {
+            security = WifiQrCode.SECURITY_SAE;
+        } else if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) {
             security = WifiQrCode.SECURITY_WPA_PSK;
         } else {
             security = WifiQrCode.SECURITY_NO_PASSWORD;
@@ -431,11 +435,11 @@
 
     private static boolean isSupportHotspotConfiguratorQrCodeGenerator(
             SoftApConfiguration softApConfiguration) {
-        // QR code generator produces QR code with ZXing's Wi-Fi network config format,
-        // it supports PSK and WEP and non security
-        // KeyMgmt.NONE is for WEP or non security
-        return softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
-                || softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OPEN;
+        final int securityType = softApConfiguration.getSecurityType();
+        return securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
+                || securityType == SoftApConfiguration.SECURITY_TYPE_OPEN;
     }
 
     private static boolean isSupportWifiDpp(Context context, int wifiEntrySecurity) {
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
index be67d22..287e971 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
@@ -43,6 +43,7 @@
     private static final String PREF_KEY = "wifi_tether_network_password";
 
     private String mPassword;
+    private int mSecurityType;
 
     private final MetricsFeatureProvider mMetricsFeatureProvider;
 
@@ -68,13 +69,13 @@
     @Override
     public void updateDisplay() {
         final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
-        if (config == null
-                || (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
-                && TextUtils.isEmpty(config.getPassphrase()))) {
+        if (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN
+                && TextUtils.isEmpty(config.getPassphrase())) {
             mPassword = generateRandomPassword();
         } else {
             mPassword = config.getPassphrase();
         }
+        mSecurityType = config.getSecurityType();
         ((ValidatedEditTextPreference) mPreference).setValidator(this);
         ((ValidatedEditTextPreference) mPreference).setIsPassword(true);
         ((ValidatedEditTextPreference) mPreference).setIsSummaryPassword(true);
@@ -105,20 +106,21 @@
         // don't actually overwrite unless we get a new config in case it was accidentally toggled.
         if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) {
             return "";
-        } else if (!isTextValid(mPassword)) {
+        } else if (!WifiUtils.isHotspotPasswordValid(mPassword, securityType)) {
             mPassword = generateRandomPassword();
             updatePasswordDisplay((EditTextPreference) mPreference);
         }
         return mPassword;
     }
 
-    public void updateVisibility(int securityType) {
+    public void setSecurityType(int securityType) {
+        mSecurityType = securityType;
         mPreference.setVisible(securityType != SoftApConfiguration.SECURITY_TYPE_OPEN);
     }
 
     @Override
     public boolean isTextValid(String value) {
-        return WifiUtils.isHotspotWpa2PasswordValid(value);
+        return WifiUtils.isHotspotPasswordValid(value, mSecurityType);
     }
 
     private static String generateRandomPassword() {
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
index 56b5031..ec5e6e0 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
@@ -1,28 +1,62 @@
+/*
+ * Copyright (C) 2020 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.settings.wifi.tether;
 
 import static com.android.settings.AllInOneTetherSettings.DEDUP_POSTFIX;
 
+import android.annotation.NonNull;
 import android.content.Context;
+import android.net.wifi.SoftApCapability;
 import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
 import android.util.FeatureFlagUtils;
+import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.ListPreference;
 import androidx.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settings.core.FeatureFlags;
 
-public class WifiTetherSecurityPreferenceController extends WifiTetherBasePreferenceController {
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class WifiTetherSecurityPreferenceController extends WifiTetherBasePreferenceController
+    implements WifiManager.SoftApCallback {
 
     private static final String PREF_KEY = "wifi_tether_security";
 
-    private final String[] mSecurityEntries;
+    private Map<Integer, String> mSecurityMap = new LinkedHashMap<Integer, String>();
     private int mSecurityValue;
+    @VisibleForTesting
+    boolean mIsWpa3Supported = true;
 
     public WifiTetherSecurityPreferenceController(Context context,
             OnTetherConfigUpdateListener listener) {
         super(context, listener);
-        mSecurityEntries = mContext.getResources().getStringArray(R.array.wifi_tether_security);
+        final String[] securityNames = mContext.getResources().getStringArray(
+                R.array.wifi_tether_security);
+        final String[] securityValues = mContext.getResources().getStringArray(
+                R.array.wifi_tether_security_values);
+        for (int i = 0; i < securityNames.length; i++) {
+            mSecurityMap.put(Integer.parseInt(securityValues[i]), securityNames[i]);
+        }
+        mWifiManager.registerSoftApCallback(context.getMainExecutor(), this);
     }
 
     @Override
@@ -33,35 +67,48 @@
 
     @Override
     public void updateDisplay() {
-        final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
-        if (config != null && config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OPEN) {
-            mSecurityValue = SoftApConfiguration.SECURITY_TYPE_OPEN;
-        } else {
-            mSecurityValue = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+        final ListPreference preference = (ListPreference) mPreference;
+        // If the device is not support WPA3 then remove the WPA3 options.
+        if (!mIsWpa3Supported && mSecurityMap.keySet()
+                .removeIf(key -> key > SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)) {
+            preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new));
+            preference.setEntryValues(mSecurityMap.keySet().stream().map(Integer::toBinaryString)
+                    .toArray(CharSequence[]::new));
         }
 
-        final ListPreference preference = (ListPreference) mPreference;
-        preference.setSummary(getSummaryForSecurityType(mSecurityValue));
+        final int securityType = mWifiManager.getSoftApConfiguration().getSecurityType();
+        mSecurityValue = mSecurityMap.get(securityType) != null
+                ? securityType : SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+
+        preference.setSummary(mSecurityMap.get(mSecurityValue));
         preference.setValue(String.valueOf(mSecurityValue));
     }
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         mSecurityValue = Integer.parseInt((String) newValue);
-        preference.setSummary(getSummaryForSecurityType(mSecurityValue));
-        mListener.onTetherConfigUpdated(this);
+        preference.setSummary(mSecurityMap.get(mSecurityValue));
+        if (mListener != null) {
+            mListener.onTetherConfigUpdated(this);
+        }
         return true;
     }
 
+    @Override
+    public void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
+        final boolean isWpa3Supported =
+                softApCapability.areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_WPA3_SAE);
+        if (!isWpa3Supported) {
+            Log.i(PREF_KEY, "WPA3 SAE is not supported on this device");
+        }
+        if (mIsWpa3Supported != isWpa3Supported) {
+            mIsWpa3Supported = isWpa3Supported;
+            updateDisplay();
+        }
+        mWifiManager.unregisterSoftApCallback(this);
+    }
+
     public int getSecurityType() {
         return mSecurityValue;
     }
-
-    private String getSummaryForSecurityType(int securityType) {
-        if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) {
-            return mSecurityEntries[1];
-        }
-        // WPA2 PSK
-        return mSecurityEntries[0];
-    }
 }
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index d6c49bc..f260298 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -192,7 +192,7 @@
     @Override
     public void onTetherConfigUpdated(AbstractPreferenceController context) {
         final SoftApConfiguration config = buildNewConfig();
-        mPasswordPreferenceController.updateVisibility(config.getSecurityType());
+        mPasswordPreferenceController.setSecurityType(config.getSecurityType());
 
         /**
          * if soft AP is stopped, bring up
@@ -216,10 +216,10 @@
         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
         final int securityType = mSecurityPreferenceController.getSecurityType();
         configBuilder.setSsid(mSSIDPreferenceController.getSSID());
-        if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
+        if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) {
             configBuilder.setPassphrase(
                     mPasswordPreferenceController.getPasswordValidated(securityType),
-                    SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+                    securityType);
         }
         configBuilder.setBand(mApBandPreferenceController.getBandIndex());
         return configBuilder.build();
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
deleted file mode 100644
index c7d0695..0000000
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.android.settings.wifi.tether;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiManager;
-
-import androidx.preference.ListPreference;
-import androidx.preference.PreferenceScreen;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiTetherSecurityPreferenceControllerTest {
-
-    private static final String WPA2_PSK =
-            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-    private static final String NONE = String.valueOf(SoftApConfiguration.SECURITY_TYPE_OPEN);
-    @Mock
-    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
-    private Context mContext;
-    @Mock
-    private ConnectivityManager mConnectivityManager;
-    @Mock
-    private WifiManager mWifiManager;
-    @Mock
-    private PreferenceScreen mScreen;
-    private WifiTetherSecurityPreferenceController mController;
-    private ListPreference mPreference;
-    private SoftApConfiguration mConfig;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mConfig = new SoftApConfiguration.Builder().setSsid("test_1234")
-                .setPassphrase("test_password",
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
-        mContext = spy(RuntimeEnvironment.application);
-
-        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(mConfig);
-        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
-                .thenReturn(mConnectivityManager);
-        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
-        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
-
-        mController = new WifiTetherSecurityPreferenceController(mContext, mListener);
-        mPreference = new ListPreference(RuntimeEnvironment.application);
-        mController.mPreference = mPreference;
-    }
-
-    @Test
-    public void onPreferenceChange_securityValueUpdated() {
-        mController.onPreferenceChange(mPreference, WPA2_PSK);
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-
-        mController.onPreferenceChange(mPreference, NONE);
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_OPEN);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
-    }
-
-    @Test
-    public void updateDisplay_preferenceUpdated() {
-        // test defaulting to WPA2-Personal on new config
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(null);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-
-        // test open tether network
-        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
-                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_OPEN);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
-
-        // test WPA2-Personal tether network
-        SoftApConfiguration config2 = new SoftApConfiguration.Builder(mConfig)
-                .setPassphrase("test_password",
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(config2);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-    }
-}
diff --git a/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java b/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
index 7a75443..1a5e852 100644
--- a/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
+++ b/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -46,12 +47,48 @@
     public void testPassword() {
         final String longPassword = "123456789012345678901234567890"
                 + "1234567890123456789012345678901234567890";
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("123")).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("12345678")).isTrue();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("1234567890")).isTrue();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid(longPassword)).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("")).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("€¥£")).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+
+        // The WPA3_SAE_TRANSITION password limitation should be same as WPA2_PSK
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+
+        // The WA3_SAE password is requested that length > 1 only.
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("1",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
new file mode 100644
index 0000000..836d4e4
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 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.settings.wifi.tether;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Looper;
+
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class WifiTetherSecurityPreferenceControllerTest {
+
+    private static final String PREF_KEY = "wifi_tether_security";
+    private static final String WPA3_SAE =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+    private static final String WPA3_SAE_TRANSITION =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+    private static final String WPA2_PSK =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+    private static final String NONE = String.valueOf(SoftApConfiguration.SECURITY_TYPE_OPEN);
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+
+    private WifiTetherSecurityPreferenceController mController;
+    private ListPreference mPreference;
+    private SoftApConfiguration mConfig;
+
+    @Before
+    public void setUp() {
+        final Context context = spy(ApplicationProvider.getApplicationContext());
+        mConfig = new SoftApConfiguration.Builder().setSsid("test_1234")
+                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
+        when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(mConfig);
+
+        mController = new WifiTetherSecurityPreferenceController(context, mListener);
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        final PreferenceManager preferenceManager = new PreferenceManager(context);
+        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);
+        mPreference = new ListPreference(context);
+        mPreference.setKey(PREF_KEY);
+        screen.addPreference(mPreference);
+        mController.displayPreference(screen);
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa3Sae_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA3_SAE);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA3-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa3SaeTransition_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA3_SAE_TRANSITION);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2/WPA3-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa2Psk_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA2_PSK);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toNone_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, NONE);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3Sae_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA3-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeTransition_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2/WPA3-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa2Psk_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toNone_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeButNotSupportWpa3_shouldBeDefaultToWpa2() {
+        mController.mIsWpa3Supported = false;
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeTransitionButNotSupportWpa3_shouldBeDefaultToWpa2() {
+        mController.mIsWpa3Supported = false;
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+}