1 android N之前 Ethernet舊的ip擷取
Android N之前,即android 5.0和android 6.0的IP擷取機制都是通過/system/bin下面的dhcpcd的bin檔去拿的ip
public void onRequestNetwork() {
// TODO: Handle DHCP renew.
Thread dhcpThread = new Thread(new Runnable() {
public void run() {
...
DhcpResults dhcpResults = new DhcpResults();
// TODO: Handle DHCP renewals better.
// In general runDhcp handles DHCP renewals for us, because
// the dhcp client stays running, but if the renewal fails,
// we will lose our IP address and connectivity without
// noticing.
//通過這個函數call到jni之後,最後run /system/bin/dhcpcd
if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
// set our score lower than any network could go
// so we get dropped.
mFactory.setScoreFilter(-1);
return;
}
不知道是不是dhcpcd本身的問題,這段code其實不是穩定,逾時拿不到ip的現象經常可以遇到。隻能在這裡多加幾次try runDhcp。
2 android N新的拿IP的機制
android N不要了runDhcpcd(),而是通過DhcpClient,這個是android M引入的wifi那邊另外一種獲得ip的方式,DhcpClient是通過framework發送dhcpcd協定的UDP請求包直接去拿IP,不再使用開源的dhcpcd。在調用DhcpClient的基礎之前,google還用了一個狀态機IpManager來管理dhcpcd成功還是失敗等狀态,将ip指派給IpConfiguration和LinkProperties傳遞到上層的framework。還有就是加入ipv6的支援,不過ipv6的部分目前還沒做好。
//EthernetNetworkFactory.java (frameworks\opt\net\ethernet\java\com\android\server\ethernet)
final Thread ipProvisioningThread = new Thread(new Runnable() {
public void run() {
if (DBG) {
Log.d(TAG, String.format("starting ipProvisioningThread(%s): mNetworkInfo=%s",
mIface, mNetworkInfo));
}
LinkProperties linkProperties;
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 {
mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
WaitForProvisioningCallback ipmCallback = new WaitForProvisioningCallback() {
@Override
public void onLinkPropertiesChange(LinkProperties newLp) {
synchronized(EthernetNetworkFactory.this) {
if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
mLinkProperties = newLp;
mNetworkAgent.sendLinkProperties(newLp);
}
}
}
};
synchronized(EthernetNetworkFactory.this) {
stopIpManagerLocked();
//IPManger,用這個代替之前的runDhcpcd()
mIpManager = new IpManager(mContext, mIface, ipmCallback);
if (config.getProxySettings() == ProxySettings.STATIC ||
config.getProxySettings() == ProxySettings.PAC) {
mIpManager.setHttpProxy(config.getHttpProxy());
}
final String tcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_ethernet_tcp_buffers);
if (!TextUtils.isEmpty(tcpBufferSizes)) {
mIpManager.setTcpBufferSizes(tcpBufferSizes);
}
final ProvisioningConfiguration provisioningConfiguration =
mIpManager.buildProvisioningConfiguration()
.withProvisioningTimeoutMs(0)
.build();
//start
mIpManager.startProvisioning(provisioningConfiguration);
}
//通過callback去拿到新的IP
linkProperties = ipmCallback.waitForProvisioning();
if (linkProperties == null) {
Log.e(TAG, "IP provisioning error");
// set our score lower than any network could go
// so we get dropped.
mFactory.setScoreFilter(-1);
synchronized(EthernetNetworkFactory.this) {
stopIpManagerLocked();
}
return;
}
}
IpManager
IpManager,是wifi和Ethernet都會用到。把它定義為擷取ip的管理器好了。
2.1 構造函數
public IpManager(Context context, String ifName, Callback callback)
throws IllegalArgumentException {
this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)));
}
/**
* An expanded constructor, useful for dependency injection.
*/
public IpManager(Context context, String ifName, Callback callback,
INetworkManagementService nwService) throws IllegalArgumentException {
super(IpManager.class.getSimpleName() + "." + ifName);
mTag = getName();
mContext = context;
//網口的名字,eth0 or wlan0
mInterfaceName = ifName;
mClatInterfaceName = CLAT_PREFIX + ifName;
mCallback = new LoggingCallbackWrapper(callback);
//NetworkManagementService對象,可以對Interface做一些操作
mNwService = nwService;
//檢測Interface的狀态變化
mNetlinkTracker = new NetlinkTracker(
mInterfaceName,
new NetlinkTracker.Callback() {
@Override
public void update() {
sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
}
}) {
@Override
public void interfaceAdded(String iface) {
super.interfaceAdded(iface);
if (mClatInterfaceName.equals(iface)) {
mCallback.setNeighborDiscoveryOffload(false);
}
}
@Override
public void interfaceRemoved(String iface) {
super.interfaceRemoved(iface);
if (mClatInterfaceName.equals(iface)) {
// TODO: consider sending a message to the IpManager main
// StateMachine thread, in case "NDO enabled" state becomes
// tied to more things that 464xlat operation.
mCallback.setNeighborDiscoveryOffload(true);
}
}
};
try {
mNwService.registerObserver(mNetlinkTracker);
} catch (RemoteException e) {
Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
}
//LinkProperties裡面記錄着網絡相關的ip和dns等資訊,這個在connectivityservice中會被使用到
resetLinkProperties();
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
// Super simple StateMachine.
//狀态機,很簡單,才三個狀态
addState(mStoppedState);
addState(mStartedState);
addState(mStoppingState);
setInitialState(mStoppedState);
mLocalLog = new LocalLog(MAX_LOG_RECORDS);
super.start();
}
2.2 ProvisioningConfiguration配置類
前面再IpManager開始之前,會new一個provisioningConfiguration對象,這個configuration就指明,是否enabled ipv6,靜态ip的對象是哪個,還有timeout的時間。
final ProvisioningConfiguration provisioningConfiguration =
mIpManager.buildProvisioningConfiguration()
.withProvisioningTimeoutMs(0)
.build();
public ProvisioningConfiguration() {}
public ProvisioningConfiguration(ProvisioningConfiguration other) {
mEnableIPv4 = other.mEnableIPv4;
mEnableIPv6 = other.mEnableIPv6;
mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
mStaticIpConfig = other.mStaticIpConfig;
mApfCapabilities = other.mApfCapabilities;
mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
}
2.3 mIpManager.startProvisioning(provisioningConfiguration)
開始擷取IP啦~
public void startProvisioning(ProvisioningConfiguration req) {
getNetworkInterface();
mCallback.setNeighborDiscoveryOffload(true);
//發送CMD_START出來
sendMessage(CMD_START, new ProvisioningConfiguration(req));
}
一開始的狀态時在StoppedState中,收到CMD_START跟着就會跳轉到mStartedState
class StoppedState extends State {
@Override
public void enter() {
try {
mNwService.disableIpv6(mInterfaceName);
mNwService.clearInterfaceAddresses(mInterfaceName);
} catch (Exception e) {
Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
}
resetLinkProperties();
if (mStartTimeMillis > 0) {
recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
mStartTimeMillis = 0;
}
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_STOP:
break;
case CMD_START:
mConfiguration = (ProvisioningConfiguration) msg.obj;
transitionTo(mStartedState);
break;
先跑StartedState的enter函數
class StartedState extends State {
private boolean mDhcpActionInFlight;
@Override
public void enter() {
//開始的時間
mStartTimeMillis = SystemClock.elapsedRealtime();
/**
* For networks that support packet filtering via APF programs, {@code ApfFilter}
* listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
* filter out redundant duplicate ones.
*
* Threading model:
* A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
* know what RAs to filter for, thus generating APF programs is dependent on mRas.
* mRas can be accessed by multiple threads:
* - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
* - callers of:
* - setMulticastFilter(), which can cause an APF program to be generated.
* - dump(), which dumps mRas among other things.
* - shutdown(), which clears mRas.
* So access to mRas is synchronized.
*
* @hide
*/
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
mCallback, mMulticastFiltering);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
//如果ipv6如果這麼寫的話,如果timout會存在卡到ipv4?ipv6和ipv4之間的邏輯關系應該是怎麼樣的?
if (mConfiguration.mEnableIPv6) {
// TODO: Consider transitionTo(mStoppingState) if this fails.
startIPv6();
}
//這個是arp鄰居表的檢測
if (mConfiguration.mUsingIpReachabilityMonitor) {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
mInterfaceName,
new IpReachabilityMonitor.Callback() {
@Override
public void notifyLost(InetAddress ip, String logMsg) {
mCallback.onReachabilityLost(logMsg);
}
});
}
//啟動ipv4
if (mConfiguration.mEnableIPv4) {
if (!startIPv4()) {
transitionTo(mStoppingState);
}
}
}
startIPv6的部分還沒寫好,跳過,看ipv4的部分
private boolean startIPv4() {
// If we have a StaticIpConfiguration attempt to apply it and
// handle the result accordingly.
//如果是靜态ip,在這裡設定一下
if (mConfiguration.mStaticIpConfig != null) {
if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
} else {
if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
recordMetric(IpManagerEvent.PROVISIONING_FAIL);
mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
return false;
}
} else {
// Start DHCPv4.
//構造DhcpClient對象,發送dhcp client請求
mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
mDhcpClient.registerForPreDhcpNotification();
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
if (mConfiguration.mProvisioningTimeoutMs > 0) {
final long alarmTime = SystemClock.elapsedRealtime() +
mConfiguration.mProvisioningTimeoutMs;
//通過鬧鐘去叫醒
/*
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
*/
mProvisioningTimeoutAlarm.schedule(alarmTime);
}
}
return true;
}
2.4 DhcpClient
接收前面發過來的mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);, 跟着跳轉到DhcpInitState:
class StoppedState extends LoggingState {
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
case CMD_START_DHCP:
if (mRegisteredForPreDhcpNotification) {
transitionTo(mWaitBeforeStartState);
} else {
transitionTo(mDhcpInitState);
}
return HANDLED;
default:
return NOT_HANDLED;
}
}
}
class DhcpInitState extends PacketRetransmittingState {
public DhcpInitState() {
super();
}
@Override
//跑DhcpInitState的父類PacketRetransmittingState的enter
public void enter() {
super.enter();
startNewTransaction();
}
protected boolean sendPacket() {
return sendDiscoverPacket();
}
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if (!(packet instanceof DhcpOfferPacket)) return;
mOffer = packet.toDhcpResults();
if (mOffer != null) {
Log.d(TAG, "Got pending lease: " + mOffer);
transitionTo(mDhcpRequestingState);
}
}
}
重點是這裡的CMD_KICK指令,發送這個指令,之後會調用sendPacket()函數,去發送請求包.
/**
* Retransmits packets using jittered exponential backoff with an optional timeout. Packet
* transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
* sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
* milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
* state.
*
* Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
* packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
* sent by the receive thread. They may also set mTimeout and implement timeout.
*/
abstract class PacketRetransmittingState extends LoggingState {
private int mTimer;
protected int mTimeout = 0;
@Override
public void enter() {
super.enter();
initTimer();
maybeInitTimeout();
sendMessage(CMD_KICK);
}
這裡發送的是DHCPDISCOVER dhcp協定的DISCOVER包
private boolean sendDiscoverPacket() {
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
DO_UNICAST, REQUESTED_PARAMS);
return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
}
另外一邊有一個接收函數,一直在收dhcp server的的資料包。
class ReceiveThread extends Thread {
private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
private volatile boolean mStopped = false;
public void halt() {
mStopped = true;
closeSockets(); // Interrupts the read() call the thread is blocked in.
}
@Override
public void run() {
if (DBG) Log.d(TAG, "Receive thread started");
while (!mStopped) {
int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
try {
length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
DhcpPacket packet = null;
packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
if (DBG) Log.d(TAG, "Received packet: " + packet);
//發送CMD_RECEIVED_PACKET,把資料包往上面送
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (IOException|ErrnoException e) {
if (!mStopped) {
Log.e(TAG, "Read error", e);
DhcpErrorEvent.logReceiveError(mIfaceName);
}
} catch (DhcpPacket.ParseException e) {
Log.e(TAG, "Can't parse packet: " + e.getMessage());
if (PACKET_DBG) {
Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
}
DhcpErrorEvent.logParseError(mIfaceName, e.errorCode);
}
}
if (DBG) Log.d(TAG, "Receive thread stopped");
}
}
接收CMD_RECEIVED_PACKET,我們還在PacketRetransmittingState 這個state中,是以他會處理這個CMD
abstract class PacketRetransmittingState extends LoggingState {
private int mTimer;
protected int mTimeout = 0;
...
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
case CMD_KICK:
sendPacket();
scheduleKick();
return HANDLED;
case CMD_RECEIVED_PACKET:
//這個會call到DhcpInitState的receivePacket函數
receivePacket((DhcpPacket) message.obj);
return HANDLED;
case CMD_TIMEOUT:
timeout();
return HANDLED;
default:
return NOT_HANDLED;
}
}
接收的資料在這裡被處理,處理完之後,将結果指派給mOffer,意思是dhcp的offer資料包,裡面當然就是存着ip的值,然後跳轉到mDhcpRequestingState
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
//判斷是不是server 放回的offer包
if (!(packet instanceof DhcpOfferPacket)) return;
mOffer = packet.toDhcpResults();
if (mOffer != null) {
Log.d(TAG, "Got pending lease: " + mOffer);
transitionTo(mDhcpRequestingState);
}
}
這個DhcpRequestingState 是繼承了PacketRetransmittingState ,還記得前面的CMD_KICK嗎?scheduleKick()函數的調用,這個CMD_KICK會定時發送,理論上dicover包要發4次。server offer之後,就要跑client發送request了,當跑到這個statemachine裡面的時候,就會跑DhcpRequestingState 這個裡面的sendPacket()了,google大神真是牛。那這次send的是dhcp協定的什麼資料包呢?
class DhcpRequestingState extends PacketRetransmittingState {
public DhcpRequestingState() {
mTimeout = DHCP_TIMEOUT_MS / 2;
}
protected boolean sendPacket() {
return sendRequestPacket(
INADDR_ANY, // ciaddr
(Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
(Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
INADDR_BROADCAST); // packet destination address
}
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if ((packet instanceof DhcpAckPacket)) {
DhcpResults results = packet.toDhcpResults();
if (results != null) {
setDhcpLeaseExpiry(packet);
acceptDhcpResults(results, "Confirmed");
transitionTo(mConfiguringInterfaceState);
}
} else if (packet instanceof DhcpNakPacket) {
// TODO: Wait a while before returning into INIT state.
Log.d(TAG, "Received NAK, returning to INIT");
mOffer = null;
transitionTo(mDhcpInitState);
}
}
@Override
protected void timeout() {
// After sending REQUESTs unsuccessfully for a while, go back to init.
transitionTo(mDhcpInitState);
}
}
DHCPREQUEST ciaddr 知道知道dhcp的協定,就知道發完discover包之後,要再發一個DHCPREQUEST。相當是這個ip我要了,然後等sever的ACK。
DHCP Client會以廣播形式向DHCP Server發送DHCPRequest封包來續租IP位址。如果DHCP Client成功收到DHCP Server發送的DHCP ACK封包.
private boolean sendRequestPacket(
Inet4Address clientAddress, Inet4Address requestedAddress,
Inet4Address serverAddress, Inet4Address to) {
// TODO: should we use the transaction ID from the server?
final int encap = INADDR_ANY.equals(clientAddress)
? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
ByteBuffer packet = DhcpPacket.buildRequestPacket(
encap, mTransactionId, getSecs(), clientAddress,
DO_UNICAST, mHwAddr, requestedAddress,
serverAddress, REQUESTED_PARAMS, null);
String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
" serverid=" + serverStr;
return transmitPacket(packet, description, encap, to);
}
sever ack之後,我們再次處理接收包。
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if ((packet instanceof DhcpAckPacket)) {
DhcpResults results = packet.toDhcpResults();
if (results != null) {
setDhcpLeaseExpiry(packet);
//這裡會調用notifySuccess,發送CMD去通知IpManager說,IP拿到了
acceptDhcpResults(results, "Confirmed");//①
//自己調到mConfiguringInterfaceState
transitionTo(mConfiguringInterfaceState);//②
}
} else if (packet instanceof DhcpNakPacket) {
// TODO: Wait a while before returning into INIT state.
Log.d(TAG, "Received NAK, returning to INIT");
mOffer = null;
transitionTo(mDhcpInitState);
}
}
①這裡調用了notifySuccess()發生CMD去通知IpManager,IP擷取成功了。
private void acceptDhcpResults(DhcpResults results, String msg) {
mDhcpLease = results;
mOffer = null;
Log.d(TAG, msg + " lease: " + mDhcpLease);
notifySuccess();
}
這裡的mController指的是IpManager裡面的接收者,發送CMD_POST_DHCP_ACTION
private void notifySuccess() {
mController.sendMessage(
CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
}
class StartedState extends State {
....
// This message is only received when:
//
// a) initial address acquisition succeeds,
// b) renew succeeds or is NAK'd,
// c) rebind succeeds or is NAK'd, or
// c) the lease expires,
//
// but never when initial address acquisition fails. The latter
// condition is now governed by the provisioning timeout.
case DhcpClient.CMD_POST_DHCP_ACTION:
stopDhcpAction();
switch (msg.arg1) {
case DhcpClient.DHCP_SUCCESS:
handleIPv4Success((DhcpResults) msg.obj);
break;
case DhcpClient.DHCP_FAILURE:
handleIPv4Failure();
break;
default:
Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
}
break;
.....
}
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
final LinkProperties newLp = assembleLinkProperties();
final ProvisioningChange delta = setLinkProperties(newLp);
if (VDBG) {
Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
}
mCallback.onNewDhcpResults(dhcpResults);
//這裡分發dhcpResults
dispatchCallback(delta, newLp);
}
private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
switch (delta) {
case GAINED_PROVISIONING:
if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
recordMetric(IpManagerEvent.PROVISIONING_OK);
mCallback.onProvisioningSuccess(newLp);
break;
case LOST_PROVISIONING:
if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
recordMetric(IpManagerEvent.PROVISIONING_FAIL);
mCallback.onProvisioningFailure(newLp);
break;
default:
if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
mCallback.onLinkPropertiesChange(newLp);
break;
}
}
dispatchCallback的結果最後會作用到WaitForProvisioningCallback ,這個就是前面EthernetNetworkFactory 中 linkProperties = ipmCallback.waitForProvisioning();,等待的結果。
public static class WaitForProvisioningCallback extends Callback {
private LinkProperties mCallbackLinkProperties;
public LinkProperties waitForProvisioning() {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {}
return mCallbackLinkProperties;
}
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
synchronized (this) {
mCallbackLinkProperties = newLp;
notify();
}
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
synchronized (this) {
mCallbackLinkProperties = null;
notify();
}
}
}
②這裡跑入enter(),給IpManager發了一個CMD_CONFIGURE_LINKADDRESS 的CMD
class ConfiguringInterfaceState extends LoggingState {
@Override
public void enter() {
super.enter();
mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
}
@Override
public boolean processMessage(Message message) {
super.processMessage(message);
switch (message.what) {
case EVENT_LINKADDRESS_CONFIGURED:
transitionTo(mDhcpBoundState);
return HANDLED;
default:
return NOT_HANDLED;
}
}
}
我們傳回到IpManager中去看,在IpManger中,我們還處于StartedState中
class StartedState extends State {
......
case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
final LinkAddress ipAddress = (LinkAddress) msg.obj;
//将IP通過NetworkManagerService寫到Interface上面
if (setIPv4Address(ipAddress)) {
mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
} else {
Log.e(mTag, "Failed to set IPv4 address!");
dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
new LinkProperties(mLinkProperties));
transitionTo(mStoppingState);
}
break;
}
......
}
這個setIPv4Address很簡單,調用了NetworkManagementService.setInterfaceConfig()
private boolean setIPv4Address(LinkAddress address) {
final InterfaceConfiguration ifcg = new InterfaceConfiguration();
ifcg.setLinkAddress(address);
try {
mNwService.setInterfaceConfig(mInterfaceName, ifcg);
if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
} catch (IllegalStateException | RemoteException e) {
Log.e(mTag, "IPv4 configuration failed: ", e);
return false;
}
return true;
}
調用Netd,通過Commandlisten.Java去往下跑,這裡不跟了。
//NetworkManagementService.java (frameworks\base\services\core\java\com\android\server)
@Override
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();
}
}
3 總結
不知道google加入的這個IpManager是否又有效,但是從整個架構看下來,為後面ipv6的加入可以說是打好了基礎,加速ipv6時代的到來。因為apple已經開始強制要求app support ipv6了,android需要加把勁。