diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index e485d1e7c9c..ae2416824bd 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -34,6 +34,9 @@ import android.content.IntentFilter; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; +import android.net.EthernetManager.InterfaceState; +import android.net.EthernetManager.Role; +import android.net.IpConfiguration; import android.net.TetheringManager; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -42,10 +45,10 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.SearchIndexableResource; -import android.text.TextUtils; import android.util.FeatureFlagUtils; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.SwitchPreference; @@ -62,6 +65,7 @@ import com.android.settingslib.search.SearchIndexable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -97,7 +101,6 @@ public class TetherSettings extends RestrictedSettingsFragment private BroadcastReceiver mTetherChangeReceiver; private String[] mBluetoothRegexs; - private String mEthernetRegex; private AtomicReference mBluetoothPan = new AtomicReference<>(); private Handler mHandler = new Handler(); @@ -106,6 +109,7 @@ public class TetherSettings extends RestrictedSettingsFragment private EthernetManager mEm; private TetheringEventCallback mTetheringEventCallback; private EthernetListener mEthernetListener; + private final HashSet mAvailableInterfaces = new HashSet<>(); private WifiTetherPreferenceController mWifiTetherPreferenceController; @@ -172,17 +176,17 @@ public class TetherSettings extends RestrictedSettingsFragment mDataSaverBackend.addListener(this); mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - mEm = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE); mTm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE); + // Some devices do not have available EthernetManager. In that case getSystemService will + // return null. + mEm = mContext.getSystemService(EthernetManager.class); mUsbRegexs = mTm.getTetherableUsbRegexs(); mBluetoothRegexs = mTm.getTetherableBluetoothRegexs(); - mEthernetRegex = mContext.getResources().getString( - com.android.internal.R.string.config_ethernet_iface_regex); final boolean usbAvailable = mUsbRegexs.length != 0; final boolean bluetoothAvailable = adapter != null && mBluetoothRegexs.length != 0; - final boolean ethernetAvailable = !TextUtils.isEmpty(mEthernetRegex); + final boolean ethernetAvailable = (mEm != null); if (!usbAvailable || Utils.isMonkeyRunning()) { getPreferenceScreen().removePreference(mUsbTether); @@ -330,7 +334,7 @@ public class TetherSettings extends RestrictedSettingsFragment mEthernetListener = new EthernetListener(); if (mEm != null) - mEm.addListener(mEthernetListener, r -> mHandler.post(r)); + mEm.addInterfaceStateListener(r -> mHandler.post(r), mEthernetListener); updateUsbState(); updateBluetoothAndEthernetState(); @@ -346,11 +350,10 @@ public class TetherSettings extends RestrictedSettingsFragment getActivity().unregisterReceiver(mTetherChangeReceiver); mTm.unregisterTetheringEventCallback(mTetheringEventCallback); if (mEm != null) - mEm.removeListener(mEthernetListener); + mEm.removeInterfaceStateListener(mEthernetListener); mTetherChangeReceiver = null; mStartTetheringCallback = null; mTetheringEventCallback = null; - mEthernetListener = null; } @VisibleForTesting @@ -483,11 +486,11 @@ public class TetherSettings extends RestrictedSettingsFragment boolean isTethered = false; for (String s : available) { - if (s.matches(mEthernetRegex)) isAvailable = true; + if (mAvailableInterfaces.contains(s)) isAvailable = true; } for (String s : tethered) { - if (s.matches(mEthernetRegex)) isTethered = true; + if (mAvailableInterfaces.contains(s)) isTethered = true; } if (DEBUG) { @@ -498,7 +501,7 @@ public class TetherSettings extends RestrictedSettingsFragment if (isTethered) { mEthernetTether.setEnabled(!mDataSaverEnabled); mEthernetTether.setChecked(true); - } else if (isAvailable || (mEm != null && mEm.isAvailable())) { + } else if (mAvailableInterfaces.size() > 0) { mEthernetTether.setEnabled(!mDataSaverEnabled); mEthernetTether.setChecked(false); } else { @@ -600,9 +603,9 @@ public class TetherSettings extends RestrictedSettingsFragment keys.add(KEY_ENABLE_BLUETOOTH_TETHERING); } - final boolean ethernetAvailable = !TextUtils.isEmpty( - context.getResources().getString( - com.android.internal.R.string.config_ethernet_iface_regex)); + final EthernetManager em = + context.getSystemService(EthernetManager.class); + final boolean ethernetAvailable = (em != null); if (!ethernetAvailable) { keys.add(KEY_ENABLE_ETHERNET_TETHERING); } @@ -646,9 +649,15 @@ public class TetherSettings extends RestrictedSettingsFragment } } - private final class EthernetListener implements EthernetManager.Listener { - public void onAvailabilityChanged(String iface, boolean isAvailable) { - mHandler.post(() -> updateBluetoothAndEthernetState()); + private final class EthernetListener implements EthernetManager.InterfaceStateListener { + public void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state, + @Role int role, @NonNull IpConfiguration configuration) { + if (state == EthernetManager.STATE_LINK_UP) { + mAvailableInterfaces.add(iface); + } else { + mAvailableInterfaces.remove(iface); + } + updateBluetoothAndEthernetState(); } } } diff --git a/src/com/android/settings/network/EthernetTetherPreferenceController.java b/src/com/android/settings/network/EthernetTetherPreferenceController.java index 5b2cab752be..58c1fd2d328 100644 --- a/src/com/android/settings/network/EthernetTetherPreferenceController.java +++ b/src/com/android/settings/network/EthernetTetherPreferenceController.java @@ -21,52 +21,63 @@ import android.net.EthernetManager; import android.net.TetheringManager; import android.os.Handler; import android.os.Looper; -import android.text.TextUtils; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.annotations.VisibleForTesting; +import java.util.HashSet; + /** * This controller helps to manage the switch state and visibility of ethernet tether switch * preference. */ public final class EthernetTetherPreferenceController extends TetherBasePreferenceController { - private final String mEthernetRegex; + private final HashSet mAvailableInterfaces = new HashSet<>(); private final EthernetManager mEthernetManager; + @VisibleForTesting - EthernetManager.Listener mEthernetListener; + EthernetManager.InterfaceStateListener mEthernetListener; public EthernetTetherPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); - mEthernetRegex = context.getString( - com.android.internal.R.string.config_ethernet_iface_regex); - mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); + mEthernetManager = context.getSystemService(EthernetManager.class); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { - mEthernetListener = (iface, isAvailable) -> updateState(mPreference); + mEthernetListener = (iface, state, role, configuration) -> { + if (state == EthernetManager.STATE_LINK_UP) { + mAvailableInterfaces.add(iface); + } else { + mAvailableInterfaces.remove(iface); + } + updateState(mPreference); + }; final Handler handler = new Handler(Looper.getMainLooper()); // Executor will execute to post the updateState event to a new handler which is created // from the main looper when the {@link EthernetManager.Listener.onAvailabilityChanged} // is triggerd. - mEthernetManager.addListener(mEthernetListener, r -> handler.post(r)); + if (mEthernetManager != null) { + mEthernetManager.addInterfaceStateListener(r -> handler.post(r), mEthernetListener); + } } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { - mEthernetManager.removeListener(mEthernetListener); - mEthernetListener = null; + if (mEthernetManager != null) { + mEthernetManager.removeInterfaceStateListener(mEthernetListener); + } } @Override public boolean shouldEnable() { + ensureRunningOnMainLoopThread(); String[] available = mTm.getTetherableIfaces(); for (String s : available) { - if (s.matches(mEthernetRegex)) { + if (mAvailableInterfaces.contains(s)) { return true; } } @@ -75,11 +86,19 @@ public final class EthernetTetherPreferenceController extends TetherBasePreferen @Override public boolean shouldShow() { - return !TextUtils.isEmpty(mEthernetRegex); + return mEthernetManager != null; } @Override public int getTetherType() { return TetheringManager.TETHERING_ETHERNET; } + + private void ensureRunningOnMainLoopThread() { + if (Looper.getMainLooper().getThread() != Thread.currentThread()) { + throw new IllegalStateException( + "Not running on main loop thread: " + + Thread.currentThread().getName()); + } + } } diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java index cd336633e73..01ec42e6a94 100644 --- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java @@ -33,8 +33,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.ConnectivityManager; +import android.net.EthernetManager; import android.net.TetheringManager; import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; import android.os.UserHandle; import android.os.UserManager; import android.util.FeatureFlagUtils; @@ -43,7 +45,6 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.core.FeatureFlags; -import com.android.settings.testutils.shadow.ShadowWifiManager; import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController; import com.android.settings.wifi.tether.WifiTetherSecurityPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -55,14 +56,12 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowWifiManager.class}) public class AllInOneTetherSettingsTest { private static final String[] WIFI_REGEXS = {"wifi_regexs"}; private static final String[] USB_REGEXS = {"usb_regexs"}; @@ -72,6 +71,8 @@ public class AllInOneTetherSettingsTest { private Context mContext; private AllInOneTetherSettings mAllInOneTetherSettings; + @Mock + private WifiManager mWifiManager; @Mock private ConnectivityManager mConnectivityManager; @Mock @@ -84,16 +85,20 @@ public class AllInOneTetherSettingsTest { private PreferenceScreen mPreferenceScreen; @Mock private PreferenceGroup mWifiTetherGroup; + @Mock + private EthernetManager mEthernetManager; @Before public void setUp() { mContext = spy(RuntimeEnvironment.application); MockitoAnnotations.initMocks(this); + doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class); doReturn(mConnectivityManager) .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); doReturn(mTetheringManager) .when(mContext).getSystemService(Context.TETHERING_SERVICE); + doReturn(mEthernetManager).when(mContext).getSystemService(EthernetManager.class); doReturn(WIFI_REGEXS).when(mTetheringManager).getTetherableWifiRegexs(); doReturn(USB_REGEXS).when(mTetheringManager).getTetherableUsbRegexs(); doReturn(BT_REGEXS).when(mTetheringManager).getTetherableBluetoothRegexs(); diff --git a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java index 8a819087296..df82a6773ce 100644 --- a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java @@ -19,6 +19,7 @@ package com.android.settings.network; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -64,10 +65,9 @@ public class EthernetTetherPreferenceControllerTest { mPreference = spy(SwitchPreference.class); when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager); when(mTetheringManager.getTetherableIfaces()).thenReturn(new String[]{ETHERNET_REGEX}); - when(mContext.getSystemService(Context.ETHERNET_SERVICE)).thenReturn(mEthernetManager); + when(mContext.getSystemService(EthernetManager.class)).thenReturn(mEthernetManager); mController = new EthernetTetherPreferenceController(mContext, "ethernet"); mController.setTetherEnabler(mTetherEnabler); - ReflectionHelpers.setField(mController, "mEthernetRegex", ETHERNET_REGEX); ReflectionHelpers.setField(mController, "mPreference", mPreference); } @@ -75,7 +75,8 @@ public class EthernetTetherPreferenceControllerTest { public void lifecycle_shouldRegisterReceiverOnStart() { mController.onStart(); - verify(mEthernetManager).addListener(eq(mController.mEthernetListener)); + verify(mEthernetManager).addInterfaceStateListener(any(), + eq(mController.mEthernetListener)); } @Test @@ -93,11 +94,10 @@ public class EthernetTetherPreferenceControllerTest { @Test public void lifecycle_shouldUnregisterReceiverOnStop() { mController.onStart(); - EthernetManager.Listener listener = mController.mEthernetListener; + EthernetManager.InterfaceStateListener listener = mController.mEthernetListener; mController.onStop(); - verify(mEthernetManager).removeListener(eq(listener)); - assertThat(mController.mEthernetListener).isNull(); + verify(mEthernetManager).removeInterfaceStateListener(eq(listener)); } @Test @@ -108,8 +108,11 @@ public class EthernetTetherPreferenceControllerTest { @Test public void shouldShow_noEthernetInterface() { - ReflectionHelpers.setField(mController, "mEthernetRegex", ""); - assertThat(mController.shouldShow()).isFalse(); + when(mContext.getSystemService(EthernetManager.class)).thenReturn(null); + + final EthernetTetherPreferenceController controller = + new EthernetTetherPreferenceController(mContext, "ethernet"); + assertThat(controller.shouldShow()).isFalse(); } @Test