Configure internal Ethernet networks

Android Auto OS 13 and higher contains features allowing you to configure and manage Ethernet networks. Figure 1 shows an example network diagram for an automobile:

Android Auto networking

Figure 1. Android Auto networking.

This figure shows your OEM networking app calling methods in the EthernetManager class to configure and manage onboard Ethernet networks (eth0.1, eth0.2, and eth0.3). The remainder of Figure 1 is out of scope for this document.

Set default Ethernet network settings

To set default network settings, use the resource overlay config_ethernet_interfaces:

<string-array translatable="false" name="config_ethernet_interfaces">
        <!--
        <item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item>
        <item>eth2;;ip=192.168.0.11/24</item>
        <item>eth3;12,13,14,15;ip=192.168.0.12/24;1</item>
        -->
    </string-array>

This example shows the config_ethernet_interfaces resource overlay from config.xml.

Key points about the code

  • eth1, eth2, and eth3 are the names of network interface being configured.
  • The consecutive numbers of 12, 13, 14, 15 represent network capabilities being enabled.
  • ip=, gateway=, and dns are used to set the initial IP address, gateway, and DNS for the network.

Enable or disable a network interface

To enable a network interface, call EthernetManager.enableInterface():

public final class InterfaceEnabler {
    private final Context mApplicationContext;
    private final EthernetManager mEthernetManager;
    private final OutcomeReceiver<String, EthernetNetworkManagementException> mOutcomeReceiver;

    public InterfaceEnabler(Context applicationContext,
            OutcomeReceiver<String, EthernetNetworkManagementException> outcomeReceiver) {
        mApplicationContext = applicationContext;
        mEthernetManager = applicationContext.getSystemService(EthernetManager.class);
        mOutcomeReceiver = outcomeReceiver;
    }

    public void enableInterface(String ifaceName) {
        mEthernetManager.enableInterface(ifaceName,
                mApplicationContext.getMainExecutor(),
                mOutcomeReceiver);
    }
}

Key points about the code

  • ifaceName is the name of the network interface to enable.
  • getMainExecutor() returns the app context.
  • OutcomeReceiver is a callback used to communicate completion returning the updated network name on success or EthernetNetworkManagementException on error.

When a network interface is enabled, it uses the configuration set by EthernetManager.updateConfiguration(). If a configuration hasn't been set by EthernetManager.updateConfiguration(), the network interface uses the resource overlay config_ethernet_interfaces or the default Ethernet network configuration if an overlay isn't available.

To disable a network interface, call EthernetManager.disableInterface():

public final class InterfaceEnabler {
    private final Context mApplicationContext;
    private final EthernetManager mEthernetManager;
    private final OutcomeReceiver<String, EthernetNetworkManagementException> mOutcomeReceiver;

    public InterfaceEnabler(Context applicationContext,
            OutcomeReceiver<String, EthernetNetworkManagementException> outcomeReceiver) {
        mApplicationContext = applicationContext;
        mEthernetManager = applicationContext.getSystemService(EthernetManager.class);
        mOutcomeReceiver = outcomeReceiver;
    }

    public void disableInterface(String ifaceName) {
        mEthernetManager.disableInterface(ifaceName,
                mApplicationContext.getMainExecutor(),
                mOutcomeReceiver);
    }
}

Key points about the code

  • ifaceName is the name of the network interface to disable.
  • getMainExecutor() returns the app context.
  • OutcomeReceiver is a callback used to communicate completion returning the updated network name on success or EthernetNetworkManagementException on error.

Update network configuration

To update Ethernet network configurations, call EthernetManager.updateConfiguration():

public final class ConfigurationUpdater {
    private final Context mApplicationContext;
    private final EthernetManager mEthernetManager;
    private final OutcomeReceiver<String, EthernetNetworkManagementException> mCallback;

    public ConfigurationUpdater(Context applicationContext,
            OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
        mApplicationContext = applicationContext;
        mEthernetManager = applicationContext.getSystemService(EthernetManager.class);
        mCallback = callback;
    }

    public void updateNetworkConfiguration(String packageNames,
            String ipConfigurationText,
            String networkCapabilitiesText,
            String interfaceName)
            throws IllegalArgumentException, PackageManager.NameNotFoundException {

        EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder()
                .setIpConfiguration(getIpConfiguration(ipConfigurationText))
                .setNetworkCapabilities(getCapabilities(
                        interfaceName, networkCapabilitiesText, packageNames))
                .build();

        mEthernetManager.updateConfiguration(interfaceName, request,
                mApplicationContext.getMainExecutor(), mCallback);

    }
}

Key points about the code

  • getCapabilities() is a helper method that gets the current network capabilities and calls convertToUIDs() to convert human-readable package names to Linux unique identifier (UID). Typically, you don't know the UIDs in advance for their associated packages. Therefore, if you want to use EthernetManager.updateConfiguration() to limit access to a subset of apps, you need to use their UIDs.
  • request is the configuration to be used for the internal network. The request can contain a new settings for the IP configuration and network capabilities. If the network is registered with the connectivity stack, it's updated as per the configuration. This configuration doesn't persist across reboots.
  • getMainExecutor() returns the executor on which the listener is invoked.
  • mCallback is the callback used to communicate completion returning the updated network name on success or EthernetNetworkManagementException on error.

updateConfiguration() might update characteristics of a network considered immutable by the Android Connectivity stack. The network is brought down, updated, and brought back up for these immutable attributes to be updated.

Restrict a network to a subset of apps

You can use EthernetManager#updateConfiguration to limit access to only a subset of allowed UIDs. Use this method to cover use cases where this is required, such as for internal vehicular networks only usable by a small subset of OEM apps.

Android primarily tracks apps by their UID. The following code from UIDToPackageNameConverter.java shows how to obtain a series of UIDs from a string of package names:

public static Set<Integer> convertToUids(Context applicationContext, String packageNames)
            throws PackageManager.NameNotFoundException {
        final PackageManager packageManager = applicationContext.getPackageManager();
        final UserManager userManager = applicationContext.getSystemService(UserManager.class);

        final Set<Integer> uids = new ArraySet<>();
        final List<UserHandle> users = userManager.getUserHandles(true);

        String[] packageNamesArray = packageNames.split(",");
        for (String packageName : packageNamesArray) {
            boolean nameNotFound = true;
            packageName = packageName.trim();
            for (final UserHandle user : users) {
                try {
                    final int uid =
                            packageManager.getApplicationInfoAsUser(packageName, 0, user).uid;
                    uids.add(uid);
                    nameNotFound = false;
                } catch (PackageManager.NameNotFoundException e) {
                    // Although this may seem like an error scenario, it is OK as all packages are
                    // not expected to be installed for all users.
                    continue;
                }
            }

            if (nameNotFound) {
                throw new PackageManager.NameNotFoundException("Not installed: " + packageName);
            }
        }
        return uids;

Key points about the code

  • getApplicationInfoAsuser().uid is used to retrieve the UID of from the package name.
  • uids is the generated array of integers.

The following code in EthernetManagerTest.kt shows how to update your network interface configuration with a UIDs of the apps allowed to use the network:

val allowedUids = setOf(Process.myUid())
        val nc = NetworkCapabilities.Builder(request.networkCapabilities)
                .setAllowedUids(allowedUids).build()
        updateConfiguration(iface, capabilities = nc).expectResult(iface.name)

Key points about the code

  • allowUids is the set of app UIDs allowed to use the network.
  • updateConfiguration() updates the configuration to restrict the network to the provided set of UIDs.