Android5.1系統啟動過程中啟動有線網絡卡併為其分配靜態IP地址
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) {
&nb