Merge "Create preference controller for each tethering option"
diff --git a/src/com/android/settings/network/BluetoothTetherPreferenceController.java b/src/com/android/settings/network/BluetoothTetherPreferenceController.java
new file mode 100644
index 0000000..10849a0
--- /dev/null
+++ b/src/com/android/settings/network/BluetoothTetherPreferenceController.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * This controller helps to manage the switch state and visibility of bluetooth tether switch
+ * preference. It stores preference value when preference changed.
+ * TODO(b/147272749): Extend BasePreferenceController.java instead.
+ */
+public final class BluetoothTetherPreferenceController extends AbstractPreferenceController
+        implements LifecycleObserver, Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "BluetoothTetherPreferenceController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    @VisibleForTesting
+    static final String PREF_KEY = "enable_bluetooth_tethering";
+    private final ConnectivityManager mCm;
+    private int mBluetoothState;
+    private Preference mPreference;
+    private final SharedPreferences mSharedPreferences;
+
+    public BluetoothTetherPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mSharedPreferences =
+                context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    public void onStart() {
+        mBluetoothState = BluetoothAdapter.getDefaultAdapter().getState();
+        mContext.registerReceiver(mBluetoothChangeReceiver,
+                new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    public void onStop() {
+        mContext.unregisterReceiver(mBluetoothChangeReceiver);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        final String[] bluetoothRegexs = mCm.getTetherableBluetoothRegexs();
+        return bluetoothRegexs != null && bluetoothRegexs.length > 0;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(PREF_KEY);
+        if (mPreference != null && mPreference instanceof SwitchPreference) {
+            ((SwitchPreference) mPreference)
+                    .setChecked(mSharedPreferences.getBoolean(PREF_KEY, false));
+        }
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        switch (mBluetoothState) {
+            case BluetoothAdapter.STATE_ON:
+            case BluetoothAdapter.STATE_OFF:
+                // fall through.
+            case BluetoothAdapter.ERROR:
+                preference.setEnabled(true);
+                break;
+            case BluetoothAdapter.STATE_TURNING_OFF:
+            case BluetoothAdapter.STATE_TURNING_ON:
+                // fall through.
+            default:
+                preference.setEnabled(false);
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @VisibleForTesting
+    final BroadcastReceiver mBluetoothChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TextUtils.equals(BluetoothAdapter.ACTION_STATE_CHANGED, intent.getAction())) {
+                mBluetoothState =
+                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+                updateState(mPreference);
+            }
+        }
+    };
+
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object o) {
+        if (DEBUG) {
+            Log.d(TAG, "preference changing to " + o);
+        }
+        final SharedPreferences.Editor editor = mSharedPreferences.edit();
+        editor.putBoolean(PREF_KEY, (Boolean) o);
+        editor.apply();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/network/TetherEnabler.java b/src/com/android/settings/network/TetherEnabler.java
index 9106aa1..6c6c959 100644
--- a/src/com/android/settings/network/TetherEnabler.java
+++ b/src/com/android/settings/network/TetherEnabler.java
@@ -32,11 +32,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.OnLifecycleEvent;
-import androidx.preference.PreferenceManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.datausage.DataSaverBackend;
@@ -52,13 +52,20 @@
  */
 
 public final class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener,
-        DataSaverBackend.Listener, LifecycleObserver {
+        DataSaverBackend.Listener, LifecycleObserver,
+        SharedPreferences.OnSharedPreferenceChangeListener {
+
+    private static final String TAG = "TetherEnabler";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    public static final String SHARED_PREF = "tether_options";
+
     @VisibleForTesting
-    static final String WIFI_TETHER_KEY = "enable_wifi_tethering";
+    static final String WIFI_TETHER_KEY = WifiTetherDisablePreferenceController.PREF_KEY;
     @VisibleForTesting
-    static final String USB_TETHER_KEY = "enable_usb_tethering";
+    static final String USB_TETHER_KEY = UsbTetherPreferenceController.PREF_KEY;
     @VisibleForTesting
-    static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering";
+    static final String BLUETOOTH_TETHER_KEY = BluetoothTetherPreferenceController.PREF_KEY;
 
     private final SwitchWidgetController mSwitchWidgetController;
     private final WifiManager mWifiManager;
@@ -76,7 +83,6 @@
                 public void onTetheringFailed() {
                     super.onTetheringFailed();
                     mSwitchWidgetController.setChecked(false);
-                    setSwitchWidgetEnabled(true);
                 }
             };
     private final AtomicReference<BluetoothPan> mBluetoothPan;
@@ -84,12 +90,12 @@
     private boolean mBluetoothEnableForTether;
     private final BluetoothAdapter mBluetoothAdapter;
 
-    TetherEnabler(Context context, SwitchWidgetController switchWidgetController,
+    public TetherEnabler(Context context, SwitchWidgetController switchWidgetController,
             AtomicReference<BluetoothPan> bluetoothPan) {
         mContext = context;
         mSwitchWidgetController = switchWidgetController;
         mDataSaverBackend = new DataSaverBackend(context);
-        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
+        mSharedPreferences = context.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
         mConnectivityManager =
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -110,6 +116,16 @@
         setSwitchWidgetEnabled(true);
     }
 
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    public void onResume() {
+        mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    public void onPause() {
+        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+    }
+
     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
     public void onStop() {
         mDataSaverBackend.remListener(this);
@@ -148,19 +164,16 @@
 
     @VisibleForTesting
     void stopTether() {
-        setSwitchWidgetEnabled(false);
 
-        // Wi-Fi tether is selected by default
+        // Wi-Fi tether is selected by default.
         if (mSharedPreferences.getBoolean(WIFI_TETHER_KEY, true)) {
             mConnectivityManager.stopTethering(TETHERING_WIFI);
         }
 
-        // USB tether is not selected by default
         if (mSharedPreferences.getBoolean(USB_TETHER_KEY, false)) {
             mConnectivityManager.stopTethering(TETHERING_USB);
         }
 
-        // Bluetooth tether is not selected by default
         if (mSharedPreferences.getBoolean(BLUETOOTH_TETHER_KEY, false)) {
             mConnectivityManager.stopTethering(TETHERING_BLUETOOTH);
         }
@@ -168,19 +181,16 @@
 
     @VisibleForTesting
     void startTether() {
-        setSwitchWidgetEnabled(false);
 
-        // Wi-Fi tether is selected by default
+        // Wi-Fi tether is selected by default.
         if (mSharedPreferences.getBoolean(WIFI_TETHER_KEY, true)) {
             startTethering(TETHERING_WIFI);
         }
 
-        // USB tether is not selected by default
         if (mSharedPreferences.getBoolean(USB_TETHER_KEY, false)) {
             startTethering(TETHERING_USB);
         }
 
-        // Bluetooth tether is not selected by default
         if (mSharedPreferences.getBoolean(BLUETOOTH_TETHER_KEY, false)) {
             startTethering(TETHERING_BLUETOOTH);
         }
@@ -188,18 +198,24 @@
 
     @VisibleForTesting
     void startTethering(int choice) {
+        if (choice == TETHERING_WIFI && mWifiManager.isWifiApEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "Wifi tether already active!");
+            }
+            return;
+        }
+
         if (choice == TETHERING_BLUETOOTH) {
-            // Turn on Bluetooth first.
             if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) {
+                if (DEBUG) {
+                    Log.d(TAG, "Turn on bluetooth first.");
+                }
                 mBluetoothEnableForTether = true;
                 mBluetoothAdapter.enable();
                 return;
             }
-        } else if (choice == TETHERING_WIFI && mWifiManager.isWifiApEnabled()) {
-            return;
         }
 
-
         mConnectivityManager.startTethering(choice, true /* showProvisioningUi */,
                 mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
     }
@@ -213,24 +229,21 @@
                         ConnectivityManager.EXTRA_ACTIVE_TETHER);
                 mSwitchWidgetController.setChecked(
                         isTethering(active.toArray(new String[active.size()])));
-                setSwitchWidgetEnabled(true);
             } else if (TextUtils.equals(BluetoothAdapter.ACTION_STATE_CHANGED, action)) {
-                if (mBluetoothEnableForTether) {
-                    switch (intent
-                            .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
-                        case BluetoothAdapter.STATE_ON:
+                switch (intent
+                        .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
+                    case BluetoothAdapter.STATE_ON:
+                        if (mBluetoothEnableForTether) {
                             startTethering(TETHERING_BLUETOOTH);
-                            mBluetoothEnableForTether = false;
-                            break;
-
-                        case BluetoothAdapter.STATE_OFF:
-                        case BluetoothAdapter.ERROR:
-                            mBluetoothEnableForTether = false;
-                            break;
-
-                        default:
-                            // ignore transition states
-                    }
+                        }
+                        // Fall through.
+                    case BluetoothAdapter.STATE_OFF:
+                        // Fall through.
+                    case BluetoothAdapter.ERROR:
+                        mBluetoothEnableForTether = false;
+                        break;
+                    default:
+                        // ignore transition states
                 }
             }
         }
@@ -251,4 +264,30 @@
     public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
         // we don't care, since we just want to read the value
     }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (!mSwitchWidgetController.isChecked()) {
+            return;
+        }
+        if (TextUtils.equals(WIFI_TETHER_KEY, key)) {
+            if (sharedPreferences.getBoolean(key, true)) {
+                startTethering(TETHERING_WIFI);
+            } else {
+                mConnectivityManager.stopTethering(TETHERING_WIFI);
+            }
+        } else if (TextUtils.equals(USB_TETHER_KEY, key)) {
+            if (sharedPreferences.getBoolean(key, false)) {
+                startTethering(TETHERING_USB);
+            } else {
+                mConnectivityManager.stopTethering(TETHERING_USB);
+            }
+        } else if (TextUtils.equals(BLUETOOTH_TETHER_KEY, key)) {
+            if (sharedPreferences.getBoolean(key, false)) {
+                startTethering(TETHERING_BLUETOOTH);
+            } else {
+                mConnectivityManager.stopTethering(TETHERING_BLUETOOTH);
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/network/UsbTetherPreferenceController.java b/src/com/android/settings/network/UsbTetherPreferenceController.java
new file mode 100644
index 0000000..74e8be8
--- /dev/null
+++ b/src/com/android/settings/network/UsbTetherPreferenceController.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.Utils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller helps to manage the switch state and visibility of USB tether switch
+ * preference. It stores preference values when preference changed.
+ * TODO(b/147272749): Extend BasePreferenceController.java instead.
+ *
+ */
+public final class UsbTetherPreferenceController extends AbstractPreferenceController implements
+        LifecycleObserver, Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "UsbTetherPrefController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    @VisibleForTesting
+    static final String PREF_KEY = "enable_usb_tethering";
+
+    private final ConnectivityManager mCm;
+    private boolean mUsbConnected;
+    private boolean mMassStorageActive;
+    private Preference mPreference;
+    private final SharedPreferences mSharedPreferences;
+
+    public UsbTetherPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mSharedPreferences =
+                context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    public void onStart() {
+        mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
+        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+        filter.addAction(Intent.ACTION_MEDIA_SHARED);
+        filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
+        mContext.registerReceiver(mUsbChangeReceiver, filter);
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    public void onStop() {
+        mContext.unregisterReceiver(mUsbChangeReceiver);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        String[] usbRegexs = mCm.getTetherableUsbRegexs();
+        return usbRegexs != null && usbRegexs.length > 0 && !Utils.isMonkeyRunning();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(PREF_KEY);
+        if (mPreference != null && mPreference instanceof SwitchPreference) {
+            ((SwitchPreference) mPreference)
+                    .setChecked(mSharedPreferences.getBoolean(PREF_KEY, false));
+        }
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        if (preference != null) {
+            if (mUsbConnected && !mMassStorageActive) {
+                preference.setEnabled(true);
+            } else {
+                preference.setEnabled(false);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    final BroadcastReceiver mUsbChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TextUtils.equals(Intent.ACTION_MEDIA_SHARED, action)) {
+                mMassStorageActive = true;
+            } else if (TextUtils.equals(Intent.ACTION_MEDIA_UNSHARED, action)) {
+                mMassStorageActive = false;
+            } else if (TextUtils.equals(UsbManager.ACTION_USB_STATE, action)) {
+                mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+            }
+            updateState(mPreference);
+        }
+    };
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object o) {
+        if (DEBUG) {
+            Log.d(TAG, "preference changing to " + o);
+        }
+        final SharedPreferences.Editor editor = mSharedPreferences.edit();
+        editor.putBoolean(PREF_KEY, (Boolean) o);
+        editor.apply();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/network/WifiTetherDisablePreferenceController.java b/src/com/android/settings/network/WifiTetherDisablePreferenceController.java
new file mode 100644
index 0000000..bf0e208
--- /dev/null
+++ b/src/com/android/settings/network/WifiTetherDisablePreferenceController.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller helps to manage the switch state and visibility of wifi tether disable switch
+ * preference. When the preference checked, wifi tether will be disabled.
+ * It stores preference value when preference changed and listens to usb tether and bluetooth tether
+ * preferences.
+ *
+ * @see BluetoothTetherPreferenceController
+ * @see UsbTetherPreferenceController
+ * TODO(b/147272749): Extend BasePreferenceController.java instead.
+ *
+ */
+public final class WifiTetherDisablePreferenceController extends AbstractPreferenceController
+        implements LifecycleObserver, Preference.OnPreferenceChangeListener,
+        SharedPreferences.OnSharedPreferenceChangeListener {
+
+    private static final String TAG = "WifiTetherDisablePreferenceController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    @VisibleForTesting
+    static final String PREF_KEY = "enable_wifi_tethering";
+    private final ConnectivityManager mCm;
+    private boolean mBluetoothTetherEnabled;
+    private boolean mUSBTetherEnabled;
+    private PreferenceScreen mScreen;
+    private Preference mPreference;
+    private final SharedPreferences mSharedPreferences;
+
+    public WifiTetherDisablePreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        mSharedPreferences =
+                context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE);
+        mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mUSBTetherEnabled = mSharedPreferences.getBoolean(
+                TetherEnabler.USB_TETHER_KEY, false);
+        mBluetoothTetherEnabled = mSharedPreferences.getBoolean(
+                TetherEnabler.BLUETOOTH_TETHER_KEY, false);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    public void onResume() {
+        mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    public void onPause() {
+        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        final String[] wifiRegexs = mCm.getTetherableWifiRegexs();
+        return wifiRegexs != null && wifiRegexs.length > 0 && shouldShow();
+    }
+
+    @VisibleForTesting
+    boolean shouldShow() {
+        return mBluetoothTetherEnabled || mUSBTetherEnabled;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREF_KEY;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        // TODO(b/146818850): Update summary accordingly.
+        return super.getSummary();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mScreen = screen;
+        mPreference = screen.findPreference(PREF_KEY);
+        if (mPreference != null && mPreference instanceof SwitchPreference) {
+            ((SwitchPreference) mPreference)
+                    .setChecked(!mSharedPreferences.getBoolean(PREF_KEY, true));
+            mPreference.setOnPreferenceChangeListener(this);
+        }
+        updateState(mPreference);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        setVisible(mScreen, PREF_KEY, shouldShow());
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+            String key) {
+        if (TextUtils.equals(TetherEnabler.USB_TETHER_KEY, key)) {
+            mUSBTetherEnabled = sharedPreferences.getBoolean(key, false);
+        } else if (TextUtils.equals(TetherEnabler.BLUETOOTH_TETHER_KEY, key)) {
+            mBluetoothTetherEnabled = sharedPreferences.getBoolean(key, false);
+        }
+
+        // Check if we are hiding this preference. If so,  make sure the preference is set to
+        // unchecked to enable wifi tether.
+        if (mPreference != null && mPreference instanceof SwitchPreference && !shouldShow()) {
+            final SwitchPreference switchPreference = (SwitchPreference) mPreference;
+            if (switchPreference.isChecked()) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            "All other types are unchecked, wifi tether enabled automatically");
+                }
+                // Need to call this method before internal state set.
+                if (switchPreference.callChangeListener(false)) {
+                    switchPreference.setChecked(false);
+                }
+            }
+        }
+
+        updateState(mPreference);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object o) {
+        // The shared preference's value is in the opposite of this preference's value.
+        final boolean enableWifi = !(boolean) o;
+        if (true) {
+            Log.d(TAG, "check state changing to " + o);
+        }
+        final SharedPreferences.Editor editor = mSharedPreferences.edit();
+        editor.putBoolean(PREF_KEY, enableWifi);
+        editor.apply();
+        return true;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/BluetoothTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/BluetoothTetherPreferenceControllerTest.java
new file mode 100644
index 0000000..c76e234
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/BluetoothTetherPreferenceControllerTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ApplicationProvider;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothTetherPreferenceControllerTest {
+
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+
+    private BluetoothTetherPreferenceController mController;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
+                mConnectivityManager);
+        when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[] {""});
+        mController = new BluetoothTetherPreferenceController(mContext, mock(Lifecycle.class));
+    }
+
+    @Test
+    public void lifecycle_shouldRegisterReceiverOnStart() {
+        mController.onStart();
+
+        verify(mContext).registerReceiver(
+                eq(mController.mBluetoothChangeReceiver),
+                any());
+    }
+
+    @Test
+    public void lifecycle_shouldUnregisterReceiverOnStop() {
+        mController.onStart();
+        mController.onStop();
+
+        verify(mContext).unregisterReceiver(
+                eq(mController.mBluetoothChangeReceiver));
+    }
+
+    @Test
+    public void display_availableChangedCorrectly() {
+        when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[] {""});
+        assertThat(mController.isAvailable()).isTrue();
+
+        when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[0]);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java
index 06f3893..9dcfa89 100644
--- a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java
+++ b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -83,10 +82,23 @@
                 mNetworkPolicyManager);
         when(mConnectivityManager.getTetherableIfaces()).thenReturn(new String[0]);
         panReference.set(mBluetoothPan);
