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

Commit dbcfe786 authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Automerger Merge Worker
Browse files

Move applying underlying caps from Vpn to ConnectivityService. am: d182c40d am: 17199d78

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1501815

Change-Id: I8f4dc858bf45095102b583f528eac296637132b4
parents 6dd16855 17199d78
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6175,6 +6175,7 @@ package android.net {
    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
    method public final void sendSocketKeepaliveEvent(int, int);
    method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
    method public void unregister();
    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+43 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

@@ -173,6 +174,14 @@ public abstract class NetworkAgent {
     */
    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;

    /**
     * Sent by the NetworkAgent to ConnectivityService to pass the current
     * list of underlying networks.
     * obj = array of Network objects
     * @hide
     */
    public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;

    /**
     * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
     * networks status - whether we could use the network or could not, due to
@@ -217,7 +226,13 @@ public abstract class NetworkAgent {
     * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
     * @hide
     */
    public static String REDIRECT_URL_KEY = "redirect URL";
    public static final String REDIRECT_URL_KEY = "redirect URL";

    /**
     * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}.
     * @hide
     */
    public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks";

     /**
     * Sent by the NetworkAgent to ConnectivityService to indicate this network was
@@ -649,6 +664,33 @@ public abstract class NetworkAgent {
        queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
    }

    /**
     * Must be called by the agent when the network's underlying networks change.
     *
     * <p>{@code networks} is one of the following:
     * <ul>
     * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
     * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
     * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
     * first in the array.</li>
     * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
     * underlying network connection, and thus, app traffic will not be sent or received.</li>
     * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
     * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
     * APIs mentioned above to send traffic over specific channels.</li>
     * </ul>
     *
     * @param underlyingNetworks the new list of underlying networks.
     * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
     */
    public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
        final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
                ? new ArrayList<>(underlyingNetworks) : null;
        final Bundle bundle = new Bundle();
        bundle.putParcelableArrayList(UNDERLYING_NETWORKS_KEY, underlyingArray);
        queueOrSendMessage(EVENT_UNDERLYING_NETWORKS_CHANGED, bundle);
    }

    /**
     * Inform ConnectivityService that this agent has now connected.
     * Call {@link #unregister} to disconnect.
+76 −27
Original line number Diff line number Diff line
@@ -2771,6 +2771,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                        networkCapabilities = new NetworkCapabilities(networkCapabilities);
                        networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
                    }
                    processCapabilitiesFromAgent(nai, networkCapabilities);
                    updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
                    break;
                }
@@ -2809,6 +2810,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    mKeepaliveTracker.handleEventSocketKeepalive(nai, msg);
                    break;
                }
                case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
                    if (!nai.supportsUnderlyingNetworks()) {
                        Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
                        break;
                    }
                    final ArrayList<Network> underlying;
                    try {
                        underlying = ((Bundle) msg.obj).getParcelableArrayList(
                                NetworkAgent.UNDERLYING_NETWORKS_KEY);
                    } catch (NullPointerException | ClassCastException e) {
                        break;
                    }
                    final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
                    nai.declaredUnderlyingNetworks = (underlying != null)
                            ? underlying.toArray(new Network[0]) : null;

                    if (!Arrays.equals(oldUnderlying, nai.declaredUnderlyingNetworks)) {
                        if (DBG) {
                            log(nai.toShortString() + " changed underlying networks to "
                                    + Arrays.toString(nai.declaredUnderlyingNetworks));
                        }
                        updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
                        notifyIfacesChangedForNetworkStats();
                    }
                }
            }
        }

@@ -3394,7 +3420,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
        mLegacyTypeTracker.remove(nai, wasDefault);
        if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
            updateAllVpnsCapabilities();
            propagateUnderlyingNetworkCapabilities();
        }
        rematchAllNetworksAndRequests();
        mLingerMonitor.noteDisconnect(nai);
@@ -4774,22 +4800,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
    }

    /**
     * Ask all VPN objects to recompute and update their capabilities.
     * Ask all networks with underlying networks to recompute and update their capabilities.
     *
     * When underlying networks change, VPNs may have to update capabilities to reflect things
     * like the metered bit, their transports, and so on. This asks the VPN objects to update
     * their capabilities, and as this will cause them to send messages to the ConnectivityService
     * handler thread through their agent, this is asynchronous. When the capabilities objects
     * are computed they will be up-to-date as they are computed synchronously from here and
     * this is running on the ConnectivityService thread.
     * When underlying networks change, such networks may have to update capabilities to reflect
     * things like the metered bit, their transports, and so on. The capabilities are calculated
     * immediately. This method runs on the ConnectivityService thread.
     */
    private void updateAllVpnsCapabilities() {
        Network defaultNetwork = getNetwork(getDefaultNetwork());
        synchronized (mVpns) {
            for (int i = 0; i < mVpns.size(); i++) {
                final Vpn vpn = mVpns.valueAt(i);
                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
                updateVpnCapabilities(vpn, nc);
    private void propagateUnderlyingNetworkCapabilities() {
        ensureRunningOnConnectivityServiceThread();
        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
            if (nai.supportsUnderlyingNetworks()) {
                updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
            }
        }
    }
@@ -5979,6 +6000,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());

        // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
        processCapabilitiesFromAgent(nai, nc);
        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
        processLinkPropertiesFromAgent(nai, nai.linkProperties);

@@ -6019,6 +6041,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
        updateUids(nai, null, nai.networkCapabilities);
    }

    /**
     * Called when receiving LinkProperties directly from a NetworkAgent.
     * Stores into |nai| any data coming from the agent that might also be written to the network's
     * LinkProperties by ConnectivityService itself. This ensures that the data provided by the
     * agent is not lost when updateLinkProperties is called.
     */
    private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
        lp.ensureDirectlyConnectedRoutes();
        nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
@@ -6314,6 +6342,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    /**
     * Called when receiving NetworkCapabilities directly from a NetworkAgent.
     * Stores into |nai| any data coming from the agent that might also be written to the network's
     * NetworkCapabilities by ConnectivityService itself. This ensures that the data provided by the
     * agent is not lost when updateCapabilities is called.
     */
    private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
        nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
    }

    /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */
    private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
        Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
        Network defaultNetwork = getNetwork(getDefaultNetwork());
        if (underlyingNetworks == null && defaultNetwork != null) {
            // null underlying networks means to track the default.
            underlyingNetworks = new Network[] { defaultNetwork };
        }

        // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
        final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
        Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered);
    }

    /**
     * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
     * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
@@ -6367,6 +6419,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
            newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
        }

        if (nai.supportsUnderlyingNetworks()) {
            mixInUnderlyingCapabilities(nai, newNc);
        }

        return newNc;
    }

@@ -6446,7 +6502,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        if (!newNc.hasTransport(TRANSPORT_VPN)) {
            // Tell VPNs about updated capabilities, since they may need to
            // bubble those changes through.
            updateAllVpnsCapabilities();
            propagateUnderlyingNetworkCapabilities();
        }

        if (!newNc.equalsTransportTypes(prevNc)) {
@@ -6766,7 +6822,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                ? newNetwork.linkProperties.getTcpBufferSizes() : null);
        notifyIfacesChangedForNetworkStats();
        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
        updateAllVpnsCapabilities();
        propagateUnderlyingNetworkCapabilities();
    }

    private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7228,7 +7284,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
                // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
                // the VPN would switch from its default, blank capabilities to those
                // that reflect the capabilities of its underlying networks.
                updateAllVpnsCapabilities();
                propagateUnderlyingNetworkCapabilities();
            }
            networkAgent.created = true;
        }
@@ -7270,8 +7326,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
            // doing.
            updateSignalStrengthThresholds(networkAgent, "CONNECT", null);

            if (networkAgent.isVPN()) {
                updateAllVpnsCapabilities();
            if (networkAgent.supportsUnderlyingNetworks()) {
                propagateUnderlyingNetworkCapabilities();
            }

            // Consider network even though it is not yet validated.
@@ -7528,13 +7584,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            throwIfLockdownEnabled();
            success = mVpns.get(user).setUnderlyingNetworks(networks);
        }
        if (success) {
            mHandler.post(() -> {
                // Update VPN's capabilities based on updated underlying network set.
                updateAllVpnsCapabilities();
                notifyIfacesChangedForNetworkStats();
            });
        }
        return success;
    }

+16 −0
Original line number Diff line number Diff line
@@ -132,6 +132,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
    // TODO: make this private with a getter.
    public NetworkCapabilities networkCapabilities;
    public final NetworkAgentConfig networkAgentConfig;

    // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
    // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
    // not guaranteed to be current or correct, or even to exist.
    public @Nullable Network[] declaredUnderlyingNetworks;

    // Whether this network is always metered even if its underlying networks are unmetered.
    // Only relevant if #supportsUnderlyingNetworks is true.
    public boolean declaredMetered;

    // Indicates if netd has been told to create this Network. From this point on the appropriate
    // routing rules are setup and routes are added so packets can begin flowing over the Network.
    // This is a sticky bit; once set it is never cleared.
@@ -474,10 +484,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
                        networkCapabilities);
    }

    /** Whether this network is a VPN. */
    public boolean isVPN() {
        return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
    }

    /** Whether this network might have underlying networks. Currently only true for VPNs. */
    public boolean supportsUnderlyingNetworks() {
        return isVPN();
    }

    private int getCurrentScore(boolean pretendValidated) {
        // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
        // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
+13 −1
Original line number Diff line number Diff line
@@ -112,7 +112,6 @@ import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.server.ConnectivityService;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -1262,6 +1261,15 @@ public class Vpn {
        mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
        mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                mConfig.allowedApplications, mConfig.disallowedApplications));

        // Only apps targeting Q and above can explicitly declare themselves as metered.
        // These VPNs are assumed metered unless they state otherwise.
        if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) {
            mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_METERED);
        } else {
            mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
        }

        final long token = Binder.clearCallingIdentity();
        try {
            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
@@ -1276,6 +1284,8 @@ public class Vpn {
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                ? Arrays.asList(mConfig.underlyingNetworks) : null);
        mNetworkInfo.setIsAvailable(true);
        updateState(DetailedState.CONNECTED, "agentConnect");
    }
@@ -1857,6 +1867,8 @@ public class Vpn {
                }
            }
        }
        mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                ? Arrays.asList(mConfig.underlyingNetworks) : null);
        return true;
    }

Loading