Loading res/xml/tether_prefs.xml +2 −2 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ android:key="tether_prefs_top_intro" settings:searchable="false"/> <com.android.settings.widget.FixedLineSummaryPreference <com.android.settingslib.PrimarySwitchPreference android:key="wifi_tether" android:title="@string/wifi_hotspot_checkbox_text" android:summary="@string/summary_placeholder" android:summary="@string/wifi_hotspot_off_subtext" android:fragment="com.android.settings.wifi.tether.WifiTetherSettings" settings:allowDividerAbove="true" settings:maxLines="2"/> Loading src/com/android/settings/network/tether/TetherSettings.java +14 −10 Original line number Diff line number Diff line Loading @@ -96,11 +96,12 @@ public class TetherSettings extends RestrictedSettingsFragment private static final String TAG = "TetheringSettings"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private RestrictedSwitchPreference mUsbTether; private SwitchPreference mBluetoothTether; private SwitchPreference mEthernetTether; @VisibleForTesting RestrictedSwitchPreference mUsbTether; @VisibleForTesting SwitchPreference mBluetoothTether; @VisibleForTesting SwitchPreference mEthernetTether; private BroadcastReceiver mTetherChangeReceiver; private BroadcastReceiver mBluetoothStateReceiver; Loading @@ -115,7 +116,8 @@ public class TetherSettings extends RestrictedSettingsFragment private EthernetListener mEthernetListener; private final HashSet<String> mAvailableInterfaces = new HashSet<>(); private WifiTetherPreferenceController mWifiTetherPreferenceController; @VisibleForTesting WifiTetherPreferenceController mWifiTetherPreferenceController; private boolean mUsbConnected; private boolean mMassStorageActive; Loading @@ -125,7 +127,8 @@ public class TetherSettings extends RestrictedSettingsFragment private DataSaverBackend mDataSaverBackend; private boolean mDataSaverEnabled; private Preference mDataSaverFooter; @VisibleForTesting Preference mDataSaverFooter; @VisibleForTesting String[] mUsbRegexs; Loading @@ -146,10 +149,10 @@ public class TetherSettings extends RestrictedSettingsFragment @Override public void onAttach(Context context) { super.onAttach(context); mWifiTetherPreferenceController = new WifiTetherPreferenceController(context, getSettingsLifecycle()); TetheringManagerModel model = new ViewModelProvider(this).get(TetheringManagerModel.class); mTm = model.mTetheringManager; mWifiTetherPreferenceController = new WifiTetherPreferenceController(context, getSettingsLifecycle(), model); mTm = model.getTetheringManager(); model.getTetheredInterfaces().observe(this, this::onTetheredInterfacesChanged); } Loading Loading @@ -248,6 +251,7 @@ public class TetherSettings extends RestrictedSettingsFragment @Override public void onDataSaverChanged(boolean isDataSaving) { mDataSaverEnabled = isDataSaving; mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled); mUsbTether.setEnabled(!mDataSaverEnabled); mBluetoothTether.setEnabled(!mDataSaverEnabled); mEthernetTether.setEnabled(!mDataSaverEnabled); Loading src/com/android/settings/network/tether/TetheringManagerModel.java +34 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ public class TetheringManagerModel extends AndroidViewModel { protected TetheringManager mTetheringManager; protected EventCallback mEventCallback = new EventCallback(); protected MutableLiveData<List<String>> mTetheredInterfaces = new MutableLiveData<>(); protected StartTetheringCallback mStartTetheringCallback = new StartTetheringCallback(); public TetheringManagerModel(@NonNull Application application) { super(application); Loading Loading @@ -62,6 +63,27 @@ public class TetheringManagerModel extends AndroidViewModel { return Transformations.distinctUntilChanged(mTetheredInterfaces); } /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. * * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. */ public void startTethering(int type) { mTetheringManager.startTethering(type, getApplication().getMainExecutor(), mStartTetheringCallback); } /** * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if * applicable. * * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. */ public void stopTethering(int type) { mTetheringManager.stopTethering(type); } /** * Callback for use with {@link TetheringManager#registerTetheringEventCallback} to find out * tethering upstream status. Loading @@ -72,4 +94,16 @@ public class TetheringManagerModel extends AndroidViewModel { mTetheredInterfaces.setValue(interfaces); } } private class StartTetheringCallback implements TetheringManager.StartTetheringCallback { @Override public void onTetheringStarted() { // Do nothing } @Override public void onTetheringFailed(int error) { // Do nothing } } } src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +74 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.wifi.tether; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; import static com.android.settings.wifi.WifiUtils.canShowWifiHotspot; import android.annotation.NonNull; Loading @@ -26,12 +29,15 @@ import android.net.wifi.WifiManager; import android.text.BidiFormatter; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.network.tether.TetheringManagerModel; import com.android.settings.widget.GenericSwitchController; import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; Loading @@ -43,7 +49,8 @@ import com.android.settingslib.wifi.WifiUtils; import java.util.List; public class WifiTetherPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, SwitchWidgetController.OnSwitchChangeListener { private static final String WIFI_TETHER_SETTINGS = "wifi_tether"; Loading @@ -51,16 +58,24 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController private boolean mIsWifiTetheringAllow; private int mSoftApState; @VisibleForTesting Preference mPreference; PrimarySwitchPreference mPreference; @VisibleForTesting WifiTetherSoftApManager mWifiTetherSoftApManager; @VisibleForTesting TetheringManagerModel mTetheringManagerModel; @VisibleForTesting boolean mIsDataSaverEnabled; @VisibleForTesting SwitchWidgetController mSwitch; public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { public WifiTetherPreferenceController(Context context, Lifecycle lifecycle, TetheringManagerModel tetheringManagerModel) { // TODO(b/246537032):Use fragment context to WifiManager service will caused memory leak this(context, lifecycle, context.getApplicationContext().getSystemService(WifiManager.class), true /* initSoftApManager */, WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context)); WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context), tetheringManagerModel); } @VisibleForTesting Loading @@ -69,11 +84,13 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController Lifecycle lifecycle, WifiManager wifiManager, boolean initSoftApManager, boolean isWifiTetheringAllow) { boolean isWifiTetheringAllow, TetheringManagerModel tetheringManagerModel) { super(context); mIsWifiTetheringAllow = isWifiTetheringAllow; if (!isWifiTetheringAllow) return; mTetheringManagerModel = tetheringManagerModel; mWifiManager = wifiManager; if (lifecycle != null) { Loading @@ -97,8 +114,13 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController // unavailable return; } if (!mIsWifiTetheringAllow && mPreference.isEnabled()) { mPreference.setEnabled(false); if (mSwitch == null) { mSwitch = new GenericSwitchController(mPreference); mSwitch.setListener(this); updateSwitch(); } mPreference.setEnabled(canEnabled()); if (!mIsWifiTetheringAllow) { mPreference.setSummary(R.string.not_allowed_by_ent); } } Loading @@ -114,6 +136,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (mWifiTetherSoftApManager != null) { mWifiTetherSoftApManager.registerSoftApCallback(); } if (mSwitch != null) { mSwitch.startListening(); } } } Loading @@ -123,6 +148,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (mWifiTetherSoftApManager != null) { mWifiTetherSoftApManager.unRegisterSoftApCallback(); } if (mSwitch != null) { mSwitch.stopListening(); } } } Loading Loading @@ -158,6 +186,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(R.string.wifi_tether_starting); break; case WifiManager.WIFI_AP_STATE_ENABLED: mSwitch.setChecked(true); final SoftApConfiguration softApConfig = mWifiManager.getSoftApConfiguration(); updateConfigSummary(softApConfig); break; Loading @@ -165,6 +194,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(R.string.wifi_tether_stopping); break; case WifiManager.WIFI_AP_STATE_DISABLED: mSwitch.setChecked(false); mPreference.setSummary(R.string.wifi_hotspot_off_subtext); break; default: Loading @@ -184,4 +214,40 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext, BidiFormatter.getInstance().unicodeWrap(softApConfig.getSsid()))); } /** * Sets the Data Saver state for preference update. */ public void setDataSaverEnabled(boolean enabled) { mIsDataSaverEnabled = enabled; if (mPreference != null) { mPreference.setEnabled(canEnabled()); } if (mSwitch != null) { mSwitch.setEnabled(canEnabled()); } } private boolean canEnabled() { return mIsWifiTetheringAllow && !mIsDataSaverEnabled; } @VisibleForTesting protected void updateSwitch() { if (mWifiManager == null) return; int wifiApState = mWifiManager.getWifiApState(); mSwitch.setEnabled(canEnabled()); mSwitch.setChecked(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED); handleWifiApStateChanged(wifiApState, SAP_START_FAILURE_GENERAL); } @Override public boolean onSwitchToggled(boolean isChecked) { if (isChecked) { mTetheringManagerModel.startTethering(TETHERING_WIFI); } else { mTetheringManagerModel.stopTethering(TETHERING_WIFI); } return true; } } tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java +70 −47 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.FeatureFlags; import com.android.settings.wifi.tether.WifiTetherPreferenceController; import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; Loading @@ -80,6 +81,18 @@ public class TetherSettingsTest { private UserManager mUserManager; @Mock private TetheringManager mTetheringManager; @Mock private WifiTetherPreferenceController mWifiTetherPreferenceController; @Mock private RestrictedSwitchPreference mUsbTether; @Mock private SwitchPreference mBluetoothTether; @Mock private SwitchPreference mEthernetTether; @Mock private Preference mDataSaverFooter; TetherSettings mTetherSettings; @Before public void setUp() throws Exception { Loading @@ -100,6 +113,14 @@ public class TetherSettingsTest { when(mTetheringManager.getTetherableUsbRegexs()).thenReturn(new String[0]); when(mTetheringManager.getTetherableBluetoothRegexs()).thenReturn(new String[0]); mTetherSettings = spy(new TetherSettings()); mTetherSettings.mContext = mContext; mTetherSettings.mWifiTetherPreferenceController = mWifiTetherPreferenceController; mTetherSettings.mUsbTether = mUsbTether; mTetherSettings.mBluetoothTether = mBluetoothTether; mTetherSettings.mEthernetTether = mEthernetTether; mTetherSettings.mDataSaverFooter = mDataSaverFooter; } @Test Loading Loading @@ -184,16 +205,14 @@ public class TetherSettingsTest { @Test public void testSetFooterPreferenceTitle_isStaApConcurrencySupported_showStaApString() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; final Preference mockPreference = mock(Preference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_TETHER_PREFS_TOP_INTRO)) when(mTetherSettings.findPreference(TetherSettings.KEY_TETHER_PREFS_TOP_INTRO)) .thenReturn(mockPreference); final WifiManager mockWifiManager = mock(WifiManager.class); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mockWifiManager); when(mockWifiManager.isStaApConcurrencySupported()).thenReturn(true); spyTetherSettings.setTopIntroPreferenceTitle(); mTetherSettings.setTopIntroPreferenceTitle(); verify(mockPreference, never()).setTitle(R.string.tethering_footer_info); verify(mockPreference).setTitle(R.string.tethering_footer_info_sta_ap_concurrency); Loading @@ -201,25 +220,23 @@ public class TetherSettingsTest { @Test public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOn() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; mTetherSettings.mTm = mTetheringManager; final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); final FragmentActivity mockActivity = mock(FragmentActivity.class); when(spyTetherSettings.getActivity()).thenReturn(mockActivity); when(mTetherSettings.getActivity()).thenReturn(mockActivity); final ArgumentCaptor<BroadcastReceiver> 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); when(mTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); when(mTetherSettings.isBluetoothTetheringOn()).thenReturn(true); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); // Simulate Bluetooth tethering state changed final BroadcastReceiver receiver = captor.getValue(); Loading @@ -234,25 +251,23 @@ public class TetherSettingsTest { @Test public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOff() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; mTetherSettings.mTm = mTetheringManager; final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); final FragmentActivity mockActivity = mock(FragmentActivity.class); when(spyTetherSettings.getActivity()).thenReturn(mockActivity); when(mTetherSettings.getActivity()).thenReturn(mockActivity); final ArgumentCaptor<BroadcastReceiver> 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); when(mTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); when(mTetherSettings.isBluetoothTetheringOn()).thenReturn(false); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); // Simulate Bluetooth tethering state changed final BroadcastReceiver receiver = captor.getValue(); Loading @@ -268,16 +283,14 @@ public class TetherSettingsTest { @Test public void updateState_usbTetheringIsEnabled_checksUsbTethering() { String [] tethered = {"rndis0"}; TetherSettings spyTetherSettings = spy(new TetherSettings()); RestrictedSwitchPreference tetheringPreference = mock(RestrictedSwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(tetheringPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; spyTetherSettings.setupTetherPreference(); spyTetherSettings.mUsbRegexs = tethered; mTetherSettings.mTm = mTetheringManager; mTetherSettings.setupTetherPreference(); mTetherSettings.mUsbRegexs = tethered; spyTetherSettings.updateUsbState(tethered); mTetherSettings.updateUsbState(tethered); verify(tetheringPreference).setEnabled(true); verify(tetheringPreference).setChecked(true); Loading @@ -286,16 +299,14 @@ public class TetherSettingsTest { @Test public void updateState_usbTetheringIsDisabled_unchecksUsbTethering() { String [] tethered = {"rndis0"}; TetherSettings spyTetherSettings = spy(new TetherSettings()); RestrictedSwitchPreference tetheringPreference = mock(RestrictedSwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(tetheringPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; spyTetherSettings.setupTetherPreference(); spyTetherSettings.mUsbRegexs = tethered; mTetherSettings.mTm = mTetheringManager; mTetherSettings.setupTetherPreference(); mTetherSettings.mUsbRegexs = tethered; spyTetherSettings.updateUsbState(new String[0]); mTetherSettings.updateUsbState(new String[0]); verify(tetheringPreference).setEnabled(false); verify(tetheringPreference).setChecked(false); Loading Loading @@ -362,6 +373,20 @@ public class TetherSettingsTest { verify(tetheringPreference, times(2)).setEnabled(true); } @Test public void onDataSaverChanged_dataSaverEnabled_setToController() { mTetherSettings.onDataSaverChanged(true); verify(mWifiTetherPreferenceController).setDataSaverEnabled(true); } @Test public void onDataSaverChanged_dataSaverDisabled_setToController() { mTetherSettings.onDataSaverChanged(false); verify(mWifiTetherPreferenceController).setDataSaverEnabled(false); } private void updateOnlyBluetoothState(TetherSettings tetherSettings) { doReturn(mTetheringManager).when(mContext) .getSystemService(Context.TETHERING_SERVICE); Loading Loading @@ -391,21 +416,19 @@ public class TetherSettingsTest { private void setupUsbStateComponents(RestrictedSwitchPreference preference, ArgumentCaptor<BroadcastReceiver> captor, FragmentActivity activity) { TetherSettings spyTetherSettings = spy(new TetherSettings()); SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(preference); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; when(spyTetherSettings.getActivity()).thenReturn(activity); mTetherSettings.mTm = mTetheringManager; when(mTetherSettings.getActivity()).thenReturn(activity); when(activity.registerReceiver(captor.capture(), any(IntentFilter.class))) .thenReturn(null); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); } } Loading
res/xml/tether_prefs.xml +2 −2 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ android:key="tether_prefs_top_intro" settings:searchable="false"/> <com.android.settings.widget.FixedLineSummaryPreference <com.android.settingslib.PrimarySwitchPreference android:key="wifi_tether" android:title="@string/wifi_hotspot_checkbox_text" android:summary="@string/summary_placeholder" android:summary="@string/wifi_hotspot_off_subtext" android:fragment="com.android.settings.wifi.tether.WifiTetherSettings" settings:allowDividerAbove="true" settings:maxLines="2"/> Loading
src/com/android/settings/network/tether/TetherSettings.java +14 −10 Original line number Diff line number Diff line Loading @@ -96,11 +96,12 @@ public class TetherSettings extends RestrictedSettingsFragment private static final String TAG = "TetheringSettings"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private RestrictedSwitchPreference mUsbTether; private SwitchPreference mBluetoothTether; private SwitchPreference mEthernetTether; @VisibleForTesting RestrictedSwitchPreference mUsbTether; @VisibleForTesting SwitchPreference mBluetoothTether; @VisibleForTesting SwitchPreference mEthernetTether; private BroadcastReceiver mTetherChangeReceiver; private BroadcastReceiver mBluetoothStateReceiver; Loading @@ -115,7 +116,8 @@ public class TetherSettings extends RestrictedSettingsFragment private EthernetListener mEthernetListener; private final HashSet<String> mAvailableInterfaces = new HashSet<>(); private WifiTetherPreferenceController mWifiTetherPreferenceController; @VisibleForTesting WifiTetherPreferenceController mWifiTetherPreferenceController; private boolean mUsbConnected; private boolean mMassStorageActive; Loading @@ -125,7 +127,8 @@ public class TetherSettings extends RestrictedSettingsFragment private DataSaverBackend mDataSaverBackend; private boolean mDataSaverEnabled; private Preference mDataSaverFooter; @VisibleForTesting Preference mDataSaverFooter; @VisibleForTesting String[] mUsbRegexs; Loading @@ -146,10 +149,10 @@ public class TetherSettings extends RestrictedSettingsFragment @Override public void onAttach(Context context) { super.onAttach(context); mWifiTetherPreferenceController = new WifiTetherPreferenceController(context, getSettingsLifecycle()); TetheringManagerModel model = new ViewModelProvider(this).get(TetheringManagerModel.class); mTm = model.mTetheringManager; mWifiTetherPreferenceController = new WifiTetherPreferenceController(context, getSettingsLifecycle(), model); mTm = model.getTetheringManager(); model.getTetheredInterfaces().observe(this, this::onTetheredInterfacesChanged); } Loading Loading @@ -248,6 +251,7 @@ public class TetherSettings extends RestrictedSettingsFragment @Override public void onDataSaverChanged(boolean isDataSaving) { mDataSaverEnabled = isDataSaving; mWifiTetherPreferenceController.setDataSaverEnabled(mDataSaverEnabled); mUsbTether.setEnabled(!mDataSaverEnabled); mBluetoothTether.setEnabled(!mDataSaverEnabled); mEthernetTether.setEnabled(!mDataSaverEnabled); Loading
src/com/android/settings/network/tether/TetheringManagerModel.java +34 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ public class TetheringManagerModel extends AndroidViewModel { protected TetheringManager mTetheringManager; protected EventCallback mEventCallback = new EventCallback(); protected MutableLiveData<List<String>> mTetheredInterfaces = new MutableLiveData<>(); protected StartTetheringCallback mStartTetheringCallback = new StartTetheringCallback(); public TetheringManagerModel(@NonNull Application application) { super(application); Loading Loading @@ -62,6 +63,27 @@ public class TetheringManagerModel extends AndroidViewModel { return Transformations.distinctUntilChanged(mTetheredInterfaces); } /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. * * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. */ public void startTethering(int type) { mTetheringManager.startTethering(type, getApplication().getMainExecutor(), mStartTetheringCallback); } /** * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if * applicable. * * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. */ public void stopTethering(int type) { mTetheringManager.stopTethering(type); } /** * Callback for use with {@link TetheringManager#registerTetheringEventCallback} to find out * tethering upstream status. Loading @@ -72,4 +94,16 @@ public class TetheringManagerModel extends AndroidViewModel { mTetheredInterfaces.setValue(interfaces); } } private class StartTetheringCallback implements TetheringManager.StartTetheringCallback { @Override public void onTetheringStarted() { // Do nothing } @Override public void onTetheringFailed(int error) { // Do nothing } } }
src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +74 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.settings.wifi.tether; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; import static com.android.settings.wifi.WifiUtils.canShowWifiHotspot; import android.annotation.NonNull; Loading @@ -26,12 +29,15 @@ import android.net.wifi.WifiManager; import android.text.BidiFormatter; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.network.tether.TetheringManagerModel; import com.android.settings.widget.GenericSwitchController; import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; Loading @@ -43,7 +49,8 @@ import com.android.settingslib.wifi.WifiUtils; import java.util.List; public class WifiTetherPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, SwitchWidgetController.OnSwitchChangeListener { private static final String WIFI_TETHER_SETTINGS = "wifi_tether"; Loading @@ -51,16 +58,24 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController private boolean mIsWifiTetheringAllow; private int mSoftApState; @VisibleForTesting Preference mPreference; PrimarySwitchPreference mPreference; @VisibleForTesting WifiTetherSoftApManager mWifiTetherSoftApManager; @VisibleForTesting TetheringManagerModel mTetheringManagerModel; @VisibleForTesting boolean mIsDataSaverEnabled; @VisibleForTesting SwitchWidgetController mSwitch; public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { public WifiTetherPreferenceController(Context context, Lifecycle lifecycle, TetheringManagerModel tetheringManagerModel) { // TODO(b/246537032):Use fragment context to WifiManager service will caused memory leak this(context, lifecycle, context.getApplicationContext().getSystemService(WifiManager.class), true /* initSoftApManager */, WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context)); WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context), tetheringManagerModel); } @VisibleForTesting Loading @@ -69,11 +84,13 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController Lifecycle lifecycle, WifiManager wifiManager, boolean initSoftApManager, boolean isWifiTetheringAllow) { boolean isWifiTetheringAllow, TetheringManagerModel tetheringManagerModel) { super(context); mIsWifiTetheringAllow = isWifiTetheringAllow; if (!isWifiTetheringAllow) return; mTetheringManagerModel = tetheringManagerModel; mWifiManager = wifiManager; if (lifecycle != null) { Loading @@ -97,8 +114,13 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController // unavailable return; } if (!mIsWifiTetheringAllow && mPreference.isEnabled()) { mPreference.setEnabled(false); if (mSwitch == null) { mSwitch = new GenericSwitchController(mPreference); mSwitch.setListener(this); updateSwitch(); } mPreference.setEnabled(canEnabled()); if (!mIsWifiTetheringAllow) { mPreference.setSummary(R.string.not_allowed_by_ent); } } Loading @@ -114,6 +136,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (mWifiTetherSoftApManager != null) { mWifiTetherSoftApManager.registerSoftApCallback(); } if (mSwitch != null) { mSwitch.startListening(); } } } Loading @@ -123,6 +148,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController if (mWifiTetherSoftApManager != null) { mWifiTetherSoftApManager.unRegisterSoftApCallback(); } if (mSwitch != null) { mSwitch.stopListening(); } } } Loading Loading @@ -158,6 +186,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(R.string.wifi_tether_starting); break; case WifiManager.WIFI_AP_STATE_ENABLED: mSwitch.setChecked(true); final SoftApConfiguration softApConfig = mWifiManager.getSoftApConfiguration(); updateConfigSummary(softApConfig); break; Loading @@ -165,6 +194,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(R.string.wifi_tether_stopping); break; case WifiManager.WIFI_AP_STATE_DISABLED: mSwitch.setChecked(false); mPreference.setSummary(R.string.wifi_hotspot_off_subtext); break; default: Loading @@ -184,4 +214,40 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext, BidiFormatter.getInstance().unicodeWrap(softApConfig.getSsid()))); } /** * Sets the Data Saver state for preference update. */ public void setDataSaverEnabled(boolean enabled) { mIsDataSaverEnabled = enabled; if (mPreference != null) { mPreference.setEnabled(canEnabled()); } if (mSwitch != null) { mSwitch.setEnabled(canEnabled()); } } private boolean canEnabled() { return mIsWifiTetheringAllow && !mIsDataSaverEnabled; } @VisibleForTesting protected void updateSwitch() { if (mWifiManager == null) return; int wifiApState = mWifiManager.getWifiApState(); mSwitch.setEnabled(canEnabled()); mSwitch.setChecked(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED); handleWifiApStateChanged(wifiApState, SAP_START_FAILURE_GENERAL); } @Override public boolean onSwitchToggled(boolean isChecked) { if (isChecked) { mTetheringManagerModel.startTethering(TETHERING_WIFI); } else { mTetheringManagerModel.stopTethering(TETHERING_WIFI); } return true; } }
tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java +70 −47 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.FeatureFlags; import com.android.settings.wifi.tether.WifiTetherPreferenceController; import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; Loading @@ -80,6 +81,18 @@ public class TetherSettingsTest { private UserManager mUserManager; @Mock private TetheringManager mTetheringManager; @Mock private WifiTetherPreferenceController mWifiTetherPreferenceController; @Mock private RestrictedSwitchPreference mUsbTether; @Mock private SwitchPreference mBluetoothTether; @Mock private SwitchPreference mEthernetTether; @Mock private Preference mDataSaverFooter; TetherSettings mTetherSettings; @Before public void setUp() throws Exception { Loading @@ -100,6 +113,14 @@ public class TetherSettingsTest { when(mTetheringManager.getTetherableUsbRegexs()).thenReturn(new String[0]); when(mTetheringManager.getTetherableBluetoothRegexs()).thenReturn(new String[0]); mTetherSettings = spy(new TetherSettings()); mTetherSettings.mContext = mContext; mTetherSettings.mWifiTetherPreferenceController = mWifiTetherPreferenceController; mTetherSettings.mUsbTether = mUsbTether; mTetherSettings.mBluetoothTether = mBluetoothTether; mTetherSettings.mEthernetTether = mEthernetTether; mTetherSettings.mDataSaverFooter = mDataSaverFooter; } @Test Loading Loading @@ -184,16 +205,14 @@ public class TetherSettingsTest { @Test public void testSetFooterPreferenceTitle_isStaApConcurrencySupported_showStaApString() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; final Preference mockPreference = mock(Preference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_TETHER_PREFS_TOP_INTRO)) when(mTetherSettings.findPreference(TetherSettings.KEY_TETHER_PREFS_TOP_INTRO)) .thenReturn(mockPreference); final WifiManager mockWifiManager = mock(WifiManager.class); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mockWifiManager); when(mockWifiManager.isStaApConcurrencySupported()).thenReturn(true); spyTetherSettings.setTopIntroPreferenceTitle(); mTetherSettings.setTopIntroPreferenceTitle(); verify(mockPreference, never()).setTitle(R.string.tethering_footer_info); verify(mockPreference).setTitle(R.string.tethering_footer_info_sta_ap_concurrency); Loading @@ -201,25 +220,23 @@ public class TetherSettingsTest { @Test public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOn() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; mTetherSettings.mTm = mTetheringManager; final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); final FragmentActivity mockActivity = mock(FragmentActivity.class); when(spyTetherSettings.getActivity()).thenReturn(mockActivity); when(mTetherSettings.getActivity()).thenReturn(mockActivity); final ArgumentCaptor<BroadcastReceiver> 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); when(mTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); when(mTetherSettings.isBluetoothTetheringOn()).thenReturn(true); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); // Simulate Bluetooth tethering state changed final BroadcastReceiver receiver = captor.getValue(); Loading @@ -234,25 +251,23 @@ public class TetherSettingsTest { @Test public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOff() { final TetherSettings spyTetherSettings = spy(new TetherSettings()); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; mTetherSettings.mTm = mTetheringManager; final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); final FragmentActivity mockActivity = mock(FragmentActivity.class); when(spyTetherSettings.getActivity()).thenReturn(mockActivity); when(mTetherSettings.getActivity()).thenReturn(mockActivity); final ArgumentCaptor<BroadcastReceiver> 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); when(mTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); when(mTetherSettings.isBluetoothTetheringOn()).thenReturn(false); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); // Simulate Bluetooth tethering state changed final BroadcastReceiver receiver = captor.getValue(); Loading @@ -268,16 +283,14 @@ public class TetherSettingsTest { @Test public void updateState_usbTetheringIsEnabled_checksUsbTethering() { String [] tethered = {"rndis0"}; TetherSettings spyTetherSettings = spy(new TetherSettings()); RestrictedSwitchPreference tetheringPreference = mock(RestrictedSwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(tetheringPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; spyTetherSettings.setupTetherPreference(); spyTetherSettings.mUsbRegexs = tethered; mTetherSettings.mTm = mTetheringManager; mTetherSettings.setupTetherPreference(); mTetherSettings.mUsbRegexs = tethered; spyTetherSettings.updateUsbState(tethered); mTetherSettings.updateUsbState(tethered); verify(tetheringPreference).setEnabled(true); verify(tetheringPreference).setChecked(true); Loading @@ -286,16 +299,14 @@ public class TetherSettingsTest { @Test public void updateState_usbTetheringIsDisabled_unchecksUsbTethering() { String [] tethered = {"rndis0"}; TetherSettings spyTetherSettings = spy(new TetherSettings()); RestrictedSwitchPreference tetheringPreference = mock(RestrictedSwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(tetheringPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; spyTetherSettings.setupTetherPreference(); spyTetherSettings.mUsbRegexs = tethered; mTetherSettings.mTm = mTetheringManager; mTetherSettings.setupTetherPreference(); mTetherSettings.mUsbRegexs = tethered; spyTetherSettings.updateUsbState(new String[0]); mTetherSettings.updateUsbState(new String[0]); verify(tetheringPreference).setEnabled(false); verify(tetheringPreference).setChecked(false); Loading Loading @@ -362,6 +373,20 @@ public class TetherSettingsTest { verify(tetheringPreference, times(2)).setEnabled(true); } @Test public void onDataSaverChanged_dataSaverEnabled_setToController() { mTetherSettings.onDataSaverChanged(true); verify(mWifiTetherPreferenceController).setDataSaverEnabled(true); } @Test public void onDataSaverChanged_dataSaverDisabled_setToController() { mTetherSettings.onDataSaverChanged(false); verify(mWifiTetherPreferenceController).setDataSaverEnabled(false); } private void updateOnlyBluetoothState(TetherSettings tetherSettings) { doReturn(mTetheringManager).when(mContext) .getSystemService(Context.TETHERING_SERVICE); Loading Loading @@ -391,21 +416,19 @@ public class TetherSettingsTest { private void setupUsbStateComponents(RestrictedSwitchPreference preference, ArgumentCaptor<BroadcastReceiver> captor, FragmentActivity activity) { TetherSettings spyTetherSettings = spy(new TetherSettings()); SwitchPreference mockSwitchPreference = mock(SwitchPreference.class); when(spyTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) when(mTetherSettings.findPreference(TetherSettings.KEY_USB_TETHER_SETTINGS)) .thenReturn(preference); when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) when(mTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING)) .thenReturn(mockSwitchPreference); spyTetherSettings.mContext = mContext; spyTetherSettings.mTm = mTetheringManager; when(spyTetherSettings.getActivity()).thenReturn(activity); mTetherSettings.mTm = mTetheringManager; when(mTetherSettings.getActivity()).thenReturn(activity); when(activity.registerReceiver(captor.capture(), any(IntentFilter.class))) .thenReturn(null); spyTetherSettings.setupTetherPreference(); spyTetherSettings.registerReceiver(); updateOnlyBluetoothState(spyTetherSettings); mTetherSettings.setupTetherPreference(); mTetherSettings.registerReceiver(); updateOnlyBluetoothState(mTetherSettings); } }