Merge "Adapt new EthernetManager APIs in Settings." am: 3340b8e3c3 am: b3fedd0633

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/1968461

Change-Id: Ifd244bd60e65937c510a5447c9642225be400e3f
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index e485d1e..ae24168 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -34,6 +34,9 @@
 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.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 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 @@
     private BroadcastReceiver mTetherChangeReceiver;
 
     private String[] mBluetoothRegexs;
-    private String mEthernetRegex;
     private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
 
     private Handler mHandler = new Handler();
@@ -106,6 +109,7 @@
     private EthernetManager mEm;
     private TetheringEventCallback mTetheringEventCallback;
     private EthernetListener mEthernetListener;
+    private final HashSet<String> mAvailableInterfaces = new HashSet<>();
 
     private WifiTetherPreferenceController mWifiTetherPreferenceController;
 
@@ -172,17 +176,17 @@
         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 @@
 
         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 @@
         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 @@
         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 @@
         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 @@
                         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 @@
         }
     }
 
-    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 5b2cab7..58c1fd2 100644
--- a/src/com/android/settings/network/EthernetTetherPreferenceController.java
+++ b/src/com/android/settings/network/EthernetTetherPreferenceController.java
@@ -21,52 +21,63 @@
 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<String> 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 @@
 
     @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 cd33663..01ec42e 100644
--- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
@@ -33,8 +33,10 @@
 
 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.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.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"};
@@ -73,6 +72,8 @@
     private AllInOneTetherSettings mAllInOneTetherSettings;
 
     @Mock
+    private WifiManager mWifiManager;
+    @Mock
     private ConnectivityManager mConnectivityManager;
     @Mock
     private TetheringManager mTetheringManager;
@@ -84,16 +85,20 @@
     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 8a81908..df82a67 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 @@
 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 @@
         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 void lifecycle_shouldRegisterReceiverOnStart() {
         mController.onStart();
 
-        verify(mEthernetManager).addListener(eq(mController.mEthernetListener));
+        verify(mEthernetManager).addInterfaceStateListener(any(),
+                eq(mController.mEthernetListener));
     }
 
     @Test
@@ -93,11 +94,10 @@
     @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 @@
 
     @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