Android以太网开关流程分析
一、引言
在Android 以太网服务启动流程中我们启动了EthernetService(ES),NetworkManagementService(NMS)等服务,其中ES作为Binder的服务端,向客户端EthernetManger提供了调用接口的引用。因此,应用可以通过EthernetManger来访问以太网的相关接口。本篇文章将从APP调用以太网的使能和关闭的接口流程作为切入点来分析,以太网框架中,上层app是如何将指令传递到底层。图1-1所示为以太网使能和关闭流程图
图1-1 以太网使能和关闭流程图
二、以太网使能流程
2.1 app端调用以太网的使能接口
应用端调用以外网使能的接口是setEthernetEnabled,具体调用过如下:
import android.net.EthernetManager;
EthernetManager et;
//获取ethernet的Binder引用端
et = (EthernetManager)getSystemService("ethernet");
//调用setEthernetEnabled使能以太网
et.setEthernetEnabled("eth0", true);
App端实现很简单,是Android 通常获取Binder通信过程,主要完成以下任务:
- 获取以太网的Binder服务的引用,以便调用其使能接口;
- 调用setEthernetEnabled接口来使能以外网
2.2 system_server进程的调用流程
App端调用setEthernetEnabled接口来使能以外网,具体代码如下:
//frameworks/base/core/java/android/net/EthernetManager.java
public boolean setEthernetEnabled(String iface, boolean enabled) {
Log.d(TAG,enabled ? "turn on Ethernet" : "turn off Ethernet");
try {
return mService.setEthernetEnabled(iface, enabled);
} catch (RemoteException e) {
return false;
}
}
其实调用很简单,直接调用服务端的setEthernetEnabled接口,需要注意的是,这里传入的iface为Hal层创建interface名称,以太网是eth0,enabled是true表示使能以太网,否则表示关闭以太网。下面我们直捣黄龙直接看服务端的setEthernetEnabled这个接口的实现。
//frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java
public class EthernetServiceImpl extends IEthernetManager.Stub {
//以太网服务的核心
private EthernetTracker mTracker;
public boolean setEthernetEnabled(String iface, boolean enable) {
//enforceChangePermission();
Log.i(TAG,"setEthernetEnabled() : enable="+enable);
return mTracker.setEthernetEnabled(iface, enable);
}
}
由Android 以太网服务启动流程我们可以知道EthernetTracker是以太网服务的核心,其能保存NetworkManagementService的引用对象,从而间接和Netd进行通信,下面我们直接看EthernetTracker中的setEthernetEnabled接口。
final class EthernetTracker {
private final INetworkManagementService mNMService;
EthernetTracker(Context context, Handler handler) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNMService = INetworkManagementService.Stub.asInterface(b);
}
public boolean setEthernetEnabled(String iface, boolean enable){
try {
if(!TextUtils.isEmpty(iface)) {
if(!enable)
//enable为false,分别调用NMService的setInterfaceDown接口
mNMService.setInterfaceDown(iface);
else
//enable为true,分别调用NMService的setInterfaceUp接口
mNMService.setInterfaceUp(iface);
return true;
} else {
Log.e(TAG,"iface is null");
}
} catch (Exception e) {
Log.e(TAG, "Error setEthernetEnabled : " + iface + " enable : "
+ enable + " exception : " + e);
}
return false;
}
}
EthernetServiceImpl中相关实现很简单,主要完成以下三件事:
- 创建INetworkManagementService引用对象,从而根据传入的enable的值,调用INetworkManagementService的相关方法;
- 如果enable为true,则调用INetworkManagementService的setInterfaceUp接口;
- 如果enable为false,则调用INetworkManagementService的setInterfaceDown接口;
此处是使能以太网,所以enable为true,所以调用setInterfaceUp接口,所以我们直接看NetworkManagementService的setInterfaceUp的接口。
public class NetworkManagementService extends INetworkManagementService.Stub {
//Netd的Binder引用对象
private INetd mNetdService;
//注册底层Netd的Listener
private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
//根据name获取Binder对象
static class SystemServices {
public IBinder getService(String name) {
return ServiceManager.getService(name);
}
public void registerLocalService(NetworkManagementInternal nmi) {
LocalServices.addService(NetworkManagementInternal.class, nmi);
}
//获取Netd的引用对象
public INetd getNetd() {
return NetdService.get();
}
}
static NetworkManagementService create(Context context, SystemServices services)
throws InterruptedException {
final NetworkManagementService service =
new NetworkManagementService(context, services);
//调用connectNativeNetdService获取底层Netd的服务对象,并且注册Listener监听底层状态的
//变化
service.connectNativeNetdService();
return service;
}
private void connectNativeNetdService() {
//获取Netd的Binder引用对象
mNetdService = mServices.getNetd();
try {
//注册Listener监听底层状态的变化
mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);
}
}
public void setInterfaceDown(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
Slog.d(TAG, "setInterfaceDown iface: " + iface);
//先从Netd获取InterfaceConfiguration,然后设置器mFlag为up
final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
ifcg.setInterfaceDown();
//然后在设置将InterfaceConfiguration设置到底层
setInterfaceConfig(iface, ifcg);
}
}
在NetworkMangementService中主要完成以下几件事:
- 获取底层Netd的Binder服务端引用对象,调用Netd的相关接口;
- 向Netd注册NetdUnsolicitedEventListener,当底层状态改变时候,通知上层;
- setInterfaceDown接口实现也简单,首先通过Netd获取InterfaceConfiguration,然后将其mFlag标记为up,然后在设置到Netd中
public InterfaceConfiguration getInterfaceConfig(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
final InterfaceConfigurationParcel result;
try {
//通过Netd获取Interface的配置信息
result = mNetdService.interfaceGetCfg(iface);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
try {
//将result转为InterfaceConfiguration
final InterfaceConfiguration cfg = fromStableParcel(result);
return cfg;
} catch (IllegalArgumentException iae) {
throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
}
}
public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
LinkAddress linkAddr = cfg.getLinkAddress();
if (linkAddr == null || linkAddr.getAddress() == null) {
throw new IllegalStateException("Null LinkAddress given");
}
final InterfaceConfigurationParcel cfgParcel = toStableParcel(cfg, iface);
try {
//通过Netd设置interface的配置文件
mNetdService.interfaceSetCfg(cfgParcel);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
从上面流程可以看出,调用Netd的interfaceGetCfg接口(传入参数为注册的网络名称,此处为eth0),获取Interface的Configuration;最后调用Netd的interfaceSetCfg的接口设置interface的Configuration。
2.3 Netd的处理流程
从上一节可知,调用Netd的interfaceGetCfg接口(传入参数为注册的网络名称,此处为eth0),获取Interface的Configuration;最后调用Netd的interfaceSetCfg的接口设置interface的Configuration。
binder::Status NetdNativeService::interfaceGetCfg(
const std::string& ifName, InterfaceConfigurationParcel* interfaceGetCfgResult) {
NETD_LOCKING_RPC(InterfaceController::mutex, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
auto entry = gLog.newEntry().prettyFunction(__PRETTY_FUNCTION__).arg(ifName);
//通过InterfaceController的getCfg来获取cfgRes
const auto& cfgRes = InterfaceController::getCfg(ifName);
RETURN_BINDER_STATUS_IF_NOT_OK(entry, cfgRes);
*interfaceGetCfgResult = cfgRes.value();
gLog.log(entry.returns(interfaceConfigurationParcelToString(*interfaceGetCfgResult))
.withAutomaticDuration());
return binder::Status::ok();
}
binder::Status NetdNativeService::interfaceSetCfg(const InterfaceConfigurationParcel& cfg) {
NETD_LOCKING_RPC(InterfaceController::mutex, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
auto entry = gLog.newEntry()
.prettyFunction(__PRETTY_FUNCTION__)
.arg(interfaceConfigurationParcelToString(cfg));
//通过InterfaceController的setCfg来设置cfg
const auto& res = InterfaceController::setCfg(cfg);
RETURN_BINDER_STATUS_IF_NOT_OK(entry, res);
gLog.log(entry.withAutomaticDuration());
return binder::Status::ok();
}
NetdNativeService中最后调用InterfaceContoller的getCfg和setCfg来处理,我们直接看这两个方法:
StatusOr<InterfaceConfigurationParcel> InterfaceController::getCfg(const std::string& ifName) {
struct in_addr addr = {};
int prefixLength = 0;
unsigned char hwaddr[ETH_ALEN] = {};
unsigned flags = 0;
InterfaceConfigurationParcel cfgResult;
//获取sys,并创建socket
const auto& sys = sSyscalls.get();
ASSIGN_OR_RETURN(auto fd, sys.socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
struct ifreq ifr = {};
strlcpy(ifr.ifr_name, ifName.c_str(), IFNAMSIZ);
//进行io流操作
if (isOk(sys.ioctl(fd, SIOCGIFADDR, &ifr))) {
addr.s_addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
}
if (isOk(sys.ioctl(fd, SIOCGIFNETMASK, &ifr))) {
prefixLength =
ipv4NetmaskToPrefixLength(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr);
}
if (isOk(sys.ioctl(fd, SIOCGIFFLAGS, &ifr))) {
flags = ifr.ifr_flags;
}
// ETH_ALEN is for ARPHRD_ETHER, it is better to check the sa_family.
// However, we keep old design for the consistency.
if (isOk(sys.ioctl(fd, SIOCGIFHWADDR, &ifr))) {
memcpy((void*) hwaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
} else {
ALOGW("Failed to retrieve HW addr for %s (%s)", ifName.c_str(), strerror(errno));
}
cfgResult.ifName = ifName;
cfgResult.hwAddr = hwAddrToStr(hwaddr);
cfgResult.ipv4Addr = std::string(inet_ntoa(addr));
cfgResult.prefixLength = prefixLength;
cfgResult.flags.push_back(flags & IFF_UP ? toStdString(INetd::IF_STATE_UP())
: toStdString(INetd::IF_STATE_DOWN()));
if (flags & IFF_BROADCAST) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_BROADCAST()));
if (flags & IFF_LOOPBACK) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_LOOPBACK()));
if (flags & IFF_POINTOPOINT)
cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_POINTOPOINT()));
if (flags & IFF_RUNNING) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_RUNNING()));
if (flags & IFF_MULTICAST) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_MULTICAST()));
return cfgResult;
}
getCfg的流程主要是创建和底层kernel的通信的socket,通过ioctrl的方式获取相关信息,最后封装为InterfaceConfigurationParcel传递给上层。
Status InterfaceController::setCfg(const InterfaceConfigurationParcel& cfg) {
//获取socket流
const auto& sys = sSyscalls.get();
ASSIGN_OR_RETURN(auto fd, sys.socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
struct ifreq ifr = {
.ifr_addr = {.sa_family = AF_INET}, // Clear the IPv4 address.
};
strlcpy(ifr.ifr_name, cfg.ifName.c_str(), IFNAMSIZ);
// Make sure that clear IPv4 address before set flag
// SIOCGIFFLAGS might override ifr and caused clear IPv4 addr ioctl error
RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCSIFADDR, &ifr));
if (!cfg.flags.empty()) {
RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCGIFFLAGS, &ifr));
uint16_t flags = ifr.ifr_flags;
for (const auto& flag : cfg.flags) {
if (flag == toStdString(INetd::IF_STATE_UP())) {
ifr.ifr_flags = ifr.ifr_flags | IFF_UP;
} else if (flag == toStdString(INetd::IF_STATE_DOWN())) {
ifr.ifr_flags = (ifr.ifr_flags & (~IFF_UP));
}
}
if (ifr.ifr_flags != flags) {
RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCSIFFLAGS, &ifr));
}
}
RETURN_STATUS_IF_IFCERROR(
ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength));
return ok;
}
setCfg的流程和getCfg类似,先获取socket流,然后通过ioctrl设置interface configuration。
至此以太网的使能流程就分析完了,Netd通过ioctrl从kernel获取interface confiuration;最后通过ioctrl将标记后的interface confiuration 设置到kernel,这样kernel将会触发interface的add和注册流程。
三、以太网关闭流程
以外网的关闭流程和使能流程类似,只是传入的enable参数为false。在NetworkManagementService中,如果enable为false,则调用setInterfaceDown。可以看出其和setInterfaceUp流程很相似。不通的地方是此处获取到InterfaceConfiguration后,将mFlag标记为down。
public void setInterfaceDown(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
Slog.d(TAG, "setInterfaceDown iface: " + iface);
final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
ifcg.setInterfaceDown();
setInterfaceConfig(iface, ifcg);
}
四、总结
这篇文章就写到这,下一篇将底层网线的插拔来说明,底层的状态是如何传递到上层的,敬请期待,谢谢大家关注。