From a990206ee7bd0810ef48e1aa91bf7ef6100e11f8 Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Fri, 20 Dec 2019 17:44:26 -0800 Subject: [PATCH] Create preference controller for each tethering option These controllers are responsible for persisting preference values and managing the visibility and switch state. TetherEnabler will be listening to changes of shared prefernce values and turn on/off corresponding type of tethering when tethering is on at that time. Bug: 146582865 Test: tests are created for each controller class. Change-Id: I7e601b2bd693da59269517d2988bade7475e7f12 --- .../BluetoothTetherPreferenceController.java | 141 +++++++++++++++ .../settings/network/TetherEnabler.java | 111 ++++++++---- .../UsbTetherPreferenceController.java | 144 +++++++++++++++ ...WifiTetherDisablePreferenceController.java | 166 ++++++++++++++++++ ...uetoothTetherPreferenceControllerTest.java | 87 +++++++++ .../settings/network/TetherEnablerTest.java | 34 ++-- .../UsbTetherPreferenceControllerTest.java | 84 +++++++++ ...TetherDisablePreferenceControllerTest.java | 133 ++++++++++++++ 8 files changed, 852 insertions(+), 48 deletions(-) create mode 100644 src/com/android/settings/network/BluetoothTetherPreferenceController.java create mode 100644 src/com/android/settings/network/UsbTetherPreferenceController.java create mode 100644 src/com/android/settings/network/WifiTetherDisablePreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/network/BluetoothTetherPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/network/UsbTetherPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/network/WifiTetherDisablePreferenceControllerTest.java diff --git a/src/com/android/settings/network/BluetoothTetherPreferenceController.java b/src/com/android/settings/network/BluetoothTetherPreferenceController.java new file mode 100644 index 00000000000..10849a0d235 --- /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 9106aa17fcb..6c6c95968d1 100644 --- a/src/com/android/settings/network/TetherEnabler.java +++ b/src/com/android/settings/network/TetherEnabler.java @@ -32,11 +32,11 @@ import android.net.wifi.WifiManager; 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 @@ import java.util.concurrent.atomic.AtomicReference; */ 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 final class TetherEnabler implements SwitchWidgetController.OnSwitchChang public void onTetheringFailed() { super.onTetheringFailed(); mSwitchWidgetController.setChecked(false); - setSwitchWidgetEnabled(true); } }; private final AtomicReference mBluetoothPan; @@ -84,12 +90,12 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang private boolean mBluetoothEnableForTether; private final BluetoothAdapter mBluetoothAdapter; - TetherEnabler(Context context, SwitchWidgetController switchWidgetController, + public TetherEnabler(Context context, SwitchWidgetController switchWidgetController, AtomicReference 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 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang 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 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang @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 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang @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 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang @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 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang 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 final class TetherEnabler implements SwitchWidgetController.OnSwitchChang 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 00000000000..74e8be8abac --- /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 00000000000..bf0e2085f26 --- /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 00000000000..c76e23433c2 --- /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 06f3893602c..9dcfa8914d0 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.doReturn; 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,9 +82,22 @@ public class TetherEnablerTest { 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 @@ public class TetherEnablerTest { @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 @@ public class TetherEnablerTest { 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 00000000000..9127e4bfd67 --- /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 00000000000..e42c47716c0 --- /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)); + } +}