Google 官方变更说明:
已更新 DHCP 服务器
为了更好地构造“IP 服务器”服务体系,我们删除了 dnsmasq。Android 10 将其 DHCPv4 服务器功能替换为单独的组件,该组件主要使用 Java 编写,以便更好地与 Java 框架控制平面集成。这提高了 DHCP 服务器的安全性和可更新性。如需了解详情,请参阅 packages/modules/NetworkStack/src/android/net/dhcp/DhcpServer.java。此更改无需任何操作即可实现:默认情况下,发布和更新到 Android 10 的所有设备均使用 DhcpServer。如果您对 DHCP 服务器进行了自定义,则可以通过设置全局设置 tether_enable_legacy_dhcp_server=1 来还原 Android 9 行为。新的 DhcpServer 包含在网络组件模块中,因此,对 DHCP 服务器功能的任何自定义都应在上游进行。
DhcpServer 是依赖于 NetworkStack 服务。因此先分析 NetworkStack 服务的创建过程和功能实现。首先 NetworkStack 在 SystemServer 中启动:
[SystemServer.java]
traceBeginAndSlog("StartNetworkStack");
try {
NetworkStackClient.getInstance().start(context);
} catch (Throwable e) {
reportWtf("starting Network Stack", e);
}
traceEnd();
NetworkStackClient 为单例模式,调用其 start 函数启动 NetworkStack 服务:
[NetworkStackClient.java]
public void start(Context context) {
final PackageManager pm = context.getPackageManager();
Intent intent = getNetworkStackIntent(pm, true /* inSystemProcess */);
...
final String packageName = intent.getComponent().getPackageName();
if (!context.bindServiceAsUser(intent, new NetworkStackConnection(context, packageName),
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
maybeCrashWithTerribleFailure(
"Could not bind to network stack in-process, or in app with " + intent,
context, packageName);
return;
}
}
NetworkStack 服务的实现源码如下:
packages/modules/NetworkStack/
NetworkStackService 类作为服务的入口,定义如下:
[NetworkStackService.java]
public class NetworkStackService extends Service {
public static synchronized IBinder makeConnector(Context context) {
if (sConnector == null) {
sConnector = new NetworkStackConnector(context);
}
return sConnector;
}
@NonNull
@Override
public IBinder onBind(Intent intent) {
return makeConnector(this);
}
}
当通过 bindServiceAsUser 启动服务时,onBind 将会被调用,并返回 IBinder 给服务代理端,此时代理端的 onServiceConnected 回调会被调用:
[NetworkStackClient.java]
private class NetworkStackConnection implements ServiceConnection {
...
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
logi("Network stack service connected");
registerNetworkStackService(service);
}
}
调用 registerNetworkStackService 注册 network_stack 服务,代码实现如下:
[NetworkStackClient.java]
private void registerNetworkStackService(@NonNull IBinder service) {
final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
...
mConnector = connector;
...
}
通过 service 参数构造 INetworkStackConnector 对象,保存到 mConnector 域中,并向 service manager 注册。如果不注册服务,通过 mConnector 域已经可以使用 network_stack 服务了,至于为什么要注册到 service manager,目前来看,可能是为了将服务的暴露出来,可供其它系统组件使用,同时可以通过 dumpsys 对服务进行调试。这里不探讨这么设计的更深意图。继续分析服务端的实现,具体的服务是通过 NetworkStackService 的内部类 NetworkStackConnector 实现。
[NetworkStackService.java]
private static class NetworkStackConnector extends INetworkStackConnector.Stub
implements NetworkStackServiceManager {
@Override
public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
@NonNull IDhcpServerCallbacks cb) throws RemoteException {
...
}
}
上面代码只截取了 DHCP 相关接口。显然 makeDhcpServer 用来创建 DHCP server。通过以上分析,可以得出以下结论:
1、NetworkStack 的作用是为了创建和注册 network_stack 服务,后续工作由 network_stack 服务承担。
2、network_stack 是一个服务中转站,通过 network_stack 可以创建 DHCP 服务等。
那么谁创建了 DHCP 服务呢?这需要提到另一个系统服务 connectivity。同样 connectivity 在 SystemServer 中启动:
[SystemServer.java]
traceBeginAndSlog("StartConnectivityService");
try {
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
/* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
networkPolicy.bindConnectivityManager(connectivity);
} catch (Throwable e) {
reportWtf("starting Connectivity Service", e);
}
traceEnd();
在 ConnectivityService 构造函数中,创建了 Tethering 对象,并通过 INetworkManagementService 监测网络变化:
[ConnectivityService.java]
mTethering = makeTethering();
mNMS.registerObserver(mTethering);
如果创建新的网络节点,会触发 Tethering 的 interfaceAdded 回调:
[Tethering.java]
@Override
public void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
synchronized (mPublicSync) {
maybeTrackNewInterfaceLocked(iface);
}
}
maybeTrackNewInterfaceLocked 源码如下:
[Tethering.java]
private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {
...
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
这里创建 IpServer 和 TetherState。其中 IpServer 为状态机。其定义如下:
[IpServer.java]
public class IpServer extends StateMachine {
public static class Dependencies {
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb) {
NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
}
}
}
当热点被创建的时候,会触发以下调用:
[Tethering.java]
tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
其中 ipServer 即为前面新建 IpServer 对象,这里发送 CMD_TETHER_REQUESTED 消息,IpServer 收到消息会触发状态改变,从而调用 LocalHotspotState 的 enter 函数,父类 BaseServingState 的 enter 也会被调用:
[IpServer.java]
public void enter() {
if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
mLog.e("Error Tethering: " + e);
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}
if (!startIPv6()) {
mLog.e("Failed to startIPv6");
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
return;
}
}
其中 startIPv4 函数启动了 DHCP 协议,具体的是内部调用 startDhcp 启动 DHCP server。而 startDhcp 是通过内部类 Dependencies 对象调用 network_stack 服务的 makeDhcpServer 接口实现 DHCP server 启动。那么服务端的 makeDhcpServer 会被调用。
[DhcpServer.java]
public class DhcpServer extends IDhcpServer.Stub {
@Override
public void start(@Nullable INetworkStackStatusCallback cb) {
mDeps.checkCaller();
mHandlerThread.start();
mHandler = new ServerHandler(mHandlerThread.getLooper());
sendMessage(CMD_START_DHCP_SERVER, cb);
}
@Override
public void stop(@Nullable INetworkStackStatusCallback cb) {
mDeps.checkCaller();
sendMessage(CMD_STOP_DHCP_SERVER, cb);
}
}
从继承关系可以看出,DhcpServer 对象具备 binder 跨进程通信能力,上面代码中的 start 和 stop 就是暴露给客户端的接口。前面提到调用回调,将 server 传给代理,现在来看代理如何处理回调:
[IpServer.java]
public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
getHandler().post(() -> {
...
mDhcpServer = server;
try {
mDhcpServer.start(new OnHandlerStatusCallback() {
@Override
public void callback(int startStatusCode) {
if (startStatusCode != STATUS_SUCCESS) {
mLog.e("Error starting DHCP server: " + startStatusCode);
handleError();
}
}
});
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
});
}
这里直接调用了 start 函数处理启动“真正的" DHCP 服务。首先服务端会发送 CMD_START_DHCP_SERVER 消息,对应的 Handler 处理此消息:
[DhcpServer.java]
case CMD_START_DHCP_SERVER:
mPacketListener = mDeps.makePacketListener();
mPacketListener.start();
cb = (INetworkStackStatusCallback) msg.obj;
以上创建了 mPacketListener 对象,用于监听 DHCP 数据包,当收到网络客户端数据包时,会调用 onReceive 回调:
protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
int srcPort) {
processPacket(packet, srcPort);
}
最终通过 processPacket 处理此数据包,根据不同数据类型,做不同处理,包括分配 ip 地址和租期。具体分配原理在此不表,详细可查阅 DHCP 协议说明。
调试方法:
dumpsys network_stack