安卓經過DHCP協議的DORA Discovery發現 Offer提供 Request請求 Ack確認 獲取IP地址的過程
安卓N以前 5.0 6.0經過 android_net_utils_runDhcp 方法運行 /system/bin/dhcpcd 獲取ip地址
安卓N以後 N不要了runDhcpcd(),而是經過DhcpClient
DhcpClient是經過framework發送dhcpcd協議的UDP請求包直接去拿IP,再也不使用開源的dhcpcd
google還用了一個狀態機 IpManager 來管理dhcpcd成功仍是失敗等狀態,
將ip賦值給IpConfiguration和LinkProperties傳遞到上層的frameworkjava
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.javaandroid
// 當WifiStateMachine狀態機進入狀態ObtainingIpState時 獲取IP地址
class ObtainingIpState extends State {
@Override
public void enter() {
mIpManager.startProvisioning(IpManager.ProvisioningConfiguration prov); //啓動獲取IP地址
}
}
ObtainingIpState所在狀態機服務器
/frameworks/base/services/net/java/android/net/ip/IpManager.javaapp
class StartedState extends State {} IpManager中的三個狀態機
class StoppedState extends State {}
class StoppingState extends State {}dom
public void startProvisioning(ProvisioningConfiguration req) { getNetworkInterface(); mCallback.setNeighborDiscoveryOffload(true); // 給初始化的狀態機 StoppedState 發送消息CMD_START sendMessage(CMD_START, new ProvisioningConfiguration(req)); }
========================================================================
class StoppedState extends State {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
…..
case CMD_START:
mConfiguration = (ProvisioningConfiguration) msg.obj;
// 接收到 CMD_START 會進行狀態的切換,調用 StartedState的enter()方法
transitionTo(mStartedState);
break;
…..
}socket
==========================================ide
class StartedState extends State {
void enter(){
if(startIPv4()) // 狀態 StartedState 的enter 進入方法,調用startIPv4()函數
}
}函數
==========================================ui
private boolean startIPv4() { // If we have a StaticIpConfiguration attempt to apply it and handle the result accordingly. if (mConfiguration.mStaticIpConfig != null) { // 若是有靜態IP 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 mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName); mDhcpClient.registerForPreDhcpNotification(); // mRegisteredForPreDhcpNotification = true //接收前面發過來的mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);跟着跳轉到DhcpInitState: mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); // 發送CMD_START_DHCP消息 if (mConfiguration.mProvisioningTimeoutMs > 0) { final long alarmTime = SystemClock.elapsedRealtime() + mConfiguration.mProvisioningTimeoutMs; mProvisioningTimeoutAlarm.schedule(alarmTime); // 在36秒後啓動timeout超時操做 } } return true; } private boolean setIPv4Address(LinkAddress address) { final InterfaceConfiguration ifcg = new InterfaceConfiguration(); ifcg.setLinkAddress(address); try { final INetworkManagementService 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; }
==========================================
//接收前面發過來的mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);跟着跳轉到 DhcpInitState:
mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); // 發送CMD_START_DHCP消息this
/frameworks/base/services/net/java/android/net/dhcp/DhcpClient.java
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); // 狀態跳轉到 DhcpInitState } return HANDLED; default: return NOT_HANDLED; } }
}
class DhcpInitState extends PacketRetransmittingState { public DhcpInitState() { super(); }
//進入狀態時啓動一次startNewTransaction
//DORA Discover發現 Offer提供 Request請求 ACK確認 開始Discovery 發現?
@Override
public void enter() {
super.enter(); // 調用父類的 enter 方法中
startNewTransaction();
}
protected boolean sendPacket() { return sendDiscoverPacket(); // 發送 DiscoverPacket 發現包 } 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); // 接收到了 Offer包 接下來發送 Request 請求包 } } } private void startNewTransaction() { mTransactionId = mRandom.nextInt(); // 傳輸包的id號? mTransactionStartMillis = SystemClock.elapsedRealtime(); } ================================================== PacketRetransmittingState abstract class PacketRetransmittingState extends LoggingState { private int mTimer; protected int mTimeout = 0; @Override public void enter() { super.enter(); initTimer(); maybeInitTimeout(); sendMessage(CMD_KICK); // 發送消息 CMD_KICK } @Override public boolean processMessage(Message message) { super.processMessage(message); switch (message.what) { case CMD_KICK:
// 調用了 sendPacket()抽象方法 因此就是以前的DhcpInitState的具體的實現 sendPacket()
sendPacket();
scheduleKick();
return HANDLED;
case CMD_RECEIVED_PACKET:
receivePacket((DhcpPacket) message.obj);
return HANDLED;
case CMD_TIMEOUT:
timeout();
return HANDLED;
default:
return NOT_HANDLED;
}
}
public void exit() { mKickAlarm.cancel(); mTimeoutAlarm.cancel(); } } abstract protected boolean sendPacket();
abstract protected void receivePacket(DhcpPacket packet);
class DhcpInitState extends PacketRetransmittingState { protected boolean sendPacket() { return sendDiscoverPacket(); // 發送 DiscoverPacket 發現包 } private boolean sendDiscoverPacket() { ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, REQUESTED_PARAMS); // 建立 ByteBuffer的UDP包
return transmitPacket(packet, 「DHCPDISCOVER」, DhcpPacket.ENCAP_L2, INADDR_BROADCAST); // 發送
}
}
private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) { try { if (encap == DhcpPacket.ENCAP_L2) { if (DBG) Log.d(TAG, "Broadcasting " + description); // 送這裏發送出去 Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) { if (DBG) Log.d(TAG, "Broadcasting " + description); // N.B.: We only need this codepath because DhcpRequestPacket // hardcodes the source IP address to 0.0.0.0. We could reuse // the packet socket if this ever changes. Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); } else { if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",description, Os.getpeername(mUdpSock))); Os.write(mUdpSock, buf); } } catch(ErrnoException|IOException e) { Log.e(TAG, "Can't send packet: ", e); return false; } return true; }
==================================================================
另一邊有一個接收線程ReceiveThread run(),一直在收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 { //讀取DHCP服務發出的OFFER提交包 length = Os.read(mPacketSock 【FileDescriptor】, mPacket, 0, mPacket.length); DhcpPacket packet = null; packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2); if (DBG) Log.d(TAG, "Received packet: " + packet); sendMessage(CMD_RECEIVED_PACKET, packet); // 發送接收到消息 CMD_RECEIVED_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"); }
}
abstract class PacketRetransmittingState extends LoggingState { @Override public boolean processMessage(Message message) { super.processMessage(message); switch (message.what) { case CMD_RECEIVED_PACKET: receivePacket((DhcpPacket) message.obj); // 調用子類具體實現的receivePacket 方法 return HANDLED; default: return NOT_HANDLED; } } }
class DhcpInitState extends PacketRetransmittingState {
class DhcpInitState extends PacketRetransmittingState { protected void receivePacket(DhcpPacket packet) { // 完成 DORA 中的 Offer的階段 if (!isValidPacket(packet)) return; if (!(packet instanceof DhcpOfferPacket)) return; mOffer = packet.toDhcpResults(); if (mOffer != null) { Log.d(TAG, "Got pending lease: " + mOffer); // 接收到了來自DHCP服務器的OFFER包,切換狀態到 DhcpRequestingState 並進入到 enter() 方法 transitionTo(mDhcpRequestingState); } } }
}
===================================================================
class DhcpRequestingState extends PacketRetransmittingState { // 進入到父類的enter 方法 public DhcpRequestingState() { mTimeout = DHCP_TIMEOUT_MS / 2; } public void enter() {// 進入到父類的enter 方法 super.enter(); initTimer(); maybeInitTimeout(); sendMessage(CMD_KICK); // 再次發送 CMD_KICK 消息 } public boolean processMessage(Message message) { super.processMessage(message); switch (message.what) { case CMD_KICK: // 收到 CMD_KICK 消息 sendPacket(); // 發送包方法,此時調用的是具體子類 DhcpRequestingState的發送方法 scheduleKick(); return HANDLED; default: return NOT_HANDLED; } } // 發送請求 完成 DORA中的 Request的階段 // 此時 繼續在接收線程中等待接收來自服務器的ACK DHCP數據包 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); } } 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); // 發送數據包 } return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); // 發送 }
}
private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) { try { if (encap == DhcpPacket.ENCAP_L2) { if (DBG) Log.d(TAG, "Broadcasting " + description); // 送這裏發送出去 真正發送 Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) { if (DBG) Log.d(TAG, "Broadcasting " + description); // N.B.: We only need this codepath because DhcpRequestPacket // hardcodes the source IP address to 0.0.0.0. We could reuse // the packet socket if this ever changes. Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); } else { if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",description, Os.getpeername(mUdpSock))); Os.write(mUdpSock, buf); } } catch(ErrnoException|IOException e) { Log.e(TAG, "Can't send packet: ", e); return false; } return true; }
===========================================================
另一邊有一個接收線程ReceiveThread run(),一直在收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 { //讀取DHCP服務發出的ACK確認包 length = Os.read(mPacketSock 【FileDescriptor】, 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 此時處理的狀態是 DhcpRequestingState 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 對應的數據包是 ACK數據包 UDP
class DhcpRequestingState extends PacketRetransmittingState {
public boolean processMessage(Message message) { super.processMessage(message); switch (message.what) { case CMD_RECEIVED_PACKET: receivePacket((DhcpPacket) message.obj); //DhcpRequestingState處理接收到的數據包 return HANDLED; default: return NOT_HANDLED; } } 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 acceptDhcpResults(results, "Confirmed"); // 分叉 //進入新的狀態 ConfiguringInterfaceState enter方法 transitionTo(mConfiguringInterfaceState); } } else if (packet instanceof DhcpNakPacket) { // 收到的是NACK 解決數據包的話 // 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拿到了 分析 acceptDhcpResults
acceptDhcpResults(results, 「Confirmed」);
private void acceptDhcpResults(DhcpResults results, String msg) { mDhcpLease = results; mOffer = null; Log.d(TAG, msg + " lease: " + mDhcpLease); notifySuccess(); // 通知成功拿到IP地址了 } private void notifySuccess() { mController【StateMachine】.sendMessage( CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease)); } class StartedState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case DhcpClient.CMD_POST_DHCP_ACTION: stopDhcpAction(); switch (msg.arg1) { case DhcpClient.DHCP_SUCCESS: handleIPv4Success((DhcpResults) msg.obj); // 處理 handleIPv4Success IPV4地址 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); dispatchCallback(delta, newLp); //這裏分發dhcpResults 把IPv4地址發出去 } 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; } } dispatchCallback 的結果最後會做用到 WaitForProvisioningCallback 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; // 把當前的IPv4保存起來 notify(); } } @Override public void onProvisioningFailure(LinkProperties newLp) { synchronized (this) { mCallbackLinkProperties = null; notify(); } }
} acceptDhcpResults 分析到此爲止 開始分析新的狀態
//進入新的狀態 ConfiguringInterfaceState enter方法
ConfiguringInterfaceState
class ConfiguringInterfaceState extends LoggingState { @Override public void enter() { super.enter();
// 發送消息 CMD_CONFIGURE_LINKADDRESS 被StartedState 處理
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; } } } ======================== class StartedState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { final LinkAddress ipAddress = (LinkAddress) msg.obj; if (setIPv4Address(ipAddress)) { // 設置 IPv4地址 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 至此得到了IP地址
private boolean setIPv4Address(LinkAddress address) {
final InterfaceConfiguration ifcg = new InterfaceConfiguration();
ifcg.setLinkAddress(address);
try {
final INetworkManagementService 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;
}
NetworkManagementService.java @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(); } }
http://m.blog.csdn.net/xiaoxiangyuhai/article/details/75219357