背景
Android kitkat 默认已经支持 Ethernet 有线网络,只要稍微配置,便可以直接使用,测试结果,网络浏览器和下载都没有没有问题,而且系统可以做到与 wifi 共存,互相不影响功能,这里简单介绍如何使能 Ethernet,并简要分析其代码和流程。
Linux 配置部分
Linux 需要能够支持有线网络,生成 eth 网络设备节点。
Android 配置
overlay
主要是 overlay 里面添加 Ethernet 网络类型支持:
frameworks/base/core/res/res/values/config.xml
"1,1"
"7,1"
"9,1"
其中 9 对应 Ethernet 的网络类型,其定义在 ConnectivityManager.java 中
/**
* The Ethernet data connection. When active, all data traffic
* will use this network type's interface by default
* (it has a default route).
*/
public static final int TYPE_ETHERNET = 9;
init..rc
init 里面需要添加 dhcp 和 ip renew 服务
# DHCPCD
# # eth0
service dhcpcd_eth0 /system/bin/dhcpcd -ABKL
class main
disabled
oneshot
# IP Renew
# # eth0
service iprenew_eth0 /system/bin/dhcpcd -n
class main
disabled
oneshot
流程分析
ConnectivityService
ConnectivityService 的构造函数里面将会读取 radioAttributes 里面的网络配置
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
// Load device network attributes from resources
String[] raStrings = context.getResources().getStringArray(
com.android.internal.R.array.radioAttributes);
for (String raString : raStrings) {
RadioAttributes r = new RadioAttributes(raString);
if (VDBG) log("raString=" + raString + " r=" + r);
if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
loge("Error in radioAttributes - ignoring attempt to define type " + r.mType);
continue;
}
if (mRadioAttributes[r.mType] != null) {
loge("Error in radioAttributes - ignoring attempt to redefine type " +
r.mType);
continue;
}
mRadioAttributes[r.mType] = r;
}
根据网络配置数据,将会创建 EthernetDataTracker , 并开始监听 startMonitoring
// Create and start trackers for hard-coded networks
for (int targetNetworkType : mPriorityList) {
final NetworkConfig config = mNetConfigs[targetNetworkType];
final NetworkStateTracker tracker;
try {
tracker = netFactory.createTracker(targetNetworkType, config);
mNetTrackers[targetNetworkType] = tracker;
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ " tracker: " + e);
continue;
}
tracker.startMonitoring(context, mTrackerHandler);
if (config.isDefault()) {
tracker.reconnect();
}
}
EthernetDataTracker
EthernetDataTracker 将会寻找第一个以 eth 开头的有线网络设备,打开并开始做 dhcp
eth\\d
sIfaceMatch = context.getResources().getString(
com.android.internal.R.string.config_ethernet_iface_regex);
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
if (iface.matches(sIfaceMatch)) {
mNMService.setInterfaceUp(iface);
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
if (getEthernetCarrierState(iface) == 1) {
mIface = iface;
mLinkUp = true;
mNetworkInfo.setIsAvailable(true);
if (config != null && mHwAddr == null) {
mHwAddr = config.getHardwareAddress();
if (mHwAddr != null) {
mNetworkInfo.setExtraInfo(mHwAddr);
}
}
}
// if a DHCP client had previously been started for this interface, then stop it
NetworkUtils.stopDhcp(iface);
}
}
reconnect();
} catch (RemoteException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
DHCP 成功后,通知NetworkManagementService 网路已经连接,这个时候上层应用便可以开始执行网络操作。
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
Android 以太网调用流程
frameworks\base\core\java\android\net\EthernetManager.java
public boolean setEthernetEnabled(boolean enabled) {
Log.d(TAG,enabled ? "turn on Ethernet" : "turn off Ethernet");
try {
return mService.setEthernetEnabled(enabled);
} catch (RemoteException e) {
return false;
}
}
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetService.java
EthernetServiceImpl.java
public boolean setEthernetEnabled(boolean enable) {
//enforceChangePermission();
Log.i(TAG,"setEthernetEnabled() : enable="+enable);
if ( enable ) {
return mTracker.setInterfaceUp();
} else {
return mTracker.setInterfaceDown();
}
}
EthernetNetworkFactory.java
public boolean setInterfaceUp() {
try {
if(!TextUtils.isEmpty(mIface)) {
mNMService.setInterfaceUp(mIface);
sendEthIfaceStateChangedBroadcast(EthernetManager.ETHER_IFACE_STATE_UP);
return true;
}
else
Log.e(TAG,"mIface is null");
}catch (Exception e) {
Log.e(TAG, "Error downing interface " + mIface + ": " + e);
}
return false;
}
frameworks\base\services\core\java\com\android\server\NetworkManagementService.java
public void setInterfaceUp(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
ifcg.setInterfaceUp();
setInterfaceConfig(iface, ifcg);
}
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 Command cmd = new Command("interface", "setcfg", iface,
linkAddr.getAddress().getHostAddress(),
linkAddr.getPrefixLength());
for (String flag : cfg.getFlags()) {
cmd.appendArg(flag);
}
try {
mConnector.execute(cmd);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
system\netd\server\CommandListener.cpp
CommandListener::InterfaceCmd::InterfaceCmd() :
NetdCommand("interface") {
}
int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
else if (!strcmp(argv[1], "setcfg")) {
// arglist: iface [addr prefixLength] flags
if (argc < 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
}
ALOGD("Setting iface cfg");
struct in_addr addr;
int index = 5;
ifc_init();
if (!inet_aton(argv[3], &addr)) {
// Handle flags only case
index = 3;
} else {
if (ifc_set_addr(argv[2], addr.s_addr)) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to set address", true);
ifc_close();
return 0;
}
// Set prefix length on a non zero address
if (addr.s_addr != 0 && ifc_set_prefixLength(argv[2], atoi(argv[4]))) {
cli->sendMsg(ResponseCode::OperationFailed, "Failed to set prefixLength", true);
ifc_close();
return 0;
}
}
/* Process flags */
for (int i = index; i < argc; i++) {
char *flag = argv[i];
if (!strcmp(flag, "up")) {
ALOGD("Trying to bring up %s", argv[2]);
if (ifc_up(argv[2])) {
ALOGE("Error upping interface");
cli->sendMsg(ResponseCode::OperationFailed, "Failed to up interface", true);
ifc_close();
return 0;
}
} else if (!strcmp(flag, "down")) {
ALOGD("Trying to bring down %s", argv[2]);
if (ifc_down(argv[2])) {
ALOGE("Error downing interface");
cli->sendMsg(ResponseCode::OperationFailed, "Failed to down interface", true);
ifc_close();
return 0;
}
} else if (!strcmp(flag, "broadcast")) {
// currently ignored
} else if (!strcmp(flag, "multicast")) {
// currently ignored
} else if (!strcmp(flag, "running")) {
// currently ignored
} else if (!strcmp(flag, "loopback")) {
// currently ignored
} else if (!strcmp(flag, "point-to-point")) {
// currently ignored
} else {
cli->sendMsg(ResponseCode::CommandParameterError, "Flag unsupported", false);
ifc_close();
return 0;
}
}
cli->sendMsg(ResponseCode::CommandOkay, "Interface configuration set", false);
ifc_close();
return 0;
}
return 0;
}
system\core\libnetutils\ifc_utils.c
发送命令到内核;
int ifc_up(const char *name)
{
int ret = ifc_set_flags(name, IFF_UP, 0);
if (DBG) printerr("ifc_up(%s) = %d", name, ret);
return ret;
}
static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
{
struct ifreq ifr;
ifc_init_ifr(name, &ifr);
if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
}