Add tether preferences into AllInOneTetherSettings

These preferences are for user to toggle a specific tethering option,
like USB, BT or WIFI.

Bug: 147323306
Test: CodeInspectionTest, AllInOneTetherSettingsTest, TetherEnablerTest
Change-Id: I1229ffd2dd12b39e9c6e48dc29c6e46ce9ad7634
diff --git a/res/xml/all_tether_prefs.xml b/res/xml/all_tether_prefs.xml
index 07e5d64..bc3471b 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"/>
     </PreferenceCategory>
 
+    <PreferenceCategory
+        android:key="tethering_options_group"
+        android:title="Tethering"
+        settings:searchable="false">
+        <SwitchPreference
+            android:key="enable_usb_tethering"
+            android:title="@string/usb_tethering_button_text"
+            android:summary="@string/usb_tethering_subtext"
+            settings:controller="com.android.settings.network.UsbTetherPreferenceController"
+            settings:keywords="@string/keywords_hotspot_tethering" />
+
+        <SwitchPreference
+            android:key="enable_bluetooth_tethering_2"
+            android:title="@string/bluetooth_tether_checkbox_text"
+            android:summary="@string/bluetooth_tethering_subtext"
+            settings:controller="com.android.settings.network.BluetoothTetherPreferenceController"
+            settings:keywords="@string/keywords_hotspot_tethering" />
+
+        <SwitchPreference
+            android:key="disable_wifi_tethering"
+            android:title="Don't use Wi-Fi hotspot"
+            settings:controller="com.android.settings.network.WifiTetherDisablePreferenceController"
+            settings:keywords="@string/keywords_hotspot_tethering" />
+    </PreferenceCategory>
+
     <Preference
         android:key="disabled_on_data_saver_2"
         android:summary="@string/tether_settings_disabled_on_data_saver"
diff --git a/src/com/android/settings/AllInOneTetherSettings.java b/src/com/android/settings/AllInOneTetherSettings.java
index 996c434..f2a0f52 100644
--- a/src/com/android/settings/AllInOneTetherSettings.java
+++ b/src/com/android/settings/AllInOneTetherSettings.java
@@ -120,22 +120,23 @@
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "updating display config due to receiving broadcast action " + action);
+                Log.d(TAG,
+                        "updating display config due to receiving broadcast action " + action);
             }
             updateDisplayWithNewConfig();
             if (TextUtils.equals(action, ACTION_TETHER_STATE_CHANGED)) {
-                if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED
-                        && mRestartWifiApAfterConfigChange) {
-                    mRestartWifiApAfterConfigChange = false;
-                    mTetherEnabler.startTethering(TETHERING_WIFI);
-                }
+                restartWifiTetherIfNeed(mWifiManager.getWifiApState());
             } else if (TextUtils.equals(action, WIFI_AP_STATE_CHANGED_ACTION)) {
-                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
-                if (state == WifiManager.WIFI_AP_STATE_DISABLED
-                        && mRestartWifiApAfterConfigChange) {
-                    mRestartWifiApAfterConfigChange = false;
-                    mTetherEnabler.startTethering(TETHERING_WIFI);
-                }
+                restartWifiTetherIfNeed(intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0));
+            }
+        }
+
+        private void restartWifiTetherIfNeed(int state) {
+            if (state == WifiManager.WIFI_AP_STATE_DISABLED
+                    && mWifiTetherChosen
+                    && mRestartWifiApAfterConfigChange) {
+                mRestartWifiApAfterConfigChange = false;
+                mTetherEnabler.startTethering(TETHERING_WIFI);
             }
         }
     };
@@ -418,7 +419,7 @@
                 @Override
                 public List<AbstractPreferenceController> 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 2ea3c04..ffb0ef2 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_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.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 @@
  * 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 @@
 
     // 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 @@
         mContext.registerReceiver(mTetherChangeReceiver, filter);
 
         mOnStartTetheringCallback = new OnStartTetheringCallback(this);
-        updateState();
+        updateState(null/*tethered*/);
     }
 
     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -133,14 +135,20 @@
         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 @@
                 if (active != null) {
                     updateState(active.toArray(new String[0]));
                 } else {
-                    updateState();
+                    updateState(null/*tethered*/);
                 }
             }
         }
@@ -371,7 +379,7 @@
         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 ddc2bf7..a7242cf 100644
--- a/src/com/android/settings/network/WifiTetherDisablePreferenceController.java
+++ b/src/com/android/settings/network/WifiTetherDisablePreferenceController.java
@@ -31,6 +31,7 @@
 
 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 @@
     @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 61db0e5..55486d2 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 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.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 @@
 @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 @@
         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<String> 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<String> 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 @@
         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 6fa2251..3aa82fe 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 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 @@
 
     private SwitchBar mSwitchBar;
     private TetherEnabler mEnabler;
+    private SwitchWidgetController mSwitchWidgetController;
 
     @Before
     public void setUp() {
@@ -74,7 +76,8 @@
 
         Context context = spy(ApplicationProvider.getApplicationContext());
         AtomicReference<BluetoothPan> 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 @@
         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 @@
 
     @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 @@
         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);
+    }
 }