diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 5da93107006..a6d595810b3 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -48,6 +48,7 @@ import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.vpn2.AdvancedVpnFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; +import com.android.settings.wifi.factory.WifiFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** @@ -192,6 +193,11 @@ public abstract class FeatureFactory { */ public abstract AdvancedVpnFeatureProvider getAdvancedVpnFeatureProvider(); + /** + * Retrieves implementation for Wi-Fi feature. + */ + public abstract WifiFeatureProvider getWifiFeatureProvider(); + public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { super("Unable to create factory. Did you misconfigure Proguard?", throwable); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index bc78f2eff01..584e72a08c5 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -77,6 +77,7 @@ import com.android.settings.vpn2.AdvancedVpnFeatureProvider; import com.android.settings.vpn2.AdvancedVpnFeatureProviderImpl; import com.android.settings.wifi.WifiTrackerLibProvider; import com.android.settings.wifi.WifiTrackerLibProviderImpl; +import com.android.settings.wifi.factory.WifiFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** @@ -112,6 +113,7 @@ public class FeatureFactoryImpl extends FeatureFactory { private AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider; private AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider; private AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider; + private WifiFeatureProvider mWifiFeatureProvider; @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { @@ -355,4 +357,12 @@ public class FeatureFactoryImpl extends FeatureFactory { } return mAdvancedVpnFeatureProvider; } + + @Override + public WifiFeatureProvider getWifiFeatureProvider() { + if (mWifiFeatureProvider == null) { + mWifiFeatureProvider = new WifiFeatureProvider(getAppContext()); + } + return mWifiFeatureProvider; + } } diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java new file mode 100644 index 00000000000..2f5f3ea88a3 --- /dev/null +++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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.factory; + +import android.content.Context; +import android.net.wifi.WifiManager; + +import androidx.annotation.NonNull; + +import com.android.settings.wifi.repository.WifiHotspotRepository; + +/** + * Wi-Fi Feature Provider + */ +public class WifiFeatureProvider { + + private final Context mAppContext; + private WifiManager mWifiManager; + private WifiHotspotRepository mWifiHotspotRepository; + + public WifiFeatureProvider(@NonNull Context appContext) { + mAppContext = appContext; + } + + /** + * Get WifiManager + */ + public WifiManager getWifiManager() { + if (mWifiManager == null) { + mWifiManager = mAppContext.getSystemService(WifiManager.class); + } + return mWifiManager; + } + + /** + * Get WifiRepository + */ + public WifiHotspotRepository getWifiHotspotRepository() { + if (mWifiHotspotRepository == null) { + mWifiHotspotRepository = new WifiHotspotRepository(mAppContext, getWifiManager()); + } + return mWifiHotspotRepository; + } +} + diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java new file mode 100644 index 00000000000..b8a25dc84be --- /dev/null +++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 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.repository; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.util.UUID; +import java.util.function.Consumer; + +/** + * Wi-Fi Hotspot Repository + */ +public class WifiHotspotRepository { + + protected final Context mAppContext; + protected final WifiManager mWifiManager; + + protected String mLastPassword; + protected LastPasswordListener mLastPasswordListener = new LastPasswordListener(); + + public WifiHotspotRepository(@NonNull Context appContext, @NonNull WifiManager wifiManager) { + mAppContext = appContext; + mWifiManager = wifiManager; + } + + /** + * Query the last configured Tethered Ap Passphrase since boot. + */ + public void queryLastPasswordIfNeeded() { + SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + if (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) { + return; + } + mWifiManager.queryLastConfiguredTetheredApPassphraseSinceBoot(mAppContext.getMainExecutor(), + mLastPasswordListener); + } + + /** + * Generate password. + */ + public String generatePassword() { + return !TextUtils.isEmpty(mLastPassword) ? mLastPassword : generateRandomPassword(); + } + + private class LastPasswordListener implements Consumer { + @Override + public void accept(String password) { + mLastPassword = password; + } + } + + private static String generateRandomPassword() { + String randomUUID = UUID.randomUUID().toString(); + //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); + } +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java index 14766f9ee41..c50357b21bf 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java @@ -33,10 +33,9 @@ import com.android.settings.core.FeatureFlags; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.ValidatedEditTextPreference; import com.android.settings.wifi.WifiUtils; +import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import java.util.UUID; - /** * Controller for logic pertaining to the password of Wi-Fi tethering. */ @@ -49,18 +48,22 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer private int mSecurityType; private final MetricsFeatureProvider mMetricsFeatureProvider; + private final WifiHotspotRepository mWifiHotspotRepository; @VisibleForTesting WifiTetherPasswordPreferenceController(Context context, OnTetherConfigUpdateListener listener, MetricsFeatureProvider provider) { super(context, listener); - mMetricsFeatureProvider = provider; + FeatureFactory featureFactory = FeatureFactory.getFactory(context); + mMetricsFeatureProvider = (provider != null) ? provider + : featureFactory.getMetricsFeatureProvider(); + mWifiHotspotRepository = featureFactory.getWifiFeatureProvider().getWifiHotspotRepository(); + mWifiHotspotRepository.queryLastPasswordIfNeeded(); } public WifiTetherPasswordPreferenceController(Context context, OnTetherConfigUpdateListener listener) { - super(context, listener); - mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + this(context, listener, null /* MetricsFeatureProvider */); } @Override @@ -74,7 +77,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); if (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN && TextUtils.isEmpty(config.getPassphrase())) { - mPassword = generateRandomPassword(); + mPassword = mWifiHotspotRepository.generatePassword(); } else { mPassword = config.getPassphrase(); } @@ -110,7 +113,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) { return ""; } else if (!WifiUtils.isHotspotPasswordValid(mPassword, securityType)) { - mPassword = generateRandomPassword(); + mPassword = mWifiHotspotRepository.generatePassword(); updatePasswordDisplay((EditTextPreference) mPreference); } return mPassword; @@ -132,12 +135,6 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer return WifiUtils.isHotspotPasswordValid(value, mSecurityType); } - private static String generateRandomPassword() { - String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx - return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); - } - private void updatePasswordDisplay(EditTextPreference preference) { ValidatedEditTextPreference pref = (ValidatedEditTextPreference) preference; pref.setText(mPassword); diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index c1e7bfbbe9f..a17ce4f8608 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -50,6 +50,7 @@ import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.vpn2.AdvancedVpnFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; +import com.android.settings.wifi.factory.WifiFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.mockito.Answers; @@ -91,6 +92,7 @@ public class FakeFeatureFactory extends FeatureFactory { public AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider; public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider; public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider; + public WifiFeatureProvider mWifiFeatureProvider; /** * Call this in {@code @Before} method of the test class to use fake factory. @@ -142,6 +144,7 @@ public class FakeFeatureFactory extends FeatureFactory { mAccessibilitySearchFeatureProvider = mock(AccessibilitySearchFeatureProvider.class); mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class); mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class); + mWifiFeatureProvider = mock(WifiFeatureProvider.class); } @Override @@ -288,4 +291,9 @@ public class FakeFeatureFactory extends FeatureFactory { public AdvancedVpnFeatureProvider getAdvancedVpnFeatureProvider() { return mAdvancedVpnFeatureProvider; } + + @Override + public WifiFeatureProvider getWifiFeatureProvider() { + return mWifiFeatureProvider; + } } diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java index 081a078b42b..500e31bc7d6 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java @@ -35,7 +35,9 @@ import android.net.wifi.WifiManager; import androidx.preference.PreferenceScreen; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.widget.ValidatedEditTextPreference; +import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; @@ -65,6 +67,8 @@ public class WifiTetherPasswordPreferenceControllerTest { private PreferenceScreen mScreen; @Mock private MetricsFeatureProvider mMetricsFeatureProvider; + @Mock + private WifiHotspotRepository mWifiHotspotRepository; private WifiTetherPasswordPreferenceController mController; private ValidatedEditTextPreference mPreference; @@ -73,6 +77,10 @@ public class WifiTetherPasswordPreferenceControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); + when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository()) + .thenReturn(mWifiHotspotRepository); + mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application); mConfig = new SoftApConfiguration.Builder().setSsid("test_1234") .setPassphrase(INITIAL_PASSWORD, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) @@ -91,6 +99,11 @@ public class WifiTetherPasswordPreferenceControllerTest { mMetricsFeatureProvider); } + @Test + public void constructor_shouldQueryLastPasswordIfNeeded() { + verify(mWifiHotspotRepository).queryLastPasswordIfNeeded(); + } + @Test public void displayPreference_shouldStylePreference() { mController.displayPreference(mScreen); diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt index a544f533dbe..da6e8231536 100644 --- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt +++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt @@ -46,6 +46,7 @@ import com.android.settings.slices.SlicesFeatureProvider import com.android.settings.users.UserFeatureProvider import com.android.settings.vpn2.AdvancedVpnFeatureProvider import com.android.settings.wifi.WifiTrackerLibProvider +import com.android.settings.wifi.factory.WifiFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider import org.mockito.Mockito.mock @@ -177,4 +178,8 @@ class FakeFeatureFactory : FeatureFactory() { override fun getAdvancedVpnFeatureProvider(): AdvancedVpnFeatureProvider { TODO("Not yet implemented") } + + override fun getWifiFeatureProvider(): WifiFeatureProvider { + TODO("Not yet implemented") + } } diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index d4127d72f9d..0150b726544 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -48,6 +48,7 @@ import com.android.settings.slices.SlicesFeatureProvider; import com.android.settings.users.UserFeatureProvider; import com.android.settings.vpn2.AdvancedVpnFeatureProvider; import com.android.settings.wifi.WifiTrackerLibProvider; +import com.android.settings.wifi.factory.WifiFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** @@ -86,6 +87,7 @@ public class FakeFeatureFactory extends FeatureFactory { public AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider; public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider; public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider; + public WifiFeatureProvider mWifiFeatureProvider; /** * Call this in {@code @Before} method of the test class to use fake factory. @@ -128,6 +130,7 @@ public class FakeFeatureFactory extends FeatureFactory { mAccessibilitySearchFeatureProvider = mock(AccessibilitySearchFeatureProvider.class); mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class); mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class); + mWifiFeatureProvider = mock(WifiFeatureProvider.class); } @Override @@ -274,4 +277,9 @@ public class FakeFeatureFactory extends FeatureFactory { public AdvancedVpnFeatureProvider getAdvancedVpnFeatureProvider() { return mAdvancedVpnFeatureProvider; } + + @Override + public WifiFeatureProvider getWifiFeatureProvider() { + return mWifiFeatureProvider; + } } diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java new file mode 100644 index 00000000000..58a9d1c87a2 --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 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.repository; + +import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN; +import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; + +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.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(AndroidJUnit4.class) +public class WifiHotspotRepositoryTest { + static final String WIFI_SSID = "wifi_ssid"; + static final String WIFI_PASSWORD = "wifi_password"; + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + WifiManager mWifiManager; + + WifiHotspotRepository mWifiHotspotRepository; + SoftApConfiguration mSoftApConfiguration; + + @Before + public void setUp() { + mWifiHotspotRepository = new WifiHotspotRepository(mContext, mWifiManager); + } + + @Test + public void queryLastPasswordIfNeeded_securityTypeIsOpen_queryLastPassword() { + mSoftApConfiguration = new SoftApConfiguration.Builder() + .setSsid(WIFI_SSID) + .setPassphrase(null, SECURITY_TYPE_OPEN) + .build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration); + + mWifiHotspotRepository.queryLastPasswordIfNeeded(); + + verify(mWifiManager).queryLastConfiguredTetheredApPassphraseSinceBoot(any(), any()); + } + + @Test + public void queryLastPasswordIfNeeded_securityTypeIsNotOpen_notQueryLastPassword() { + mSoftApConfiguration = new SoftApConfiguration.Builder() + .setSsid(WIFI_SSID) + .setPassphrase(WIFI_PASSWORD, SECURITY_TYPE_WPA3_SAE) + .build(); + when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration); + + mWifiHotspotRepository.queryLastPasswordIfNeeded(); + + verify(mWifiManager, never()) + .queryLastConfiguredTetheredApPassphraseSinceBoot(any(), any()); + } + + @Test + public void generatePassword_haveLastPassword_returnLastPassword() { + mWifiHotspotRepository.mLastPassword = WIFI_PASSWORD; + + assertThat(mWifiHotspotRepository.generatePassword()).isEqualTo(WIFI_PASSWORD); + } + + @Test + public void generatePassword_noLastPassword_returnRandomPassword() { + mWifiHotspotRepository.mLastPassword = ""; + + String password = mWifiHotspotRepository.generatePassword(); + + assertThat(password).isNotEqualTo(WIFI_PASSWORD); + assertThat(password.length()).isNotEqualTo(0); + } +}