From bfd3722e06889290beddbb9d2c1f5ba034c03495 Mon Sep 17 00:00:00 2001 From: Nikhil Nayunigari Date: Mon, 20 Jan 2025 02:56:29 +0000 Subject: [PATCH] Changes to add Ethernet settings row item Adds EthernetSettings row item to NetworkProviderSettings and its controller. Flag: com.android.settings.connectivity.ethernet_settings Test: atest SettingsRoboTests: com.android.settings.network.EthernetSwitchPreferenceControllerTest Change-Id: I27965ad0c8563657b17e0aa6d3bd19b97fcf5615 --- Android.bp | 2 +- res/xml/network_provider_settings.xml | 6 ++ .../network/NetworkProviderSettings.java | 20 ++++ .../ethernet/EthernetInterfaceTracker.kt | 48 ++++++---- .../EthernetSwitchPreferenceController.kt | 96 +++++++++++++++++++ .../network/NetworkProviderSettingsTest.java | 4 + .../ethernet/EthernetInterfaceTrackerTest.kt | 8 +- .../EthernetSwitchPreferenceControllerTest.kt | 96 +++++++++++++++++++ 8 files changed, 257 insertions(+), 23 deletions(-) create mode 100644 src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt create mode 100644 tests/robotests/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceControllerTest.kt diff --git a/Android.bp b/Android.bp index ed094cfae72..150bdaf84a1 100644 --- a/Android.bp +++ b/Android.bp @@ -114,8 +114,8 @@ android_library { "setupdesign-lottie-loading-layout", "device_policy_aconfig_flags_lib", "keyboard_flags_lib", - "settings_connectivity_flags", "com_android_systemui_flags_lib", + "settings_connectivity_flags_lib", ], plugins: [ diff --git a/res/xml/network_provider_settings.xml b/res/xml/network_provider_settings.xml index 74ec948713c..73bed545aa5 100644 --- a/res/xml/network_provider_settings.xml +++ b/res/xml/network_provider_settings.xml @@ -36,6 +36,12 @@ android:layout="@layout/airplane_mode_message_preference" settings:allowDividerBelow="true"/> + + ) } - private val ethernetManager = - context.getSystemService(Context.ETHERNET_SERVICE) as EthernetManager + private val ethernetManager: EthernetManager? = + context.applicationContext.getSystemService(EthernetManager::class.java) private val TAG = "EthernetInterfaceTracker" // Maps ethernet interface identifier to EthernetInterface object private val ethernetInterfaces = mutableMapOf() - private val interfaceListeners = mutableListOf() - private val mExecutor = ContextCompat.getMainExecutor(context) - - init { - ethernetManager.addInterfaceStateListener(mExecutor, this) - } + private val interfaceListeners = mutableListOf() fun getInterface(id: String): EthernetInterface? { return ethernetInterfaces.get(id) } - fun getAvailableInterfaces(): Collection { - return ethernetInterfaces.values - } + val availableInterfaces: Collection + get() = ethernetInterfaces.values - fun registerInterfaceListener(listener: EthernetInterfaceListListener) { + fun registerInterfaceListener(listener: EthernetInterfaceTrackerListener) { + if (interfaceListeners.isEmpty()) { + ethernetManager?.addInterfaceStateListener(ContextCompat.getMainExecutor(context), this) + } interfaceListeners.add(listener) } - fun unregisterInterfaceListener(listener: EthernetInterfaceListListener) { + fun unregisterInterfaceListener(listener: EthernetInterfaceTrackerListener) { interfaceListeners.remove(listener) + if (interfaceListeners.isEmpty()) { + ethernetManager?.removeInterfaceStateListener(this) + } } override fun onInterfaceStateChanged(id: String, state: Int, role: Int, cfg: IpConfiguration?) { @@ -68,8 +67,21 @@ class EthernetInterfaceTracker(private val context: Context) : } if (interfacesChanged) { for (listener in interfaceListeners) { - listener.onInterfaceListChanged() + listener.onInterfaceListChanged(ethernetInterfaces.values.toList()) } } } + + companion object { + @Volatile private var INSTANCE: EthernetInterfaceTracker? = null + + fun getInstance(context: Context): EthernetInterfaceTracker { + return INSTANCE + ?: synchronized(this) { + val instance = EthernetInterfaceTracker(context.applicationContext) + INSTANCE = instance + instance + } + } + } } diff --git a/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt b/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt new file mode 100644 index 00000000000..5836f554bf0 --- /dev/null +++ b/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2025 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.ethernet + +import android.content.Context +import android.net.EthernetManager +import androidx.core.content.ContextCompat +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner +import androidx.preference.Preference +import androidx.preference.PreferenceScreen +import com.android.settings.connectivity.Flags +import com.android.settingslib.RestrictedSwitchPreference +import com.android.settingslib.core.AbstractPreferenceController +import com.google.common.annotations.VisibleForTesting +import java.util.concurrent.Executor + +class EthernetSwitchPreferenceController(context: Context, private val lifecycle: Lifecycle) : + AbstractPreferenceController(context), + LifecycleEventObserver, + EthernetInterfaceTracker.EthernetInterfaceTrackerListener { + + private val ethernetManager: EthernetManager? = + context.getSystemService(EthernetManager::class.java) + private var preference: RestrictedSwitchPreference? = null + private val executor = ContextCompat.getMainExecutor(context) + private val ethernetInterfaceTracker = EthernetInterfaceTracker.getInstance(context) + + init { + lifecycle.addObserver(this) + } + + override fun getPreferenceKey(): String { + return KEY + } + + override fun isAvailable(): Boolean { + return (Flags.ethernetSettings() && ethernetInterfaceTracker.availableInterfaces.size > 0) + } + + override fun displayPreference(screen: PreferenceScreen) { + super.displayPreference(screen) + preference = screen.findPreference(KEY) + preference?.setOnPreferenceChangeListener(this::onPreferenceChange) + } + + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + when (event) { + Lifecycle.Event.ON_START -> { + ethernetManager?.addEthernetStateListener(executor, this::onEthernetStateChanged) + ethernetInterfaceTracker.registerInterfaceListener(this) + } + + Lifecycle.Event.ON_STOP -> { + ethernetManager?.removeEthernetStateListener(this::onEthernetStateChanged) + ethernetInterfaceTracker.unregisterInterfaceListener(this) + } + + else -> {} + } + } + + fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val isChecked = newValue as Boolean + ethernetManager?.setEthernetEnabled(isChecked) + return true + } + + @VisibleForTesting + fun onEthernetStateChanged(state: Int) { + preference?.setChecked(state == EthernetManager.ETHERNET_STATE_ENABLED) + } + + override fun onInterfaceListChanged(ethernetInterfaces: List) { + preference?.setVisible(ethernetInterfaces.size > 0) + } + + companion object { + private val KEY = "main_toggle_ethernet" + } +} diff --git a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java index a8862cdaee7..0d251c79580 100644 --- a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java +++ b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java @@ -47,6 +47,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.location.LocationManager; +import android.net.EthernetManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -157,6 +158,8 @@ public class NetworkProviderSettingsTest { PreferenceCategory mFirstWifiEntryPreferenceCategory; @Mock NetworkProviderSettings.WifiRestriction mWifiRestriction; + @Mock + EthernetManager mEtherentManager; private NetworkProviderSettings mNetworkProviderSettings; @@ -178,6 +181,7 @@ public class NetworkProviderSettingsTest { doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class); doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE); doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class); + doReturn(mEtherentManager).when(mContext).getSystemService(Context.ETHERNET_SERVICE); when(mUserManager.hasBaseUserRestriction(any(), any())).thenReturn(true); doReturn(mContext).when(mPreferenceManager).getContext(); mNetworkProviderSettings.mAddWifiNetworkPreference = new AddWifiNetworkPreference(mContext); diff --git a/tests/robotests/src/com/android/settings/network/ethernet/EthernetInterfaceTrackerTest.kt b/tests/robotests/src/com/android/settings/network/ethernet/EthernetInterfaceTrackerTest.kt index 369eb1a5515..b1516d17964 100644 --- a/tests/robotests/src/com/android/settings/network/ethernet/EthernetInterfaceTrackerTest.kt +++ b/tests/robotests/src/com/android/settings/network/ethernet/EthernetInterfaceTrackerTest.kt @@ -42,7 +42,7 @@ class EthernetInterfaceTrackerTest { } } - private val ethernetInterfaceTracker = EthernetInterfaceTracker(context) + private val ethernetInterfaceTracker = EthernetInterfaceTracker.getInstance(context) @Test fun getInterface_shouldReturnEmpty() { @@ -51,7 +51,7 @@ class EthernetInterfaceTrackerTest { @Test fun getAvailableInterfaces_shouldReturnEmpty() { - assertEquals(ethernetInterfaceTracker.getAvailableInterfaces().size, 0) + assertEquals(ethernetInterfaceTracker.availableInterfaces.size, 0) } @Test @@ -64,7 +64,7 @@ class EthernetInterfaceTrackerTest { ) assertNotNull(ethernetInterfaceTracker.getInterface("id0")) - assertEquals(ethernetInterfaceTracker.getAvailableInterfaces().size, 1) + assertEquals(ethernetInterfaceTracker.availableInterfaces.size, 1) ethernetInterfaceTracker.onInterfaceStateChanged( "id0", @@ -74,6 +74,6 @@ class EthernetInterfaceTrackerTest { ) assertNull(ethernetInterfaceTracker.getInterface("id0")) - assertEquals(ethernetInterfaceTracker.getAvailableInterfaces().size, 0) + assertEquals(ethernetInterfaceTracker.availableInterfaces.size, 0) } } diff --git a/tests/robotests/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceControllerTest.kt b/tests/robotests/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceControllerTest.kt new file mode 100644 index 00000000000..bb79296de5e --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/ethernet/EthernetSwitchPreferenceControllerTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2025 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.ethernet + +import android.content.Context +import android.content.ContextWrapper +import android.net.EthernetManager +import androidx.lifecycle.Lifecycle +import androidx.preference.PreferenceScreen +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.RestrictedSwitchPreference +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class EthernetSwitchPreferenceControllerTest { + private val mockEthernetManager = mock() + private val preferenceScreen = mock() + private val switchPreference = mock() + + private val context: Context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when (name) { + getSystemServiceName(EthernetManager::class.java) -> mockEthernetManager + else -> super.getSystemService(name) + } + } + + private val lifecycle = mock() + + private val controller: EthernetSwitchPreferenceController = + EthernetSwitchPreferenceController(context, lifecycle) + + @Before + fun setUp() { + preferenceScreen.stub { + on { findPreference("main_toggle_ethernet") } doReturn + switchPreference + } + controller.displayPreference(preferenceScreen) + } + + @Test + fun getPreferenceKey_shouldReturnCorrectKey() { + assertEquals(controller.getPreferenceKey(), "main_toggle_ethernet") + } + + @Test + fun onPreferenceChange_shouldCallEthernetManager() { + assertTrue(controller.onPreferenceChange(switchPreference, true)) + verify(mockEthernetManager).setEthernetEnabled(true) + + assertTrue(controller.onPreferenceChange(switchPreference, false)) + verify(mockEthernetManager).setEthernetEnabled(false) + } + + @Test + fun ethernetEnabled_shouldUpdatePreferenceState() { + switchPreference.stub { on { isChecked } doReturn false } + + controller.onEthernetStateChanged(EthernetManager.ETHERNET_STATE_ENABLED) + + verify(switchPreference).setChecked(true) + } + + @Test + fun ethernetDisabled_shouldUpdatePreferenceState() { + switchPreference.stub { on { isChecked } doReturn true } + + controller.onEthernetStateChanged(EthernetManager.ETHERNET_STATE_DISABLED) + + verify(switchPreference).setChecked(false) + } +}