天天看點

Android Ethernet IP擷取機制—IpManager(Android N)

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需要加把勁。