+        when(context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE))
+                .thenReturn(mSharedPreferences);
         mEnabler = new TetherEnabler(context, new SwitchBarController(mSwitchBar), panReference);
     }
 
     @Test
+    public void lifecycle_onPause_unRegisterSharedPreferenceListener() {
+        mEnabler.onResume();
+        verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(
+                eq(mEnabler));
+
+        mEnabler.onPause();
+        verify(mSharedPreferences).unregisterOnSharedPreferenceChangeListener(
+                eq(mEnabler));
+    }
+
+    @Test
     public void lifecycle_onStart_setCheckedCorrectly() {
         when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{""});
 
@@ -122,29 +134,27 @@
 
     @Test
     public void onSwitchToggled_onlyStartsWifiTetherWhenNeeded() {
+        when(mSharedPreferences.getBoolean(TetherEnabler.WIFI_TETHER_KEY, true)).thenReturn(true);
         when(mWifiManager.isWifiApEnabled()).thenReturn(true);
         mEnabler.onSwitchToggled(true);
-
         verify(mConnectivityManager, never()).startTethering(anyInt(), anyBoolean(), any(), any());
 
         doReturn(false).when(mWifiManager).isWifiApEnabled();
         mEnabler.onSwitchToggled(true);
-
-        verify(mConnectivityManager, times(1))
-                .startTethering(anyInt(), anyBoolean(), any(), any());
+        verify(mConnectivityManager).startTethering(anyInt(), anyBoolean(), any(), any());
     }
 
     @Test
     public void onSwitchToggled_shouldStartUSBTetherWhenSelected() {
         SharedPreferences preference = mock(SharedPreferences.class);
         ReflectionHelpers.setField(mEnabler, "mSharedPreferences", preference);
-        when(preference.getBoolean(mEnabler.WIFI_TETHER_KEY, true)).thenReturn(false);
-        when(preference.getBoolean(mEnabler.USB_TETHER_KEY, false)).thenReturn(true);
-        when(preference.getBoolean(mEnabler.BLUETOOTH_TETHER_KEY, true)).thenReturn(false);
+        when(preference.getBoolean(TetherEnabler.WIFI_TETHER_KEY, true)).thenReturn(false);
+        when(preference.getBoolean(TetherEnabler.USB_TETHER_KEY, false)).thenReturn(true);
+        when(preference.getBoolean(TetherEnabler.BLUETOOTH_TETHER_KEY, true)).thenReturn(false);
 
         mEnabler.startTether();
-        verify(mConnectivityManager, times(1))
-                .startTethering(eq(ConnectivityManager.TETHERING_USB), anyBoolean(), any(), any());
+        verify(mConnectivityManager).startTethering(
+                eq(ConnectivityManager.TETHERING_USB), anyBoolean(), any(), any());
         verify(mConnectivityManager, never())
                 .startTethering(eq(ConnectivityManager.TETHERING_WIFI), anyBoolean(), any(), any());
         verify(mConnectivityManager, never()).startTethering(
@@ -158,11 +168,11 @@
         when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_OFF);
 
         mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH);
