Loading src/com/android/settings/network/TetherEnabler.java 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * 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 android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; 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.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; 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; import com.android.settings.widget.SwitchWidgetController; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; /** * TetherEnabler is a helper to manage Tethering switch on/off state. It turns on/off * different types of tethering based on stored values in {@link SharedPreferences} and ensures * tethering state updated by data saver state. */ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener, DataSaverBackend.Listener, LifecycleObserver { @VisibleForTesting static final String WIFI_TETHER_KEY = "enable_wifi_tethering"; @VisibleForTesting static final String USB_TETHER_KEY = "enable_usb_tethering"; @VisibleForTesting static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering"; private final SwitchWidgetController mSwitchWidgetController; private final WifiManager mWifiManager; private final ConnectivityManager mConnectivityManager; private final DataSaverBackend mDataSaverBackend; private boolean mDataSaverEnabled; private final Context mContext; @VisibleForTesting final ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback = new ConnectivityManager.OnStartTetheringCallback() { @Override public void onTetheringFailed() { super.onTetheringFailed(); mSwitchWidgetController.setChecked(false); setSwitchWidgetEnabled(true); } }; private final AtomicReference<BluetoothPan> mBluetoothPan; private final SharedPreferences mSharedPreferences; private boolean mBluetoothEnableForTether; private final BluetoothAdapter mBluetoothAdapter; TetherEnabler(Context context, SwitchWidgetController switchWidgetController, AtomicReference<BluetoothPan> bluetoothPan) { mContext = context; mSwitchWidgetController = switchWidgetController; mDataSaverBackend = new DataSaverBackend(context); mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothPan = bluetoothPan; mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { mDataSaverBackend.addListener(this); mSwitchWidgetController.setListener(this); mSwitchWidgetController.startListening(); IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mTetherChangeReceiver, filter); mSwitchWidgetController.setChecked(isTethering()); setSwitchWidgetEnabled(true); } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { mDataSaverBackend.remListener(this); mSwitchWidgetController.stopListening(); mContext.unregisterReceiver(mTetherChangeReceiver); } private void setSwitchWidgetEnabled(boolean enabled) { mSwitchWidgetController.setEnabled(enabled && !mDataSaverEnabled); } private boolean isTethering() { String[] tethered = mConnectivityManager.getTetheredIfaces(); return isTethering(tethered); } private boolean isTethering(String[] tethered) { if (tethered != null && tethered.length != 0) { return true; } final BluetoothPan pan = mBluetoothPan.get(); return pan != null && pan.isTetheringOn(); } @Override public boolean onSwitchToggled(boolean isChecked) { if (isChecked) { startTether(); } else { stopTether(); } return true; } @VisibleForTesting void stopTether() { setSwitchWidgetEnabled(false); // 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); } } @VisibleForTesting void startTether() { setSwitchWidgetEnabled(false); // 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); } } @VisibleForTesting void startTethering(int choice) { if (choice == TETHERING_BLUETOOTH) { // Turn on Bluetooth first. if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { mBluetoothEnableForTether = true; mBluetoothAdapter.enable(); return; } } else if (choice == TETHERING_WIFI && mWifiManager.isWifiApEnabled()) { return; } mConnectivityManager.startTethering(choice, true /* showProvisioningUi */, mOnStartTetheringCallback, new Handler(Looper.getMainLooper())); } private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (TextUtils.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, action)) { ArrayList<String> active = intent.getStringArrayListExtra( 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: startTethering(TETHERING_BLUETOOTH); mBluetoothEnableForTether = false; break; case BluetoothAdapter.STATE_OFF: case BluetoothAdapter.ERROR: mBluetoothEnableForTether = false; break; default: // ignore transition states } } } } }; @Override public void onDataSaverChanged(boolean isDataSaving) { mDataSaverEnabled = isDataSaving; setSwitchWidgetEnabled(!isDataSaving); } @Override public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) { // we don't care, since we just want to read the value } @Override public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) { // we don't care, since we just want to read the value } } tests/robotests/src/com/android/settings/network/TetherEnablerTest.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * 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.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; 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; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.content.Context; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkPolicyManager; import android.net.wifi.WifiManager; import androidx.test.core.app.ApplicationProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; 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; import java.util.concurrent.atomic.AtomicReference; @RunWith(RobolectricTestRunner.class) public class TetherEnablerTest { @Mock private WifiManager mWifiManager; @Mock private ConnectivityManager mConnectivityManager; @Mock private NetworkPolicyManager mNetworkPolicyManager; @Mock private BluetoothPan mBluetoothPan; @Mock private SharedPreferences mSharedPreferences; private SwitchBar mSwitchBar; private TetherEnabler mEnabler; @Before public void setUp() { MockitoAnnotations.initMocks(this); Context context = spy(ApplicationProvider.getApplicationContext()); AtomicReference<BluetoothPan> panReference = spy(AtomicReference.class); mSwitchBar = new SwitchBar(context); when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( mConnectivityManager); when(context.getSystemService(Context.NETWORK_POLICY_SERVICE)).thenReturn( mNetworkPolicyManager); when(mConnectivityManager.getTetherableIfaces()).thenReturn(new String[0]); panReference.set(mBluetoothPan); mEnabler = new TetherEnabler(context, new SwitchBarController(mSwitchBar), panReference); } @Test public void lifecycle_onStart_setCheckedCorrectly() { when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{""}); mEnabler.onStart(); assertThat(mSwitchBar.isChecked()).isTrue(); } @Test public void startTether_fail_resetSwitchBar() { when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false); mEnabler.startTether(); mEnabler.mOnStartTetheringCallback.onTetheringFailed(); assertThat(mSwitchBar.isChecked()).isFalse(); assertThat(mSwitchBar.isEnabled()).isTrue(); } @Test public void onDataSaverChanged_setsEnabledCorrectly() { assertThat(mSwitchBar.isEnabled()).isTrue(); // try to turn data saver on when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(true); mEnabler.onDataSaverChanged(true); assertThat(mSwitchBar.isEnabled()).isFalse(); // lets turn data saver off again when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false); mEnabler.onDataSaverChanged(false); assertThat(mSwitchBar.isEnabled()).isTrue(); } @Test public void onSwitchToggled_onlyStartsWifiTetherWhenNeeded() { 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()); } @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); mEnabler.startTether(); verify(mConnectivityManager, times(1)) .startTethering(eq(ConnectivityManager.TETHERING_USB), anyBoolean(), any(), any()); verify(mConnectivityManager, never()) .startTethering(eq(ConnectivityManager.TETHERING_WIFI), anyBoolean(), any(), any()); verify(mConnectivityManager, never()).startTethering( eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any()); } @Test public void startTether_startsBluetoothTetherWhenOff() { BluetoothAdapter adapter = mock(BluetoothAdapter.class); ReflectionHelpers.setField(mEnabler, "mBluetoothAdapter", adapter); when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_OFF); mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH); verify(adapter, times(1)).enable(); when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_ON); mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH); verify(mConnectivityManager, times(1)).startTethering( eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any()); } } Loading
src/com/android/settings/network/TetherEnabler.java 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * 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 android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; 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.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; 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; import com.android.settings.widget.SwitchWidgetController; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; /** * TetherEnabler is a helper to manage Tethering switch on/off state. It turns on/off * different types of tethering based on stored values in {@link SharedPreferences} and ensures * tethering state updated by data saver state. */ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener, DataSaverBackend.Listener, LifecycleObserver { @VisibleForTesting static final String WIFI_TETHER_KEY = "enable_wifi_tethering"; @VisibleForTesting static final String USB_TETHER_KEY = "enable_usb_tethering"; @VisibleForTesting static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering"; private final SwitchWidgetController mSwitchWidgetController; private final WifiManager mWifiManager; private final ConnectivityManager mConnectivityManager; private final DataSaverBackend mDataSaverBackend; private boolean mDataSaverEnabled; private final Context mContext; @VisibleForTesting final ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback = new ConnectivityManager.OnStartTetheringCallback() { @Override public void onTetheringFailed() { super.onTetheringFailed(); mSwitchWidgetController.setChecked(false); setSwitchWidgetEnabled(true); } }; private final AtomicReference<BluetoothPan> mBluetoothPan; private final SharedPreferences mSharedPreferences; private boolean mBluetoothEnableForTether; private final BluetoothAdapter mBluetoothAdapter; TetherEnabler(Context context, SwitchWidgetController switchWidgetController, AtomicReference<BluetoothPan> bluetoothPan) { mContext = context; mSwitchWidgetController = switchWidgetController; mDataSaverBackend = new DataSaverBackend(context); mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothPan = bluetoothPan; mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { mDataSaverBackend.addListener(this); mSwitchWidgetController.setListener(this); mSwitchWidgetController.startListening(); IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mTetherChangeReceiver, filter); mSwitchWidgetController.setChecked(isTethering()); setSwitchWidgetEnabled(true); } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { mDataSaverBackend.remListener(this); mSwitchWidgetController.stopListening(); mContext.unregisterReceiver(mTetherChangeReceiver); } private void setSwitchWidgetEnabled(boolean enabled) { mSwitchWidgetController.setEnabled(enabled && !mDataSaverEnabled); } private boolean isTethering() { String[] tethered = mConnectivityManager.getTetheredIfaces(); return isTethering(tethered); } private boolean isTethering(String[] tethered) { if (tethered != null && tethered.length != 0) { return true; } final BluetoothPan pan = mBluetoothPan.get(); return pan != null && pan.isTetheringOn(); } @Override public boolean onSwitchToggled(boolean isChecked) { if (isChecked) { startTether(); } else { stopTether(); } return true; } @VisibleForTesting void stopTether() { setSwitchWidgetEnabled(false); // 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); } } @VisibleForTesting void startTether() { setSwitchWidgetEnabled(false); // 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); } } @VisibleForTesting void startTethering(int choice) { if (choice == TETHERING_BLUETOOTH) { // Turn on Bluetooth first. if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) { mBluetoothEnableForTether = true; mBluetoothAdapter.enable(); return; } } else if (choice == TETHERING_WIFI && mWifiManager.isWifiApEnabled()) { return; } mConnectivityManager.startTethering(choice, true /* showProvisioningUi */, mOnStartTetheringCallback, new Handler(Looper.getMainLooper())); } private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (TextUtils.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, action)) { ArrayList<String> active = intent.getStringArrayListExtra( 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: startTethering(TETHERING_BLUETOOTH); mBluetoothEnableForTether = false; break; case BluetoothAdapter.STATE_OFF: case BluetoothAdapter.ERROR: mBluetoothEnableForTether = false; break; default: // ignore transition states } } } } }; @Override public void onDataSaverChanged(boolean isDataSaving) { mDataSaverEnabled = isDataSaving; setSwitchWidgetEnabled(!isDataSaving); } @Override public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) { // we don't care, since we just want to read the value } @Override public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) { // we don't care, since we just want to read the value } }
tests/robotests/src/com/android/settings/network/TetherEnablerTest.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * 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.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; 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; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.content.Context; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkPolicyManager; import android.net.wifi.WifiManager; import androidx.test.core.app.ApplicationProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; 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; import java.util.concurrent.atomic.AtomicReference; @RunWith(RobolectricTestRunner.class) public class TetherEnablerTest { @Mock private WifiManager mWifiManager; @Mock private ConnectivityManager mConnectivityManager; @Mock private NetworkPolicyManager mNetworkPolicyManager; @Mock private BluetoothPan mBluetoothPan; @Mock private SharedPreferences mSharedPreferences; private SwitchBar mSwitchBar; private TetherEnabler mEnabler; @Before public void setUp() { MockitoAnnotations.initMocks(this); Context context = spy(ApplicationProvider.getApplicationContext()); AtomicReference<BluetoothPan> panReference = spy(AtomicReference.class); mSwitchBar = new SwitchBar(context); when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn( mConnectivityManager); when(context.getSystemService(Context.NETWORK_POLICY_SERVICE)).thenReturn( mNetworkPolicyManager); when(mConnectivityManager.getTetherableIfaces()).thenReturn(new String[0]); panReference.set(mBluetoothPan); mEnabler = new TetherEnabler(context, new SwitchBarController(mSwitchBar), panReference); } @Test public void lifecycle_onStart_setCheckedCorrectly() { when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{""}); mEnabler.onStart(); assertThat(mSwitchBar.isChecked()).isTrue(); } @Test public void startTether_fail_resetSwitchBar() { when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false); mEnabler.startTether(); mEnabler.mOnStartTetheringCallback.onTetheringFailed(); assertThat(mSwitchBar.isChecked()).isFalse(); assertThat(mSwitchBar.isEnabled()).isTrue(); } @Test public void onDataSaverChanged_setsEnabledCorrectly() { assertThat(mSwitchBar.isEnabled()).isTrue(); // try to turn data saver on when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(true); mEnabler.onDataSaverChanged(true); assertThat(mSwitchBar.isEnabled()).isFalse(); // lets turn data saver off again when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false); mEnabler.onDataSaverChanged(false); assertThat(mSwitchBar.isEnabled()).isTrue(); } @Test public void onSwitchToggled_onlyStartsWifiTetherWhenNeeded() { 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()); } @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); mEnabler.startTether(); verify(mConnectivityManager, times(1)) .startTethering(eq(ConnectivityManager.TETHERING_USB), anyBoolean(), any(), any()); verify(mConnectivityManager, never()) .startTethering(eq(ConnectivityManager.TETHERING_WIFI), anyBoolean(), any(), any()); verify(mConnectivityManager, never()).startTethering( eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any()); } @Test public void startTether_startsBluetoothTetherWhenOff() { BluetoothAdapter adapter = mock(BluetoothAdapter.class); ReflectionHelpers.setField(mEnabler, "mBluetoothAdapter", adapter); when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_OFF); mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH); verify(adapter, times(1)).enable(); when(adapter.getState()).thenReturn(BluetoothAdapter.STATE_ON); mEnabler.startTethering(ConnectivityManager.TETHERING_BLUETOOTH); verify(mConnectivityManager, times(1)).startTethering( eq(ConnectivityManager.TETHERING_BLUETOOTH), anyBoolean(), any(), any()); } }