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

Commit 95c5c5b3 authored by Yan Yan's avatar Yan Yan Committed by Gerrit Code Review
Browse files

Merge "Refactor VCN route selection with UnderlyingNetworkEvaluator" into main

parents c3cf4375 90864bc8
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -915,9 +915,11 @@ public class VcnGatewayConnection extends StateMachine {
            // TODO(b/180132994): explore safely removing this Thread check
            mVcnContext.ensureRunningOnLooperThread();

            if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) {
                logInfo(
                        "Selected underlying network changed: "
                                + (underlying == null ? null : underlying.network));
            }

            // TODO(b/179091925): Move the delayed-message handling to BaseState

+10 −19
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import com.android.server.vcn.VcnContext;

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

/** @hide */
@@ -86,7 +85,6 @@ class NetworkPriorityClassifier {
     * <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 */
@@ -96,7 +94,7 @@ class NetworkPriorityClassifier {
            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
            ParcelUuid subscriptionGroup,
            TelephonySubscriptionSnapshot snapshot,
            UnderlyingNetworkRecord currentlySelected,
            boolean isSelected,
            PersistableBundleWrapper carrierConfig) {
        // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED

@@ -118,7 +116,7 @@ class NetworkPriorityClassifier {
                    networkRecord,
                    subscriptionGroup,
                    snapshot,
                    currentlySelected,
                    isSelected,
                    carrierConfig)) {
                return priorityIndex;
            }
@@ -140,12 +138,9 @@ class NetworkPriorityClassifier {
            UnderlyingNetworkRecord networkRecord,
            ParcelUuid subscriptionGroup,
            TelephonySubscriptionSnapshot snapshot,
            UnderlyingNetworkRecord currentlySelected,
            boolean isSelected,
            PersistableBundleWrapper carrierConfig) {
        final NetworkCapabilities caps = networkRecord.networkCapabilities;
        final boolean isSelectedUnderlyingNetwork =
                currentlySelected != null
                        && Objects.equals(currentlySelected.network, networkRecord.network);

        final int meteredMatch = networkPriority.getMetered();
        final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -159,7 +154,7 @@ class NetworkPriorityClassifier {
        if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
                || (caps.getLinkUpstreamBandwidthKbps()
                                < networkPriority.getMinEntryUpstreamBandwidthKbps()
                        && !isSelectedUnderlyingNetwork)) {
                        && !isSelected)) {
            return false;
        }

@@ -167,7 +162,7 @@ class NetworkPriorityClassifier {
                        < networkPriority.getMinExitDownstreamBandwidthKbps()
                || (caps.getLinkDownstreamBandwidthKbps()
                                < networkPriority.getMinEntryDownstreamBandwidthKbps()
                        && !isSelectedUnderlyingNetwork)) {
                        && !isSelected)) {
            return false;
        }

@@ -191,7 +186,7 @@ class NetworkPriorityClassifier {
            return checkMatchesWifiPriorityRule(
                    (VcnWifiUnderlyingNetworkTemplate) networkPriority,
                    networkRecord,
                    currentlySelected,
                    isSelected,
                    carrierConfig);
        }