-        verify(adapter, times(1)).enable();
+        verify(adapter).enable();
 
         when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_ON);
         mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH);
-        verify(mConnectivityManager, times(1)).startTethering(
+        verify(mConnectivityManager).startTethering(
                 eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/network/UsbTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/UsbTetherPreferenceControllerTest.java
new file mode 100644
index 0000000..9127e4b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/UsbTetherPreferenceControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ApplicationProvider;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class UsbTetherPreferenceControllerTest {
+
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+
+    private Context mContext;
+    private UsbTetherPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
+                mConnectivityManager);
+        when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[] {""});
+        mController = new UsbTetherPreferenceController(mContext, mock(Lifecycle.class));
+    }
+
+    @Test
+    public void lifecycle_shouldRegisterReceiverOnStart() {
+        mController.onStart();
+
+        verify(mContext).registerReceiver(eq(mController.mUsbChangeReceiver), any());
+    }
+
+    @Test
+    public void lifecycle_shouldUnregisterReceiverOnStop() {
+        mController.onStart();
+        mController.onStop();
+
+        verify(mContext).unregisterReceiver(eq(mController.mUsbChangeReceiver));
+    }
+
+    @Test
+    public void display_availableChangedCorrectly() {
+        when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[] {""});
+        assertThat(mController.isAvailable()).isTrue();
+
+        when(mConnectivityManager.getTetherableUsbRegexs()).thenReturn(new String[0]);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/WifiTetherDisablePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/WifiTetherDisablePreferenceControllerTest.java
new file mode 100644
index 0000000..e42c477
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/WifiTetherDisablePreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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.network;
+
+import static com.android.settings.network.WifiTetherDisablePreferenceController.PREF_KEY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+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.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiTetherDisablePreferenceControllerTest {
+
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private SharedPreferences mSharedPreferences;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private SwitchPreference mPreference;
+    private Context mContext;
+    private WifiTetherDisablePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        mPreference = spy(SwitchPreference.class);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
+                mConnectivityManager);
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{""});
+        when(mContext.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE))
+                .thenReturn(mSharedPreferences);
+        mController = new WifiTetherDisablePreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen);
+        ReflectionHelpers.setField(mController, "mPreference", mPreference);
+        when(mPreferenceScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
+    }
+
+    @Test
+
+    public void lifecycle_shouldRegisterReceiverOnResume() {
+        mController.onResume();
+
+        verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(eq(mController));
+    }
+
+    @Test
+    public void lifecycle_shouldUnregisterReceiverOnStop() {
+        mController.onResume();
+        mController.onPause();
+
+        verify(mSharedPreferences).unregisterOnSharedPreferenceChangeListener(eq(mController));
+    }
+
+    @Test
+    public void display_availableChangedCorrectly() {
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[0]);
+        assertThat(mController.isAvailable()).isFalse();
+
+        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"test"});
+        ReflectionHelpers.setField(mController, "mBluetoothTetherEnabled", false);
+        ReflectionHelpers.setField(mController, "mUSBTetherEnabled", false);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void switch_shouldListenToUsbAndBluetooth() {
+        when(mSharedPreferences.getBoolean(
+                BluetoothTetherPreferenceController.PREF_KEY, false)).thenReturn(true);
+        mController.onSharedPreferenceChanged(mSharedPreferences,
+                BluetoothTetherPreferenceController.PREF_KEY);
+        verify(mPreference).setVisible(eq(true));
+
+        when(mSharedPreferences.getBoolean(
+                UsbTetherPreferenceController.PREF_KEY, false)).thenReturn(true);
+        mController.onSharedPreferenceChanged(mSharedPreferences,
+                UsbTetherPreferenceController.PREF_KEY);
+        assertThat(mController.shouldShow()).isTrue();
+
+        when(mSharedPreferences.getBoolean(
+                UsbTetherPreferenceController.PREF_KEY, false)).thenReturn(false);
+        mController.onSharedPreferenceChanged(mSharedPreferences,
+                UsbTetherPreferenceController.PREF_KEY);
+        assertThat(mController.shouldShow()).isTrue();
+
+        when(mSharedPreferences.getBoolean(
+                BluetoothTetherPreferenceController.PREF_KEY, false)).thenReturn(false);
+        when(mSharedPreferences.edit()).thenReturn(mock(SharedPreferences.Editor.class));
+        when(mPreference.isChecked()).thenReturn(true);
+        mController.onSharedPreferenceChanged(mSharedPreferences,
+                BluetoothTetherPreferenceController.PREF_KEY);
+        verify(mPreference).setChecked(eq(false));
+        verify(mPreference).setVisible(eq(false));
+    }
+}