Merge "Edits ethernet interface details settings subpage with more info" into main

This commit is contained in:
Nikhil Nayunigari
2025-03-11 15:57:38 -07:00
committed by Android (Google) Code Review
7 changed files with 225 additions and 18 deletions

View File

@@ -3912,6 +3912,18 @@
<!-- Label for bluetooth tether checkbox [CHAR LIMIT=25]-->
<string name="bluetooth_tether_checkbox_text">Bluetooth tethering</string>
<!-- Ethernet settings-->
<!-- Label for ethernet IP address [CHAR LIMIT=NONE]-->
<string name="ethernet_ip_address">IP address</string>
<!-- Label for ethernet MAC address [CHAR LIMIT=NONE]-->
<string name="ethernet_mac_address_title">MAC address</string>
<!-- Label for ethernet transfer speed [CHAR LIMIT=NONE]-->
<string name="tx_ethernet_speed">Transit link speed</string>
<!-- Label for ethernet receive speed [CHAR LIMIT=NONE]-->
<string name="rx_ethernet_speed">Receive link speed</string>
<!-- Label for ethernet network usage [CHAR LIMIT=NONE]-->
<string name="ethernet_network_usage">Network usage</string>
<!-- Ethernet Tethering settings-->
<!-- Label for ethernet tether checkbox [CHAR LIMIT=NONE]-->
<string name="ethernet_tether_checkbox_text">Ethernet tethering</string>

View File

@@ -24,4 +24,36 @@
android:selectable="false"
android:order="-10000"/>
<ListPreference
android:key="metered"
android:icon="@drawable/ic_attach_money_black_24dp"
android:title="@string/wifi_metered_title"
android:entries="@array/wifi_metered_entries"
android:entryValues="@array/wifi_metered_values"/>
<!-- Network Details -->
<PreferenceCategory
android:key="ip_details_category">
<Preference
android:key="ethernet_ip_address"
android:title="@string/ethernet_ip_address"
android:selectable="false"
settings:enableCopying="true"/>
<Preference
android:key="ethernet_mac_address"
android:title="@string/ethernet_mac_address_title"
android:selectable="false"
settings:enableCopying="true"/>
<Preference
android:key="ethernet_tx_link_speed"
android:title="@string/tx_ethernet_speed"
android:selectable="false"
settings:enableCopying="true"/>
<Preference
android:key="ethernet_rx_link_speed"
android:title="@string/rx_ethernet_speed"
android:selectable="false"
settings:enableCopying="true"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -18,34 +18,68 @@ package com.android.settings.network.ethernet
import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.EthernetManager
import android.net.EthernetManager.STATE_ABSENT
import android.net.EthernetNetworkManagementException
import android.net.EthernetNetworkUpdateRequest
import android.net.IpConfiguration
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.OutcomeReceiver
import android.util.Log
import androidx.core.content.ContextCompat
import com.google.common.annotations.VisibleForTesting
class EthernetInterface(private val context: Context, private val id: String) :
EthernetManager.InterfaceStateListener {
interface EthernetInterfaceStateListener {
fun interfaceUpdated()
}
private val ethernetManager: EthernetManager? =
context.getSystemService(EthernetManager::class.java)
private val connectivityManager: ConnectivityManager? =
context.getSystemService(ConnectivityManager::class.java)
private val executor = ContextCompat.getMainExecutor(context)
private val interfaceListeners = mutableListOf<EthernetInterfaceStateListener>()
private val TAG = "EthernetInterface"
private val networkRequest: NetworkRequest =
NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.build()
private var interfaceState = STATE_ABSENT
private var ipConfiguration = IpConfiguration()
private var linkProperties = LinkProperties()
fun getInterfaceState() = interfaceState
fun getId() = id
fun getConfiguration(): IpConfiguration {
return ipConfiguration
fun getConfiguration() = ipConfiguration
fun getLinkProperties() = linkProperties
fun registerListener(listener: EthernetInterfaceStateListener) {
if (interfaceListeners.isEmpty()) {
ethernetManager?.addInterfaceStateListener(ContextCompat.getMainExecutor(context), this)
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)
}
interfaceListeners.add(listener)
}
fun unregisterListener(listener: EthernetInterfaceStateListener) {
interfaceListeners.remove(listener)
if (interfaceListeners.isEmpty()) {
connectivityManager?.unregisterNetworkCallback(networkCallback)
ethernetManager?.removeInterfaceStateListener(this)
}
}
fun setConfiguration(ipConfiguration: IpConfiguration) {
@@ -67,10 +101,28 @@ class EthernetInterface(private val context: Context, private val id: String) :
)
}
private fun notifyListeners() {
for (listener in interfaceListeners) {
listener.interfaceUpdated()
}
}
override fun onInterfaceStateChanged(id: String, state: Int, role: Int, cfg: IpConfiguration?) {
if (id == this.id) {
ipConfiguration = cfg ?: IpConfiguration()
interfaceState = state
notifyListeners()
}
}
@VisibleForTesting
val networkCallback =
object : NetworkCallback() {
override fun onLinkPropertiesChanged(network: Network, lp: LinkProperties) {
if (lp.getInterfaceName().equals(id)) {
linkProperties = lp
notifyListeners()
}
}
}
}

