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

Commit dccf7eda authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Gerrit Code Review
Browse files

Merge changes from topic "networkagent-set-underlying-networks"

* changes:
  Stop accessing VPNs in getAllVpnInfo.
  Clear calling identity in registerNetworkAgent.
  Allow tests to create TRANSPORT_TEST|TRANSPORT_VPN networks.
  Move applying underlying caps from Vpn to ConnectivityService.
parents 136fcd2a d00d440f
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.
+4 −1
Original line number Diff line number Diff line
@@ -712,6 +712,7 @@ public final class NetworkCapabilities implements Parcelable {
        if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
            setAdministratorUids(new int[] {creatorUid});
        }
        // There is no need to clear the UIDs, they have already been cleared by clearAll() above.
    }

    /**
@@ -805,7 +806,9 @@ public final class NetworkCapabilities implements Parcelable {
     */
    private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
            // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
            | 1 << TRANSPORT_ETHERNET;
            | 1 << TRANSPORT_ETHERNET
            // Test VPN networks can be created but their UID ranges must be empty.
            | 1 << TRANSPORT_VPN;

    /**
     * Adds the given transport type to this {@code NetworkCapability} instance.
+128 −62
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);
@@ -4704,10 +4730,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
            if (mLockdownEnabled) {
                return new VpnInfo[0];
            }

            List<VpnInfo> infoList = new ArrayList<>();
            for (int i = 0; i < mVpns.size(); i++) {
                VpnInfo info = createVpnInfo(mVpns.valueAt(i));
            for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                VpnInfo info = createVpnInfo(nai);
                if (info != null) {
                    infoList.add(info);
                }
@@ -4720,13 +4745,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
     * @return VPN information for accounting, or null if we can't retrieve all required
     *         information, e.g underlying ifaces.
     */
    @Nullable
    private VpnInfo createVpnInfo(Vpn vpn) {
        VpnInfo info = vpn.getVpnInfo();
        if (info == null) {
            return null;
        }
        Network[] underlyingNetworks = vpn.getUnderlyingNetworks();
    private VpnInfo createVpnInfo(NetworkAgentInfo nai) {
        if (!nai.isVPN()) return null;

        Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
        // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
        // the underlyingNetworks list.
        if (underlyingNetworks == null) {
@@ -4735,23 +4757,33 @@ public class ConnectivityService extends IConnectivityManager.Stub
                underlyingNetworks = new Network[] { defaultNai.network };
            }
        }
        if (underlyingNetworks != null && underlyingNetworks.length > 0) {

        if (ArrayUtils.isEmpty(underlyingNetworks)) return null;

        List<String> interfaces = new ArrayList<>();
        for (Network network : underlyingNetworks) {
                LinkProperties lp = getLinkProperties(network);
                if (lp != null) {
            NetworkAgentInfo underlyingNai = getNetworkAgentInfoForNetwork(network);
            if (underlyingNai == null) continue;
            LinkProperties lp = underlyingNai.linkProperties;
            for (String iface : lp.getAllInterfaceNames()) {
                if (!TextUtils.isEmpty(iface)) {
                    interfaces.add(iface);
                }
            }
        }
            }
            if (!interfaces.isEmpty()) {
                info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
            }
        }
        return info.underlyingIfaces == null ? null : info;

        if (interfaces.isEmpty()) return null;

        VpnInfo info = new VpnInfo();
        info.ownerUid = nai.networkCapabilities.getOwnerUid();
        info.vpnIface = nai.linkProperties.getInterfaceName();
        // Must be non-null or NetworkStatsService will crash.
        // Cannot happen in production code because Vpn only registers the NetworkAgent after the
        // tun or ipsec interface is created.
        if (info.vpnIface == null) return null;
        info.underlyingIfaces = interfaces.toArray(new String[0]);

        return info;
    }

    /**
@@ -4774,22 +4806,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);
            }
        }
    }
@@ -5959,13 +5986,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
        if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
            enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
        } else {
            enforceNetworkFactoryPermission();
        }

        final int uid = Binder.getCallingUid();
        final long token = Binder.clearCallingIdentity();
        try {
            return registerNetworkAgentInternal(messenger, networkInfo, linkProperties,
                    networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private Network registerNetworkAgentInternal(Messenger messenger, NetworkInfo networkInfo,
            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
        if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
            // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
            // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
            // sees capabilities that may be malicious, which might prevent mistakes in the future.
            networkCapabilities = new NetworkCapabilities(networkCapabilities);
            networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid());
        } else {
            enforceNetworkFactoryPermission();
            networkCapabilities.restrictCapabilitesForTestNetwork(uid);
        }

        LinkProperties lp = new LinkProperties(linkProperties);
@@ -5976,9 +6019,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
        final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
                this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
                this, mNetd, mDnsResolver, mNMS, providerId, uid);

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

@@ -5986,13 +6030,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
        final String name = TextUtils.isEmpty(extraInfo)
                ? nai.networkCapabilities.getSsid() : extraInfo;
        if (DBG) log("registerNetworkAgent " + nai);
        final long token = Binder.clearCallingIdentity();
        try {
        mDeps.getNetworkStack().makeNetworkMonitor(
                nai.network, name, new NetworkMonitorCallbacks(nai));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        // NetworkAgentInfo registration will finish when the NetworkMonitor is created.
        // If the network disconnects or sends any other event before that, messages are deferred by
        // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
@@ -6019,6 +6058,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 +6359,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 +6436,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
            newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
        }

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

        return newNc;
    }

@@ -6446,7 +6519,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 +6839,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 +7301,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 +7343,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 +7601,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
Loading