Configurar redes Ethernet internas

El SO Android Auto 13 y versiones posteriores contiene funciones que te permiten configurar y administrar redes Ethernet. En la Figura 1, se muestra un ejemplo de diagrama de red para un automóvil:

Redes de Android Auto

Figura 1: Redes de Android Auto

En esta imagen, se muestra la app de redes del OEM que llama a métodos en la clase EthernetManager para configurar y administrar redes Ethernet integradas (eth0.1, eth0.2 y eth0.3). El resto de la Figura 1 está fuera del alcance de este documento.

Establece la configuración de red Ethernet predeterminada

Para establecer la configuración de red predeterminada, usa la superposición de recursos 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>

En este ejemplo, se muestra la superposición de recursos config_ethernet_interfaces de config.xml.

Puntos clave sobre el código

  • eth1, eth2 y eth3 son los nombres de la interfaz de red que se está configurando.
  • Los números consecutivos de 12, 13, 14, 15 representan las funciones de red que se habilitan.
  • ip=, gateway= y dns se usan para establecer la dirección IP, la puerta de enlace y el DNS iniciales de la red.

Habilita o inhabilita una interfaz de red

Para habilitar una interfaz de red, llama a 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);
    }
}

Puntos clave sobre el código

  • ifaceName es el nombre de la interfaz de red que se habilitará.
  • getMainExecutor() muestra el contexto de la app.
  • OutcomeReceiver es una devolución de llamada que se usa para comunicar la finalización y muestra el nombre de red actualizado si se realiza correctamente o EthernetNetworkManagementException si se produce un error.

Cuando se habilita una interfaz de red, usa la configuración que estableció EthernetManager.updateConfiguration(). Si EthernetManager.updateConfiguration() no configuró una configuración, la interfaz de red usa la superposición de recursos config_ethernet_interfaces o la configuración predeterminada de la red Ethernet si no hay una superposición disponible.

Para inhabilitar una interfaz de red, llama a 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);
    }
}

Puntos clave sobre el código

  • ifaceName es el nombre de la interfaz de red que se inhabilitará.
  • getMainExecutor() muestra el contexto de la app.
  • OutcomeReceiver es una devolución de llamada que se usa para comunicar la finalización y muestra el nombre de la red actualizado si se realiza correctamente o EthernetNetworkManagementException si se produce un error.

Actualiza la configuración de red

Para actualizar la configuración de la red Ethernet, llama a 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);

    }
}

Puntos clave sobre el código

  • getCapabilities() es un método auxiliar que obtiene las capacidades de red actuales y llama a convertToUIDs() para convertir los nombres de paquetes legibles por humanos en un identificador único (UID) de Linux. Por lo general, no conoces los UIDs con anticipación para sus paquetes asociados. Por lo tanto, si deseas usar EthernetManager.updateConfiguration() para limitar el acceso a un subconjunto de apps, debes usar sus UIDs.
  • request es la configuración que se usará para la red interna. La solicitud puede contener una configuración nueva para la configuración de IP y las capacidades de red. Si la red está registrada en la pila de conectividad, se actualiza según la configuración. Esta configuración no persiste en los reinicios.
  • getMainExecutor() muestra el ejecutor en el que se invoca el objeto de escucha.
  • mCallback es la devolución de llamada que se usa para comunicar la finalización y muestra el nombre de red actualizado si se realiza correctamente o EthernetNetworkManagementException si se produce un error.

updateConfiguration() podría actualizar las características de una red que la pila de conectividad de Android considera inmutable. La red se baja, se actualiza y se vuelve a subir para que se actualicen estos atributos inmutables.

Cómo restringir una red a un subconjunto de apps

Puedes usar EthernetManager#updateConfiguration para limitar el acceso solo a un subconjunto de UIDs permitidos. Usa este método para cubrir los casos de uso en los que esto es obligatorio, como en el caso de las redes vehiculares internas que solo puede usar un subconjunto pequeño de apps de OEM.

Android realiza un seguimiento de las apps principalmente por su UID. En el siguiente código de UIDToPackageNameConverter.java, se muestra cómo obtener una serie de UIDs a partir de una cadena de nombres de paquetes:

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;

Puntos clave sobre el código

  • getApplicationInfoAsuser().uid se usa para recuperar el UID del nombre del paquete.
  • uids es el array de números enteros generado.

En el siguiente código de EthernetManagerTest.kt, se muestra cómo actualizar la configuración de la interfaz de red con un UID de las apps que pueden usar la red:

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

Puntos clave sobre el código

  • allowUids es el conjunto de UIDs de apps que pueden usar la red.
  • updateConfiguration() actualiza la configuración para restringir la red al conjunto de UIDs proporcionado.