Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3027f472 authored by Hugo Benichi's avatar Hugo Benichi Committed by Android (Google) Code Review
Browse files

Merge changes I34c4a0c3,Iebc7d153,Ibb028886 into oc-mr1-dev

* changes:
  Nat464Xlat: interface notification handler on ConnectivityService
  Nat464Xlat: internal state guards cleanup + state enum
  Nat464Xlat: clat management cleanup
parents ced95e7f 72479c03
Loading
Loading
Loading
Loading
+19 −24
Original line number Diff line number Diff line
@@ -2011,16 +2011,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    break;
                }
                case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
                    if (VDBG) {
                        log("Update of LinkProperties for " + nai.name() +
                                "; created=" + nai.created +
                                "; everConnected=" + nai.everConnected);
                    }
                    LinkProperties oldLp = nai.linkProperties;
                    synchronized (nai) {
                        nai.linkProperties = (LinkProperties)msg.obj;
                    }
                    if (nai.everConnected) updateLinkProperties(nai, oldLp);
                    handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
                    break;
                }
                case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2269,7 +2260,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
            }
            nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
            mNetworkAgentInfos.remove(msg.replyTo);
            updateClat(null, nai.linkProperties, nai);
            nai.maybeStopClat();
            synchronized (mNetworkForNetId) {
                // Remove the NetworkAgent, but don't mark the netId as
                // available until we've told netd to delete it below.
@@ -4382,7 +4373,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
        updateRoutes(newLp, oldLp, netId);
        updateDnses(newLp, oldLp, netId);

        updateClat(newLp, oldLp, networkAgent);
        // Start or stop clat accordingly to network state.
        networkAgent.updateClat(mNetd);
        if (isDefaultNetwork(networkAgent)) {
            handleApplyDefaultProxy(newLp.getHttpProxy());
        } else {
@@ -4397,18 +4389,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
    }

    private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
        final boolean wasRunningClat = nai.clatd != null && nai.clatd.isStarted();
        final boolean shouldRunClat = Nat464Xlat.requiresClat(nai);

        if (!wasRunningClat && shouldRunClat) {
            nai.clatd = new Nat464Xlat(mContext, mNetd, mTrackerHandler, nai);
            nai.clatd.start();
        } else if (wasRunningClat && !shouldRunClat) {
            nai.clatd.stop();
        }
    }

    private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
        // Marks are only available on WiFi interaces. Checking for
        // marks on unsupported interfaces is harmless.
