配置内部以太网

Android Auto OS 13 及更高版本包含一些可让您配置和管理以太网的功能。图 1 显示了汽车的一个网络图示例:

Android Auto 网络

图 1. Android Auto 网络。

此图显示了您的 OEM 网络应用在 EthernetManager 类中调用的方法,以配置和管理车载以太网网络(eth0.1、eth0.2 和 eth0.3)。图 1 的其余部分不在本文档的介绍范围内。

设定默认以太网设置

如需设定默认网络设置,请使用资源叠加层 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>

此示例显示了来自 config.xmlconfig_ethernet_interfaces 资源叠加层。

代码要点

  • eth1eth2eth3 是正在配置的网络接口的名称。
  • 连续数字 12, 13, 14, 15 表示处于启用状态的网络功能
  • ip=gateway=dns 用于为网络设置初始 IP 地址、网关和 DNS。

启用或停用网络接口

如需启用网络接口,请调用 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);
    }
}

代码要点

  • ifaceName 是要启用的网络接口的名称。
  • getMainExecutor() 会返回应用环境。
  • OutcomeReceiver 是用于传达完成情况的回调:在成功时返回更新后的网络名称,或在出错时返回 EthernetNetworkManagementException

网络接口处于启用状态时,会使用 EthernetManager.updateConfiguration() 设置的配置。如果 EthernetManager.updateConfiguration() 尚未设置配置,网络接口会使用资源叠加层 config_ethernet_interfaces,如果没有叠加层,则会使用默认以太网配置。

如需停用网络接口,请调用 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);
    }
}

代码要点

  • ifaceName 是要停用的网络接口的名称。
  • getMainExecutor() 会返回应用环境。
  • OutcomeReceiver 是用于传达完成情况的回调:在成功时返回更新后的网络名称,或在出错时返回 EthernetNetworkManagementException

更新网络配置

如需更新以太网配置,请调用 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);

    }
}

代码要点

  • getCapabilities() 是一个辅助方法,用于获取当前网络功能,并调用 convertToUIDs() 将人类可读的软件包名称转换为 Linux 唯一标识符 (UID)。通常,您无法提前知道关联软件包的 UID。因此,如果您想使用 EthernetManager.updateConfiguration() 以仅限部分应用可以访问,则需要使用这些应用的 UID。
  • request 是要用于内部网络的配置。该请求可以包含 IP 配置和网络功能的新设置。如果相应网络已向连接堆栈注册,则会根据配置进行更新。此配置在重新启动后不会保留。
  • getMainExecutor() 会返回在上面调用监听器的执行程序。
  • mCallback 是用于传达完成情况的回调:在成功时返回更新后的网络名称,或在出错时返回 EthernetNetworkManagementException

updateConfiguration() 可能会更新 Android 连接堆栈认为不可变的网络特征。网络会被关闭、更新,然后重新启用,以便更新这些不可变属性。

将网络限制为仅部分应用可访问

您可以使用 EthernetManager#updateConfiguration 以仅限部分已获准的 UID 可以访问。使用此方法可涵盖需要此功能的用例,例如仅供一小部分 OEM 应用使用的内部车载网络。

Android 主要通过 UID 跟踪应用。UIDToPackageNameConverter.java 中的以下代码展示了如何根据一串软件包名称获取一系列 UID:

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;

代码要点

  • getApplicationInfoAsuser().uid 用于从软件包名称中检索 UID。
  • uids 是生成的整数数组。

EthernetManagerTest.kt 中的以下代码展示了如何通过获准使用相应网络的应用的 UID 更新网络接口配置:

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

代码要点

  • allowUids 是获准使用相应网络的一组应用 UID。
  • updateConfiguration() 会更新配置,以将网络限制为仅所提供的一组 UID 可访问。