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

Commit 4b3eb31c authored by Yan Yan's avatar Yan Yan
Browse files

Support selecting non-INTERNET networks as underlying networks

This commit allows VCN to select underlying Cell networks
according to caller configured network capabilities instead
of always only selecting INTERNET networks as underlying
networks.

This commit also makes sure that if all of the network candidates
fail to match caller configured network templates, VCN can still
select an INTERNET network as a fallback but will never select
a non-INTERNET network.

Bug: 245618320
Test: FrameworksVcnTests(new tests), CtsVcnTestCases
Change-Id: I6eca0331e4bc6804a500fbed9909bb91b1e80d2d
parent 4153670e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -353,6 +353,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork
        return mCapabilitiesMatchCriteria.get(NET_CAPABILITY_RCS);
    }

    /** @hide */
    public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
        return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
    }

    @Override
    public int hashCode() {
        return Objects.hash(
+39 −5
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.server.vcn.routeselection;

import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,6 +46,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription
import com.android.server.vcn.VcnContext;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@@ -69,9 +71,23 @@ class NetworkPriorityClassifier {
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;

    /** Priority for any other networks (including unvalidated, etc) */
    /**
     * Priority for networks that VCN can fall back to.
     *
     * <p>If none of the network candidates are validated or match any template, VCN will fall back
     * to any INTERNET network.
     */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final int PRIORITY_ANY = Integer.MAX_VALUE;
    static final int PRIORITY_FALLBACK = Integer.MAX_VALUE;

    /**
     * Priority for networks that cannot be selected as VCN's underlying networks.
     *
     * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any
     * template as the underlying network.
     */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static final int PRIORITY_INVALID = -1;

    /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */
    public static int calculatePriorityClass(
@@ -86,12 +102,12 @@ class NetworkPriorityClassifier {

        if (networkRecord.isBlocked) {
            logWtf("Network blocked for System Server: " + networkRecord.network);
            return PRIORITY_ANY;
            return PRIORITY_INVALID;
        }

        if (snapshot == null) {
            logWtf("Got null snapshot");
            return PRIORITY_ANY;
            return PRIORITY_INVALID;
        }

        int priorityIndex = 0;
@@ -108,7 +124,13 @@ class NetworkPriorityClassifier {
            }
            priorityIndex++;
        }
        return PRIORITY_ANY;

        final NetworkCapabilities caps = networkRecord.networkCapabilities;
        if (caps.hasCapability(NET_CAPABILITY_INTERNET)
                || (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST))) {
            return PRIORITY_FALLBACK;
        }
        return PRIORITY_INVALID;
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -297,6 +319,18 @@ class NetworkPriorityClassifier {
            return false;
        }

        for (Map.Entry<Integer, Integer> entry :
                networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
            final int cap = entry.getKey();
            final int matchCriteria = entry.getValue();

            if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
                return false;
            } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
                return false;
            }
        }

        return true;
    }

+102 −21
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.vcn.routeselection;

import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;

