当前位置: 首页 > news >正文

Android 13 完整实现 USB 网卡支持与网络优先级配置(USB>WiFi>4G)

一、需求背景与核心目标

在嵌入式 Android 设备(如工业平板、智能车机、机顶盒)开发中,常需通过 USB 转网卡扩展稳定的有线网络,但 Android 原生系统存在两大问题:

  1. 对 USB 网卡的识别与配置支持有限,默认不显示 USB 网卡接口;
  2. 网络优先级默认是 WiFi > 以太网 > 4G,无法满足 “USB 网卡优先” 的场景(如工业场景需稳定有线网络)。

本文基于 Android 13 AOSP 源码,从 内核驱动→框架层识别→Settings 可视化配置→网络优先级强制调整 全流程落地,最终实现:

  1. USB 网卡自动识别、热插拔兼容与 DHCP / 静态 IP 配置;
  2. 网络优先级严格遵循 USB 网卡(以太网)> WiFi > 4G
  3. 配置持久化与系统权限适配(含 SELinux 规则)。

二、环境准备

类别具体要求
硬件1. 支持 USB Host 的 Android 设备(如 RK3568 开发板);2. USB 转网卡(推荐 Realtek RTL8153/ASIX AX88179,驱动兼容性好)。
软件1. Android 13 AOSP 源码;2. ADB 工具与内核编译环境;3. 文本编辑器(如 VS Code)。
前置知识了解 Android 网络框架(ConnectivityService)、Udev 设备命名、SELinux 规则、Binder 通信。

三、Step 1:内核层适配 USB 网卡驱动

USB 网卡需内核驱动支持才能被系统识别,需先确保驱动编译进内核。

1.1 启用 USB 网卡驱动配置

内核配置文件路径(以 RK3568 为例):kernel/arch/arm64/configs/rk3568_defconfig

添加以下配置(根据 USB 网卡芯片选择,可通过 lsusb 查看芯片型号):

bash

# 通用 USB 以太网驱动(基础依赖)
CONFIG_USB_NET_CDCETHER=y
# Realtek 芯片(如 RTL8153,常见于 USB 3.0 网卡)
CONFIG_USB_NET_R8152=y
# ASIX 芯片(如 AX88179,常见于千兆 USB 网卡)
CONFIG_USB_NET_AX88179_178A=y
# 启用 USB Host 模式(确保设备能识别 USB 外设)
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_XHCI_HCD=y

1.2 编译内核与验证驱动加载

bash

# 编译内核(生成 zImage 和设备树 dtb)
make ARCH=arm64 rk3568_defconfig -j8
make ARCH=arm64 -j8# 刷入内核后,插入 USB 网卡,通过日志验证驱动是否加载
adb shell dmesg | grep -i "usb ethernet"
# 成功日志示例:usb 1-1: r8152: loaded - eth_usb (USB 网卡接口名)

四、Step 2:框架层修改(核心:让系统识别 USB 网卡)

Android 13 以太网核心逻辑已迁移至 packages/modules/Connectivity,需修改 接口识别 与 网络评分 相关文件,确保 USB 网卡被跟踪且优先级高于其他网络。

2.1 固定 USB 网卡接口名(避免随机变化)

USB 网卡默认接口名(如 eth1/enx001e0630a0b1)可能随插拔变化,需通过 Udev 规则固定为 eth_usb,方便上层统一识别。

2.1.1 创建 Udev 规则文件

文件路径:system/core/rootdir/etc/udev/rules.d/70-persistent-net.rules

添加规则(需替换为你的 USB 网卡 MAC 地址,通过 adb shell cat /sys/class/net/<临时接口名>/address 获取):

bash

# USB 网卡固定接口名:SUBSYSTEM=="net"(网络设备),ACTION=="add"(设备插入),匹配 MAC 地址后命名为 eth_usb
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:1e:06:30:a0:b1", NAME="eth_usb"
2.1.2 验证接口名

