diff --git a/res/values/strings.xml b/res/values/strings.xml index 75b7958bf3d..13575abe3b0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2416,6 +2416,10 @@ Turn off hotspot automatically When no devices are connected + + Maximize compatibility + + This may reduce speed for devices connected to this hotspot and use more power Turning hotspot on\u2026 diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index 34d8032cbb9..8648cff0f43 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -37,12 +37,13 @@ android:persistent="false" android:title="@string/wifi_hotspot_password_title"/> - - + + diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java new file mode 100644 index 00000000000..bc87d5cc5d0 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 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 android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +/** + * This controller helps to manage the state of maximize compatibility switch preference. + */ +public class WifiTetherMaximizeCompatibilityPreferenceController extends + WifiTetherBasePreferenceController { + + private static final String TAG = "WifiTetherMaximizeCompatibilityPref"; + public static final String PREF_KEY = "wifi_tether_maximize_compatibility"; + + private boolean mIsChecked; + + public WifiTetherMaximizeCompatibilityPreferenceController(Context context, + WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { + super(context, listener); + mIsChecked = isMaximizeCompatibilityEnabled(); + } + + @Override + public String getPreferenceKey() { + return PREF_KEY; + } + + @Override + public void updateDisplay() { + if (mPreference == null) { + return; + } + mPreference.setEnabled(is5GhzBandSupported()); + ((SwitchPreference) mPreference).setChecked(mIsChecked); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mIsChecked = (Boolean) newValue; + if (mListener != null) { + mListener.onTetherConfigUpdated(this); + } + return true; + } + + private boolean is5GhzBandSupported() { + if (mWifiManager == null) { + return false; + } + if (!mWifiManager.is5GHzBandSupported() || mWifiManager.getCountryCode() == null) { + return false; + } + return true; + } + + @VisibleForTesting + boolean isMaximizeCompatibilityEnabled() { + if (mWifiManager == null) { + return false; + } + final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + if (config == null) { + return false; + } + if (mWifiManager.isBridgedApConcurrencySupported()) { + final boolean isEnabled = config.isBridgedModeOpportunisticShutdownEnabled(); + Log.d(TAG, "isBridgedModeOpportunisticShutdownEnabled:" + isEnabled); + return isEnabled; + } + + // If the BridgedAp Concurrency is not supported in early Pixel devices (e.g. Pixel 2~5), + // show toggle on if the band includes SoftApConfiguration.BAND_5GHZ. + final int band = config.getBand(); + Log.d(TAG, "getBand:" + band); + return (band & SoftApConfiguration.BAND_5GHZ) > 0; + } + + /** + * Setup the Maximize Compatibility setting to the SoftAp Configuration + * + * @param builder The builder to build the SoftApConfiguration. + */ + public void setupMaximizeCompatibility(SoftApConfiguration.Builder builder) { + if (builder == null) { + return; + } + final boolean enabled = mIsChecked; + if (mWifiManager.isBridgedApConcurrencySupported()) { + int[] bands = { + SoftApConfiguration.BAND_2GHZ, + SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; + builder.setBands(bands); + Log.d(TAG, "setBridgedModeOpportunisticShutdownEnabled:" + enabled); + builder.setBridgedModeOpportunisticShutdownEnabled(enabled); + } else { + int band = enabled + ? SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ + : SoftApConfiguration.BAND_2GHZ; + Log.d(TAG, "setBand:" + band); + builder.setBand(band); + } + } +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 939f0778943..e34255035e5 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -54,8 +54,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment private static final String TAG = "WifiTetherSettings"; private static final IntentFilter TETHER_STATE_CHANGE_FILTER; private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; - private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3; - private static final int EXPANDED_CHILD_COUNT_DEFAULT = 4; + private static final int EXPANDED_CHILD_COUNT_DEFAULT = 3; @VisibleForTesting static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; @@ -64,13 +63,14 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @VisibleForTesting static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; @VisibleForTesting - static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; + static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = + WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; private WifiTetherPasswordPreferenceController mPasswordPreferenceController; - private WifiTetherApBandPreferenceController mApBandPreferenceController; private WifiTetherSecurityPreferenceController mSecurityPreferenceController; + private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; private WifiManager mWifiManager; private boolean mRestartWifiApAfterConfigChange; @@ -116,7 +116,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); - mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); + mMaxCompatibilityPrefController = + use(WifiTetherMaximizeCompatibilityPreferenceController.class); } @Override @@ -180,10 +181,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); - controllers.add(new WifiTetherApBandPreferenceController(context, listener)); controllers.add( new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); - + controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener)); return controllers; } @@ -219,7 +219,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mPasswordPreferenceController.getPasswordValidated(securityType), securityType); } - configBuilder.setBand(mApBandPreferenceController.getBandIndex()); + mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder); return configBuilder.build(); } @@ -229,14 +229,10 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } private void updateDisplayWithNewConfig() { - use(WifiTetherSSIDPreferenceController.class) - .updateDisplay(); - use(WifiTetherSecurityPreferenceController.class) - .updateDisplay(); - use(WifiTetherPasswordPreferenceController.class) - .updateDisplay(); - use(WifiTetherApBandPreferenceController.class) - .updateDisplay(); + use(WifiTetherSSIDPreferenceController.class).updateDisplay(); + use(WifiTetherSecurityPreferenceController.class).updateDisplay(); + use(WifiTetherPasswordPreferenceController.class).updateDisplay(); + use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay(); } public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = @@ -250,7 +246,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment keys.add(KEY_WIFI_TETHER_NETWORK_NAME); keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); keys.add(KEY_WIFI_TETHER_AUTO_OFF); - keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); + keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } // Remove duplicate @@ -294,22 +290,17 @@ public class WifiTetherSettings extends RestrictedDashboardFragment private void reConfigInitialExpandedChildCount() { final PreferenceGroup screen = getPreferenceScreen(); - if (mSecurityPreferenceController.getSecurityType() - == SoftApConfiguration.SECURITY_TYPE_OPEN) { - screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_WITH_SECURITY_NON); - return; + if (screen != null) { + screen.setInitialExpandedChildrenCount(getInitialExpandedChildCount()); } - screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_DEFAULT); } @Override public int getInitialExpandedChildCount() { - if (mSecurityPreferenceController == null) { - return EXPANDED_CHILD_COUNT_DEFAULT; + if (mSecurityPreferenceController != null && mSecurityPreferenceController.getSecurityType() + == SoftApConfiguration.SECURITY_TYPE_OPEN) { + return (EXPANDED_CHILD_COUNT_DEFAULT - 1); } - - return (mSecurityPreferenceController.getSecurityType() - == SoftApConfiguration.SECURITY_TYPE_OPEN) - ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT; + return EXPANDED_CHILD_COUNT_DEFAULT; } } diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index a2412961919..2ecc7d26d54 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -101,7 +101,7 @@ public class WifiTetherSettingsTest { assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } @Test @@ -115,7 +115,7 @@ public class WifiTetherSettingsTest { assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } @Test diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java new file mode 100644 index 00000000000..a2b99bf614a --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2021 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.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; +import android.os.Looper; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; +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 WifiTetherMaximizeCompatibilityPreferenceControllerTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock + private WifiManager mWifiManager; + @Mock + private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener; + + private WifiTetherMaximizeCompatibilityPreferenceController mController; + private SwitchPreference mPreference; + private SoftApConfiguration mConfig; + + @Before + public void setUp() { + final Context context = spy(ApplicationProvider.getApplicationContext()); + mConfig = new SoftApConfiguration.Builder() + .setSsid("test_Ssid") + .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN) + .setBridgedModeOpportunisticShutdownEnabled(true) + .build(); + doReturn(mWifiManager).when(context).getSystemService(Context.WIFI_SERVICE); + doReturn(true).when(mWifiManager).isBridgedApConcurrencySupported(); + doReturn(mConfig).when(mWifiManager).getSoftApConfiguration(); + + mController = new WifiTetherMaximizeCompatibilityPreferenceController(context, mListener); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + final PreferenceManager preferenceManager = new PreferenceManager(context); + final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context); + mPreference = new SwitchPreference(context); + mPreference.setKey(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); + screen.addPreference(mPreference); + mController.displayPreference(screen); + } + + @Test + public void getPreferenceKey_shouldBeCorrect() { + assertThat(mController.getPreferenceKey()) + .isEqualTo(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); + } + + @Test + public void updateDisplay_notSupport5GHzBand_setPreferenceDisabled() { + doReturn(false).when(mWifiManager).is5GHzBandSupported(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(false); + } + + @Test + public void updateDisplay_getNullCountryCode_setPreferenceDisabled() { + doReturn(null).when(mWifiManager).getCountryCode(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(false); + } + + @Test + public void updateDisplay_supported5GHzBandAndCountryCodeIsNotNull_setPreferenceEnabled() { + doReturn(true).when(mWifiManager).is5GHzBandSupported(); + doReturn("US").when(mWifiManager).getCountryCode(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(true); + } + + @Test + public void onPreferenceChange_callbackOnTetherConfigUpdated() { + mController.onPreferenceChange(mPreference, true); + verify(mListener).onTetherConfigUpdated(any()); + } + + @Test + public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnTure() { + // The preconditions are ready in setup(). + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnFalse() { + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBridgedModeOpportunisticShutdownEnabled(false) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gOnly_returnFalse() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_2GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand5gOnly_returnTrue() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_5GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gAnd5g_returnTrue() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setDisabled() { + // The precondition of the concurrency supported is ready in setup(). + mController.onPreferenceChange(mPreference, false); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(false); + } + + @Test + public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setEnabled() { + // The precondition of the concurrency supported is ready in setup(). + mController.onPreferenceChange(mPreference, true); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(true); + } + + @Test + public void setupMaximizeCompatibility_noConcurrencyAndSetDisabled_setBand2gOnly() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + mController.onPreferenceChange(mPreference, false); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); + } + + @Test + public void setupMaximizeCompatibility_noConcurrencyAndSetEnabled_setBand2gAnd5g() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + mController.onPreferenceChange(mPreference, true); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().getBand()) + .isEqualTo(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ); + } +}