Loading Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -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: [ Loading res/xml/network_provider_settings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,12 @@ android:layout="@layout/airplane_mode_message_preference" settings:allowDividerBelow="true"/> <com.android.settingslib.RestrictedSwitchPreference android:key="main_toggle_ethernet" android:title="@string/ethernet" settings:restrictedSwitchSummary="@string/not_allowed_by_ent" settings:allowDividerAbove="true"/> <Preference android:key="connected_ethernet_network" android:title="@string/ethernet" Loading src/com/android/settings/network/NetworkProviderSettings.java +20 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.location.WifiScanningFragment; import com.android.settings.network.ethernet.EthernetSwitchPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.wifi.AddNetworkFragment; import com.android.settings.wifi.AddWifiNetworkPreference; Loading @@ -84,6 +85,7 @@ import com.android.settings.wifi.dpp.WifiDppUtils; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.search.Indexable; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.utils.StringUtil; Loading Loading @@ -129,6 +131,8 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; @VisibleForTesting static final String PREF_KEY_WIFI_TOGGLE = "main_toggle_wifi"; @VisibleForTesting static final String PREF_KEY_ETHERNET_TOGGLE = "main_toggle_ethernet"; // TODO(b/70983952): Rename these to use WifiEntry instead of AccessPoint. @VisibleForTesting static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; Loading Loading @@ -242,10 +246,12 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment LayoutPreference mResetInternetPreference; @VisibleForTesting ConnectedEthernetNetworkController mConnectedEthernetNetworkController; private EthernetSwitchPreferenceController mEthernetSwitchPreferenceController; @VisibleForTesting FooterPreference mWifiStatusMessagePreference; @VisibleForTesting MenuProvider mMenuProvider; RestrictedSwitchPreference mEthernetSwitchPreference; /** * Mobile networks list for provider model Loading Loading @@ -383,12 +389,18 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment mDataUsagePreference.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI) .build(), SubscriptionManager.INVALID_SUBSCRIPTION_ID); mResetInternetPreference = findPreference(PREF_KEY_RESET_INTERNET); mEthernetSwitchPreference = findPreference(PREF_KEY_ETHERNET_TOGGLE); if (mResetInternetPreference != null) { mResetInternetPreference.setVisible(false); } addNetworkMobileProviderController(); addConnectedEthernetNetworkController(); addWifiSwitchPreferenceController(); if (com.android.settings.connectivity.Flags.ethernetSettings()) { addEthernetSwitchPreferenceController(); } else { mEthernetSwitchPreference.setVisible(false); } mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE); checkConnectivityRecovering(); Loading Loading @@ -441,6 +453,14 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void addEthernetSwitchPreferenceController() { if (mEthernetSwitchPreferenceController == null) { mEthernetSwitchPreferenceController = new EthernetSwitchPreferenceController(getContext(), getSettingsLifecycle()); } mEthernetSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void checkConnectivityRecovering() { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(), mNetworkMobileProviderController, Loading src/com/android/settings/network/ethernet/EthernetInterfaceTracker.kt +30 −18 Original line number Diff line number Diff line Loading @@ -20,41 +20,40 @@ import android.content.Context import android.net.EthernetManager import android.net.IpConfiguration import androidx.core.content.ContextCompat import java.util.concurrent.Executor class EthernetInterfaceTracker(private val context: Context) : class EthernetInterfaceTracker private constructor(private val context: Context) : EthernetManager.InterfaceStateListener { interface EthernetInterfaceListListener { fun onInterfaceListChanged() interface EthernetInterfaceTrackerListener { fun onInterfaceListChanged(ethernetInterfaces: List<EthernetInterface>) } 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<String, EthernetInterface>() private val interfaceListeners = mutableListOf<EthernetInterfaceListListener>() private val mExecutor = ContextCompat.getMainExecutor(context) init { ethernetManager.addInterfaceStateListener(mExecutor, this) } private val interfaceListeners = mutableListOf<EthernetInterfaceTrackerListener>() fun getInterface(id: String): EthernetInterface? { return ethernetInterfaces.get(id) } fun getAvailableInterfaces(): Collection<EthernetInterface> { return ethernetInterfaces.values } val availableInterfaces: Collection<EthernetInterface> 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?) { Loading @@ -68,7 +67,20 @@ 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 } } } Loading src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * 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<EthernetInterface>) { preference?.setVisible(ethernetInterfaces.size > 0) } companion object { private val KEY = "main_toggle_ethernet" } } Loading
Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -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: [ Loading
res/xml/network_provider_settings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,12 @@ android:layout="@layout/airplane_mode_message_preference" settings:allowDividerBelow="true"/> <com.android.settingslib.RestrictedSwitchPreference android:key="main_toggle_ethernet" android:title="@string/ethernet" settings:restrictedSwitchSummary="@string/not_allowed_by_ent" settings:allowDividerAbove="true"/> <Preference android:key="connected_ethernet_network" android:title="@string/ethernet" Loading
src/com/android/settings/network/NetworkProviderSettings.java +20 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.location.WifiScanningFragment; import com.android.settings.network.ethernet.EthernetSwitchPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.wifi.AddNetworkFragment; import com.android.settings.wifi.AddWifiNetworkPreference; Loading @@ -84,6 +85,7 @@ import com.android.settings.wifi.dpp.WifiDppUtils; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.search.Indexable; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.utils.StringUtil; Loading Loading @@ -129,6 +131,8 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; @VisibleForTesting static final String PREF_KEY_WIFI_TOGGLE = "main_toggle_wifi"; @VisibleForTesting static final String PREF_KEY_ETHERNET_TOGGLE = "main_toggle_ethernet"; // TODO(b/70983952): Rename these to use WifiEntry instead of AccessPoint. @VisibleForTesting static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; Loading Loading @@ -242,10 +246,12 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment LayoutPreference mResetInternetPreference; @VisibleForTesting ConnectedEthernetNetworkController mConnectedEthernetNetworkController; private EthernetSwitchPreferenceController mEthernetSwitchPreferenceController; @VisibleForTesting FooterPreference mWifiStatusMessagePreference; @VisibleForTesting MenuProvider mMenuProvider; RestrictedSwitchPreference mEthernetSwitchPreference; /** * Mobile networks list for provider model Loading Loading @@ -383,12 +389,18 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment mDataUsagePreference.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI) .build(), SubscriptionManager.INVALID_SUBSCRIPTION_ID); mResetInternetPreference = findPreference(PREF_KEY_RESET_INTERNET); mEthernetSwitchPreference = findPreference(PREF_KEY_ETHERNET_TOGGLE); if (mResetInternetPreference != null) { mResetInternetPreference.setVisible(false); } addNetworkMobileProviderController(); addConnectedEthernetNetworkController(); addWifiSwitchPreferenceController(); if (com.android.settings.connectivity.Flags.ethernetSettings()) { addEthernetSwitchPreferenceController(); } else { mEthernetSwitchPreference.setVisible(false); } mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE); checkConnectivityRecovering(); Loading Loading @@ -441,6 +453,14 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void addEthernetSwitchPreferenceController() { if (mEthernetSwitchPreferenceController == null) { mEthernetSwitchPreferenceController = new EthernetSwitchPreferenceController(getContext(), getSettingsLifecycle()); } mEthernetSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void checkConnectivityRecovering() { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(), mNetworkMobileProviderController, Loading
src/com/android/settings/network/ethernet/EthernetInterfaceTracker.kt +30 −18 Original line number Diff line number Diff line Loading @@ -20,41 +20,40 @@ import android.content.Context import android.net.EthernetManager import android.net.IpConfiguration import androidx.core.content.ContextCompat import java.util.concurrent.Executor class EthernetInterfaceTracker(private val context: Context) : class EthernetInterfaceTracker private constructor(private val context: Context) : EthernetManager.InterfaceStateListener { interface EthernetInterfaceListListener { fun onInterfaceListChanged() interface EthernetInterfaceTrackerListener { fun onInterfaceListChanged(ethernetInterfaces: List<EthernetInterface>) } 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<String, EthernetInterface>() private val interfaceListeners = mutableListOf<EthernetInterfaceListListener>() private val mExecutor = ContextCompat.getMainExecutor(context) init { ethernetManager.addInterfaceStateListener(mExecutor, this) } private val interfaceListeners = mutableListOf<EthernetInterfaceTrackerListener>() fun getInterface(id: String): EthernetInterface? { return ethernetInterfaces.get(id) } fun getAvailableInterfaces(): Collection<EthernetInterface> { return ethernetInterfaces.values } val availableInterfaces: Collection<EthernetInterface> 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?) { Loading @@ -68,7 +67,20 @@ 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 } } } Loading
src/com/android/settings/network/ethernet/EthernetSwitchPreferenceController.kt 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * 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<EthernetInterface>) { preference?.setVisible(ethernetInterfaces.size > 0) } companion object { private val KEY = "main_toggle_ethernet" } }