android ethernet 管理, 可以分为两类:
1. 是网线插拔,unplug/plug。
unplug 时, eth0 可以up, 但是IP 必须为0, 因为打开internel时,必须快速出现连不上页面,而不是延迟好几十秒,才出现连不上。
plug in之后,IP 能自动恢复。
2. 是Setting里EthernetConfiguration 的勾选项。enable/disable。
disable ethernet 时, eth0 必须是down的, enable之后,才up,然后能自动恢复IP。
3. 是static ip configuration
IP
NETMASK
DNS
GATEWAY
4个IP选项,静态IP设置。
ethernet related files:
1. packages/app/Settings/: //Setting中添加选项代码
packages/apps/Settings/src/com/android/settings/ethernet/EthernetSettings.java
packages/apps/Settings/src/com/android/settings/ethernet/EthernetEnabler.java
packages/apps/Settings/src/com/android/settings/ethernet/EthernetConfigDialog.java
2. frameworks/base/ :
SystemUI: //状态栏(status_bar)显示部分代码
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java //现实statusbar
ConnectivityService:
frameworks/base/services/java/com/android/server/ConnectivityService.java //这里是ethernet部分程序的起始点
jni:
frameworks/base/core/jni/android_net_ethernet.cpp //新加的一些jni
本来就有的ethernet:
frameworks/base/services/java/com/android/server/EthernetService.java
frameworks/base/services/java/com/android/server/NetworkManagementService.java
frameworks/base/core/java/android/net/NetworkStats.java
新添加的ethernet:
frameworks/base/ethernet/* // 这是主要ethernet部分,java api 代码。
frameworks/base/ethernet/java/android/net/ethernet/EthernetManager.java
netcfg //查看ip情况
netcfg eth0 up dhcp //通过dhcp 自动获取ip和网关
2. ifconfig
4. dns 配置
echo "nameserver 128.224.160.11" > resolv.conf
nameserver 128.224.160.11
setprop net.dns1 128.224.160.11
setprop net.dns2 147.11.100.301. 一个是 system/core/libnetutils/* , 即libnetutils.so 库. 这里面有两个.c经常会调用到
ifc_utils.c
dhcp_utils.c
java会 通过 JNI (CPP)再调用到 C代码
2. 第二个是 /external/dhcpcd/*, 生成/system/bin/dhcpcd.
这个是 守护进程 dhcpcd_eth0 会用到的工具。
u最近在研究android 中的 ethernet 部分,主要集中在上层JAVA service 和java api。
整个 android 系统的mobile, wifi,wimax 和 bluetooth 都是通过ConnectivitySerivice 来提供服务的。
android本身不自带ethernet服务,需要新加 android-x86 里拿的ics-ethernet patch。
接下来分析 ConnectivityService 是怎么提供 ethernet 服务的。
1. 最开始,ConnectivityService 中会创建 EthernetStateTracker 和 EthernetService。
frameworks/base/services/java/com/android/server/ConnectivityService.java:
class=java name="code"> //向ServiceManager 中添加 ETHERNET_SERVICE 服务
"BACKGROUND-COLOR: rgb(255,255,255)">"COLOR: #663300; FONT-SIZE: 16px">"FONT-FAMILY: 宋体">a. mNetTrackers 定义:
class=java name="code"> /**
class=java name="code"> public void startMonitoring(Context context, Handler target) {
class=java name="code"> public EthernetStateTracker(Context context, Handler target){
class=java name="code"> private class DhcpHandler extends Handler {
class=java name="code">/**
class=java name="code">/**
class=java name="code"> public EthernetService(Context context, EthernetStateTracker Tracker){
class=java name="code"> /**
class=java name="code"> private int scanDevice() {
class=java name="code"> public void StartPolling() {
EthernetMornitor 这个类位于: frameworks/base/ethernet/java/android/net/ethernet/EthernetMonitor.java
它会监听底层ethernet 状态 的 event.
1. EthernetMonitor :
/**
* Listens for events from kernel, and passes them on
* to the {@link EtherentStateTracker} for handling. Runs in its own thread.
*
* @hide
*/
public class EthernetMonitor {
private static final String TAG = "EthernetMonitor";
private static final int CONNECTED = 1;
private static final int DISCONNECTED = 2;
private static final int PHYUP = 3;
private static final String connectedEvent = "CONNECTED";
private static final String disconnectedEvent = "DISCONNECTED";
private static final int ADD_ADDR = 20;
private static final int RM_ADDR = 21;
private static final int NEW_LINK = 16;
private static final int DEL_LINK = 17;
private static final boolean localLOGV = false;
private EthernetStateTracker mTracker;
//EthernetMonitor 是 EthernetStateTraker 的一个辅助
public EthernetMonitor(EthernetStateTracker tracker) {
mTracker = tracker;
}
public void startMonitoring() {
new MonitorThread().start(); //线程开始
}
class MonitorThread extends Thread {
public MonitorThread() {
super("EthMonitor");
}
public void run() {
//noinspection InfiniteLoopStatement
for (;;) { //不断的在监听 kernel event
int index;
int i;
int cmd;
String dev;
if (localLOGV) Slog.v(TAG, "go poll events");
String eventName = EthernetNative.waitForEvent(); //wait for event
if (eventName == null) {
continue;
}
if (localLOGV) Slog.v(TAG, "get event " + eventName);
/*
* Map event name into event enum
*/
i = 0;
while (i < eventName.length()) {
index = eventName.substring(i).indexOf(":");
if (index == -1)
break;
dev = eventName.substring(i, index);
i += index + 1;
index = eventName.substring(i).indexOf(":");
if (index == -1)
break;
cmd = Integer.parseInt(eventName.substring(i, i+index));
i += index + 1;
//dev 指eth0, cmd 是event参数。
if (localLOGV) Slog.v(TAG, "dev: " + dev + " ev " + cmd);
switch (cmd) {
case DEL_LINK: //断开连接
handleEvent(dev, DISCONNECTED);
break;
case ADD_ADDR: //添加IP address,即链接成功
handleEvent(dev, CONNECTED);
break;
case NEW_LINK: //phy up 状态
handleEvent(dev, PHYUP);
break;
}
}
}
}
b. handleEvent(String ifname,int event):
/**
* Handle all supplicant events except STATE-CHANGE
* @param event the event type
* @param remainder the rest of the string following the
* event name and " — "
*/
void handleEvent(String ifname,int event) {
switch (event) {
case DISCONNECTED:
mTracker.notifyStateChange(ifname,NetworkInfo.DetailedState.DISCONNECTED);
break;
case CONNECTED:
mTracker.notifyStateChange(ifname,NetworkInfo.DetailedState.CONNECTED);
break;
case PHYUP:
mTracker.notifyPhyConnected(ifname);
break;
default:
mTracker.notifyStateChange(ifname,NetworkInfo.DetailedState.FAILED);
break;
}
}
public void notifyStateChange(String ifname,DetailedState state) {
if (localLOGV) Slog.i(TAG, "report new state " + state.toString() + " on dev " + ifname);
if (ifname.equals(mInterfaceName)) { //判断eth0名称是否正确
if (localLOGV) Slog.v(TAG, "update network state tracker");
synchronized(this) { //发送 Connected or Disconnected 消息
this.sendEmptyMessage(state.equals(DetailedState.CONNECTED)
? EVENT_HW_CONNECTED : EVENT_HW_DISCONNECTED);
}
}
}
d. EthernetStateTraker.notifyPhyConnected():
public void notifyPhyConnected(String ifname) {
if (localLOGV) Slog.v(TAG, "report interface is up for " + ifname);
synchronized(this) { //发送Phy connected 消息
this.sendEmptyMessage(EVENT_HW_PHYCONNECTED);
}
}
2. 发送消息之后,EthernetStateTraker 会handleMessage():
public void handleMessage(Message msg) {
synchronized (this) {
switch (msg.what) {
case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED: //连接成功
if (localLOGV) Slog.i(TAG, "received configured succeeded, stack=" + mStackConnected + " HW=" + mHWConnected);
mStackConnected = true;
if (mHWConnected)
setState(true, msg.what);
break;
case EVENT_INTERFACE_CONFIGURATION_FAILED: //链接失败
mStackConnected = false;
//start to retry ?
break;
case EVENT_HW_CONNECTED: //HW连接上了
if (localLOGV) Slog.i(TAG, "received HW connected, stack=" + mStackConnected + " HW=" + mHWConnected);
mHWConnected = true;
if (mStackConnected)
setState(true, msg.what);
break;
case EVENT_HW_DISCONNECTED: //hw断开
if (localLOGV) Slog.i(TAG, "received disconnected events, stack=" + mStackConnected + " HW=" + mHWConnected);
setState(false, msg.what);
break;
case EVENT_HW_PHYCONNECTED: //phy 连上
if (localLOGV) Slog.i(TAG, "interface up event, kick off connection request");
if (!mStartingDhcp) {
int state = mEM.getState();
if (state != mEM.ETHERNET_STATE_DISABLED) {
EthernetDevInfo info = mEM.getSavedConfig(); //获得以前已有的连接IP
if (info != null && mEM.isConfigured()) {
try {
configureInterface(info); //配置IP
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
Slog.e(TAG, "Cannot configure interface");
}
}
}
}
setState(true, msg.what);
break;
}
}
}
3. setState(boolean state, int event):
private void setState(boolean state, int event) {
if (mNetworkInfo.isConnected() != state) {
if (state) { //connected or disconnected.
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
} else {
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
stopInterface(true);
}
mNetworkInfo.setIsAvailable(state);
postNotification(event);
}
}
/**
* Stop etherent interface
* @param suspend {@code false} disable the interface {@code true} only reset the connection without disable the interface
* @return true
*/
public boolean stopInterface(boolean suspend) {
if (mEM != null) {
EthernetDevInfo info = mEM.getSavedConfig();
if (info != null && mEM.isConfigured()) {
synchronized (mDhcpTarget) {
mInterfaceStopped = true;
if (localLOGV) Slog.i(TAG, "stop dhcp and interface");
// stop DhcpHandler Looper
mDhcpTarget.removeMessages(EVENT_DHCP_START);
String ifname = info.getIfName(); // eth0
if (!NetworkUtils.stopDhcp(ifname)) { //stop dhcpcd_eth0 service
if (localLOGV) Slog.w(TAG, "Could not stop DHCP");
}
//reset eth0, remove route and ip
NetworkUtils.resetConnections(ifname, NetworkUtils.RESET_ALL_ADDRESSES);
NetworkUtils.removeDefaultRoute(ifname);
mStartingDhcp = false;
if (!suspend) // if suspend = false, turn eth0 to down.
NetworkUtils.disableInterface(ifname);
mLinkProperties.clear();
}
}
}
return true;
}
b. postNotification(int event), 给ConnectivityService 发送 event state changed 消息.
private void postNotification(int event) {
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
msg.sendToTarget();
}
class=java name="code"> /**
EthernetStateTracker 中会,用到 NetWorkUtils 类中的一些函数, 这些函数,会调用JNI: android_net_NetUtils.cpp, 然后调到 libnetutils 库中去。
如:
NetworkUtils.disableInterface(ifname);
NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo);
NetworkUtils.stopDhcp(mInterfaceName);
NetworkUtils.resetConnections(mInterfaceName, NetworkUtils.RESET_ALL_ADDRESSES);
NetworkUtils.configureInterface(info.getIfName(), mDhcpInfo1);
NetworkUtils.removeDefaultRoute(ifname);
等等。
1. NetworkUtils 类: frameworks/base/core/java/android/net/NetworkUtils.java
/**
* Native methods for managing network interfaces.
*
* {@hide}
*/
public class NetworkUtils {
private static final String TAG = "NetworkUtils";
/** Bring the named network interface up. */
public native static int enableInterface(String interfaceName);
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
/** Remove the default route for the named interface. */
public native static int removeDefaultRoute(String interfaceName);
/** Setting bit 0 indicates reseting of IPv4 addresses required */
public static final int RESET_IPV4_ADDRESSES = 0x01;
/** Setting bit 1 indicates reseting of IPv4 addresses required */
public static final int RESET_IPV6_ADDRESSES = 0x02;
/** Reset all addresses */
public static final int RESET_ALL_ADDRESSES = RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES;
/**
* Reset IPv6 or IPv4 sockets that are connected via the named interface.
*
* @param interfaceName is the interface to reset
* @param mask {@see #RESET_IPV4_ADDRESSES} and {@see #RESET_IPV6_ADDRESSES}
*/
public native static int resetConnections(String interfaceName, int mask);
/**
* Start the DHCP client daemon, in order to have it request addresses
* for the named interface, and then configure the interface with those
* addresses. This call blocks until it obtains a result (either success
* or failure) from the daemon.
* @param interfaceName the name of the interface to configure
* @param ipInfo if the request succeeds, this object is filled in with
* the IP address information.
* @return {@code true} for success, {@code false} for failure
*/
public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo);
/**
* Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
* a result (either success or failure) from the daemon.
* @param interfaceName the name of the interface to configure
* @param ipInfo if the request succeeds, this object is filled in with
* the IP address information.
* @return {@code true} for success, {@code false} for failure
*/
public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo);
/**
* Shut down the DHCP client daemon.
* @param interfaceName the name of the interface for which the daemon
* should be stopped
* @return {@code true} for success, {@code false} for failure
*/
public native static boolean stopDhcp(String interfaceName);
/**
* When static IP configuration has been specified, configure the network
* interface according to the values supplied.
* @param interfaceName the name of the interface to configure
* @param ipInfo the IP address, default gateway, and DNS server addresses
* with which to configure the interface.
* @return {@code true} for success, {@code false} for failure
*/
public static boolean configureInterface(String interfaceName, DhcpInfo ipInfo) {
return configureNative(interfaceName,
ipInfo.ipAddress,
ipInfo.netmask,
ipInfo.gateway,
ipInfo.dns1,
ipInfo.dns2);
}
private native static boolean configureNative(
String interfaceName, int ipAddress, int netmask, int gateway, int dns1, int dns2);
/*
* JNI registration.
*/
static JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
{ "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
{ "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp },
{ "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew },
{ "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
{ "configureNative", "(Ljava/lang/String;IIIII)Z", (void *)android_net_utils_configureInterface },
};
static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
result = ::ifc_enable(nameStr); //这里就会调用到 libnetutils 中的 ifc_utils.c 去
env->ReleaseStringUTFChars(ifname, nameStr);
return (jint)result;
}
static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
result = ::ifc_disable(nameStr);
env->ReleaseStringUTFChars(ifname, nameStr);
return (jint)result;
}
static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
jstring ifname, jint mask)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
LOGD("android_net_utils_resetConnections in env=%p clazz=%p iface=%s mask=0x%x\n",
env, clazz, nameStr, mask);
result = ::ifc_reset_connections(nameStr, mask);
env->ReleaseStringUTFChars(ifname, nameStr);
return (jint)result;
}
static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
jobject info, bool renew)
{
int result;
char ipaddr[PROPERTY_VALUE_MAX];
uint32_t prefixLength;
char gateway[PROPERTY_VALUE_MAX];
char dns1[PROPERTY_VALUE_MAX];
char dns2[PROPERTY_VALUE_MAX];
char server[PROPERTY_VALUE_MAX];
uint32_t lease;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
if (nameStr == NULL) return (jboolean)false;
if (renew) {
result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
dns1, dns2, server, &lease);
} else {
result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
dns1, dns2, server, &lease); //调用 dhcp_utils.c 中的函数,获取route
}
env->ReleaseStringUTFChars(ifname, nameStr);
if (result == 0) {
env->SetObjectField(info, dhcpInfoInternalFieldIds.ipaddress, env->NewStringUTF(ipaddr));
// set the gateway
jclass cls = env->FindClass("java/net/InetAddress");
jmethodID method = env->GetStaticMethodID(cls, "getByName",
"(Ljava/lang/String;)Ljava/net/InetAddress;");
jvalue args[1];
args[0].l = env->NewStringUTF(gateway);
jobject inetAddressObject = env->CallStaticObjectMethodA(cls, method, args);
if (!env->ExceptionOccurred()) {
cls = env->FindClass("android/net/RouteInfo");
method = env->GetMethodID(cls, "", "(Ljava/net/InetAddress;)V");
args[0].l = inetAddressObject;
jobject routeInfoObject = env->NewObjectA(cls, method, args);
cls = env->FindClass("android/net/DhcpInfoInternal");
method = env->GetMethodID(cls, "addRoute", "(Landroid/net/RouteInfo;)V");
args[0].l = routeInfoObject;
env->CallVoidMethodA(info, method, args);
} else {
// if we have an exception (host not found perhaps), just don't add the route
env->ExceptionClear();
}
env->SetIntField(info, dhcpInfoInternalFieldIds.prefixLength, prefixLength);
env->SetObjectField(info, dhcpInfoInternalFieldIds.dns1, env->NewStringUTF(dns1));
env->SetObjectField(info, dhcpInfoInternalFieldIds.dns2, env->NewStringUTF(dns2));
env->SetObjectField(info, dhcpInfoInternalFieldIds.serverAddress,
env->NewStringUTF(server));
env->SetIntField(info, dhcpInfoInternalFieldIds.leaseDuration, lease);
}
return (jboolean)(result == 0);
}
static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
{
return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
}
static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
{
return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
}
static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
result = ::dhcp_stop(nameStr); // 这个调用 libnetutils 中的 dhcp_utils.c
env->ReleaseStringUTFChars(ifname, nameStr);
return (jboolean)(result == 0);
}
static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
result = ::ifc_remove_default_route(nameStr);
env->ReleaseStringUTFChars(ifname, nameStr);
return (jint)result;
}
static jboolean android_net_utils_configureInterface(JNIEnv* env,
jobject clazz,
jstring ifname,
jint ipaddr,
jint mask,
jint gateway,
jint dns1,
jint dns2)
{
int result;
uint32_t lease;
const char *nameStr = env->GetStringUTFChars(ifname, NULL);
result = ::ifc_configure(nameStr, ipaddr, mask, gateway, dns1, dns2);
env->ReleaseStringUTFChars(ifname, nameStr);
return (jboolean)(result == 0);
}
最近在调android ethernet功能,android本身不带 ethernet 功能,需要打patch。这个patch可以在setting里出来 ethernet configuration 选项。即添加了用户配置IP的功能。
我打上patch之后,点击选上DHCP功能,结果路由器一直不能自动分配IP。
经检测,命令行里运行 netcfg eth0 up dhcp 时,ethernet能被正常启动,DHCP能分配到IP。
但是Setting里选上时,dhcp却不能正常分配IP。 这很费解,我先后查看了,
/system/core/libnetutils/*
/externel/dhcpcd/*
发现都没什么问题。
在Setting中点击turn on ethernet选项时,从log看到能调到
dhcpcd 源代码 位于 external/dhcpcd/*
这个目录的源代码生成 /system/bin/dhcpcd 命令工具和一些信息文件。
0605/external/dhcpcd$ ls *
android.conf CleanSpec.mk configure.h dhcpcd.conf dhcp.h if-linux.c logger.c NOTICE ThirdPartyProject.prop
Android.mk client.c control.c dhcpcd.conf.5 duid.c if-linux-wireless.c logger.h platform-bsd.c
arp.c client.h control.h dhcpcd.conf.5.in duid.h if-options.c lpf.c platform.h
arp.h common.c defs.h dhcpcd.h eloop.c if-options.h Makefile platform-linux.c
bind.c common.h dhcp.c dhcpcd-run-hooks eloop.h if-pref.c Makefile.inc README
bind.h config.h dhcpcd.8 dhcpcd-run-hooks.8 ifaddrs.c if-pref.h MODULE_LICENSE_BSD_LIKE showlease.c
bpf.c config.mk dhcpcd.8.in dhcpcd-run-hooks.8.in ifaddrs.h ipv4ll.c net.c signals.c
bpf-filter.h configure.c dhcpcd.c dhcpcd-run-hooks.in if-bsd.c ipv4ll.h net.h signals.h
compat:
arc4random.c arc4random.h closefrom.c closefrom.h getline.c getline.h linkaddr.c strlcpy.c strlcpy.h
dhcpcd-hooks:
01-test 10-mtu 20-resolv.conf 30-hostname 50-ntp.conf 50-yp.conf 95-configured
02-dump 20-dns.conf 29-lookup-hostname 50-dhcpcd-compat 50-ypbind 90-NetworkManager Makefile
mk:
cc.mk depend.mk dist.mk files.mk man.mk os-BSD.mk os-Darwin.mk os-Linux.mk os.mk prog.mk scripts.mk sys.mk
这里代码比较多,暂不细究了。