核心推荐 :Android 14+ / Android 16 优先使用
EthernetManager.updateConfiguration()+EthernetNetworkUpdateRequest。该 API 是官方推荐的新接口(尤其适用于 Automotive 及新网络栈),支持异步回调、异常处理更完善,同样会将配置持久化到
/data/misc/ethernet/ipconfig.txt,重启后仍然生效。
1. 前提条件(必须满足)
| 项目 | 要求 |
|---|---|
| 签名 | 平台签名(platform.x509.pem + platform.pk8) |
| 安装位置 | /system/priv-app/ 或 /product/priv-app/ 或 /system_ext/priv-app/ |
| sharedUserId | android:sharedUserId="android.uid.system"(强烈推荐) |
| 关键权限 | MANAGE_ETHERNET_NETWORKS(Android 14+ 必须)、CONNECTIVITY_INTERNAL、NETWORK_STACK、CHANGE_NETWORK_STATE 等 |
| 接口名 | 通常为 eth0(可通过 EthernetManager.getAvailableInterfaces() 确认) |
2. 系统 App 工程配置
2.1 AndroidManifest.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.ethernetconfig"
android:sharedUserId="android.uid.system">
<!-- Android 14+ 新 API 核心权限 -->
<uses-permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.NETWORK_STACK" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:persistent="true"
android:process="system"
android:directBootAware="true">
<!-- 你的 Activity / Service / Application -->
</application>
</manifest>
2.2 privapp-permissions 白名单(必须)
在 frameworks/base/data/etc/privapp-permissions-platform.xml 或设备 overlay 中添加:
xml
<privapp-permissions package="com.yourcompany.ethernetconfig">
<permission name="android.permission.MANAGE_ETHERNET_NETWORKS"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.NETWORK_STACK"/>
</privapp-permissions>
2.3 签名配置(build.gradle 示例)
gradle
android {
signingConfigs {
platform {
storeFile file("platform.keystore") // 替换为你的 platform 密钥
storePassword "android"
keyAlias "platform"
keyPassword "android"
}
}
buildTypes {
release {
signingConfig signingConfigs.platform
}
}
}
2.4 AOSP Android.bp 示例
bp
android_app {
name: "EthernetConfig",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
platform_apis: true,
privileged: true,
certificate: "platform",
system_ext_specific: true, // 或 product_specific / proprietary
}
在 device.mk 中加入:
makefile
PRODUCT_PACKAGES += EthernetConfig
3. 新 API 核心实现(推荐)
Android 14 引入、Android 16 完全支持的新 API:
EthernetNetworkUpdateRequestEthernetManager.updateConfiguration(String iface, EthernetNetworkUpdateRequest request, Executor executor, OutcomeReceiver<String, EthernetNetworkManagementException> receiver)
3.1 完整 Helper 类
java
package com.yourcompany.ethernetconfig;
import android.content.Context;
import android.net.EthernetManager;
import android.net.EthernetNetworkManagementException;
import android.net.EthernetNetworkUpdateRequest;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.StaticIpConfiguration;
import android.os.OutcomeReceiver;
import android.util.Log;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.concurrent.Executor;
public class EthernetIpHelperNewApi {
private static final String TAG = "EthernetIpHelperNewApi";
private static final String IFACE = "eth0"; // 可通过 getAvailableInterfaces() 动态获取
private final EthernetManager mEthManager;
private final Executor mExecutor;
public EthernetIpHelperNewApi(Context context) {
mEthManager = context.getSystemService(EthernetManager.class);
// 推荐使用主线程或自定义线程池
mExecutor = context.getMainExecutor();
}
/**
* 设置静态 IP(使用新 API,持久化)
*
* @param ip 例如 "192.168.1.100"
* @param prefixOrMask 前缀长度 "24" 或掩码 "255.255.255.0"
* @param gateway 网关,可为 null
* @param dns1 主 DNS,可为 null
* @param dns2 备 DNS,可为 null
* @param callback 结果回调(可选)
*/
public void setStaticIp(String ip,
String prefixOrMask,
String gateway,
String dns1,
String dns2,
ResultCallback callback) {
try {
// 1. 构建 StaticIpConfiguration
StaticIpConfiguration.Builder staticBuilder = new StaticIpConfiguration.Builder();
InetAddress ipAddr = InetAddress.getByName(ip);
int prefix = parsePrefix(prefixOrMask);
staticBuilder.setIpAddress(new LinkAddress(ipAddr, prefix));
if (gateway != null && !gateway.isEmpty()) {
staticBuilder.setGateway(InetAddress.getByName(gateway));
}
ArrayList<InetAddress> dnsList = new ArrayList<>();
if (dns1 != null && !dns1.isEmpty()) {
dnsList.add(InetAddress.getByName(dns1));
}
if (dns2 != null && !dns2.isEmpty()) {
dnsList.add(InetAddress.getByName(dns2));
}
if (!dnsList.isEmpty()) {
staticBuilder.setDnsServers(dnsList);
}
StaticIpConfiguration staticConfig = staticBuilder.build();
// 2. 构建 IpConfiguration
IpConfiguration config = new IpConfiguration();
config.setIpAssignment(IpAssignment.STATIC);
config.setProxySettings(ProxySettings.NONE);
config.setStaticIpConfiguration(staticConfig);
// 3. 构建新 API 请求
EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder()
.setIpConfiguration(config)
.build();
// 4. 调用新 API
mEthManager.updateConfiguration(IFACE, request, mExecutor,
new OutcomeReceiver<String, EthernetNetworkManagementException>() {
@Override
public void onResult(String result) {
Log.i(TAG, "Static IP update success: " + result);
if (callback != null) {
callback.onSuccess(result);
}
}
@Override
public void onError(EthernetNetworkManagementException e) {
Log.e(TAG, "Static IP update failed", e);
if (callback != null) {
callback.onError(e);
}
}
});
} catch (Exception e) {
Log.e(TAG, "setStaticIp exception", e);
if (callback != null) {
callback.onError(e);
}
}
}
/**
* 切换回 DHCP(使用新 API)
*/
public void setDhcp(ResultCallback callback) {
try {
IpConfiguration config = new IpConfiguration();
config.setIpAssignment(IpAssignment.DHCP);
config.setProxySettings(ProxySettings.NONE);
config.setStaticIpConfiguration(null);
EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder()
.setIpConfiguration(config)
.build();
mEthManager.updateConfiguration(IFACE, request, mExecutor,
new OutcomeReceiver<String, EthernetNetworkManagementException>() {
@Override
public void onResult(String result) {
Log.i(TAG, "DHCP update success: " + result);
if (callback != null) {
callback.onSuccess(result);
}
}
@Override
public void onError(EthernetNetworkManagementException e) {
Log.e(TAG, "DHCP update failed", e);
if (callback != null) {
callback.onError(e);
}
}
});
} catch (Exception e) {
Log.e(TAG, "setDhcp exception", e);
if (callback != null) {
callback.onError(e);
}
}
}
/**
* 获取当前配置(旧 API 仍可用,或使用 getConfiguration)
*/
public IpConfiguration getCurrentConfig() {
return mEthManager.getConfiguration(IFACE);
}
/**
* 获取可用接口列表
*/
public String[] getAvailableInterfaces() {
return mEthManager.getAvailableInterfaces();
}
private int parsePrefix(String maskOrPrefix) {
if (maskOrPrefix.contains(".")) {
// 255.255.255.0 → 24
try {
return android.net.NetworkUtils.netmaskToPrefixLength(
(Inet4Address) InetAddress.getByName(maskOrPrefix));
} catch (Exception e) {
Log.e(TAG, "parsePrefix failed", e);
return 24; // 默认
}
}
return Integer.parseInt(maskOrPrefix);
}
/** 结果回调接口 */
public interface ResultCallback {
void onSuccess(String result);
void onError(Exception e);
}
}
3.2 使用示例
java
EthernetIpHelperNewApi helper = new EthernetIpHelperNewApi(this);
// 设置静态 IP
helper.setStaticIp(
"192.168.1.100", // IP
"24", // 前缀长度(或 "255.255.255.0")
"192.168.1.1", // 网关
"8.8.8.8", // DNS1
"8.8.4.4", // DNS2
new EthernetIpHelperNewApi.ResultCallback() {
@Override
public void onSuccess(String result) {
// 成功,配置已持久化
Log.i("Main", "IP 设置成功: " + result);
}
@Override
public void onError(Exception e) {
// 失败处理
Log.e("Main", "IP 设置失败", e);
}
}
);
// 切换回 DHCP
helper.setDhcp(new EthernetIpHelperNewApi.ResultCallback() {
@Override
public void onSuccess(String result) {
Log.i("Main", "已切换到 DHCP");
}
@Override
public void onError(Exception e) {
Log.e("Main", "切换 DHCP 失败", e);
}
});
4. 新旧 API 对比
| 特性 | 旧 API setConfiguration() |
新 API updateConfiguration() |
|---|---|---|
| 引入版本 | 早期(@hide / @UnsupportedAppUsage) | Android 14+ |
| 调用方式 | 同步 | 异步 + OutcomeReceiver |
| 异常处理 | 抛出 Exception | 结构化 EthernetNetworkManagementException |
| 权限要求 | CONNECTIVITY_INTERNAL 等 | 必须 MANAGE_ETHERNET_NETWORKS |
| 持久化 | 是(写入 ipconfig.txt) | 是(相同路径) |
| 官方推荐(Android 16) | 仍可用,但不推荐新项目 | 强烈推荐 |
| Automotive / 新网络栈 | 兼容性一般 | 最佳支持 |
5. 编译与部署
-
将工程放入
packages/apps/YourEthernetConfig/ -
使用上方
Android.bp配置 -
在
device.mk添加PRODUCT_PACKAGES += EthernetConfig -
全编译或模块编译:
bashmmm packages/apps/YourEthernetConfig -
刷机或
adb root && adb remount && adb push ...
6. 验证方法
bash
# 查看当前接口状态
adb shell ip addr show eth0
# 查看持久化配置文件
adb shell cat /data/misc/ethernet/ipconfig.txt
# 重启验证是否持久化
adb reboot
adb shell ip addr show eth0
# 查看可用接口
adb shell dumpsys ethernet
7. 注意事项与常见问题
- 必须平台签名 + priv-app,普通签名应用无法调用。
MANAGE_ETHERNET_NETWORKS是 Android 14+ 新权限,必须在 privapp-permissions 中声明。- 接口名不一定永远是
eth0,建议先调用getAvailableInterfaces()确认。 - 配置写入
/data/misc/ethernet/ipconfig.txt后,系统网络栈会在启动时自动加载。 - 如果同时存在 init 脚本或 vendor 层强制配置,可能会产生冲突,优先使用 Framework API。
- 新 API 是异步的,UI 操作请在回调中更新状态。
总结推荐路径(Android 16)
- 做成 priv-app + 平台签名
- 使用 EthernetManager.updateConfiguration() + EthernetNetworkUpdateRequest
- 正确添加
MANAGE_ETHERNET_NETWORKS权限白名单 - 配置自动持久化,重启后仍然生效
这是目前 Android 16 AOSP 上最符合官方架构、最干净、最可维护的系统 App 修改 eth0 IP 方案。