diff --git a/res/xml/all_tether_prefs.xml b/res/xml/all_tether_prefs.xml index 07e5d64b4bf..bc3471b4445 100644 --- a/res/xml/all_tether_prefs.xml +++ b/res/xml/all_tether_prefs.xml @@ -53,6 +53,31 @@ android:title="@string/wifi_hotspot_ap_band_title"/> + + + + + + + + createPreferenceControllers( Context context) { - return buildPreferenceControllers(context, null /* AllTetherSettings */); + return buildPreferenceControllers(context, null /*listener*/); } }; } diff --git a/src/com/android/settings/network/TetherEnabler.java b/src/com/android/settings/network/TetherEnabler.java index 2ea3c0498e3..ffb0ef25d97 100644 --- a/src/com/android/settings/network/TetherEnabler.java +++ b/src/com/android/settings/network/TetherEnabler.java @@ -20,6 +20,8 @@ import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; +import static com.android.settings.AllInOneTetherSettings.DEDUP_POSTFIX; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.content.BroadcastReceiver; @@ -34,6 +36,7 @@ import android.os.Looper; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; @@ -50,9 +53,11 @@ import java.util.concurrent.atomic.AtomicReference; * TetherEnabler is a helper to manage Tethering switch on/off state. It turns on/off * different types of tethering based on stored values in {@link SharedPreferences} and ensures * tethering state updated by data saver state. + * + * This class is not designed for extending. It's extendable solely for the test purpose. */ -public final class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener, +public class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener, DataSaverBackend.Listener, LifecycleObserver, SharedPreferences.OnSharedPreferenceChangeListener { @@ -63,12 +68,9 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang // This KEY is used for a shared preference value, not for any displayed preferences. public static final String KEY_ENABLE_WIFI_TETHERING = "enable_wifi_tethering"; - @VisibleForTesting - static final String WIFI_TETHER_DISABLE_KEY = "disable_wifi_tethering"; - @VisibleForTesting - static final String USB_TETHER_KEY = "enable_usb_tethering"; - @VisibleForTesting - static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering"; + public static final String WIFI_TETHER_DISABLE_KEY = "disable_wifi_tethering"; + public static final String USB_TETHER_KEY = "enable_usb_tethering"; + public static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering" + DEDUP_POSTFIX; private final SwitchWidgetController mSwitchWidgetController; private final WifiManager mWifiManager; @@ -113,7 +115,7 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang mContext.registerReceiver(mTetherChangeReceiver, filter); mOnStartTetheringCallback = new OnStartTetheringCallback(this); - updateState(); + updateState(null/*tethered*/); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -133,14 +135,20 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang mContext.unregisterReceiver(mTetherChangeReceiver); } - private void updateState() { - mSwitchWidgetController.setChecked(isTethering()); + @VisibleForTesting + void updateState(@Nullable String[] tethered) { + boolean isTethering = tethered == null ? isTethering() : isTethering(tethered); + if (DEBUG) { + Log.d(TAG, "updateState: " + isTethering); + } + setSwitchCheckedInternal(isTethering); mSwitchWidgetController.setEnabled(!mDataSaverEnabled); } - private void updateState(String[] tethered) { - mSwitchWidgetController.setChecked(isTethering(tethered)); - mSwitchWidgetController.setEnabled(!mDataSaverEnabled); + private void setSwitchCheckedInternal(boolean checked) { + mSwitchWidgetController.stopListening(); + mSwitchWidgetController.setChecked(checked); + mSwitchWidgetController.startListening(); } private boolean isTethering() { @@ -269,7 +277,7 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang if (active != null) { updateState(active.toArray(new String[0])); } else { - updateState(); + updateState(null/*tethered*/); } } } @@ -371,7 +379,7 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang private void update() { TetherEnabler enabler = mTetherEnabler.get(); if (enabler != null) { - enabler.updateState(); + enabler.updateState(null/*tethered*/); } } } diff --git a/src/com/android/settings/network/WifiTetherDisablePreferenceController.java b/src/com/android/settings/network/WifiTetherDisablePreferenceController.java index ddc2bf76586..a7242cfdc74 100644 --- a/src/com/android/settings/network/WifiTetherDisablePreferenceController.java +++ b/src/com/android/settings/network/WifiTetherDisablePreferenceController.java @@ -31,6 +31,7 @@ import androidx.preference.SwitchPreference; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.TetherUtil; /** * This controller helps to manage the switch state and visibility of wifi tether disable switch @@ -84,7 +85,8 @@ public final class WifiTetherDisablePreferenceController extends BasePreferenceC @Override public int getAvailabilityStatus() { final String[] wifiRegexs = mCm.getTetherableWifiRegexs(); - if (wifiRegexs == null || wifiRegexs.length == 0 || !shouldShow()) { + if (wifiRegexs == null || wifiRegexs.length == 0 || !shouldShow() + || !TetherUtil.isTetherAvailable(mContext)) { return CONDITIONALLY_UNAVAILABLE; } else { return AVAILABLE; diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java index 61db0e501af..55486d26a15 100644 --- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java @@ -16,9 +16,14 @@ package com.android.settings; +import static com.android.settings.network.TetherEnabler.BLUETOOTH_TETHER_KEY; +import static com.android.settings.network.TetherEnabler.USB_TETHER_KEY; +import static com.android.settings.network.TetherEnabler.WIFI_TETHER_DISABLE_KEY; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -31,6 +36,7 @@ import android.util.FeatureFlagUtils; import com.android.settings.core.FeatureFlags; import com.android.settings.testutils.shadow.ShadowWifiManager; import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; import org.junit.Test; @@ -40,6 +46,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.List; @@ -48,6 +55,8 @@ import java.util.List; @Config(shadows = {ShadowWifiManager.class}) public class AllInOneTetherSettingsTest { private static final String[] WIFI_REGEXS = {"wifi_regexs"}; + private static final String[] USB_REGEXS = {"usb_regexs"}; + private static final String[] BT_REGEXS = {"bt_regexs"}; private Context mContext; private AllInOneTetherSettings mAllInOneTetherSettings; @@ -65,33 +74,54 @@ public class AllInOneTetherSettingsTest { doReturn(mConnectivityManager) .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); doReturn(WIFI_REGEXS).when(mConnectivityManager).getTetherableWifiRegexs(); + doReturn(USB_REGEXS).when(mConnectivityManager).getTetherableUsbRegexs(); + doReturn(BT_REGEXS).when(mConnectivityManager).getTetherableBluetoothRegexs(); doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE); + // Assume the feature is enabled for most test cases. + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE, true); mAllInOneTetherSettings = new AllInOneTetherSettings(); + ReflectionHelpers.setField(mAllInOneTetherSettings, "mLifecycle", mock(Lifecycle.class)); } @Test - public void getNonIndexableKeys_tetherAvailable_keysNotReturned() { + public void getNonIndexableKeys_tetherAvailable_featureEnabled_keysReturnedCorrectly() { // To let TetherUtil.isTetherAvailable return true, select one of the combinations setupIsTetherAvailable(true); + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE, true); + final List niks = + AllInOneTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); + + assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).doesNotContain( + AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(niks).doesNotContain(BLUETOOTH_TETHER_KEY); + assertThat(niks).doesNotContain(USB_TETHER_KEY); + + // This key should be returned because it's not visible by default. + assertThat(niks).contains(WIFI_TETHER_DISABLE_KEY); + } + + @Test + public void getNonIndexableKeys_tetherAvailable_featureDisabled_keysReturned() { + setupIsTetherAvailable(true); + FeatureFlagUtils.setEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE, false); + final List niks = AllInOneTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext); - if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) { - assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(niks).doesNotContain( - AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); - assertThat(niks).doesNotContain(AllInOneTetherSettings.KEY_WIFI_TETHER_SECURITY); - } else { - assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); - assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); - assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); - assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_SECURITY); - } + assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); + assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); + assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); + assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(niks).contains(WIFI_TETHER_DISABLE_KEY); + assertThat(niks).contains(BLUETOOTH_TETHER_KEY); + assertThat(niks).contains(USB_TETHER_KEY); } @Test @@ -107,6 +137,9 @@ public class AllInOneTetherSettingsTest { assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); assertThat(niks).contains(AllInOneTetherSettings.KEY_WIFI_TETHER_SECURITY); + assertThat(niks).contains(WIFI_TETHER_DISABLE_KEY); + assertThat(niks).doesNotContain(BLUETOOTH_TETHER_KEY); + assertThat(niks).doesNotContain(USB_TETHER_KEY); } @Test diff --git a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java index 6fa2251c82e..3aa82fe7c9a 100644 --- a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java +++ b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java @@ -41,6 +41,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; +import com.android.settings.widget.SwitchWidgetController; import org.junit.Before; import org.junit.Test; @@ -67,6 +68,7 @@ public class TetherEnablerTest { private SwitchBar mSwitchBar; private TetherEnabler mEnabler; + private SwitchWidgetController mSwitchWidgetController; @Before public void setUp() { @@ -74,7 +76,8 @@ public class TetherEnablerTest { Context context = spy(ApplicationProvider.getApplicationContext()); AtomicReference panReference = spy(AtomicReference.class); - mSwitchBar = new SwitchBar(context); + mSwitchBar = spy(new SwitchBar(context)); + mSwitchWidgetController = spy(new SwitchBarController(mSwitchBar)); when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( mConnectivityManager); @@ -84,7 +87,7 @@ public class TetherEnablerTest { panReference.set(mBluetoothPan); when(context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE)) .thenReturn(mSharedPreferences); - mEnabler = new TetherEnabler(context, new SwitchBarController(mSwitchBar), panReference); + mEnabler = spy(new TetherEnabler(context, mSwitchWidgetController, panReference)); } @Test @@ -121,7 +124,7 @@ public class TetherEnablerTest { @Test public void onDataSaverChanged_setsEnabledCorrectly() { - assertThat(mSwitchBar.isEnabled()).isTrue(); + mSwitchBar.setEnabled(true); // try to turn data saver on when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(true); @@ -179,4 +182,33 @@ public class TetherEnablerTest { verify(mConnectivityManager).startTethering( eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any()); } + + @Test + public void updateState_onSwitchToggleNeverCalled() { + mSwitchWidgetController.setListener(mEnabler); + mSwitchWidgetController.startListening(); + + mEnabler.updateState(null/*tethered*/); + verify(mEnabler, never()).onSwitchToggled(anyBoolean()); + } + + @Test + public void updateState_shouldEnableSwitchBarNotTethering() { + mSwitchWidgetController.setListener(mEnabler); + mSwitchWidgetController.startListening(); + + ReflectionHelpers.setField(mEnabler, "mDataSaverEnabled", false); + mEnabler.updateState(null/*tethered*/); + verify(mSwitchBar).setEnabled(true); + } + + @Test + public void updateState_shouldEnableSwitchBarTethering() { + mSwitchWidgetController.setListener(mEnabler); + mSwitchWidgetController.startListening(); + + ReflectionHelpers.setField(mEnabler, "mDataSaverEnabled", false); + mEnabler.updateState(new String[]{""}); + verify(mSwitchBar).setEnabled(true); + } }