Loading services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java +97 −11 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.server.vcn.routeselection; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; Loading @@ -38,6 +40,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.server.vcn.VcnContext; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.BitSet; import java.util.Objects; import java.util.concurrent.TimeUnit; Loading @@ -56,8 +62,32 @@ import java.util.concurrent.TimeUnit; public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); @VisibleForTesting(visibility = Visibility.PRIVATE) static final int PACKET_LOSS_UNAVALAIBLE = -1; private static final int PACKET_LOSS_PERCENT_UNAVAILABLE = -1; @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"PACKET_LOSS_"}, value = { PACKET_LOSS_RATE_VALID, PACKET_LOSS_RATE_INVALID, }) @Target({ElementType.TYPE_USE}) private @interface PacketLossResultType {} /** Indicates a valid packet loss rate is available */ private static final int PACKET_LOSS_RATE_VALID = 0; /** * Indicates that the detector cannot get a valid packet loss rate due to one of the following * reasons: * * <ul> * <li>The replay window did not proceed and thus all packets might have been delivered out of * order * <li>There are unexpected errors * </ul> */ private static final int PACKET_LOSS_RATE_INVALID = 1; // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and Loading Loading @@ -307,24 +337,24 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { return; } final int packetLossRate = final PacketLossCalculationResult calculateResult = mPacketLossCalculator.getPacketLossRatePercentage( mLastIpSecTransformState, state, getLogPrefix()); if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { if (calculateResult.getResultType() == PACKET_LOSS_RATE_INVALID) { return; } final String logMsg = "packetLossRate: " + packetLossRate "calculateResult: " + calculateResult + "% in the past " + (state.getTimestampMillis() - mLastIpSecTransformState.getTimestampMillis()) + "ms"; mLastIpSecTransformState = state; if (packetLossRate < mPacketLossRatePercentThreshold) { if (calculateResult.getPacketLossRatePercent() < mPacketLossRatePercentThreshold) { logV(logMsg); onValidationResultReceivedInternal(false /* isFailed */); } else { Loading @@ -343,7 +373,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { @VisibleForTesting(visibility = Visibility.PRIVATE) public static class PacketLossCalculator { /** Calculate the packet loss rate between two timestamps */ public int getPacketLossRatePercentage( public PacketLossCalculationResult getPacketLossRatePercentage( @NonNull IpSecTransformState oldState, @NonNull IpSecTransformState newState, String logPrefix) { Loading @@ -359,7 +389,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { // The replay window did not proceed and all packets might have been delivered out // of order return PACKET_LOSS_UNAVALAIBLE; return PacketLossCalculationResult.invalid(); } // Get the expected packet count by assuming there is no packet loss. In this case, SA Loading @@ -386,10 +416,11 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { || actualPktCntDiff < 0 || actualPktCntDiff > expectedPktCntDiff) { logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); return PACKET_LOSS_UNAVALAIBLE; return PacketLossCalculationResult.invalid(); } return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); final int percent = 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); return PacketLossCalculationResult.valid(percent); } } Loading @@ -409,4 +440,59 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { return BitSet.valueOf(state.getReplayBitmap()).cardinality(); } @VisibleForTesting(visibility = Visibility.PRIVATE) public static class PacketLossCalculationResult { @PacketLossResultType private final int mResultType; private final int mPacketLossRatePercent; private PacketLossCalculationResult(@PacketLossResultType int type, int percent) { mResultType = type; mPacketLossRatePercent = percent; } /** Construct an instance that contains a valid packet loss rate */ public static PacketLossCalculationResult valid(int percent) { return new PacketLossCalculationResult(PACKET_LOSS_RATE_VALID, percent); } /** Construct an instance indicating the inability to get a valid packet loss rate */ public static PacketLossCalculationResult invalid() { return new PacketLossCalculationResult( PACKET_LOSS_RATE_INVALID, PACKET_LOSS_PERCENT_UNAVAILABLE); } @PacketLossResultType public int getResultType() { return mResultType; } public int getPacketLossRatePercent() { return mPacketLossRatePercent; } @Override public int hashCode() { return Objects.hash(mResultType, mPacketLossRatePercent); } @Override public boolean equals(@Nullable Object other) { if (!(other instanceof PacketLossCalculationResult)) { return false; } final PacketLossCalculationResult rhs = (PacketLossCalculationResult) other; return mResultType == rhs.mResultType && mPacketLossRatePercent == rhs.mPacketLossRatePercent; } @Override public String toString() { return "mResultType: " + mResultType + " | mPacketLossRatePercent: " + mPacketLossRatePercent; } } } tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java +32 −8 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server.vcn.routeselection; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; Loading @@ -44,6 +43,7 @@ import android.net.IpSecTransformState; import android.os.OutcomeReceiver; import android.os.PowerManager; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculationResult; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; Loading Loading @@ -293,7 +293,9 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { } private void checkHandleLossRate( int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) PacketLossCalculationResult mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) throws Exception { final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = startMonitorAndCaptureStateReceiver(); Loading Loading @@ -327,26 +329,32 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { @Test public void testHandleLossRate_validationPass() throws Exception { checkHandleLossRate( 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); PacketLossCalculationResult.valid(2), true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); } @Test public void testHandleLossRate_validationFail() throws Exception { checkHandleLossRate( 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); PacketLossCalculationResult.valid(22), true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); verify(mConnectivityManager).reportNetworkConnectivity(mNetwork, false); } @Test public void testHandleLossRate_resultUnavalaible() throws Exception { checkHandleLossRate( PACKET_LOSS_UNAVALAIBLE, PacketLossCalculationResult.invalid(), false /* isLastStateExpectedToUpdate */, false /* isCallbackExpected */); } private void checkGetPacketLossRate( IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) IpSecTransformState oldState, IpSecTransformState newState, PacketLossCalculationResult expectedLossRate) throws Exception { assertEquals( expectedLossRate, Loading @@ -362,14 +370,30 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { throws Exception { final IpSecTransformState newState = newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); checkGetPacketLossRate( oldState, newState, PacketLossCalculationResult.valid(expectedDataLossRate)); } private void checkGetPacketLossRate( IpSecTransformState oldState, int rxSeqNo, int packetCount, int packetInWin, PacketLossCalculationResult expectedDataLossRate) throws Exception { final IpSecTransformState newState = newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); checkGetPacketLossRate(oldState, newState, expectedDataLossRate); } @Test public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { checkGetPacketLossRate( mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); mTransformStateInitial, mTransformStateInitial, PacketLossCalculationResult.invalid()); checkGetPacketLossRate( mTransformStateInitial, 3000, 2000, 2000, PacketLossCalculationResult.invalid()); } @Test Loading Loading
services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java +97 −11 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.server.vcn.routeselection; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; Loading @@ -38,6 +40,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.server.vcn.VcnContext; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.BitSet; import java.util.Objects; import java.util.concurrent.TimeUnit; Loading @@ -56,8 +62,32 @@ import java.util.concurrent.TimeUnit; public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); @VisibleForTesting(visibility = Visibility.PRIVATE) static final int PACKET_LOSS_UNAVALAIBLE = -1; private static final int PACKET_LOSS_PERCENT_UNAVAILABLE = -1; @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"PACKET_LOSS_"}, value = { PACKET_LOSS_RATE_VALID, PACKET_LOSS_RATE_INVALID, }) @Target({ElementType.TYPE_USE}) private @interface PacketLossResultType {} /** Indicates a valid packet loss rate is available */ private static final int PACKET_LOSS_RATE_VALID = 0; /** * Indicates that the detector cannot get a valid packet loss rate due to one of the following * reasons: * * <ul> * <li>The replay window did not proceed and thus all packets might have been delivered out of * order * <li>There are unexpected errors * </ul> */ private static final int PACKET_LOSS_RATE_INVALID = 1; // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and Loading Loading @@ -307,24 +337,24 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { return; } final int packetLossRate = final PacketLossCalculationResult calculateResult = mPacketLossCalculator.getPacketLossRatePercentage( mLastIpSecTransformState, state, getLogPrefix()); if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { if (calculateResult.getResultType() == PACKET_LOSS_RATE_INVALID) { return; } final String logMsg = "packetLossRate: " + packetLossRate "calculateResult: " + calculateResult + "% in the past " + (state.getTimestampMillis() - mLastIpSecTransformState.getTimestampMillis()) + "ms"; mLastIpSecTransformState = state; if (packetLossRate < mPacketLossRatePercentThreshold) { if (calculateResult.getPacketLossRatePercent() < mPacketLossRatePercentThreshold) { logV(logMsg); onValidationResultReceivedInternal(false /* isFailed */); } else { Loading @@ -343,7 +373,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { @VisibleForTesting(visibility = Visibility.PRIVATE) public static class PacketLossCalculator { /** Calculate the packet loss rate between two timestamps */ public int getPacketLossRatePercentage( public PacketLossCalculationResult getPacketLossRatePercentage( @NonNull IpSecTransformState oldState, @NonNull IpSecTransformState newState, String logPrefix) { Loading @@ -359,7 +389,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { // The replay window did not proceed and all packets might have been delivered out // of order return PACKET_LOSS_UNAVALAIBLE; return PacketLossCalculationResult.invalid(); } // Get the expected packet count by assuming there is no packet loss. In this case, SA Loading @@ -386,10 +416,11 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { || actualPktCntDiff < 0 || actualPktCntDiff > expectedPktCntDiff) { logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); return PACKET_LOSS_UNAVALAIBLE; return PacketLossCalculationResult.invalid(); } return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); final int percent = 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); return PacketLossCalculationResult.valid(percent); } } Loading @@ -409,4 +440,59 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { return BitSet.valueOf(state.getReplayBitmap()).cardinality(); } @VisibleForTesting(visibility = Visibility.PRIVATE) public static class PacketLossCalculationResult { @PacketLossResultType private final int mResultType; private final int mPacketLossRatePercent; private PacketLossCalculationResult(@PacketLossResultType int type, int percent) { mResultType = type; mPacketLossRatePercent = percent; } /** Construct an instance that contains a valid packet loss rate */ public static PacketLossCalculationResult valid(int percent) { return new PacketLossCalculationResult(PACKET_LOSS_RATE_VALID, percent); } /** Construct an instance indicating the inability to get a valid packet loss rate */ public static PacketLossCalculationResult invalid() { return new PacketLossCalculationResult( PACKET_LOSS_RATE_INVALID, PACKET_LOSS_PERCENT_UNAVAILABLE); } @PacketLossResultType public int getResultType() { return mResultType; } public int getPacketLossRatePercent() { return mPacketLossRatePercent; } @Override public int hashCode() { return Objects.hash(mResultType, mPacketLossRatePercent); } @Override public boolean equals(@Nullable Object other) { if (!(other instanceof PacketLossCalculationResult)) { return false; } final PacketLossCalculationResult rhs = (PacketLossCalculationResult) other; return mResultType == rhs.mResultType && mPacketLossRatePercent == rhs.mPacketLossRatePercent; } @Override public String toString() { return "mResultType: " + mResultType + " | mPacketLossRatePercent: " + mPacketLossRatePercent; } } }
tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java +32 −8 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server.vcn.routeselection; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; Loading @@ -44,6 +43,7 @@ import android.net.IpSecTransformState; import android.os.OutcomeReceiver; import android.os.PowerManager; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculationResult; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; Loading Loading @@ -293,7 +293,9 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { } private void checkHandleLossRate( int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) PacketLossCalculationResult mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) throws Exception { final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = startMonitorAndCaptureStateReceiver(); Loading Loading @@ -327,26 +329,32 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { @Test public void testHandleLossRate_validationPass() throws Exception { checkHandleLossRate( 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); PacketLossCalculationResult.valid(2), true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); } @Test public void testHandleLossRate_validationFail() throws Exception { checkHandleLossRate( 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); PacketLossCalculationResult.valid(22), true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); verify(mConnectivityManager).reportNetworkConnectivity(mNetwork, false); } @Test public void testHandleLossRate_resultUnavalaible() throws Exception { checkHandleLossRate( PACKET_LOSS_UNAVALAIBLE, PacketLossCalculationResult.invalid(), false /* isLastStateExpectedToUpdate */, false /* isCallbackExpected */); } private void checkGetPacketLossRate( IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) IpSecTransformState oldState, IpSecTransformState newState, PacketLossCalculationResult expectedLossRate) throws Exception { assertEquals( expectedLossRate, Loading @@ -362,14 +370,30 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { throws Exception { final IpSecTransformState newState = newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); checkGetPacketLossRate( oldState, newState, PacketLossCalculationResult.valid(expectedDataLossRate)); } private void checkGetPacketLossRate( IpSecTransformState oldState, int rxSeqNo, int packetCount, int packetInWin, PacketLossCalculationResult expectedDataLossRate) throws Exception { final IpSecTransformState newState = newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); checkGetPacketLossRate(oldState, newState, expectedDataLossRate); } @Test public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { checkGetPacketLossRate( mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); mTransformStateInitial, mTransformStateInitial, PacketLossCalculationResult.invalid()); checkGetPacketLossRate( mTransformStateInitial, 3000, 2000, 2000, PacketLossCalculationResult.invalid()); } @Test Loading