bash

# 插入 USB 网卡后,执行以下命令确认接口名为 eth_usb
adb shell ip link show | grep eth_usb
# 成功输出示例:2: eth_usb: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000

2.2 修改 EthernetTracker:识别 USB 网卡接口

EthernetTracker 负责扫描系统网络接口,需扩展其接口匹配正则,确保 eth_usb 被识别为合法以太网接口。

文件路径:packages/modules/Connectivity/service-t/src/com/android/server/ethernet/EthernetTracker.java

2.2.1 扩展接口匹配正则

找到 updateIfaceMatchRegexp 方法(负责生成接口匹配规则),修改为:

java

运行

private void updateIfaceMatchRegexp() {// 从系统资源获取默认以太网正则(通常为 "eth\\d+",匹配内置以太网如 eth0)final String defaultMatch = mDeps.getInterfaceRegexFromResource(mContext);// 新增 USB 网卡接口模式:eth_usb(固定名)+ enx开头(兼容自动命名场景)final String usbIfacePattern = "eth_usb|enx[0-9a-fA-F]+";// 组合正则:保留原有 eth 接口,新增 USB 网卡支持final String extendedMatch = "(" + defaultMatch + "|" + usbIfacePattern + ")";// 结合测试接口配置(原有逻辑不变,测试接口如 tap0 不影响实际功能)mIfaceMatch = mIncludeTestInterfaces? "(" + extendedMatch + "|" + TEST_IFACE_REGEXP + ")": extendedMatch;Log.d(TAG, "USB 网卡接口匹配正则生效:" + mIfaceMatch);
}
2.2.2 验证识别效果

bash

# 重启设备后,通过日志确认 USB 网卡被跟踪
adb logcat -s EthernetTracker | grep "Tracking interface"
# 成功日志示例:Tracking interface in client mode: eth_usb(eth_usb 被识别为客户端模式接口)

2.3 修改 EthernetNetworkFactory:提高 USB 网卡优先级

Android 网络优先级由 网络评分(NetworkScore) 决定,评分越高越优先。需修改 EthernetNetworkFactory 中评分生成逻辑,让 USB 网卡评分高于 WiFi 和 4G。

文件路径:packages/modules/Connectivity/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java

2.3.1 理解评分调用流程

getBestNetworkScore() 是生成以太网评分的核心方法,仅在以下场景被调用:

  1. USB 网卡链路接通时:链路从 down 变为 up,调用 registerNetworkOffer 注册网络服务,传入评分;
  2. 网络能力更新时:修改 USB 网卡的网络能力(如支持互联网),重新注册服务时传入评分。

调用链简化:updateLinkState(up=true) / setCapabilities() → registerNetworkOffer() → getBestNetworkScore() → 评分传递给 ConnectivityService。

2.3.2 修改评分生成逻辑

原代码默认返回空评分(无优先级),需设置具体高分(如 100,假设系统评分范围 0-100):

java

运行

// NetworkInterfaceState 类中的 getBestNetworkScore 方法
private static NetworkScore getBestNetworkScore() {// 核心修改:设置评分 100(高于 WiFi 默认评分 50、4G 默认评分 40)return new NetworkScore.Builder().setScore(100)  // 评分值,越高优先级越高.build();
}
2.3.3 验证评分生效

bash

# 查看 ConnectivityService 日志,确认以太网评分
adb logcat -s ConnectivityService | grep "score"
# 成功日志示例:Ethernet network score=100, selecting as default(以太网评分 100,被选为默认网络)

五、Step 3:Settings 可视化配置(用户可操作 USB 网卡)

需在系统设置中添加 USB 网卡的 接口选择 和 IP 配置 界面,支持用户切换 DHCP / 静态 IP。

3.1 新增布局文件(接口选择与配置项)

文件路径:packages/apps/Settings/res/xml/ethernet_settings.xml

xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"><!-- 1. USB 网卡/内置以太网接口选择 --><ListPreferenceandroid:key="ethernet_interface_selector"android:title="@string/ethernet_interface_title"android:summary="@string/ethernet_interface_summary"android:entries="@array/ethernet_interface_entries"android:entryValues="@array/ethernet_interface_values"android:defaultValue="eth0" /><!-- 2. IP 模式选择(DHCP/静态) --><ListPreferenceandroid:key="ethernet_ip_mode"android:title="@string/ethernet_ip_mode_title"android:summary="@string/ethernet_ip_mode_summary"android:entries="@array/ethernet_ip_modes"android:entryValues="@array/ethernet_ip_mode_values"android:defaultValue="dhcp" /><!-- 3. 静态 IP 配置项(动态显示/隐藏,默认隐藏) --><EditTextPreferenceandroid:key="ethernet_static_ip"android:title="@string/ethernet_static_ip_title"android:hint="192.168.1.100"  <!-- 默认提示 -->android:inputType="textUri"    <!-- 限制 IP 格式输入 -->android:visible="false" /><EditTextPreferenceandroid:key="ethernet_static_gateway"android:title="@string/ethernet_static_gateway_title"android:hint="192.168.1.1"android:inputType="textUri"android:visible="false" /><EditTextPreferenceandroid:key="ethernet_static_netmask"android:title="@string/ethernet_static_netmask_title"android:hint="255.255.255.0"android:inputType="textUri"android:visible="false" /><EditTextPreferenceandroid:key="ethernet_static_dns1"android:title="@string/ethernet_static_dns1_title"android:hint="8.8.8.8"android:inputType="textUri"android:visible="false" />
</PreferenceScreen>

3.2 新增字符串资源

文件路径:packages/apps/Settings/res/values/strings.xml

xml

<!-- USB 网卡配置相关字符串 -->
<string name="ethernet_interface_title">以太网接口</string>
<string name="ethernet_interface_summary">选择需配置的以太网接口</string>
<string-array name="ethernet_interface_entries"><item>内置以太网(eth0)</item><item>USB 网卡(eth_usb)</item>
</string-array>
<string-array name="ethernet_interface_values"><item>eth0</item><item>eth_usb</item>
</string-array><string name="ethernet_ip_mode_title">IP 配置模式</string>
<string name="ethernet_ip_mode_summary">选择 IP 获取方式</string>
<string-array name="ethernet_ip_modes"><item>DHCP(自动获取)</item><item>静态 IP(手动配置)</item>
</string-array>
<string-array name="ethernet_ip_mode_values"><item>dhcp</item><item>static</item>
</string-array><string name="ethernet_static_ip_title">IP 地址</string>
<string name="ethernet_static_gateway_title">网关</string>
<string name="ethernet_static_netmask_title">子网掩码</string>
<string name="ethernet_static_dns1_title">DNS 服务器 1</string>

3.3 实现逻辑代码(刷新接口与保存配置)

文件路径:packages/apps/Settings/src/com/android/settings/ethernet/EthernetSettings.java

核心代码片段(含接口刷新、配置加载、保存逻辑):

java

运行

