Loading services/core/java/com/android/server/vcn/VcnGatewayConnection.java +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +10 −19 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 */ Loading @@ -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 Loading @@ -118,7 +116,7 @@ class NetworkPriorityClassifier { networkRecord, subscriptionGroup, snapshot, currentlySelected, isSelected, carrierConfig)) { return priorityIndex; } Loading @@ -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); Loading @@ -159,7 +154,7 @@ class NetworkPriorityClassifier { if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps() || (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinEntryUpstreamBandwidthKbps() && !isSelectedUnderlyingNetwork)) { && !isSelected)) { return false; } Loading @@ -167,7 +162,7 @@ class NetworkPriorityClassifier { < networkPriority.getMinExitDownstreamBandwidthKbps() || (caps.getLinkDownstreamBandwidthKbps() < networkPriority.getMinEntryDownstreamBandwidthKbps() && !isSelectedUnderlyingNetwork)) { && !isSelected)) { return false; } Loading @@ -191,7 +186,7 @@ class NetworkPriorityClassifier { return checkMatchesWifiPriorityRule( (VcnWifiUnderlyingNetworkTemplate) networkPriority, networkRecord, currentlySelected, isSelected, carrierConfig); } Loading @@ -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; Loading @@ -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; } Loading @@ -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; } Loading services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +120 −67 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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 Loading Loading @@ -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) Loading @@ -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); } } /** Loading @@ -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(); } Loading @@ -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(); } } Loading @@ -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(); } } Loading Loading @@ -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(); Loading Loading @@ -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); } } } services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java 0 → 100644 +217 −0 File added.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +11 −114 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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 Loading @@ -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); Loading Loading @@ -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
services/core/java/com/android/server/vcn/VcnGatewayConnection.java +5 −3 Original line number Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +10 −19 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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 */ Loading @@ -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 Loading @@ -118,7 +116,7 @@ class NetworkPriorityClassifier { networkRecord, subscriptionGroup, snapshot, currentlySelected, isSelected, carrierConfig)) { return priorityIndex; } Loading @@ -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); Loading @@ -159,7 +154,7 @@ class NetworkPriorityClassifier { if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps() || (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinEntryUpstreamBandwidthKbps() && !isSelectedUnderlyingNetwork)) { && !isSelected)) { return false; } Loading @@ -167,7 +162,7 @@ class NetworkPriorityClassifier { < networkPriority.getMinExitDownstreamBandwidthKbps() || (caps.getLinkDownstreamBandwidthKbps() < networkPriority.getMinEntryDownstreamBandwidthKbps() && !isSelectedUnderlyingNetwork)) { && !isSelected)) { return false; } Loading @@ -191,7 +186,7 @@ class NetworkPriorityClassifier { return checkMatchesWifiPriorityRule( (VcnWifiUnderlyingNetworkTemplate) networkPriority, networkRecord, currentlySelected, isSelected, carrierConfig); } Loading @@ -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; Loading @@ -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; } Loading @@ -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; } Loading
services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +120 −67 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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 Loading Loading @@ -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) Loading @@ -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); } } /** Loading @@ -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(); } Loading @@ -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(); } } Loading @@ -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(); } } Loading Loading @@ -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(); Loading Loading @@ -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); } } }
services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java 0 → 100644 +217 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +11 −114 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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 Loading @@ -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); Loading Loading @@ -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); } } }