import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -32,6 +35,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
@@ -40,6 +44,7 @@ import android.os.ParcelUuid;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +54,7 @@ import com.android.server.vcn.VcnContext;
import com.android.server.vcn.util.LogUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -126,6 +132,63 @@ public class UnderlyingNetworkController {
        registerOrUpdateNetworkRequests();
    }

    private static class CapabilityMatchCriteria {
        public final int capability;
        public final int matchCriteria;

        CapabilityMatchCriteria(int capability, int matchCriteria) {
            this.capability = capability;
            this.matchCriteria = matchCriteria;
        }

        @Override
        public int hashCode() {
            return Objects.hash(capability, matchCriteria);
        }

        @Override
        public boolean equals(@Nullable Object other) {
            if (!(other instanceof CapabilityMatchCriteria)) {
                return false;
            }

            final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
            return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
        }
    }

    private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
            VcnGatewayConnectionConfig connectionConfig) {
        final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();

        for (VcnUnderlyingNetworkTemplate template :
                connectionConfig.getVcnUnderlyingNetworkPriorities()) {
            if (template instanceof VcnCellUnderlyingNetworkTemplate) {
                final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();

                for (Map.Entry<Integer, Integer> entry :
                        ((VcnCellUnderlyingNetworkTemplate) template)
                                .getCapabilitiesMatchCriteria()
                                .entrySet()) {

                    final int capability = entry.getKey();
                    final int matchCriteria = entry.getValue();
                    if (matchCriteria != MATCH_ANY) {
                        capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
                    }
                }

                dedupedCapsMatchSets.add(capsMatchSet);
            }
        }

        dedupedCapsMatchSets.add(
                Collections.singleton(
                        new CapabilityMatchCriteria(
                                NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
        return dedupedCapsMatchSets;
    }

    private void registerOrUpdateNetworkRequests() {
        NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
        NetworkCallback oldWifiCallback = mWifiBringupCallback;
@@ -158,11 +221,14 @@ public class UnderlyingNetworkController {
                    getWifiNetworkRequest(), mWifiBringupCallback, mHandler);

            for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
                for (Set<CapabilityMatchCriteria> capsMatchCriteria :
                        dedupAndGetCapRequirementsForCell(mConnectionConfig)) {
                    final NetworkBringupCallback cb = new NetworkBringupCallback();
                    mCellBringupCallbacks.add(cb);

                    mConnectivityManager.requestBackgroundNetwork(
                        getCellNetworkRequestForSubId(subId), cb, mHandler);
                            getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler);
                }
            }
        } else {
            mRouteSelectionCallback = null;
@@ -214,6 +280,13 @@ public class UnderlyingNetworkController {
                .build();
    }

    private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() {
        return getBaseNetworkRequestBuilder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
    }

    /**
     * Builds the WiFi bringup request
     *
@@ -224,10 +297,7 @@ public class UnderlyingNetworkController {
     * but will NEVER bring up a Carrier WiFi network itself.
     */
    private NetworkRequest getWifiNetworkRequest() {
        return getBaseNetworkRequestBuilder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
                .build();
        return getBaseWifiNetworkRequestBuilder().build();
    }

    /**
@@ -238,9 +308,7 @@ public class UnderlyingNetworkController {
     * pace to effectively select a short-lived WiFi offload network.
     */
    private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
        return getBaseNetworkRequestBuilder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
        return getBaseWifiNetworkRequestBuilder()
                // Ensure wifi updates signal strengths when crossing this threshold.
                .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
                .build();
@@ -254,9 +322,7 @@ public class UnderlyingNetworkController {
     * pace to effectively select away from a failing WiFi network.
     */
    private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
        return getBaseNetworkRequestBuilder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
        return getBaseWifiNetworkRequestBuilder()
                // Ensure wifi updates signal strengths when crossing this threshold.
                .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
                .build();
@@ -273,11 +339,25 @@ public class UnderlyingNetworkController {
     * <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
     * in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
     */
    private NetworkRequest getCellNetworkRequestForSubId(int subId) {
        return getBaseNetworkRequestBuilder()
    private NetworkRequest getCellNetworkRequestForSubId(
            int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) {
        final NetworkRequest.Builder nrBuilder =
                getBaseNetworkRequestBuilder()
                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
                .build();
                        .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));

        for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
            final int cap = capMatchCriteria.capability;
            final int matchCriteria = capMatchCriteria.matchCriteria;

            if (matchCriteria == MATCH_REQUIRED) {
                nrBuilder.addCapability(cap);
            } else if (matchCriteria == MATCH_FORBIDDEN) {
                nrBuilder.addForbiddenCapability(cap);
            }
        }

        return nrBuilder.build();
    }

    /**
@@ -285,7 +365,6 @@ public class UnderlyingNetworkController {
     */
    private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
        return new NetworkRequest.Builder()
                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -406,9 +485,11 @@ public class UnderlyingNetworkController {
                                    mLastSnapshot,
                                    mCurrentRecord,
                                    mCarrierConfig);
                    if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
                        sorted.add(record);
                    }
                }
            }

            return sorted;
        }
+2 −1
Original line number Diff line number Diff line
@@ -32,7 +32,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
    private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
    private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();

    private static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
    // Public for use in UnderlyingNetworkControllerTest
    public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
        return new VcnCellUnderlyingNetworkTemplate.Builder()
                .setMetered(MATCH_FORBIDDEN)
                .setMinUpstreamBandwidthKbps(
+9 −3
Original line number Diff line number Diff line
@@ -100,14 +100,20 @@ public class VcnGatewayConnectionConfigTest {
                EXPOSED_CAPS);
    }

    // Public for use in VcnGatewayConnectionTest
    public static VcnGatewayConnectionConfig buildTestConfig() {
    // Public for use in UnderlyingNetworkControllerTest
    public static VcnGatewayConnectionConfig buildTestConfig(
            List<VcnUnderlyingNetworkTemplate> nwTemplates) {
        final VcnGatewayConnectionConfig.Builder builder =
                newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
                newBuilder().setVcnUnderlyingNetworkPriorities(nwTemplates);

        return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
    }

    // Public for use in VcnGatewayConnectionTest
    public static VcnGatewayConnectionConfig buildTestConfig() {
        return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
    }

    private static VcnGatewayConnectionConfig.Builder newBuilder() {
        // Append a unique identifier to the name prefix to guarantee that all created
        // VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
Loading