1. framework 层修改,添加 app 所需的控制接口

  • 修改 frameworks/base/core/java/android/net/IEthernetManager.aidl
1
2
3
4
5
6
7
8
interface IEthernetManager
{
// ... 原有代码 ...

//为新方法添加AIDL接口
void setEthernetEnabled(boolean enabled);
boolean isEthernetEnabled();
}
  • 修改 frameworks/base/core/java/android/net/EthernetManager.java

添加方法 setEthernetEnabledisEthernetEnabled 注意注释中的@hide 不要省略

在 Java 开发中,*@hide* 是一种特殊的 Javadoc 注释,用于隐藏某些类、方法或字段,使其在生成的 API 文档中不可见。尽管这些元素在代码中是公开的,但通过 @hide 注释可以避免它们被外部开发者误用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* A class representing the IP configuration of the Ethernet network.
*
* @hide
*/
@SystemService(Context.ETHERNET_SERVICE)
public class EthernetManager {

// ... 原有代码 ...

/**
* 设置以太网启用/禁用状态。
* 禁用时系统会自动回退到 Wi-Fi。
* @hide
*/
public void setEthernetEnabled(boolean enabled) {
try {
mService.setEthernetEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

/**
* 获取以太网当前启用状态。
* @hide
*/
public boolean isEthernetEnabled() {
try {
return mService.isEthernetEnabled();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
  • 修改 frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java

添加成员变量``mEthernetEnabled、方法setEthernetEnabled和方法isEthernetEnabled`。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* EthernetServiceImpl handles remote Ethernet operation requests by implementing
* the IEthernetManager interface.
*/
public class EthernetServiceImpl extends IEthernetManager.Stub {
// ... 原有成员变量 ...

//新成员变量记录网口禁用状态
private boolean mEthernetEnabled = true;

// ... 原有代码 ...

/**
* Set Ethernet enable/disable state.
* @hide
*/
@Override
public void setEthernetEnabled(boolean enabled) {
enforceConnectivityInternalPermission();

if (mEthernetEnabled == enabled) return;
mEthernetEnabled = enabled;

mTracker.setEthernetEnabled(enabled);
}

/**
* Get Ethernet current enabled state.
* @hide
*/
@Override
public boolean isEthernetEnabled() {
return mEthernetEnabled;
}
}
  • 修改 frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetTracker.java

在方法 getInterfaces 添加 setEthernetEnabled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Tracks Ethernet interfaces and manages interface configurations.
*
* <p> Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined
* in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by
* not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag.
* Interfaces could have associated {@link android.net.IpConfiguration}.
* Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters
* connected over USB). This class supports multiple interfaces. When an interface appears on the
* system (or is present at boot time) this class will start tracking it and bring it up. Only
* interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are
* tracked.
*
* <p> All public or package private methods must be thread-safe unless stated otherwise.
*/
final class EthernetTracker {
// ... 原有代码 ...

String[] getInterfaces(boolean includeRestricted) {
return mFactory.getAvailableInterfaces(includeRestricted);
}
/**
* 新增方法
* Enable or disable all Ethernet interfaces.
* Uses INetworkManagementService to physically bring interface up/down.
*/
void setEthernetEnabled(boolean enabled) {
String[] ifaces = getInterfaces(true);
for (String iface : ifaces) {
try {
if (enabled) {
mNMService.setInterfaceUp(iface);
Log.i(TAG, "Interface " + iface + " brought UP");
} else {
mNMService.setInterfaceDown(iface);
Log.i(TAG, "Interface " + iface + " brought DOWN");
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to set interface state for " + iface, e);
}
}
}
}

将编译好的 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 添加到 Android Studio 项目 EthernetControl/app/libs/framework.jar

记得在 build.gradle 导入该 jar 包使用 compileOnly 表示只用于编译不打包进 apk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
plugins {
id 'com.android.application'
}

android {
compileSdkVersion 29
buildToolsVersion "29.0.3"

defaultConfig {
applicationId "com.rk3288.ethernetcontrol"
minSdkVersion 29
targetSdkVersion 29
versionCode 1
versionName "1.0"
}

// 签名配置
signingConfigs {
platform {
storeFile file("rk3288_android10.jks")
storePassword "123456"
keyAlias "rk3288_android10"
keyPassword "123456"
}
}

buildTypes {
debug {
signingConfig signingConfigs.platform
}
release {
signingConfig signingConfigs.platform
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

lintOptions {
checkReleaseBuilds false
abortOnError false
}
}

dependencies {
compileOnly files('libs/framework.jar')
}

2.app 代码(可以使用生成好的 apk, 该信息用于后期修改查看)

主要的 MainActivity.java 代码,且 app 也只有该代码实现全部业务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package com.rk3288.ethernetcontrol;

import android.app.Activity;
import android.content.Context;
import android.net.EthernetManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

/**
* Ethernet Control App
*
* Requires Framework modifications:
* 1. IEthernetManager.aidl: add setEthernetEnabled(boolean) and isEthernetEnabled()
* 2. EthernetManager.java: add setEthernetEnabled() and isEthernetEnabled()
* 3. EthernetServiceImpl.java: implement the methods
* 4. EthernetTracker.java: add setEthernetEnabled() to control interface state
*
* This app must be signed with platform key (android: sharedUserId = "android.uid.system")
*/
public class MainActivity extends Activity {
private static final String TAG = "EthernetControl";
private static final String ETH_IFACE = "eth0";
private static final String ETH_SERVICE = "ethernet";

private Switch mEthernetSwitch;
private TextView mStatusText;
private TextView mDetailText;
private Button mRefreshBtn;
private EthernetManager mEthernetManager;
private Handler mHandler = new Handler(Looper.getMainLooper());

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initViews();
initEthernetManager();
refreshStatus();
}

private void initViews() {
mEthernetSwitch = findViewById(R.id.ethernet_switch);
mStatusText = findViewById(R.id.status_text);
mDetailText = findViewById(R.id.detail_text);
mRefreshBtn = findViewById(R.id.refresh_btn);

mEthernetSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (!buttonView.isPressed()) return;
setEthernetState(isChecked);
}
});

mRefreshBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
refreshStatus();
Toast.makeText(MainActivity.this, "Status refreshed", Toast.LENGTH_SHORT).show();
}
});
}

private void initEthernetManager() {
mEthernetManager = (EthernetManager) getSystemService(ETH_SERVICE);
if (mEthernetManager == null) {
showError("Failed to get EthernetManager service");
return;
}

if (!isEthernetAvailable()) {
showError("No Ethernet interface detected (" + ETH_IFACE + ")");
mEthernetSwitch.setEnabled(false);
}
}

private boolean isEthernetAvailable() {
try {
String[] interfaces = mEthernetManager.getAvailableInterfaces();
if (interfaces == null || interfaces.length == 0) {
return false;
}
for (String iface : interfaces) {
if (ETH_IFACE.equals(iface)) {
return true;
}
}
return false;
} catch (Exception e) {
Log.e(TAG, "Failed to check ethernet availability", e);
return false;
}
}

private void setEthernetState(boolean enabled) {
try {
Log.d(TAG, "setEthernetState called: " + enabled);
try {
Log.d(TAG, "Calling setEthernetEnabled...");
mEthernetManager.setEthernetEnabled(enabled);
Log.d(TAG, "setEthernetEnabled returned successfully");
// ...
} catch (Exception e) {
Log.e(TAG, "setEthernetEnabled failed", e);
// ...
}

String msg = enabled ? "Ethernet enabled" : "Ethernet disabled, Wi-Fi will take over";
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();

mHandler.postDelayed(new Runnable() {
@Override
public void run() {
refreshStatus();
}
}, 1000);

} catch (Exception e) {
Log.e(TAG, "Failed to set ethernet state", e);
Toast.makeText(this, "Operation failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
refreshStatus();
}
}, 500);
}
}

private void refreshStatus() {
try {
boolean isEnabled = mEthernetManager.isEthernetEnabled();
mEthernetSwitch.setChecked(isEnabled);

StringBuilder detail = new StringBuilder();
detail.append("Interface: ").append(ETH_IFACE).append("\n");
detail.append("System State: ").append(isEnabled ? "Enabled" : "Disabled").append("\n");

try {
String ip = mEthernetManager.getIpAddress(ETH_IFACE);
if (ip != null && !ip.isEmpty()) {
detail.append("IP: ").append(ip).append("\n");
}
String netmask = mEthernetManager.getNetmask(ETH_IFACE);
if (netmask != null && !netmask.isEmpty()) {
detail.append("Netmask: ").append(netmask).append("\n");
}
String gateway = mEthernetManager.getGateway(ETH_IFACE);
if (gateway != null && !gateway.isEmpty()) {
detail.append("Gateway: ").append(gateway).append("\n");
}
String dns = mEthernetManager.getDns(ETH_IFACE);
if (dns != null && !dns.isEmpty()) {
detail.append("DNS: ").append(dns).append("\n");
}
} catch (Exception e) {
detail.append("Network details unavailable").append("\n");
}

mStatusText.setText(isEnabled ? "Ethernet Enabled" : "Ethernet Disabled");
mStatusText.setTextColor(isEnabled ? 0xFF4CAF50 : 0xFFF44336);
mDetailText.setText(detail.toString());

} catch (Exception e) {
Log.e(TAG, "Failed to refresh status", e);
mStatusText.setText("Status unavailable");
mStatusText.setTextColor(0xFFF44336);
mDetailText.setText("Error: " + e.getMessage());
}
}

private void showError(String msg) {
mStatusText.setText(msg);
mStatusText.setTextColor(0xFFF44336);
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}

@Override
protected void onResume() {
super.onResume();
refreshStatus();
}
}

注意: app 签名教程:请查看 apk签名.md 文档

3. 将 app 添加到系统预装

  • 将签名后的 APK 放入系统镜像:
1
2
3
4
5
# 创建目录
mkdir -p device/rockchip/rk3288/rk3288_Android10/EthernetControl

# 放入 APK 和 Android.mk
cp app-signed.apk device/rockchip/rk3288/rk3288_Android10/EthernetControl
  • 创建 Android.mk
1
2
3
4
5
6
7
8
9
10
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := EthernetControl
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PREBUILT)

将该文件创建在 device/rockchip/rk3288/rk3288_Android10/EthernetControl 路径下

  • device/rockchip/rk3288/rk3288_Android10/rk3288_Android10.mk 中添加:
1
PRODUCT_PACKAGES += EthernetControl

4.业务流程(可忽略)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
┌─────────────────────────────────────────────────────────────────────────────┐
│ APP 层 │
│ com.rk3288.ethernetcontrol.MainActivity │
│ │ │
│ ▼ │
│ getSystemService("ethernet") ──► EthernetManager │
│ │ │
│ ▼ │
│ mService.setEthernetEnabled(false) ──► IPC (Binder) │
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ AIDL 接口层 │
│ IEthernetManager.aidl │
│ ├─ setEthernetEnabled(boolean enabled) ◄── App 调用 │
│ └─ isEthernetEnabled() ◄── App 查询 │
│ │ │
│ ▼ │
│ 编译后生成 IEthernetManager.Stub / IEthernetManager.Stub.Proxy │
│ 负责 Binder 序列化/反序列化 │
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ 服务端实现层 (system_server) │
│ EthernetServiceImpl extends IEthernetManager.Stub │
│ ├─ setEthernetEnabled(boolean enabled) │
│ │ ├── enforceConnectivityInternalPermission() // 权限检查 │
│ │ ├── mEthernetEnabled = enabled // 保存状态 │
│ │ └── mTracker.setEthernetEnabled(enabled) // 调用 Tracker │
│ │ │
│ └─ isEthernetEnabled() │
│ └── return mEthernetEnabled │
│ │ │
│ ▼ │
│ EthernetTracker │
│ ├─ setEthernetEnabled(boolean enabled) │
│ │ └── 遍历所有接口: │
│ │ enabled ? mNMService.setInterfaceUp(iface) │
│ │ : mNMService.setInterfaceDown(iface) // 物理控制网口 │
│ │ │
│ ├─ mNMService = INetworkManagementService.Stub.asInterface(...) │
│ │ └── 最终调用 NetworkManagementService (netd) │
│ │ │
│ └─ InterfaceObserver │
│ └── interfaceLinkStateChanged() / interfaceAdded() / interfaceRemoved() │
│ └── 驱动事件回调,管理网络代理生命周期 │
│ │ │
│ ▼ │
│ EthernetNetworkFactory extends NetworkFactory │
│ ├─ addInterface() // 注册网络代理 │
│ ├─ removeInterface() // 注销网络代理 │
│ ├─ updateInterfaceLinkState() // 链路状态变化 │
│ └─ NetworkInterfaceState // 内部类,管理单个网口的 IP/代理/评分 │
│ ├─ start() // 启动 IpClient,申请 IP │
│ ├─ stop() // 停止 IpClient,注销 NetworkAgent │
│ └─ updateLinkState() // 根据链路 up/down 启停 │
└─────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────┐
│ 内核/驱动层 │
│ NetworkManagementService (system_server ──► netd ──► kernel) │
│ ├─ setInterfaceDown("eth0") │
│ │ └── ioctl(SIOCSIFFLAGS) 清除 IFF_UP 标志 │
│ │ └── 网口 PHY/MAC 关闭,LED 熄灭 │
│ │ │
│ └─ setInterfaceUp("eth0") │
│ └── ioctl(SIOCSIFFLAGS) 设置 IFF_UP 标志 │
│ └── 网口 PHY/MAC 启动,LED 亮起 │
│ │ │
│ ▼ │
│ 驱动上报 netlink 事件 ──► InterfaceObserver 回调 │
│ ──► ConnectivityService 重新评估网络 ──► Wi-Fi 接管 │
└─────────────────────────────────────────────────────────────────────────────┘