public class EthernetSettings extends PreferenceFragmentCompat {private static final String TAG = "EthernetSettings";private ListPreference mInterfaceSelector; // 接口选择器private ListPreference mIpModeSelector;    // IP 模式选择器private EditTextPreference mStaticIp;      // 静态 IP 输入框private EthernetManager mEthManager;private String mSelectedInterface;         // 当前选中接口(如 eth_usb)@Overridepublic void onCreatePreferences(Bundle savedInstanceState, String rootKey) {setPreferencesFromResource(R.xml.ethernet_settings, rootKey);mEthManager = getContext().getSystemService(EthernetManager.class);initPreferences(); // 初始化偏好设置}// 初始化选择器与监听事件private void initPreferences() {// 1. 接口选择器:刷新可用接口(含 USB 网卡)mInterfaceSelector = findPreference("ethernet_interface_selector");mInterfaceSelector.setOnPreferenceChangeListener((pref, newValue) -> {mSelectedInterface = (String) newValue;updateIpConfig(); // 切换接口后加载对应配置return true;});// 2. IP 模式选择器:控制静态 IP 项显示/隐藏mIpModeSelector = findPreference("ethernet_ip_mode");mIpModeSelector.setOnPreferenceChangeListener((pref, newValue) -> {boolean isStatic = "static".equals(newValue);showStaticIpItems(isStatic); // 静态模式显示输入框,DHCP 隐藏return true;});// 3. 静态 IP 配置项初始化(网关、子网掩码、DNS 类似)mStaticIp = findPreference("ethernet_static_ip");// 绑定保存按钮(可在布局中添加 ButtonPreference,此处简化为返回键保存)getActivity().getOnBackPressedDispatcher().addCallback(this, () -> {saveConfig(); // 返回时保存配置getActivity().finish();});}// 刷新可用接口列表(含 USB 网卡)private void refreshInterfaceList() {try {// 从 EthernetManager 获取所有可用以太网接口(含 eth_usb)String[] ifaces = mEthManager.getAvailableInterfaces();List<CharSequence> entries = new ArrayList<>();List<CharSequence> values = new ArrayList<>();for (String iface : ifaces) {// 区分内置以太网与 USB 网卡,显示友好名称String label = iface.equals("eth_usb") ? "USB 网卡(" + iface + ")" : "内置以太网(" + iface + ")";entries.add(label);values.add(iface);}// 更新选择器选项mInterfaceSelector.setEntries(entries.toArray(new CharSequence[0]));mInterfaceSelector.setEntryValues(values.toArray(new CharSequence[0]));// 默认选中第一个接口(优先 USB 网卡)if (!values.isEmpty()) {mSelectedInterface = (String) values.get(0);mInterfaceSelector.setValue(mSelectedInterface);}} catch (RemoteException e) {Log.e(TAG, "获取接口列表失败:" + e.getMessage());}}// 加载当前接口的 IP 配置(DHCP/静态)private void updateIpConfig() {if (mSelectedInterface == null) return;// 从 EthernetManager 获取当前接口的配置IpConfiguration config = mEthManager.getConfiguration(mSelectedInterface);if (config == null) return;// 1. 更新 IP 模式选择器String mode = config.getIpAssignment() == IpAssignment.STATIC ? "static" : "dhcp";mIpModeSelector.setValue(mode);showStaticIpItems(mode.equals("static"));// 2. 加载静态 IP 配置到输入框(DHCP 模式无需加载)if (config.getStaticIpConfiguration() != null) {StaticIpConfiguration staticConfig = config.getStaticIpConfiguration();// IP 地址(如 192.168.1.100)mStaticIp.setText(staticConfig.getIpAddress().getAddress().getHostAddress());// 网关、子网掩码、DNS 加载(类似 mStaticIp,略)}}// 显示/隐藏静态 IP 配置项private void showStaticIpItems(boolean show) {mStaticIp.setVisible(show);findPreference("ethernet_static_gateway").setVisible(show);findPreference("ethernet_static_netmask").setVisible(show);findPreference("ethernet_static_dns1").setVisible(show);}// 保存 USB 网卡配置(DHCP/静态 IP)private void saveConfig() {if (mSelectedInterface == null) return;IpConfiguration config = new IpConfiguration();// 1. 设置 IP 模式(DHCP/静态)if ("dhcp".equals(mIpModeSelector.getValue())) {config.setIpAssignment(IpAssignment.DHCP); // DHCP 自动获取} else {// 2. 构建静态 IP 配置(校验 IP 格式)try {StaticIpConfiguration staticConfig = new StaticIpConfiguration.Builder().setIpAddress(new LinkAddress(InetAddress.parseNumericAddress(mStaticIp.getText()), 24 // 子网前缀 24(255.255.255.0))).setGateway(InetAddress.parseNumericAddress(findPreference("ethernet_static_gateway").getText())).setDnsServers(List.of(InetAddress.parseNumericAddress(findPreference("ethernet_static_dns1").getText()))).build();config.setIpAssignment(IpAssignment.STATIC);config.setStaticIpConfiguration(staticConfig);} catch (Exception e) {Toast.makeText(getContext(), "静态 IP 配置无效(如格式错误)!", Toast.LENGTH_SHORT).show();return;}}// 3. 保存配置到 USB 网卡接口(如 eth_usb)try {mEthManager.setConfiguration(mSelectedInterface, config);Toast.makeText(getContext(), "配置保存成功!", Toast.LENGTH_SHORT).show();} catch (Exception e) {Log.e(TAG, "保存配置失败:" + e.getMessage());Toast.makeText(getContext(), "保存失败,请检查权限!", Toast.LENGTH_SHORT).show();}}// 页面 resume 时刷新接口列表(兼容热插拔)@Overridepublic void onResume() {super.onResume();refreshInterfaceList();updateIpConfig();}
}

六、Step 4:权限与 SELinux 适配(避免访问被拒)

USB 网卡操作需系统权限与 SELinux 规则支持,否则会出现 “权限不足” 或 “访问被拒” 错误。

6.1 配置 Settings 权限

文件路径:packages/apps/Settings/AndroidManifest.xml

添加网络配置与 USB 访问权限:

xml

<manifest ...><!-- 网络配置权限 --><uses-permission android:name="android.permission.NETWORK_SETTINGS" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!-- USB 设备访问权限 --><uses-permission android:name="android.permission.USB_PERMISSION" /><uses-feature android:name="android.hardware.usb.host" required="false" />
</manifest>

6.2 添加 SELinux 规则(允许以太网服务访问 USB 网卡)

SELinux 开启 enforcing 模式时,需允许 ethernet_service 访问 USB 网卡设备节点,否则会出现 avc: denied 错误。

文件路径:device/<厂商>/<设备>/sepolicy/private/ethernet.te(如 device/rockchip/rk3568/sepolicy/private/ethernet.te

添加规则:

te

# 允许 ethernet_service 访问 USB 网卡的网络设备(类型为 netdev_usb)
allow ethernet_service netdev_usb:netdev { create open read write ioctl getattr setattr add_name remove_name rename };
# 允许 ethernet_service 访问 USB 设备节点(如 /dev/bus/usb)
allow ethernet_service usb_device:chr_file { read write open ioctl };

编译并刷入 SELinux 规则:

bash

# 重新编译 SELinux 策略
make sepolicy -j8
# 刷入设备(或随系统镜像一起刷入)
adb push out/target/product/rk3568/obj/ETC/sepolicy_intermediates/sepolicy /sys/fs/selinux/policy

七、Step 5:全流程测试验证

7.1 功能验证

  1. 接口识别:插入 USB 网卡,进入 “设置→网络与互联网→以太网”,下拉框应显示 “USB 网卡(eth_usb)”。

  2. DHCP 测试:选择 “USB 网卡”+“DHCP 模式”,保存后执行 adb shell ifconfig eth_usb,确认自动获取 IP(如 192.168.1.105)。

  3. 静态 IP 测试:配置静态 IP(如 192.168.1.100)、网关(192.168.1.1)、DNS(8.8.8.8),执行 adb shell ip addr show eth_usb 确认配置生效。

  4. 优先级测试:同时连接 USB 网卡、WiFi、4G,执行 adb shell dumpsys connectivity,确认默认网络为以太网(score=100);断开 USB 网卡,默认网络切换为 WiFi;断开 WiFi,切换为 4G。

7.2 常见问题与解决方案

问题现象原因分析解决方案
USB 网卡不识别,无 eth_usb1. 内核驱动未加载;2. Udev 规则无效。1. 检查 `dmesggrep usb确认驱动加载;<br>2. 重新执行adb shell udevadm control --reload-rules` 刷新 Udev 规则。
静态 IP 保存失败1. 权限不足;2. IP 格式错误。1. 确认 Settings 已声明 NETWORK_SETTINGS 权限;2. 检查 IP 是否符合格式(如 255.255.255.0)。
SELinux 访问被拒(avc: denied)SELinux 规则未添加 USB 网卡访问权限。1. 执行 `adb logcatgrep "avc: denied"查看缺失权限;<br>2. 在ethernet.te` 中添加对应规则并重新编译。
优先级不生效,WiFi 仍优先以太网评分未设置或低于 WiFi 评分。1. 确认 getBestNetworkScore() 已设置 setScore(100);2. 检查 WiFi 评分(通常在 WifiNetworkFactory 中,确保低于 100)。

八、总结

本文基于 Android 13 AOSP 源码,从底层到上层完整实现 USB 网卡支持,核心流程可概括为:

  1. 内核驱动:启用 USB 网卡驱动,确保硬件被识别;
  2. 接口固定:通过 Udev 规则将 USB 网卡命名为 eth_usb,避免随机变化;
  3. 框架识别:修改 EthernetTracker 扩展接口匹配正则,让系统跟踪 eth_usb
  4. 优先级提升:修改 EthernetNetworkFactory 的 getBestNetworkScore(),设置高分(100)确保 USB 网卡优先;
  5. 可视化配置:在 Settings 中添加接口选择与 IP 配置界面,支持用户操作;
  6. 权限适配:配置系统权限与 SELinux 规则,避免访问被拒。

该方案适用于嵌入式 Android 设备、工业控制、智能车机等场景,可根据实际硬件(如不同 USB 网卡芯片)和系统定制需求灵活调整。

http://www.dtcms.com/a/411000.html

相关文章:

  • 加强网站建设的措施莱芜中医院网站
  • 衡水企业网站设计网站上怎么做推广
  • 狄利克雷卷积
  • 沈阳 教育 公司 网站建设种子搜索网站怎么做的
  • 矩阵置零--leetcode
  • 删除iCloud中的照片但保留iPhone上的照片的两种方法
  • 巴中市住房和城乡建设局官方网站广告牌子设计图片
  • 找学校的网站塔城网站seo
  • oracle linux 10 +pg18 源码安装要点
  • 如何快速排查服务器宕机故障
  • 分sheet写入excel
  • 怎么把网站黑了传统文化网站建设
  • 【开题答辩全过程】以 SpringBootVue的旅游租车管理系统为例,包含答辩的问题和答案
  • Windows配置PicGo+Gitee图床——解决你的MarkDown笔记分享无图片的问题
  • 网站排名alexawordpress 商城安全
  • 【开题答辩过程】以《分布式菌菇销售系统》为例,不会开题答辩的可以进来看看
  • 广州优化网站关键词静态网站用什么做
  • 油棕种植密度控制:单位面积产值与光照利用优化
  • 紫金桥软件:深耕工业数据价值,赋能智造未来
  • Unity / C# 闭包详解 —— 按钮回调、协程、事件中的坑与修复
  • 使用Windbg分析dmp文件的方法以及实战分析实例分享
  • 什么是m3u8协议
  • 富文本编辑器Tinymce的使用、语言包配置、工具栏与工具栏组添加
  • 西安网站优化seo网站ico在后台哪里找到
  • 生成式人工智能教育应用的治理策略:构建“法律-伦理-标准”三位一体治理体系
  • 哪些行业对做网站的需求大qux wordpress
  • 智能中控终端:多系统联动管控中枢-LKONE立控信息
  • Mac M2安装VUE3
  • clang编译器 abseil-cpp中的ABSL_MUST_USE_RESULT
  • 网站营销怎么做做公众号用什么网站吗