@@ -4643,6 +4623,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
        if (VDBG) {
            log("Update of LinkProperties for " + nai.name() +
                    "; created=" + nai.created +
                    "; everConnected=" + nai.everConnected);
        }
        LinkProperties oldLp = nai.linkProperties;
        synchronized (nai) {
            nai.linkProperties = newLp;
        }
        if (nai.everConnected) {
            updateLinkProperties(nai, oldLp);
        }
    }

    private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
        for (int i = 0; i < nai.numNetworkRequests(); i++) {
            NetworkRequest nr = nai.requestAt(i);
+140 −116
Original line number Diff line number Diff line
@@ -16,36 +16,36 @@

package com.android.server.connectivity;

import java.net.Inet4Address;

import android.content.Context;
import android.net.InterfaceConfiguration;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.RouteInfo;
import android.os.Handler;
import android.os.Message;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.net.BaseNetworkObserver;
import com.android.internal.util.ArrayUtils;
import com.android.server.net.BaseNetworkObserver;

import java.net.Inet4Address;
import java.util.Objects;

/**
 * @hide
 * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
 * from a consistent and unique thread context. It is the responsability of ConnectivityService to
 * call into this class from its own Handler thread.
 *
 * Class to manage a 464xlat CLAT daemon.
 * @hide
 */
public class Nat464Xlat extends BaseNetworkObserver {
    private static final String TAG = "Nat464Xlat";
    private static final String TAG = Nat464Xlat.class.getSimpleName();

    // This must match the interface prefix in clatd.c.
    private static final String CLAT_PREFIX = "v4-";

    // The network types we will start clatd on.
    // The network types we will start clatd on,
    // allowing clat only on networks for which we can support IPv6-only.
    private static final int[] NETWORK_TYPES = {
            ConnectivityManager.TYPE_MOBILE,
            ConnectivityManager.TYPE_WIFI,
@@ -54,33 +54,21 @@ public class Nat464Xlat extends BaseNetworkObserver {

    private final INetworkManagementService mNMService;

    // ConnectivityService Handler for LinkProperties updates.
    private final Handler mHandler;

    // The network we're running on, and its type.
    private final NetworkAgentInfo mNetwork;

    // Internal state variables.
    //
    // The possible states are:
    //  - Idle: start() not called. Everything is null.
    //  - Starting: start() called. Interfaces are non-null. isStarted() returns true.
    //    mIsRunning is false.
    //  - Running: start() called, and interfaceLinkStateChanged() told us that mIface is up.
    //    mIsRunning is true.
    //
    // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
    // its handler thread must not modify any internal state variables; they are only updated by the
    // interface observers, called on the notification threads.
    private enum State {
        IDLE,       // start() not called. Base iface and stacked iface names are null.
        STARTING,   // start() called. Base iface and stacked iface names are known.
        RUNNING;    // start() called, and the stacked iface is known to be up.
    }

    private String mBaseIface;
    private String mIface;
    private boolean mIsRunning;
    private State mState = State.IDLE;

    public Nat464Xlat(
            Context context, INetworkManagementService nmService,
            Handler handler, NetworkAgentInfo nai) {
    public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
        mNMService = nmService;
        mHandler = handler;
        mNetwork = nai;
    }

@@ -90,34 +78,58 @@ public class Nat464Xlat extends BaseNetworkObserver {
     * @return true if the network requires clat, false otherwise.
     */
    public static boolean requiresClat(NetworkAgentInfo nai) {
        // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
        final int netType = nai.networkInfo.getType();
        final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
        final boolean connected = nai.networkInfo.isConnected();
        // We only run clat on networks that don't have a native IPv4 address.
        final boolean hasIPv4Address =
                (nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false;
        // Only support clat on mobile and wifi for now, because these are the only IPv6-only
        // networks we can connect to.
        return connected && !hasIPv4Address && ArrayUtils.contains(NETWORK_TYPES, netType);
                (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
        return supported && connected && !hasIPv4Address;
    }

    /**
     * Determines whether clatd is started. Always true, except a) if start has not yet been called,
     * or b) if our interface was removed.
     * @return true if clatd has been started and has not yet stopped.
     * A true result corresponds to internal states STARTING and RUNNING.
     */
    public boolean isStarted() {
        return mIface != null;
        return mState != State.IDLE;
    }

    /**
     * @return true if clatd has been started but the stacked interface is not yet up.
     */
    public boolean isStarting() {
        return mState == State.STARTING;
    }

    /**
     * Clears internal state. Must not be called by ConnectivityService.
     * @return true if clatd has been started and the stacked interface is up.
     */
    private void clear() {
    public boolean isRunning() {
        return mState == State.RUNNING;
    }

    /**
     * Sets internal state.
     */
    private void enterStartingState(String baseIface) {
        mIface = CLAT_PREFIX + baseIface;
        mBaseIface = baseIface;
        mState = State.STARTING;
    }

    /**
     * Clears internal state.
     */
    private void enterIdleState() {
        mIface = null;
        mBaseIface = null;
        mIsRunning = false;
        mState = State.IDLE;
    }

    /**
     * Starts the clat daemon. Called by ConnectivityService on the handler thread.
     * Starts the clat daemon.
     */
    public void start() {
        if (isStarted()) {
@@ -137,45 +149,39 @@ public class Nat464Xlat extends BaseNetworkObserver {
            return;
        }

        mBaseIface = mNetwork.linkProperties.getInterfaceName();
        if (mBaseIface == null) {
        String baseIface = mNetwork.linkProperties.getInterfaceName();
        if (baseIface == null) {
            Slog.e(TAG, "startClat: Can't start clat on null interface");
            return;
        }
        mIface = CLAT_PREFIX + mBaseIface;
        // From now on, isStarted() will return true.
        // TODO: should we only do this if mNMService.startClatd() succeeds?
        enterStartingState(baseIface);

        Slog.i(TAG, "Starting clatd on " + mBaseIface);
        try {
            mNMService.startClatd(mBaseIface);
        } catch(RemoteException|IllegalStateException e) {
            Slog.e(TAG, "Error starting clatd: " + e);
            Slog.e(TAG, "Error starting clatd on " + mBaseIface, e);
        }
    }

    /**
     * Stops the clat daemon. Called by ConnectivityService on the handler thread.
     * Stops the clat daemon.
     */
    public void stop() {
        if (isStarted()) {
            Slog.i(TAG, "Stopping clatd");
        if (!isStarted()) {
            Slog.e(TAG, "stopClat: already stopped or not started");
            return;
        }

        Slog.i(TAG, "Stopping clatd on " + mBaseIface);
        try {
            mNMService.stopClatd(mBaseIface);
        } catch(RemoteException|IllegalStateException e) {
                Slog.e(TAG, "Error stopping clatd: " + e);
            }
            // When clatd stops and its interface is deleted, interfaceRemoved() will notify
            // ConnectivityService and call clear().
        } else {
            Slog.e(TAG, "clatd: already stopped");
            Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
        }
    }

    private void updateConnectivityService(LinkProperties lp) {
        Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
        msg.replyTo = mNetwork.messenger;
        Slog.i(TAG, "sending message to ConnectivityService: " + msg);
        msg.sendToTarget();
        // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
        // ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
    }

    /**
@@ -184,16 +190,19 @@ public class Nat464Xlat extends BaseNetworkObserver {
     * has no idea that 464xlat is running on top of it.
     */
    public void fixupLinkProperties(LinkProperties oldLp) {
        if (mNetwork.clatd != null &&
                mIsRunning &&
                mNetwork.linkProperties != null &&
                !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) {
        if (!isRunning()) {
            return;
        }
        LinkProperties lp = mNetwork.linkProperties;
        if (lp == null || lp.getAllInterfaceNames().contains(mIface)) {
            return;
        }

        Slog.d(TAG, "clatd running, updating NAI for " + mIface);
        for (LinkProperties stacked: oldLp.getStackedLinks()) {
                if (mIface.equals(stacked.getInterfaceName())) {
                    mNetwork.linkProperties.addStackedLink(stacked);
                    break;
                }
            if (Objects.equals(mIface, stacked.getInterfaceName())) {
                lp.addStackedLink(stacked);
                return;
            }
        }
    }
@@ -227,6 +236,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
    }

    private void maybeSetIpv6NdOffload(String iface, boolean on) {
        // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
        if (mNetwork.networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
            return;
        }
@@ -238,52 +248,66 @@ public class Nat464Xlat extends BaseNetworkObserver {
        }
    }

    @Override
    public void interfaceLinkStateChanged(String iface, boolean up) {
        // Called by the InterfaceObserver on its own thread, so can race with stop().
        if (isStarted() && up && mIface.equals(iface)) {
            Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");

            if (!mIsRunning) {
    /**
     * Adds stacked link on base link and transitions to RUNNING state.
     */
    private void handleInterfaceLinkStateChanged(String iface, boolean up) {
        if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
            return;
        }
        LinkAddress clatAddress = getLinkAddress(iface);
        if (clatAddress == null) {
            Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
            return;
        }
                mIsRunning = true;
        mState = State.RUNNING;
        Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
                mIface, mIface, mBaseIface));

        maybeSetIpv6NdOffload(mBaseIface, false);
        LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
        lp.addStackedLink(makeLinkProperties(clatAddress));
                Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface);
                updateConnectivityService(lp);
            }
        }
        mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
    }

    @Override
    public void interfaceRemoved(String iface) {
        if (isStarted() && mIface.equals(iface)) {
            Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false");
    /**
     * Removes stacked link on base link and transitions to IDLE state.
     */
    private void handleInterfaceRemoved(String iface) {
        if (!isRunning() || !Objects.equals(mIface, iface)) {
            return;
        }

            if (mIsRunning) {
        Slog.i(TAG, "interface " + iface + " removed");
        // The interface going away likely means clatd has crashed. Ask netd to stop it,
        // because otherwise when we try to start it again on the same base interface netd
        // will complain that it's already started.
                //
                // Note that this method can be called by the interface observer at the same time
                // that ConnectivityService calls stop(). In this case, the second call to
                // stopClatd() will just throw IllegalStateException, which we'll ignore.
        try {
            mNMService.unregisterObserver(this);
            // TODO: add STOPPING state to avoid calling stopClatd twice.
            mNMService.stopClatd(mBaseIface);
        } catch(RemoteException|IllegalStateException e) {
                    // Well, we tried.
            Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
        }
        maybeSetIpv6NdOffload(mBaseIface, true);
        LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
        lp.removeStackedLink(mIface);
                clear();
                updateConnectivityService(lp);
        enterIdleState();
        mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
    }

    @Override
    public void interfaceLinkStateChanged(String iface, boolean up) {
        mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
    }

    @Override
    public void interfaceRemoved(String iface) {
        mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
    }

    @Override
    public String toString() {
        return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
    }
}
+38 −10
Original line number Diff line number Diff line
@@ -27,7 +27,9 @@ import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -247,9 +249,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {

    private static final String TAG = ConnectivityService.class.getSimpleName();
    private static final boolean VDBG = false;
    private final ConnectivityService mConnService;
    public final ConnectivityService connService;
    private final Context mContext;
    private final Handler mHandler;
    final Handler handler;

    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
            LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
@@ -261,10 +263,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        linkProperties = lp;
        networkCapabilities = nc;
        currentScore = score;
        mConnService = connService;
        this.connService = connService;
        mContext = context;
        mHandler = handler;
        networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
        this.handler = handler;
        networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
        networkMisc = misc;
    }

@@ -430,7 +432,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
    private boolean ignoreWifiUnvalidationPenalty() {
        boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
                networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
        boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
        return isWifi && !avoidBadWifi && everValidated;
    }

@@ -514,8 +516,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        }

        if (newExpiry > 0) {
            mLingerMessage = mConnService.makeWakeupMessage(
                    mContext, mHandler,
            mLingerMessage = connService.makeWakeupMessage(
                    mContext, handler,
                    "NETWORK_LINGER_COMPLETE." + network.netId,
                    EVENT_NETWORK_LINGER_COMPLETE, this);
            mLingerMessage.schedule(newExpiry);
@@ -551,6 +553,32 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
    }

    public void updateClat(INetworkManagementService netd) {
        if (Nat464Xlat.requiresClat(this)) {
            maybeStartClat(netd);
        } else {
            maybeStopClat();
        }
    }

    /** Ensure clat has started for this network. */
    public void maybeStartClat(INetworkManagementService netd) {
        if (clatd != null && clatd.isStarted()) {
            return;
        }
        clatd = new Nat464Xlat(netd, this);
        clatd.start();
    }

    /** Ensure clat has stopped for this network. */
    public void maybeStopClat() {
        if (clatd == null) {
            return;
        }
        clatd.stop();
        clatd = null;
    }

    public String toString() {
        return "NetworkAgentInfo{ ni{" + networkInfo + "}  " +
                "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  " +
@@ -562,13 +590,13 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
                "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
                "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
                "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " +
                "clat{" + clatd + "} " +
                "}";
    }

    public String name() {
        return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
                networkInfo.getSubtypeName() + ") - " +
                (network == null ? "null" : network.toString()) + "]";
                networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
    }

    // Enables sorting in descending order of score.