Skip to content
Snippets Groups Projects
Commit 8145b28a authored by Yan Yan's avatar Yan Yan Committed by Automerger Merge Worker
Browse files

Merge "Enable NetworkMetricMonitor and support penalizing networks" into main...

Merge "Enable NetworkMetricMonitor and support penalizing networks" into main am: 658c0765 am: f8de3428 am: 6e771ed5

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



Change-Id: Ia9a06c229c8429d68ffba80e6715e96f43521f4f
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 8b77acda 6e771ed5
No related branches found
No related tags found
No related merge requests found
Showing
with 625 additions and 28 deletions
......@@ -80,8 +80,6 @@ public class VcnManager {
* <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater
* than, or equal to this threshold.
*
* <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
*
* @hide
*/
@NonNull
......@@ -94,8 +92,6 @@ public class VcnManager {
* <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold,
* the VCN will attempt to migrate away from the Carrier WiFi network.
*
* <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
*
* @hide
*/
@NonNull
......@@ -120,6 +116,15 @@ public class VcnManager {
public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY =
"vcn_network_selection_ipsec_packet_loss_percent_threshold";
/**
* Key for the list of timeouts in minute to stop penalizing an underlying network candidate
*
* @hide
*/
@NonNull
public static final String VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY =
"vcn_network_selection_penalty_timeout_minutes_list";
// TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
/**
......@@ -168,6 +173,7 @@ public class VcnManager {
VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY,
VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY,
VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY,
VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY,
VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY,
......
......@@ -1910,6 +1910,12 @@ public class VcnGatewayConnection extends StateMachine {
// Transforms do not need to be persisted; the IkeSession will keep them alive
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
if (direction == IpSecManager.DIRECTION_IN
&& mVcnContext.isFlagNetworkMetricMonitorEnabled()
&& mVcnContext.isFlagIpSecTransformStateEnabled()) {
mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform);
}
// For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as
// needed)
final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities();
......
......@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
......@@ -52,6 +53,7 @@ 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 com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
......@@ -201,6 +203,14 @@ public class UnderlyingNetworkController {
NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
mCellBringupCallbacks.clear();
if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
&& mVcnContext.isFlagIpSecTransformStateEnabled()) {
for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
evaluator.close();
}
}
mUnderlyingNetworkRecords.clear();
// Register new callbacks. Make-before-break; always register new callbacks before removal
......@@ -417,11 +427,42 @@ public class UnderlyingNetworkController {
if (oldSnapshot
.getAllSubIdsInGroup(mSubscriptionGroup)
.equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
&& mVcnContext.isFlagIpSecTransformStateEnabled()) {
reevaluateNetworks();
}
return;
}
registerOrUpdateNetworkRequests();
}
/**
* Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring
*
* <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration
*/
public void updateInboundTransform(
@NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
|| !mVcnContext.isFlagIpSecTransformStateEnabled()) {
logWtf("#updateInboundTransform: unexpected call; flags missing");
return;
}
Objects.requireNonNull(currentNetwork, "currentNetwork is null");
Objects.requireNonNull(transform, "transform is null");
if (mCurrentRecord == null
|| mRouteSelectionCallback == null
|| !Objects.equals(currentNetwork.network, mCurrentRecord.network)) {
// The caller (VcnGatewayConnection) is out-of-dated. Ignore this call.
return;
}
mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform);
}
/** Tears down this Tracker, and releases all underlying network requests. */
public void teardown() {
mVcnContext.ensureRunningOnLooperThread();
......@@ -438,7 +479,7 @@ public class UnderlyingNetworkController {
private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
TreeSet<UnderlyingNetworkEvaluator> sorted =
new TreeSet<>(UnderlyingNetworkEvaluator.getComparator());
new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext));
for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
......@@ -525,11 +566,17 @@ public class UnderlyingNetworkController {
mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
mSubscriptionGroup,
mLastSnapshot,
mCarrierConfig));
mCarrierConfig,
new NetworkEvaluatorCallbackImpl()));
}
@Override
public void onLost(@NonNull Network network) {
if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
&& mVcnContext.isFlagIpSecTransformStateEnabled()) {
mUnderlyingNetworkRecords.get(network).close();
}
mUnderlyingNetworkRecords.remove(network);
reevaluateNetworks();
......@@ -598,6 +645,21 @@ public class UnderlyingNetworkController {
}
}
@VisibleForTesting
class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
@Override
public void onEvaluationResultChanged() {
if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
|| !mVcnContext.isFlagIpSecTransformStateEnabled()) {
logWtf("#onEvaluationResultChanged: unexpected call; flags missing");
return;
}
mVcnContext.ensureRunningOnLooperThread();
reevaluateNetworks();
}
}
private String getLogPrefix() {
return "("
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
......@@ -690,21 +752,22 @@ public class UnderlyingNetworkController {
@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) {
@Nullable PersistableBundleWrapper carrierConfig,
@NonNull NetworkEvaluatorCallback evaluatorCallback) {
return new UnderlyingNetworkEvaluator(
vcnContext,
network,
underlyingNetworkTemplates,
subscriptionGroup,
lastSnapshot,
carrierConfig);
carrierConfig,
evaluatorCallback);
}
}
}
......@@ -16,23 +16,32 @@
package com.android.server.vcn.routeselection;
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
import android.os.ParcelUuid;
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;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for
......@@ -43,20 +52,41 @@ import java.util.Objects;
public class UnderlyingNetworkEvaluator {
private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName();
private static final int[] PENALTY_TIMEOUT_MINUTES_DEFAULT = new int[] {5};
@NonNull private final VcnContext mVcnContext;
@NonNull private final Handler mHandler;
@NonNull private final Object mCancellationToken = new Object();
@NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder;
@NonNull private final NetworkEvaluatorCallback mEvaluatorCallback;
@NonNull private final List<NetworkMetricMonitor> mMetricMonitors = new ArrayList<>();
@NonNull private final Dependencies mDependencies;
// TODO: Support back-off timeouts
private long mPenalizedTimeoutMs;
private boolean mIsSelected;
private boolean mIsPenalized;
private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID;
@VisibleForTesting(visibility = Visibility.PRIVATE)
public UnderlyingNetworkEvaluator(
@NonNull VcnContext vcnContext,
@NonNull Network network,
@NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot lastSnapshot,
@Nullable PersistableBundleWrapper carrierConfig) {
@Nullable PersistableBundleWrapper carrierConfig,
@NonNull NetworkEvaluatorCallback evaluatorCallback,
@NonNull Dependencies dependencies) {
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mHandler = new Handler(mVcnContext.getLooper());
mDependencies = Objects.requireNonNull(dependencies, "Missing dependencies");
mEvaluatorCallback = Objects.requireNonNull(evaluatorCallback, "Missing deps");
Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates");
Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
......@@ -66,9 +96,76 @@ public class UnderlyingNetworkEvaluator {
new UnderlyingNetworkRecord.Builder(
Objects.requireNonNull(network, "Missing network"));
mIsSelected = false;
mIsPenalized = false;
mPenalizedTimeoutMs = getPenaltyTimeoutMs(carrierConfig);
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
if (isIpSecPacketLossDetectorEnabled()) {
try {
mMetricMonitors.add(
mDependencies.newIpSecPacketLossDetector(
mVcnContext,
mNetworkRecordBuilder.getNetwork(),
carrierConfig,
new MetricMonitorCallbackImpl()));
} catch (IllegalAccessException e) {
// No action. Do not add anything to mMetricMonitors
}
}
}
public UnderlyingNetworkEvaluator(
@NonNull VcnContext vcnContext,
@NonNull Network network,
@NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot lastSnapshot,
@Nullable PersistableBundleWrapper carrierConfig,
@NonNull NetworkEvaluatorCallback evaluatorCallback) {
this(
vcnContext,
network,
underlyingNetworkTemplates,
subscriptionGroup,
lastSnapshot,
carrierConfig,
evaluatorCallback,
new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
/** Get an IpSecPacketLossDetector instance */
public IpSecPacketLossDetector newIpSecPacketLossDetector(
@NonNull VcnContext vcnContext,
@NonNull Network network,
@Nullable PersistableBundleWrapper carrierConfig,
@NonNull NetworkMetricMonitor.NetworkMetricMonitorCallback callback)
throws IllegalAccessException {
return new IpSecPacketLossDetector(vcnContext, network, carrierConfig, callback);
}
}
/** Callback to notify caller to reevaluate network selection */
public interface NetworkEvaluatorCallback {
/**
* Called when mIsPenalized changed
*
* <p>When receiving this call, UnderlyingNetworkController should reevaluate all network
* candidates for VCN underlying network selection
*/
void onEvaluationResultChanged();
}
private class MetricMonitorCallbackImpl
implements NetworkMetricMonitor.NetworkMetricMonitorCallback {
public void onValidationResultReceived() {
mVcnContext.ensureRunningOnLooperThread();
handleValidationResult();
}
}
private void updatePriorityClass(
......@@ -91,8 +188,25 @@ public class UnderlyingNetworkEvaluator {
}
}
public static Comparator<UnderlyingNetworkEvaluator> getComparator() {
private boolean isIpSecPacketLossDetectorEnabled() {
return isIpSecPacketLossDetectorEnabled(mVcnContext);
}
private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) {
return vcnContext.isFlagIpSecTransformStateEnabled()
&& vcnContext.isFlagNetworkMetricMonitorEnabled();
}
/** Get the comparator for UnderlyingNetworkEvaluator */
public static Comparator<UnderlyingNetworkEvaluator> getComparator(VcnContext vcnContext) {
return (left, right) -> {
if (isIpSecPacketLossDetectorEnabled(vcnContext)) {
if (left.mIsPenalized != right.mIsPenalized) {
// A penalized network should have lower priority which means a larger index
return left.mIsPenalized ? 1 : -1;
}
}
final int leftIndex = left.mPriorityClass;
final int rightIndex = right.mPriorityClass;
......@@ -112,6 +226,64 @@ public class UnderlyingNetworkEvaluator {
};
}
private static long getPenaltyTimeoutMs(@Nullable PersistableBundleWrapper carrierConfig) {
final int[] timeoutMinuteList;
if (carrierConfig != null) {
timeoutMinuteList =
carrierConfig.getIntArray(
VcnManager.VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY,
PENALTY_TIMEOUT_MINUTES_DEFAULT);
} else {
timeoutMinuteList = PENALTY_TIMEOUT_MINUTES_DEFAULT;
}
// TODO: Add the support of back-off timeouts and return the full list
return TimeUnit.MINUTES.toMillis(timeoutMinuteList[0]);
}
private void handleValidationResult() {
final boolean wasPenalized = mIsPenalized;
mIsPenalized = false;
for (NetworkMetricMonitor monitor : mMetricMonitors) {
mIsPenalized |= monitor.isValidationFailed();
}
if (wasPenalized == mIsPenalized) {
return;
}
logInfo(
"#handleValidationResult: wasPenalized "
+ wasPenalized
+ " mIsPenalized "
+ mIsPenalized);
if (mIsPenalized) {
mHandler.postDelayed(
new ExitPenaltyBoxRunnable(), mCancellationToken, mPenalizedTimeoutMs);
} else {
// Exit the penalty box
mHandler.removeCallbacksAndEqualMessages(mCancellationToken);
}
mEvaluatorCallback.onEvaluationResultChanged();
}
public class ExitPenaltyBoxRunnable implements Runnable {
@Override
public void run() {
if (!mIsPenalized) {
logWtf("Evaluator not being penalized but ExitPenaltyBoxRunnable was scheduled");
return;
}
// TODO: There might be a future metric monitor (e.g. ping) that will require the
// validation to pass before exiting the penalty box.
mIsPenalized = false;
mEvaluatorCallback.onEvaluationResultChanged();
}
}
/** Set the NetworkCapabilities */
public void setNetworkCapabilities(
@NonNull NetworkCapabilities nc,
......@@ -162,6 +334,10 @@ public class UnderlyingNetworkEvaluator {
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
for (NetworkMetricMonitor monitor : mMetricMonitors) {
monitor.setIsSelectedUnderlyingNetwork(isSelected);
}
}
/**
......@@ -174,6 +350,35 @@ public class UnderlyingNetworkEvaluator {
@Nullable PersistableBundleWrapper carrierConfig) {
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
// The already scheduled event will not be affected. The followup events will be scheduled
// with the new timeout
mPenalizedTimeoutMs = getPenaltyTimeoutMs(carrierConfig);
for (NetworkMetricMonitor monitor : mMetricMonitors) {
monitor.setCarrierConfig(carrierConfig);
}
}
/** Update the inbound IpSecTransform applied to the network */
public void setInboundTransform(@NonNull IpSecTransform transform) {
if (!mIsSelected) {
logWtf("setInboundTransform on an unselected evaluator");
return;
}
for (NetworkMetricMonitor monitor : mMetricMonitors) {
monitor.setInboundTransform(transform);
}
}
/** Close the evaluator and stop all the underlying network metric monitors */
public void close() {
mHandler.removeCallbacksAndEqualMessages(mCancellationToken);
for (NetworkMetricMonitor monitor : mMetricMonitors) {
monitor.close();
}
}
/** Return whether this network evaluator is valid */
......@@ -196,6 +401,11 @@ public class UnderlyingNetworkEvaluator {
return mPriorityClass;
}
/** Return whether the network is being penalized */
public boolean isPenalized() {
return mIsPenalized;
}
/** Dump the information of this instance */
public void dump(IndentingPrintWriter pw) {
pw.println("UnderlyingNetworkEvaluator:");
......@@ -211,7 +421,22 @@ public class UnderlyingNetworkEvaluator {
pw.println("mIsSelected: " + mIsSelected);
pw.println("mPriorityClass: " + mPriorityClass);
pw.println("mIsPenalized: " + mIsPenalized);
pw.decreaseIndent();
}
private String getLogPrefix() {
return "[Network " + mNetworkRecordBuilder.getNetwork() + "] ";
}
private void logInfo(String msg) {
Slog.i(TAG, getLogPrefix() + msg);
LOCAL_LOG.log("[INFO ] " + TAG + getLogPrefix() + msg);
}
private void logWtf(String msg) {
Slog.wtf(TAG, getLogPrefix() + msg);
LOCAL_LOG.log("[WTF ] " + TAG + getLogPrefix() + msg);
}
}
......@@ -269,6 +269,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test
public void testCreatedTransformsAreApplied() throws Exception {
verifyVcnTransformsApplied(mGatewayConnection, false /* expectForwardTransform */);
verify(mUnderlyingNetworkController).updateInboundTransform(any(), any());
}
@Test
......@@ -327,6 +328,8 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
verify(mUnderlyingNetworkController).updateInboundTransform(any(), any());
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
final List<ChildSaProposal> saProposals =
......
......@@ -223,6 +223,8 @@ public class VcnGatewayConnectionTestBase {
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
......
......@@ -26,6 +26,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.IpSecConfig;
import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
......@@ -141,4 +143,8 @@ public abstract class NetworkEvaluationTestBase {
mock(Handler.class));
setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class);
}
protected IpSecTransform makeDummyIpSecTransform() throws Exception {
return new IpSecTransform(mContext, new IpSecConfig());
}
}
......@@ -47,6 +47,8 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.IpSecConfig;
import android.net.IpSecTransform;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
......@@ -70,6 +72,7 @@ import com.android.server.vcn.routeselection.UnderlyingNetworkController.Depende
import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
import org.junit.Before;
import org.junit.Test;
......@@ -153,11 +156,13 @@ public class UnderlyingNetworkControllerTest {
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
@Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
@Mock private NetworkEvaluatorCallback mEvaluatorCallback;
@Mock private Network mNetwork;
@Spy private Dependencies mDependencies = new Dependencies();
@Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
@Captor private ArgumentCaptor<NetworkEvaluatorCallback> mEvaluatorCallbackCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
......@@ -176,7 +181,7 @@ public class UnderlyingNetworkControllerTest {
mTestLooper.getLooper(),
mVcnNetworkProvider,
false /* isInTestMode */));
resetVcnContext();
resetVcnContext(mVcnContext);
setupSystemService(
mContext,
......@@ -202,10 +207,11 @@ public class UnderlyingNetworkControllerTest {
.getVcnUnderlyingNetworkPriorities(),
SUB_GROUP,
mSubscriptionSnapshot,
null));
null,
mEvaluatorCallback));
doReturn(mNetworkEvaluator)
.when(mDependencies)
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any());
mUnderlyingNetworkController =
new UnderlyingNetworkController(
......@@ -217,9 +223,11 @@ public class UnderlyingNetworkControllerTest {
mDependencies);
}
private void resetVcnContext() {
reset(mVcnContext);
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
private void resetVcnContext(VcnContext vcnContext) {
reset(vcnContext);
doNothing().when(vcnContext).ensureRunningOnLooperThread();
doReturn(true).when(vcnContext).isFlagNetworkMetricMonitorEnabled();
doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled();
}
// Package private for use in NetworkPriorityClassifierTest
......@@ -245,11 +253,13 @@ public class UnderlyingNetworkControllerTest {
final ConnectivityManager cm = mock(ConnectivityManager.class);
setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
final VcnContext vcnContext =
new VcnContext(
mContext,
mTestLooper.getLooper(),
mVcnNetworkProvider,
true /* isInTestMode */);
spy(
new VcnContext(
mContext,
mTestLooper.getLooper(),
mVcnNetworkProvider,
true /* isInTestMode */));
resetVcnContext(vcnContext);
new UnderlyingNetworkController(
vcnContext,
......@@ -554,6 +564,45 @@ public class UnderlyingNetworkControllerTest {
verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any());
}
@Test
public void testUpdateIpSecTransform() {
verifyRegistrationOnAvailableAndGetCallback();
final UnderlyingNetworkRecord expectedRecord =
getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
final IpSecTransform expectedTransform = new IpSecTransform(mContext, new IpSecConfig());
mUnderlyingNetworkController.updateInboundTransform(expectedRecord, expectedTransform);
verify(mNetworkEvaluator).setInboundTransform(expectedTransform);
}
@Test
public void testOnEvaluationResultChanged() {
verifyRegistrationOnAvailableAndGetCallback();
// Verify #reevaluateNetworks is called by checking #getNetworkRecord
verify(mNetworkEvaluator).getNetworkRecord();
// Trigger the callback
verify(mDependencies)
.newUnderlyingNetworkEvaluator(
any(),
any(),
any(),
any(),
any(),
any(),
mEvaluatorCallbackCaptor.capture());
mEvaluatorCallbackCaptor.getValue().onEvaluationResultChanged();
// Verify #reevaluateNetworks is called again
verify(mNetworkEvaluator, times(2)).getNetworkRecord();
}
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
}
......@@ -682,7 +731,7 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
verifyOnSelectedUnderlyingNetworkChanged(null);
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null);
}
@Test
......@@ -690,6 +739,7 @@ public class UnderlyingNetworkControllerTest {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onLost(mNetwork);
verify(mNetworkEvaluator).close();
verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null);
}
......@@ -755,10 +805,11 @@ public class UnderlyingNetworkControllerTest {
underlyingNetworkTemplates,
SUB_GROUP,
mSubscriptionSnapshot,
null));
null,
mEvaluatorCallback));
doReturn(evaluator)
.when(mDependencies)
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any());
.newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any());
cb.onAvailable(network);
cb.onCapabilitiesChanged(network, responseNetworkCaps);
......
......@@ -16,27 +16,65 @@
package com.android.server.vcn.routeselection;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.net.IpSecTransform;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.os.PersistableBundle;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.Dependencies;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import java.util.concurrent.TimeUnit;
public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
private PersistableBundleWrapper mCarrierConfig;
private static final int PENALTY_TIMEOUT_MIN = 10;
private static final long PENALTY_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(PENALTY_TIMEOUT_MIN);
@Mock private PersistableBundleWrapper mCarrierConfig;
@Mock private IpSecPacketLossDetector mIpSecPacketLossDetector;
@Mock private Dependencies mDependencies;
@Mock private NetworkEvaluatorCallback mEvaluatorCallback;
@Captor private ArgumentCaptor<NetworkMetricMonitorCallback> mMetricMonitorCbCaptor;
private UnderlyingNetworkEvaluator mNetworkEvaluator;
@Before
public void setUp() throws Exception {
super.setUp();
mCarrierConfig = new PersistableBundleWrapper(new PersistableBundle());
when(mDependencies.newIpSecPacketLossDetector(any(), any(), any(), any()))
.thenReturn(mIpSecPacketLossDetector);
when(mCarrierConfig.getIntArray(
eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
.thenReturn(new int[] {PENALTY_TIMEOUT_MIN});
mNetworkEvaluator = newValidUnderlyingNetworkEvaluator();
}
private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() {
......@@ -46,7 +84,34 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig,
mEvaluatorCallback,
mDependencies);
}
private UnderlyingNetworkEvaluator newValidUnderlyingNetworkEvaluator() {
final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator();
evaluator.setNetworkCapabilities(
CELL_NETWORK_CAPABILITIES,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
evaluator.setLinkProperties(
LINK_PROPERTIES,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
evaluator.setIsBlocked(
false /* isBlocked */,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
return evaluator;
}
@Test
......@@ -98,4 +163,174 @@ public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
assertEquals(2, evaluator.getPriorityClass());
assertEquals(expectedRecord, evaluator.getNetworkRecord());
}
private void checkSetSelectedNetwork(boolean isSelected) {
mNetworkEvaluator.setIsSelected(
isSelected,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
verify(mIpSecPacketLossDetector).setIsSelectedUnderlyingNetwork(isSelected);
}
@Test
public void testSetIsSelected_selected() throws Exception {
checkSetSelectedNetwork(true /* isSelectedExpected */);
}
@Test
public void testSetIsSelected_unselected() throws Exception {
checkSetSelectedNetwork(false /* isSelectedExpected */);
}
@Test
public void testSetIpSecTransform_onSelectedNetwork() throws Exception {
final IpSecTransform transform = makeDummyIpSecTransform();
// Make the network selected
mNetworkEvaluator.setIsSelected(
true,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
mNetworkEvaluator.setInboundTransform(transform);
verify(mIpSecPacketLossDetector).setInboundTransform(transform);
}
@Test
public void testSetIpSecTransform_onUnSelectedNetwork() throws Exception {
mNetworkEvaluator.setIsSelected(
false,
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
mNetworkEvaluator.setInboundTransform(makeDummyIpSecTransform());
verify(mIpSecPacketLossDetector, never()).setInboundTransform(any());
}
@Test
public void close() throws Exception {
mNetworkEvaluator.close();
verify(mIpSecPacketLossDetector).close();
mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS);
assertNull(mTestLooper.nextMessage());
}
private NetworkMetricMonitorCallback getMetricMonitorCbCaptor() throws Exception {
verify(mDependencies)
.newIpSecPacketLossDetector(any(), any(), any(), mMetricMonitorCbCaptor.capture());
return mMetricMonitorCbCaptor.getValue();
}
private void checkPenalizeNetwork() throws Exception {
assertFalse(mNetworkEvaluator.isPenalized());
// Validation failed
when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(true);
getMetricMonitorCbCaptor().onValidationResultReceived();
// Verify the evaluator is penalized
assertTrue(mNetworkEvaluator.isPenalized());
verify(mEvaluatorCallback).onEvaluationResultChanged();
}
@Test
public void testRcvValidationResult_penalizeNetwork_penaltyTimeout() throws Exception {
checkPenalizeNetwork();
// Penalty timeout
mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS);
mTestLooper.dispatchAll();
// Verify the evaluator is not penalized
assertFalse(mNetworkEvaluator.isPenalized());
verify(mEvaluatorCallback, times(2)).onEvaluationResultChanged();
}
@Test
public void testRcvValidationResult_penalizeNetwork_passValidation() throws Exception {
checkPenalizeNetwork();
// Validation passed
when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(false);
getMetricMonitorCbCaptor().onValidationResultReceived();
// Verify the evaluator is not penalized and penalty timeout is canceled
assertFalse(mNetworkEvaluator.isPenalized());
verify(mEvaluatorCallback, times(2)).onEvaluationResultChanged();
mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS);
assertNull(mTestLooper.nextMessage());
}
@Test
public void testRcvValidationResult_penalizeNetwork_closeEvaluator() throws Exception {
checkPenalizeNetwork();
mNetworkEvaluator.close();
// Verify penalty timeout is canceled
mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS);
assertNull(mTestLooper.nextMessage());
}
@Test
public void testRcvValidationResult_PenaltyStateUnchanged() throws Exception {
assertFalse(mNetworkEvaluator.isPenalized());
// Validation passed
when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(false);
getMetricMonitorCbCaptor().onValidationResultReceived();
// Verifications
assertFalse(mNetworkEvaluator.isPenalized());
verify(mEvaluatorCallback, never()).onEvaluationResultChanged();
}
@Test
public void testSetCarrierConfig() throws Exception {
final int additionalTimeoutMin = 10;
when(mCarrierConfig.getIntArray(
eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
.thenReturn(new int[] {PENALTY_TIMEOUT_MIN + additionalTimeoutMin});
// Update evaluator and penalize the network
mNetworkEvaluator.reevaluate(
VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
mCarrierConfig);
checkPenalizeNetwork();
// Verify penalty timeout is changed
mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS);
assertNull(mTestLooper.nextMessage());
mTestLooper.moveTimeForward(TimeUnit.MINUTES.toMillis(additionalTimeoutMin));
assertNotNull(mTestLooper.nextMessage());
// Verify NetworkMetricMonitor is notified
verify(mIpSecPacketLossDetector).setCarrierConfig(any());
}
@Test
public void testCompare() throws Exception {
when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(true);
getMetricMonitorCbCaptor().onValidationResultReceived();
final UnderlyingNetworkEvaluator penalized = mNetworkEvaluator;
final UnderlyingNetworkEvaluator notPenalized = newValidUnderlyingNetworkEvaluator();
assertEquals(penalized.getPriorityClass(), notPenalized.getPriorityClass());
final int result =
UnderlyingNetworkEvaluator.getComparator(mVcnContext)
.compare(penalized, notPenalized);
assertEquals(1, result);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment