转自:http://blog.csdn.net/moyu123456789/article/details/50002099
Android5.1系统启动过程中启动有线网卡并为其分配静态IP地址
遇到这个问题的时候刚开始自己以为在init.rc中添加两行命令就能解决问题,可是后来发现并非如此简单,所以用下面的文字记录一下自己解决这个问题的方法。
首先想到的方法是在init.rc中用ifup eth0命令将端口启动起来,然后写一个服务区调用ifconfig来配置静态地址就会搞定,但是并没有如愿。接着在网上找了下相关的代码,根据这篇博文(http://blog.csdn.net/tankai19880619/article/details/44559419)的提示仔细分析了ethernet启动的过程,不清楚ethernet启动过程的可以参考这篇博文,然后阅读源码来熟悉。
分析ethernet启动的过程中,根据源码追踪到了readPermissionsFromXml方法里,Android启动过程中会调用readPermissionsFromXml方法来解析一些xml文件,表明系统有哪些permission。其中就有对系统支持的feature的解析:
private void readPermissionsFromXml(FilepermFile, boolean onlyFeatures) {
……
else if ("feature".equals(name)) {
Stringfname = parser.getAttributeValue(null, "name");
booleanallowed;
if(!lowRam) {
allowed = true;
} else {
String notLowRam =parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname== null) {
Slog.w(TAG, "<feature> without name in " + permFile +" at "
+ parser.getPositionDescription());
} else if(allowed) {
//Log.i(TAG, "Got feature " + fname);
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
}
}
Ethernet作为系统的一个feature,如果要支持的话就得在其解析的platform.xml(frameworks/base/data/etc/platform.xml)中添加,对于platform.xml文件在这里就不解释了,网上相关的解释已经够多了,有需要了解的可以根据网上的介绍自己对着原文件来阅读理解。看了下platform.xml文件,其中果然没有表示ethernet的feature,于是果断的在platform.xml中添加了下面这几行:
<!--add for ethernet begin-->
<featurename="android.hardware.ethernet" >
<groupgid="net_admin" />
</feature>
<!--add for ethernetend-->
编译运行后果然看到了我需要的eth0端口变为了up状态,启动指定网卡的工作是不是到此就算结束了呢?我自己在分析网卡启动的源码流程的时候,发现了启动端口的下面这段代码也需要特别注意:
public synchronized void start(Contextcontext, Handler target) {
……
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
synchronized(this) {
if(maybeTrackInterface(iface)) {
// We have ourinterface. Track it.
// Note: if theinterface already has link (e.g., if we
// crashed and got restartedwhile it was running),
// we need to fake alink up notification so we start
// configuring it.Since we're already holding the lock,
// any real linkup/down notification will only arrive
// after we've donethis.
if(mNMService.getInterfaceConfig(iface).hasFlag("running")) {
updateInterfaceState(iface, true);
}
break;
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
……
这里的String[]ifaces保存了当前系统里的所有端口,但是在后边启动端口的代码中,启动了一个端口后就调用了break;语句,因此在这里只能启动一个网卡,如果有多个网卡时,就得注意此处启动的是否是你需要的端口,我自己使用的板子上回出现eth0和eth1两个网卡,而我需要启动的是eth0而非eth1,所以后来在此处也做了处理,这样一来就可以做到在系统启动阶段启动我们指定的网卡的目的。
下面在说一下为该网卡分配静态IP地址的方法吧。Android中的端口默认都是由DHCP来自动分配IP地址的,那么Android中是怎样分配静态地址的呢?我们会发现,当我们在Android手机中对网络设置了静态地址后,以后每次启动都是原来设置的静态地址,但是我现在想实现的是在系统启动的时候在代码中自动的分配我指定的静态地址。
还是来分析网络启动的过程,因为端口的IP地址必然是在网络启动的过程中分配的。
启动网络的方法:
protected void startNetwork() {
onRequestNetwork();
}
onRequestNetwork方法如下,在onRequestNetwork方法中启动了一个dhcp线程,在该线程中获取的LinkProperties linkProperties里便保存了端口将会分配到的地址:
/* Called by the NetworkFactory on the handler thread. */
public voidonRequestNetwork() {
// TODO: Handle DHCPrenew.
Thread dhcpThread =new Thread(new Runnable() {
public void run(){
if (DBG)Log.i(TAG, "dhcpThread(+" + mIface + "): mNetworkInfo=" +mNetworkInfo);
LinkPropertieslinkProperties;
IpConfiguration config = mEthernetManager.getConfiguration();
if(config.getIpAssignment() == IpAssignment.STATIC) {//如果是静态地址
if(!setStaticIpAddress(config.getStaticIpConfiguration())) {
//We've already logged an error.
return;
}
linkProperties =config.getStaticIpConfiguration().toLinkProperties(mIface);
} else {//DHCP分配的动态地址
mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null,mHwAddr);
DhcpResultsdhcpResults = new DhcpResults();
// TODO:Handle DHCP renewals better.
// Ingeneral runDhcp handles DHCP renewals for us, because
// thedhcp client stays running, but if the renewal fails,
// we willlose our IP address and connectivity without
//noticing.
if(!NetworkUtils.runDhcp(mIface, dhcpResults)) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
// setour score lower than any network could go
// sowe get dropped.
mFactory.setScoreFilter(-1);
return;
}
linkProperties = dhcpResults.toLinkProperties(mIface);
}
if(config.getProxySettings() == ProxySettings.STATIC ||
config.getProxySettings() == ProxySettings.PAC) {
linkProperties.setHttpProxy(config.getHttpProxy());
}
StringtcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_ethernet_tcp_buffers);
if (TextUtils.isEmpty(tcpBufferSizes)== false) {
linkProperties.setTcpBufferSizes(tcpBufferSizes);
}
synchronized(EthernetNetworkFactory.this) {
if(mNetworkAgent != null) {
Log.e(TAG, "Already have aNetworkAgent - aborting new request");
return;
}
mLinkProperties = linkProperties;
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED,null, mHwAddr);
// Createour NetworkAgent.
mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext,
NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
NETWORK_SCORE) {
publicvoid unwanted() {
synchronized(EthernetNetworkFactory.this) {
if (this == mNetworkAgent) {
NetworkUtils.stopDhcp(mIface);
mLinkProperties.clear();
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null,
mHwAddr);
updateAgent();
mNetworkAgent = null;
try {
mNMService.clearInterfaceAddresses(mIface);
} catch (Exception e) {
Log.e(TAG, "Failed to clear addresses or disable ipv6" + e);
}
} else {
Log.d(TAG, "Ignoring unwanted as we have a more modern " +
"instance");
}
}
};
};
}
}
});
dhcpThread.start();
}
阅读上面的代码,我们会发现当前接口是否要为其分配静态地址是根据该接口对应的config.getIpAssignment()来决定的,那么我们就得去找决定config.getIpAssignment()的地方在哪里?顺藤摸瓜吧:
frameworks/base/core/java/android/net/EthernetManager.java
public IpConfiguration getConfiguration() {
try {
return mService.getConfiguration();
} catch (NullPointerException | RemoteException e) {
return new IpConfiguration();
}
}
frameworks/opt/net/ethernet/java/com/android/server/Ethernet/EthernetServiceImple.java
public IpConfiguration getConfiguration()
enforceAccessPermission();
synchronized (mIpConfiguration) {
return new IpConfiguration(mIpConfiguration);
}
}
public EthernetServiceImpl(Context context){
mContext = context;
Log.i(TAG, "Creating EthernetConfigStore");
mEthernetConfigStore = new EthernetConfigStore();
mIpConfiguration= mEthernetConfigStore.readIpAndProxyConfigurations();
Log.i(TAG, "Read stored IP configuration: " +mIpConfiguration);
mTracker = new EthernetNetworkFactory(mListeners);
}
frameworks/opt/net/ethernet/java/com/android/server/Ethernet/EthernetConfigStore.java
private staticfinal String ipConfigFile =Environment.getDataDirectory() +
"/misc/ethernet/ipconfig.txt";
publicIpConfiguration readIpAndProxyConfigurations() {
SparseArray<IpConfiguration>networks = readIpAndProxyConfigurations(ipConfigFile);
if (networks.size() == 0) {
Log.w(TAG, "No Ethernet configurationfound. Using default.");
return newIpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
}
if (networks.size() > 1) {
// Currently we only support asingle Ethernet interface.
Log.w(TAG, "Multiple Ethernetconfigurations detected. Only reading first one.");
}
return networks.valueAt(0);
}
终于找到了下面这个文件:
frameworks/base/services/core/java/com/android/server/net/IpConfigStore.java
publicSparseArray<IpConfiguration> readIpAndProxyConfigurations(StringfilePath) {
SparseArray<IpConfiguration>networks = new SparseArray<IpConfiguration>();
DataInputStream in =null;
try {
in = newDataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
int version= in.readInt();
if (version!= 2 && version != 1) {
loge("Badversion on IP configuration file, ignore read");
returnnull;
}
while(true) {
intid = -1;
//Default is DHCP with no proxy
IpAssignmentipAssignment = IpAssignment.DHCP;
ProxySettingsproxySettings = ProxySettings.NONE;
StaticIpConfigurationstaticIpConfiguration = new StaticIpConfiguration();
StringproxyHost = null;
StringpacFileUrl = null;
intproxyPort = -1;
StringexclusionList = null;
Stringkey;
do{
key= in.readUTF();
try{
if(key.equals(ID_KEY)) {
id= in.readInt();
}else if (key.equals(IP_ASSIGNMENT_KEY)) {
ipAssignment= IpAssignment.valueOf(in.readUTF());
}else if (key.equals(LINK_ADDRESS_KEY)) {
LinkAddresslinkAddr = new LinkAddress(
NetworkUtils.numericToInetAddress(in.readUTF()),in.readInt());
if(linkAddr.getAddress() instanceof Inet4Address &&
staticIpConfiguration.ipAddress== null) {
staticIpConfiguration.ipAddress= linkAddr;
}else {
loge("Non-IPv4or duplicate address: " + linkAddr);
}
}else if (key.equals(GATEWAY_KEY)) {
LinkAddressdest = null;
InetAddress gateway = null;
if(version == 1) {
//only supported default gateways - leave the dest/prefix empty
gateway= NetworkUtils.numericToInetAddress(in.readUTF());
if(staticIpConfiguration.gateway == null) {
staticIpConfiguration.gateway= gateway;
}else {
loge("Duplicategateway: " + gateway.getHostAddress());
}
}else {
if(in.readInt() == 1) {
dest= new LinkAddress(
NetworkUtils.numericToInetAddress(in.readUTF()),
in.readInt());
}
if(in.readInt() == 1) {
gateway= NetworkUtils.numericToInetAddress(in.readUTF());
}
RouteInforoute = new RouteInfo(dest, gateway);
if(route.isIPv4Default() &&
staticIpConfiguration.gateway== null) {
staticIpConfiguration.gateway= gateway;
}else {
loge("Non-IPv4default or duplicate route: " + route);
}
}
}else if (key.equals(DNS_KEY)) {
staticIpConfiguration.dnsServers.add(
NetworkUtils.numericToInetAddress(in.readUTF()));
}else if (key.equals(PROXY_SETTINGS_KEY)) {
proxySettings= ProxySettings.valueOf(in.readUTF());
}else if (key.equals(PROXY_HOST_KEY)) {
proxyHost= in.readUTF();
}else if (key.equals(PROXY_PORT_KEY)) {
proxyPort= in.readInt();
}else if (key.equals(PROXY_PAC_FILE)) {
pacFileUrl= in.readUTF();
}else if (key.equals(EXCLUSION_LIST_KEY)) {
exclusionList= in.readUTF();
}else if (key.equals(EOS)) {
break;
}else {
loge("Ignoreunknown key " + key + "while reading");
}
}catch (IllegalArgumentException e) {
loge("Ignoreinvalid address while reading" + e);
}
}while (true);
if(id != -1) {
IpConfigurationconfig = new IpConfiguration();
networks.put(id,config);
switch(ipAssignment) {
caseSTATIC:
config.staticIpConfiguration= staticIpConfiguration;
config.ipAssignment= ipAssignment;
break;
caseDHCP:
config.ipAssignment= ipAssignment;
break;
caseUNASSIGNED:
loge("BUG:Found UNASSIGNED IP on file, use DHCP");
config.ipAssignment= IpAssignment.DHCP;
break;
default:
loge("Ignoreinvalid ip assignment while reading.");
config.ipAssignment= IpAssignment.UNASSIGNED;
break;
}
switch(proxySettings) {
caseSTATIC:
ProxyInfoproxyInfo =
newProxyInfo(proxyHost, proxyPort, exclusionList);
config.proxySettings= proxySettings;
config.httpProxy= proxyInfo;
break;
casePAC:
ProxyInfoproxyPacProperties = new ProxyInfo(pacFileUrl);
config.proxySettings= proxySettings;
config.httpProxy= proxyPacProperties;
break;
caseNONE:
config.proxySettings= proxySettings;
break;
caseUNASSIGNED:
loge("BUG:Found UNASSIGNED proxy on file, use NONE");
config.proxySettings= ProxySettings.NONE;
break;
default:
loge("Ignoreinvalid proxy settings while reading");
config.proxySettings= ProxySettings.UNASSIGNED;
break;
}
}else {
if(DBG) log("Missing id while parsing configuration");
}
}
} catch(EOFException ignore) {
} catch (IOExceptione) {
loge("Errorparsing configuration: " + e);
} finally {
if (in !=null) {
try{
in.close();
}catch (Exception e) {}
}
}
return networks;
}
看到上边的代码流程后,对于Android中设置静态地址已经非常清楚了。不错,Android就是在系统的/misc/ethernet/ipconfig.txt文件中来读取要设置的静态地址信息的,那么我们也就可以根据readIpAndProxyConfigurations方法来构造一个这样的ipconfig.txt文件对接口地址进行设置。当然本人是比较懒的,直接在readIpAndProxyConfigurations方法中进行修改,而不是从ipconfig.txt中读取,这样就可以对接口设置静态地址了。
DataInputStream in = null;
try {
//in = new DataInputStream(newBufferedInputStream(new FileInputStream(filePath)));
int version = 2;
if (version != 2 && version!= 1) {
loge("Bad version on IPconfiguration file, ignore read");
return null;
}
//while (true) {
int id = -1;
// Default is DHCP with noproxy
IpAssignment ipAssignment =IpAssignment.STATIC;
ProxySettings proxySettings =ProxySettings.NONE;
StaticIpConfigurationstaticIpConfiguration = new StaticIpConfiguration();
String proxyHost = null;
String pacFileUrl = null;
int proxyPort = -1;
String exclusionList = null;
String key;
id = 1;
//ipAssignment= IpAssignment.valueOf(in.readUTF());
id = 1;
LinkAddresslinkAddr = new LinkAddress(
NetworkUtils.numericToInetAddress("192.168.1.77"),24);
staticIpConfiguration.ipAddress= linkAddr;
LinkAddressdest = null;
InetAddress gateway = null;
gateway = NetworkUtils.numericToInetAddress("192.168.1.1");
staticIpConfiguration.gateway= gateway;
staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress("192.168.1.254"));
当然,如果你有多个网卡的话,就有必要在onRequestNetwork里边做修改了,以使对应的接口能够进入if (config.getIpAssignment() ==IpAssignment.STATIC) {分支,比如增加下面的修改,以对接口eth0配置静态地址。
public voidonRequestNetwork() {
// TODO: Handle DHCP renew.
Thread dhcpThread = new Thread(newRunnable() {
public void run() {
if (DBG) Log.i(TAG,"dhcpThread(+" + mIface + "): mNetworkInfo=" + mNetworkInfo);
LinkProperties linkProperties;
IpConfiguration config =mEthernetManager.getConfiguration();
if ((config.getIpAssignment() ==IpAssignment.STATIC) && (mIface.equals("eth0"))){
if(!setStaticIpAddress(config.getStaticIpConfiguration())) {
// We've already loggedan error.
return;
}
linkProperties =config.getStaticIpConfiguration().toLinkProperties(mIface);
} else {