diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 0325842c26c..26c699b0d58 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -80,6 +80,10 @@ public class TetherSettings extends RestrictedSettingsFragment @VisibleForTesting static final String KEY_TETHER_PREFS_FOOTER = "tether_prefs_footer"; + @VisibleForTesting + static final String BLUETOOTH_TETHERING_STATE_CHANGED = + "android.bluetooth.pan.profile.action.TETHERING_STATE_CHANGED"; + private static final String TAG = "TetheringSettings"; private SwitchPreference mUsbTether; @@ -154,9 +158,7 @@ public class TetherSettings extends RestrictedSettingsFragment BluetoothProfile.PAN); } - mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS); - mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING); - mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING); + setupTetherPreference(); setFooterPreferenceTitle(); mDataSaverBackend.addListener(this); @@ -208,6 +210,13 @@ public class TetherSettings extends RestrictedSettingsFragment super.onDestroy(); } + @VisibleForTesting + void setupTetherPreference() { + mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS); + mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING); + mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING); + } + @Override public void onDataSaverChanged(boolean isDataSaving) { mDataSaverEnabled = isDataSaving; @@ -281,6 +290,8 @@ public class TetherSettings extends RestrictedSettingsFragment } } updateState(); + } else if (action.equals(BLUETOOTH_TETHERING_STATE_CHANGED)) { + updateState(); } } } @@ -297,32 +308,13 @@ public class TetherSettings extends RestrictedSettingsFragment return; } - final Activity activity = getActivity(); mStartTetheringCallback = new OnStartTetheringCallback(this); mTetheringEventCallback = new TetheringEventCallback(); mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback); mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState()); - mTetherChangeReceiver = new TetherChangeReceiver(); - IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); - Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); - - filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_STATE); - activity.registerReceiver(mTetherChangeReceiver, filter); - - filter = new IntentFilter(); - filter.addAction(Intent.ACTION_MEDIA_SHARED); - filter.addAction(Intent.ACTION_MEDIA_UNSHARED); - filter.addDataScheme("file"); - activity.registerReceiver(mTetherChangeReceiver, filter); - - filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - activity.registerReceiver(mTetherChangeReceiver, filter); - - if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); + registerReceiver(); mEthernetListener = new EthernetListener(); if (mEm != null) @@ -348,10 +340,38 @@ public class TetherSettings extends RestrictedSettingsFragment mEthernetListener = null; } + @VisibleForTesting + void registerReceiver() { + final Activity activity = getActivity(); + + mTetherChangeReceiver = new TetherChangeReceiver(); + IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); + final Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); + + filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_STATE); + activity.registerReceiver(mTetherChangeReceiver, filter); + + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + filter.addAction(Intent.ACTION_MEDIA_UNSHARED); + filter.addDataScheme("file"); + activity.registerReceiver(mTetherChangeReceiver, filter); + + filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + filter.addAction(BLUETOOTH_TETHERING_STATE_CHANGED); + activity.registerReceiver(mTetherChangeReceiver, filter); + + if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); + } + private void updateState() { - String[] available = mCm.getTetherableIfaces(); - String[] tethered = mCm.getTetheredIfaces(); - String[] errored = mCm.getTetheringErroredIfaces(); + final ConnectivityManager cm = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + final String[] available = cm.getTetherableIfaces(); + final String[] tethered = cm.getTetheredIfaces(); + final String[] errored = cm.getTetheringErroredIfaces(); updateState(available, tethered, errored); } @@ -362,7 +382,8 @@ public class TetherSettings extends RestrictedSettingsFragment updateEthernetState(available, tethered); } - private void updateUsbState(String[] available, String[] tethered, + @VisibleForTesting + void updateUsbState(String[] available, String[] tethered, String[] errored) { boolean usbAvailable = mUsbConnected && !mMassStorageActive; int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR; @@ -400,20 +421,33 @@ public class TetherSettings extends RestrictedSettingsFragment } } - private void updateBluetoothState() { + @VisibleForTesting + int getBluetoothState() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter == null) { + return BluetoothAdapter.ERROR; + } + return adapter.getState(); + } + + @VisibleForTesting + boolean isBluetoothTetheringOn() { + final BluetoothPan bluetoothPan = mBluetoothPan.get(); + return bluetoothPan != null && bluetoothPan.isTetheringOn(); + } + + private void updateBluetoothState() { + final int btState = getBluetoothState(); + if (btState == BluetoothAdapter.ERROR) { return; } - int btState = adapter.getState(); + if (btState == BluetoothAdapter.STATE_TURNING_OFF) { mBluetoothTether.setEnabled(false); } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { mBluetoothTether.setEnabled(false); } else { - BluetoothPan bluetoothPan = mBluetoothPan.get(); - if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null - && bluetoothPan.isTetheringOn()) { + if (btState == BluetoothAdapter.STATE_ON && isBluetoothTetheringOn()) { mBluetoothTether.setChecked(true); mBluetoothTether.setEnabled(!mDataSaverEnabled); } else { @@ -423,7 +457,8 @@ public class TetherSettings extends RestrictedSettingsFragment } } - private void updateEthernetState(String[] available, String[] tethered) { + @VisibleForTesting + void updateEthernetState(String[] available, String[] tethered) { boolean isAvailable = false; boolean isTethered = false; diff --git a/tests/robotests/src/com/android/settings/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/TetherSettingsTest.java index 3c38c51dbaf..99c112c1920 100644 --- a/tests/robotests/src/com/android/settings/TetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/TetherSettingsTest.java @@ -18,6 +18,8 @@ package com.android.settings; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -25,20 +27,27 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.UserHandle; import android.os.UserManager; import android.util.FeatureFlagUtils; +import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; +import androidx.preference.SwitchPreference; import com.android.settings.core.FeatureFlags; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -160,6 +169,80 @@ public class TetherSettingsTest { verify(mockPreference).setTitle(R.string.tethering_footer_info_sta_ap_concurrency); } + @Test + public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOn() { + final TetherSettings spyTetherSettings = spy(new TetherSettings()); + when(spyTetherSettings.getContext()).thenReturn(mContext); + final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); + when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) + .thenReturn(mockSwitchPreference); + final FragmentActivity mockActivity = mock(FragmentActivity.class); + when(spyTetherSettings.getActivity()).thenReturn(mockActivity); + final ArgumentCaptor captor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + when(mockActivity.registerReceiver(captor.capture(), any(IntentFilter.class))) + .thenReturn(null); + // Bluetooth tethering state is on + when(spyTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); + when(spyTetherSettings.isBluetoothTetheringOn()).thenReturn(true); + + spyTetherSettings.setupTetherPreference(); + spyTetherSettings.registerReceiver(); + updateOnlyBluetoothState(spyTetherSettings); + + // Simulate Bluetooth tethering state changed + final BroadcastReceiver receiver = captor.getValue(); + final Intent bluetoothTetheringChanged = + new Intent(TetherSettings.BLUETOOTH_TETHERING_STATE_CHANGED); + receiver.onReceive(mockActivity, bluetoothTetheringChanged); + + verify(mockSwitchPreference).setEnabled(true); + verify(mockSwitchPreference).setChecked(true); + } + + @Test + public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOff() { + final TetherSettings spyTetherSettings = spy(new TetherSettings()); + when(spyTetherSettings.getContext()).thenReturn(mContext); + final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); + when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) + .thenReturn(mockSwitchPreference); + final FragmentActivity mockActivity = mock(FragmentActivity.class); + when(spyTetherSettings.getActivity()).thenReturn(mockActivity); + final ArgumentCaptor captor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + when(mockActivity.registerReceiver(captor.capture(), any(IntentFilter.class))) + .thenReturn(null); + // Bluetooth tethering state is off + when(spyTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); + when(spyTetherSettings.isBluetoothTetheringOn()).thenReturn(false); + + spyTetherSettings.setupTetherPreference(); + spyTetherSettings.registerReceiver(); + updateOnlyBluetoothState(spyTetherSettings); + + // Simulate Bluetooth tethering state changed + final BroadcastReceiver receiver = captor.getValue(); + final Intent bluetoothTetheringChanged = + new Intent(TetherSettings.BLUETOOTH_TETHERING_STATE_CHANGED); + receiver.onReceive(mockActivity, bluetoothTetheringChanged); + + verify(mockSwitchPreference).setEnabled(true); + verify(mockSwitchPreference).setChecked(false); + } + + private void updateOnlyBluetoothState(TetherSettings tetherSettings) { + doReturn(mConnectivityManager).when(tetherSettings) + .getSystemService(Context.CONNECTIVITY_SERVICE); + when(mConnectivityManager.getTetherableIfaces()).thenReturn(new String[0]); + when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[0]); + when(mConnectivityManager.getTetheringErroredIfaces()).thenReturn(new String[0]); + doNothing().when(tetherSettings).updateUsbState(any(String[].class), any(String[].class), + any(String[].class)); + doNothing().when(tetherSettings).updateEthernetState(any(String[].class), + any(String[].class)); + } + private void setupIsTetherAvailable(boolean returnValue) { when(mConnectivityManager.isTetheringSupported()).thenReturn(true);