安卓通过DHCP协议获取IP地址的过程

安卓通过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传递到上层的framework

/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

// 当WifiStateMachine状态机进入状态ObtainingIpState时 获取IP地址
class ObtainingIpState extends State {
@Override
public void enter() {
mIpManager.startProvisioning(IpManager.ProvisioningConfiguration prov); //启动获取IP地址
}
}
ObtainingIpState所在状态机

安卓通过DHCP协议获取IP地址的过程_第1张图片

/frameworks/base/services/net/java/android/net/ip/IpManager.java

class StartedState extends State {} IpManager中的三个状态机
class StoppedState extends State {}
class StoppingState extends State {}

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;
…..
}

==========================================

class StartedState extends State {
void enter(){
if(startIPv4()) // 状态 StartedState 的enter 进入方法,调用startIPv4()函数
}
}

==========================================

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消息

/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

你可能感兴趣的:(Wifi)