View File

@@ -18,7 +18,14 @@ package com.android.settings.network.ethernet
import android.content.Context
import android.net.EthernetManager
import android.net.IpConfiguration
import android.net.LinkProperties
import android.net.StaticIpConfiguration
import android.widget.ImageView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import com.android.settings.R
@@ -30,13 +37,25 @@ class EthernetInterfaceDetailsController(
context: Context,
private val fragment: PreferenceFragmentCompat,
private val preferenceId: String,
) : AbstractPreferenceController(context) {
private val lifecycle: Lifecycle,
) :
AbstractPreferenceController(context),
EthernetInterface.EthernetInterfaceStateListener,
LifecycleEventObserver {
private val KEY_HEADER = "ethernet_details"
private val ethernetManager = context.getSystemService(EthernetManager::class.java)
private val ethernetInterface =
EthernetTrackerImpl.getInstance(context).getInterface(preferenceId)
private lateinit var entityHeaderController: EntityHeaderController
private var ipAddressPref: Preference? = null
init {
lifecycle.addObserver(this)
}
override fun isAvailable(): Boolean {
return true
}
@@ -45,10 +64,24 @@ class EthernetInterfaceDetailsController(
return KEY_HEADER
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
when (event) {
Lifecycle.Event.ON_START -> {
ethernetInterface?.registerListener(this)
}
Lifecycle.Event.ON_STOP -> {
ethernetInterface?.unregisterListener(this)
}
else -> {}
}
}
override fun displayPreference(screen: PreferenceScreen) {
val headerPref: LayoutPreference? = screen.findPreference(KEY_HEADER)
val mEntityHeaderController =
entityHeaderController =
EntityHeaderController.newInstance(
fragment.getActivity(),
fragment,
@@ -59,7 +92,8 @@ class EthernetInterfaceDetailsController(
iconView?.setScaleType(ImageView.ScaleType.CENTER_INSIDE)
mEntityHeaderController
if (entityHeaderController != null) {
entityHeaderController
.setLabel("Ethernet")
.setSummary(
if (ethernetInterface?.getInterfaceState() == EthernetManager.STATE_LINK_UP) {
@@ -72,4 +106,35 @@ class EthernetInterfaceDetailsController(
.setIcon(mContext.getDrawable(R.drawable.ic_settings_ethernet))
.done(true /* rebind */)
}
ipAddressPref = screen.findPreference<Preference>("ethernet_ip_address")
if (ethernetInterface?.getInterfaceState() == EthernetManager.STATE_LINK_UP) {
initializeIpDetails()
}
}
override fun interfaceUpdated() {
entityHeaderController?.setSummary(
if (ethernetInterface?.getInterfaceState() == EthernetManager.STATE_LINK_UP) {
mContext.getString(R.string.network_connected)
} else {
mContext.getString(R.string.network_disconnected)
}
)
initializeIpDetails()
}
private fun initializeIpDetails() {
val ipConfiguration: IpConfiguration? = ethernetInterface?.getConfiguration()
val linkProperties: LinkProperties? = ethernetInterface?.getLinkProperties()
if (ipConfiguration?.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
val staticIp: StaticIpConfiguration? = ipConfiguration?.getStaticIpConfiguration()
ipAddressPref?.setSummary(staticIp?.getIpAddress().toString())
} else {
val addresses = linkProperties?.getAddresses()
ipAddressPref?.setSummary(addresses?.first().toString())
}
}
}

View File

@@ -50,6 +50,13 @@ class EthernetInterfaceDetailsFragment : DashboardFragment() {
override public fun createPreferenceControllers(
context: Context
): List<AbstractPreferenceController> {
return listOf(EthernetInterfaceDetailsController(context, this, preferenceId ?: ""))
return listOf(
EthernetInterfaceDetailsController(
context,
this,
preferenceId ?: "",
getSettingsLifecycle(),
)
)
}
}

View File

@@ -18,22 +18,30 @@ package com.android.settings.network.ethernet
import android.content.Context
import android.content.ContextWrapper
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class EthernetInterfaceDetailsControllerTest {
private val ethernetInterfaceDetailsFragment = EthernetInterfaceDetailsFragment()
private val lifecycle = mock<Lifecycle>()
private val context: Context =
object : ContextWrapper(ApplicationProvider.getApplicationContext()) {}
private val ethernetInterfaceDetailsController =
EthernetInterfaceDetailsController(context, ethernetInterfaceDetailsFragment, "eth0")
EthernetInterfaceDetailsController(
context,
ethernetInterfaceDetailsFragment,
"eth0",
lifecycle,
)
@Test
fun isAvailable_ShouldReturnTrue() {

View File

@@ -18,14 +18,19 @@ package com.android.settings.network.ethernet
import android.content.Context
import android.content.ContextWrapper
import android.net.ConnectivityManager
import android.net.EthernetManager
import android.net.EthernetManager.STATE_ABSENT
import android.net.EthernetManager.STATE_LINK_DOWN
import android.net.EthernetManager.STATE_LINK_UP
import android.net.IpConfiguration
import android.net.LinkProperties
import android.net.Network
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -34,12 +39,15 @@ import org.mockito.kotlin.mock
class EthernetInterfaceTest {
private val mockEthernetManager = mock<EthernetManager>()
private val mockConnectivityManager = mock<ConnectivityManager>()
private val mockNetwork = mock<Network>()
private val context: Context =
object : ContextWrapper(ApplicationProvider.getApplicationContext()) {
override fun getSystemService(name: String): Any? =
when (name) {
Context.ETHERNET_SERVICE -> mockEthernetManager
Context.CONNECTIVITY_SERVICE -> mockConnectivityManager
else -> super.getSystemService(name)
}
}
@@ -85,4 +93,27 @@ class EthernetInterfaceTest {
IpConfiguration.IpAssignment.UNASSIGNED,
)
}
@Test
fun linkPropertiesChanged_shouldUpdate() {
val linkProperties = LinkProperties()
linkProperties.setInterfaceName("eth0")
linkProperties.setUsePrivateDns(true)
ethernetInterface.networkCallback.onLinkPropertiesChanged(mockNetwork, linkProperties)
assertEquals(ethernetInterface.getLinkProperties().getInterfaceName(), "eth0")
assertTrue(ethernetInterface.getLinkProperties().isPrivateDnsActive())
}
@Test
fun linkPropertiesChanged_iddoesnotmatch_shouldNotUpdate() {
val linkProperties = LinkProperties()
linkProperties.setInterfaceName("eth1")
linkProperties.setUsePrivateDns(true)
ethernetInterface.networkCallback.onLinkPropertiesChanged(mockNetwork, linkProperties)
assertFalse(ethernetInterface.getLinkProperties().isPrivateDnsActive())
}
}