@@ -214,7 +209,7 @@ class NetworkPriorityClassifier {
    public static boolean checkMatchesWifiPriorityRule(
            VcnWifiUnderlyingNetworkTemplate networkPriority,
            UnderlyingNetworkRecord networkRecord,
            UnderlyingNetworkRecord currentlySelected,
            boolean isSelected,
            PersistableBundleWrapper carrierConfig) {
        final NetworkCapabilities caps = networkRecord.networkCapabilities;

@@ -223,7 +218,7 @@ class NetworkPriorityClassifier {
        }

        // TODO: Move the Network Quality check to the network metric monitor framework.
        if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
        if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) {
            return false;
        }

@@ -237,15 +232,11 @@ class NetworkPriorityClassifier {

    private static boolean isWifiRssiAcceptable(
            UnderlyingNetworkRecord networkRecord,
            UnderlyingNetworkRecord currentlySelected,
            boolean isSelected,
            PersistableBundleWrapper carrierConfig) {
        final NetworkCapabilities caps = networkRecord.networkCapabilities;
        final boolean isSelectedNetwork =
                currentlySelected != null
                        && networkRecord.network.equals(currentlySelected.network);

        if (isSelectedNetwork
                && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
        if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
            return true;
        }

+120 −67
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
@@ -83,6 +84,9 @@ public class UnderlyingNetworkController {
    @NonNull private final TelephonyCallback mActiveDataSubIdListener =
            new VcnActiveDataSubscriptionIdListener();

    private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords =
            new ArrayMap<>();

    @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
    @Nullable private NetworkCallback mWifiBringupCallback;
    @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
@@ -105,7 +109,8 @@ public class UnderlyingNetworkController {
        this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
    }

    private UnderlyingNetworkController(
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    UnderlyingNetworkController(
            @NonNull VcnContext vcnContext,
            @NonNull VcnGatewayConnectionConfig connectionConfig,
            @NonNull ParcelUuid subscriptionGroup,
@@ -196,6 +201,7 @@ public class UnderlyingNetworkController {
        NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
        List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
        mCellBringupCallbacks.clear();
        mUnderlyingNetworkRecords.clear();

        // Register new callbacks. Make-before-break; always register new callbacks before removal
        // of old callbacks
@@ -395,6 +401,18 @@ public class UnderlyingNetworkController {
        // Update carrier config
        mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);

        // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier
        // config to calculate their cached priority classes. For simplicity, the
        // UnderlyingNetworkController does not listen for changes in VCN-related carrier config
        // keys, and changes are applied at restart of the VcnGatewayConnection
        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
            evaluator.reevaluate(
                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                    mSubscriptionGroup,
                    mLastSnapshot,
                    mCarrierConfig);
        }

        // Only trigger re-registration if subIds in this group have changed
        if (oldSnapshot
                .getAllSubIdsInGroup(mSubscriptionGroup)
@@ -418,32 +436,62 @@ public class UnderlyingNetworkController {
                .unregisterTelephonyCallback(mActiveDataSubIdListener);
    }

    private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
        TreeSet<UnderlyingNetworkEvaluator> sorted =
                new TreeSet<>(UnderlyingNetworkEvaluator.getComparator());

        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
            if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
                sorted.add(evaluator);
            }
        }

        return sorted;
    }

    private void reevaluateNetworks() {
        if (mIsQuitting || mRouteSelectionCallback == null) {
            return; // UnderlyingNetworkController has quit.
        }

        TreeSet<UnderlyingNetworkRecord> sorted =
                mRouteSelectionCallback.getSortedUnderlyingNetworks();
        UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
        TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();

        UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
        UnderlyingNetworkRecord candidate =
                candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord();
        if (Objects.equals(mCurrentRecord, candidate)) {
            return;
        }

        String allNetworkPriorities = "";
        for (UnderlyingNetworkRecord record : sorted) {
        for (UnderlyingNetworkEvaluator recordEvaluator : sorted) {
            if (!allNetworkPriorities.isEmpty()) {
                allNetworkPriorities += ", ";
            }
            allNetworkPriorities += record.network + ": " + record.priorityClass;
            allNetworkPriorities +=
                    recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass();
        }

        if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) {
            logInfo(
                    "Selected network changed to "
                            + (candidate == null ? null : candidate.network)
                            + ", selected from list: "
                            + allNetworkPriorities);
        }

        mCurrentRecord = candidate;
        mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);

        // Need to update all evaluators to ensure the previously selected one is unselected
        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
            evaluator.setIsSelected(
                    candidateEvaluator == evaluator,
                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                    mSubscriptionGroup,
                    mLastSnapshot,
                    mCarrierConfig);
        }
    }

    /**
@@ -463,46 +511,26 @@ public class UnderlyingNetworkController {
     */
    @VisibleForTesting
    class UnderlyingNetworkListener extends NetworkCallback {
        private final Map<Network, UnderlyingNetworkRecord.Builder>
                mUnderlyingNetworkRecordBuilders = new ArrayMap<>();

        UnderlyingNetworkListener() {
            super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
        }

        private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() {
            TreeSet<UnderlyingNetworkRecord> sorted =
                    new TreeSet<>(UnderlyingNetworkRecord.getComparator());

            for (UnderlyingNetworkRecord.Builder builder :
                    mUnderlyingNetworkRecordBuilders.values()) {
                if (builder.isValid()) {
                    final UnderlyingNetworkRecord record =
                            builder.build(
        @Override
        public void onAvailable(@NonNull Network network) {
            mUnderlyingNetworkRecords.put(
                    network,
                    mDeps.newUnderlyingNetworkEvaluator(
                            mVcnContext,
                            network,
                            mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                            mSubscriptionGroup,
                            mLastSnapshot,
                                    mCurrentRecord,
                                    mCarrierConfig);
                    if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) {
                        sorted.add(record);
                    }
                }
            }

            return sorted;
        }

        @Override
        public void onAvailable(@NonNull Network network) {
            mUnderlyingNetworkRecordBuilders.put(
                    network, new UnderlyingNetworkRecord.Builder(network));
                            mCarrierConfig));
        }

        @Override
        public void onLost(@NonNull Network network) {
            mUnderlyingNetworkRecordBuilders.remove(network);
            mUnderlyingNetworkRecords.remove(network);

            reevaluateNetworks();
        }
@@ -510,15 +538,20 @@ public class UnderlyingNetworkController {
        @Override
        public void onCapabilitiesChanged(
                @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
            final UnderlyingNetworkRecord.Builder builder =
                    mUnderlyingNetworkRecordBuilders.get(network);
            if (builder == null) {
            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
            if (evaluator == null) {
                logWtf("Got capabilities change for unknown key: " + network);
                return;
            }

            builder.setNetworkCapabilities(networkCapabilities);
            if (builder.isValid()) {
            evaluator.setNetworkCapabilities(
                    networkCapabilities,
                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                    mSubscriptionGroup,
                    mLastSnapshot,
                    mCarrierConfig);

            if (evaluator.isValid()) {
                reevaluateNetworks();
            }
        }
@@ -526,30 +559,40 @@ public class UnderlyingNetworkController {
        @Override
        public void onLinkPropertiesChanged(
                @NonNull Network network, @NonNull LinkProperties linkProperties) {
            final UnderlyingNetworkRecord.Builder builder =
                    mUnderlyingNetworkRecordBuilders.get(network);
            if (builder == null) {
            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
            if (evaluator == null) {
                logWtf("Got link properties change for unknown key: " + network);
                return;
            }

            builder.setLinkProperties(linkProperties);
            if (builder.isValid()) {
            evaluator.setLinkProperties(
                    linkProperties,
                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                    mSubscriptionGroup,
                    mLastSnapshot,
                    mCarrierConfig);

            if (evaluator.isValid()) {
                reevaluateNetworks();
            }
        }

        @Override
        public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
            final UnderlyingNetworkRecord.Builder builder =
                    mUnderlyingNetworkRecordBuilders.get(network);
            if (builder == null) {
            final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
            if (evaluator == null) {
                logWtf("Got blocked status change for unknown key: " + network);
                return;
            }

            builder.setIsBlocked(isBlocked);
            if (builder.isValid()) {
            evaluator.setIsBlocked(
                    isBlocked,
                    mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                    mSubscriptionGroup,
                    mLastSnapshot,
                    mCarrierConfig);

            if (evaluator.isValid()) {
                reevaluateNetworks();
            }
        }
@@ -614,16 +657,8 @@ public class UnderlyingNetworkController {
        pw.println("Underlying networks:");
        pw.increaseIndent();
        if (mRouteSelectionCallback != null) {
            for (UnderlyingNetworkRecord record :
                    mRouteSelectionCallback.getSortedUnderlyingNetworks()) {
                record.dump(
                        mVcnContext,
                        pw,
                        mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
                        mSubscriptionGroup,
                        mLastSnapshot,
                        mCurrentRecord,
                        mCarrierConfig);
            for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) {
                recordEvaluator.dump(pw);
            }
        }
        pw.decreaseIndent();
@@ -653,5 +688,23 @@ public class UnderlyingNetworkController {
                @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
    }

    private static class Dependencies {}
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public static class Dependencies {
        /** Construct a new UnderlyingNetworkEvaluator */
        public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
                @NonNull VcnContext vcnContext,
                @NonNull Network network,
                @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
                @NonNull ParcelUuid subscriptionGroup,
                @NonNull TelephonySubscriptionSnapshot lastSnapshot,
                @Nullable PersistableBundleWrapper carrierConfig) {
            return new UnderlyingNetworkEvaluator(
                    vcnContext,
                    network,
                    underlyingNetworkTemplates,
                    subscriptionGroup,
                    lastSnapshot,
                    carrierConfig);
        }
    }
}
+217 −0

File added.

Preview size limit exceeded, changes collapsed.

+11 −114
Original line number Diff line number Diff line
@@ -16,24 +16,17 @@

package com.android.server.vcn.routeselection;

import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
@@ -46,54 +39,17 @@ public class UnderlyingNetworkRecord {
    @NonNull public final NetworkCapabilities networkCapabilities;
    @NonNull public final LinkProperties linkProperties;
    public final boolean isBlocked;
    public final boolean isSelected;
    public final int priorityClass;

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public UnderlyingNetworkRecord(
            @NonNull Network network,
            @NonNull NetworkCapabilities networkCapabilities,
            @NonNull LinkProperties linkProperties,
            boolean isBlocked,
            VcnContext vcnContext,
            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
            ParcelUuid subscriptionGroup,
            TelephonySubscriptionSnapshot snapshot,
            UnderlyingNetworkRecord currentlySelected,
            PersistableBundleWrapper carrierConfig) {
            boolean isBlocked) {
        this.network = network;
        this.networkCapabilities = networkCapabilities;
        this.linkProperties = linkProperties;
        this.isBlocked = isBlocked;

        this.isSelected = isSelected(this.network, currentlySelected);

        priorityClass =
                NetworkPriorityClassifier.calculatePriorityClass(
                        vcnContext,
                        this,
                        underlyingNetworkTemplates,
                        subscriptionGroup,
                        snapshot,
                        currentlySelected,
                        carrierConfig);
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public UnderlyingNetworkRecord(
            @NonNull Network network,
            @NonNull NetworkCapabilities networkCapabilities,
            @NonNull LinkProperties linkProperties,
            boolean isBlocked,
            boolean isSelected,
            int priorityClass) {
        this.network = network;
        this.networkCapabilities = networkCapabilities;
        this.linkProperties = linkProperties;
        this.isBlocked = isBlocked;
        this.isSelected = isSelected;

        this.priorityClass = priorityClass;
    }

    @Override
@@ -113,64 +69,20 @@ public class UnderlyingNetworkRecord {
        return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
    }

    /** Returns if two records are equal including their priority classes. */
    public static boolean isEqualIncludingPriorities(
            UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) {
        if (left != null && right != null) {
            return left.equals(right)
                    && left.isSelected == right.isSelected
                    && left.priorityClass == right.priorityClass;
        }

        return left == right;
    }

    static Comparator<UnderlyingNetworkRecord> getComparator() {
        return (left, right) -> {
            final int leftIndex = left.priorityClass;
            final int rightIndex = right.priorityClass;

            // In the case of networks in the same priority class, prioritize based on other
            // criteria (eg. actively selected network, link metrics, etc)
            if (leftIndex == rightIndex) {
                // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord
                // fall into the same priority class.
                if (left.isSelected) {
                    return -1;
                }
                if (right.isSelected) {
                    return 1;
                }
            }
            return Integer.compare(leftIndex, rightIndex);
        };
    }

    private static boolean isSelected(
            Network networkToCheck, UnderlyingNetworkRecord currentlySelected) {
        if (currentlySelected == null) {
            return false;
        }
        if (currentlySelected.network.equals(networkToCheck)) {
            return true;
        }
        return false;
    /** Return whether two records represent the same network */
    public static boolean isSameNetwork(
            @Nullable UnderlyingNetworkRecord leftRecord,
            @Nullable UnderlyingNetworkRecord rightRecord) {
        final Network left = leftRecord == null ? null : leftRecord.network;
        final Network right = rightRecord == null ? null : rightRecord.network;
        return Objects.equals(left, right);
    }

    /** Dumps the state of this record for logging and debugging purposes. */
    void dump(
            VcnContext vcnContext,
            IndentingPrintWriter pw,
            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
            ParcelUuid subscriptionGroup,
            TelephonySubscriptionSnapshot snapshot,
            UnderlyingNetworkRecord currentlySelected,
            PersistableBundleWrapper carrierConfig) {
    void dump(IndentingPrintWriter pw) {
        pw.println("UnderlyingNetworkRecord:");
        pw.increaseIndent();

        pw.println("priorityClass: " + priorityClass);
        pw.println("isSelected: " + isSelected);
        pw.println("mNetwork: " + network);
        pw.println("mNetworkCapabilities: " + networkCapabilities);
        pw.println("mLinkProperties: " + linkProperties);
@@ -218,29 +130,14 @@ public class UnderlyingNetworkRecord {
            return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
        }

        UnderlyingNetworkRecord build(
                VcnContext vcnContext,
                List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
                ParcelUuid subscriptionGroup,
                TelephonySubscriptionSnapshot snapshot,
                UnderlyingNetworkRecord currentlySelected,
                PersistableBundleWrapper carrierConfig) {
        UnderlyingNetworkRecord build() {
            if (!isValid()) {
                throw new IllegalArgumentException(
                        "Called build before UnderlyingNetworkRecord was valid");
            }

            return new UnderlyingNetworkRecord(
                    mNetwork,
                    mNetworkCapabilities,
                    mLinkProperties,
                    mIsBlocked,
                    vcnContext,
                    underlyingNetworkTemplates,
                    subscriptionGroup,
                    snapshot,
                    currentlySelected,
                    carrierConfig);
                    mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
        }
    }
}
Loading