Loading services/core/java/com/android/server/vcn/VcnGatewayConnection.java +104 −4 Original line number Original line Diff line number Diff line Loading @@ -38,10 +38,12 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.Network; import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgent; import android.net.NetworkAgent.ValidationStatus; import android.net.NetworkAgentConfig; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.annotations.PolicyDirection; import android.net.annotations.PolicyDirection; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionConfiguration; Loading Loading @@ -151,6 +153,9 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String SAFEMODE_TIMEOUT_ALARM = TAG + "_SAFEMODE_TIMEOUT_ALARM"; private static final int[] MERGED_CAPABILITIES = private static final int[] MERGED_CAPABILITIES = new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; Loading @@ -167,6 +172,9 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) static final int TEARDOWN_TIMEOUT_SECONDS = 5; static final int TEARDOWN_TIMEOUT_SECONDS = 5; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int SAFEMODE_TIMEOUT_SECONDS = 30; private interface EventInfo {} private interface EventInfo {} /** /** Loading Loading @@ -409,6 +417,23 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/178426520): implement handling of this event // TODO(b/178426520): implement handling of this event private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; /** * Sent when this VcnGatewayConnection has entered Safemode. * * <p>A VcnGatewayConnection enters Safemode when it takes over {@link * #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}. * * <p>When a VcnGatewayConnection enters safe mode, it will fire {@link * VcnGatewayStatusCallback#onEnteredSafemode()} to notify its Vcn. The Vcn will then shut down * its VcnGatewayConnectin(s). * * <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not * validated yet), and RetryTimeoutState. * * @param arg1 The "all" token; this signal is always honored. */ private static final int EVENT_SAFEMODE_TIMEOUT_EXCEEDED = 10; @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull @NonNull final DisconnectedState mDisconnectedState = new DisconnectedState(); final DisconnectedState mDisconnectedState = new DisconnectedState(); Loading Loading @@ -520,11 +545,13 @@ public class VcnGatewayConnection extends StateMachine { * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable * otherwise. * otherwise. */ */ private NetworkAgent mNetworkAgent; @VisibleForTesting(visibility = Visibility.PRIVATE) NetworkAgent mNetworkAgent; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; @Nullable private WakeupMessage mSafemodeTimeoutAlarm; public VcnGatewayConnection( public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext, Loading Loading @@ -611,6 +638,7 @@ public class VcnGatewayConnection extends StateMachine { cancelTeardownTimeoutAlarm(); cancelTeardownTimeoutAlarm(); cancelDisconnectRequestAlarm(); cancelDisconnectRequestAlarm(); cancelRetryTimeoutAlarm(); cancelRetryTimeoutAlarm(); cancelSafemodeAlarm(); mUnderlyingNetworkTracker.teardown(); mUnderlyingNetworkTracker.teardown(); } } Loading Loading @@ -899,6 +927,30 @@ public class VcnGatewayConnection extends StateMachine { removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); } } @VisibleForTesting(visibility = Visibility.PRIVATE) void setSafemodeAlarm() { // Only schedule a NEW alarm if none is already set. if (mSafemodeTimeoutAlarm != null) { return; } final Message delayedMessage = obtainMessage(EVENT_SAFEMODE_TIMEOUT_EXCEEDED, TOKEN_ALL); mSafemodeTimeoutAlarm = createScheduledAlarm( SAFEMODE_TIMEOUT_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); } private void cancelSafemodeAlarm() { if (mSafemodeTimeoutAlarm != null) { mSafemodeTimeoutAlarm.cancel(); mSafemodeTimeoutAlarm = null; } removeEqualMessages(EVENT_SAFEMODE_TIMEOUT_EXCEEDED); } private void sessionLost(int token, @Nullable Exception exception) { private void sessionLost(int token, @Nullable Exception exception) { sendMessageAndAcquireWakeLock( sendMessageAndAcquireWakeLock( EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); Loading Loading @@ -1072,6 +1124,8 @@ public class VcnGatewayConnection extends StateMachine { if (mIkeSession != null || mNetworkAgent != null) { if (mIkeSession != null || mNetworkAgent != null) { Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); } } cancelSafemodeAlarm(); } } @Override @Override Loading @@ -1095,6 +1149,12 @@ public class VcnGatewayConnection extends StateMachine { break; break; } } } } @Override protected void exitState() { // Safe to blindly set up, as it is cancelled and cleared on entering this state setSafemodeAlarm(); } } } private abstract class ActiveBaseState extends BaseState { private abstract class ActiveBaseState extends BaseState { Loading Loading @@ -1185,6 +1245,10 @@ public class VcnGatewayConnection extends StateMachine { transitionTo(mDisconnectedState); transitionTo(mDisconnectedState); } } break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1267,6 +1331,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1310,6 +1378,14 @@ public class VcnGatewayConnection extends StateMachine { public void unwanted() { public void unwanted() { teardownAsynchronously(); teardownAsynchronously(); } } @Override public void onValidationStatus( @ValidationStatus int status, @Nullable Uri redirectUri) { if (status == NetworkAgent.VALIDATION_STATUS_VALID) { clearFailedAttemptCounterAndSafeModeAlarm(); } } }; }; agent.register(); agent.register(); Loading @@ -1318,6 +1394,14 @@ public class VcnGatewayConnection extends StateMachine { return agent; return agent; } } protected void clearFailedAttemptCounterAndSafeModeAlarm() { mVcnContext.ensureRunningOnLooperThread(); // Validated connection, clear failed attempt counter mFailedAttempts = 0; cancelSafemodeAlarm(); } protected void applyTransform( protected void applyTransform( int token, int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull IpSecTunnelInterface tunnelIface, Loading Loading @@ -1397,9 +1481,6 @@ public class VcnGatewayConnection extends StateMachine { teardownAsynchronously(); teardownAsynchronously(); } } } } // Successful connection, clear failed attempt counter mFailedAttempts = 0; } } @Override @Override Loading Loading @@ -1436,6 +1517,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1478,7 +1563,18 @@ public class VcnGatewayConnection extends StateMachine { mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); } else { } else { updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig); updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig); // mNetworkAgent not null, so the VCN Network has already been established. Clear // the failed attempt counter and safe mode alarm since this transition is complete. clearFailedAttemptCounterAndSafeModeAlarm(); } } } @Override protected void exitState() { // Attempt to set the safe mode alarm - this requires the Vcn Network being validated // while in ConnectedState (which cancels the previous alarm) setSafemodeAlarm(); } } } } Loading Loading @@ -1526,6 +1622,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +28 −1 Original line number Original line Diff line number Diff line Loading @@ -34,8 +34,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; Loading Loading @@ -72,6 +74,11 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); } } @Test public void testEnterStateDoesNotCancelSafemodeAlarm() { verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test @Test public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { mGatewayConnection mGatewayConnection Loading Loading @@ -122,6 +129,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection @Test @Test public void testChildOpenedRegistersNetwork() throws Exception { public void testChildOpenedRegistersNetwork() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); final VcnChildSessionConfiguration mMockChildSessionConfig = final VcnChildSessionConfiguration mMockChildSessionConfig = mock(VcnChildSessionConfiguration.class); mock(VcnChildSessionConfiguration.class); doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) Loading Loading @@ -163,24 +173,41 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection for (int cap : mConfig.getAllExposedCapabilities()) { for (int cap : mConfig.getAllExposedCapabilities()) { assertTrue(nc.hasCapability(cap)); assertTrue(nc.hasCapability(cap)); } } // Now that Vcn Network is up, notify it as validated and verify the Safemode alarm is // canceled mGatewayConnection.mNetworkAgent.onValidationStatus( NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */); verify(mSafemodeTimeoutAlarm).cancel(); } } @Test @Test public void testChildSessionClosedTriggersDisconnect() throws Exception { public void testChildSessionClosedTriggersDisconnect() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); getChildSessionCallback().onClosed(); getChildSessionCallback().onClosed(); mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); // Since network never validated, verify mSafemodeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafemodeTimeoutAlarm); } } @Test @Test public void testIkeSessionClosedTriggersDisconnect() throws Exception { public void testIkeSessionClosedTriggersDisconnect() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); getIkeSessionCallback().onClosed(); getIkeSessionCallback().onClosed(); mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); // Since network never validated, verify mSafemodeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafemodeTimeoutAlarm); } } } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -106,4 +106,9 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio verify(mIkeSession).close(); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } @Test public void testSafemodeTimeoutNotifiesCallback() { verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState); } } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +4 −1 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,8 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect .createIpSecTunnelInterface( .createIpSecTunnelInterface( DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network); DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network); mGatewayConnection.setTunnelInterface(tunnelIface); mGatewayConnection.setTunnelInterface(tunnelIface); mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState); // Don't need to transition to DisconnectedState because it is the starting state mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); } } Loading Loading @@ -78,6 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); } } @Test @Test Loading @@ -98,5 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect assertNull(mGatewayConnection.getCurrentState()); assertNull(mGatewayConnection.getCurrentState()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verifySafemodeTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -80,4 +80,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } } @Test public void testSafemodeTimeoutNotifiesCallback() { verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState); } } } Loading
services/core/java/com/android/server/vcn/VcnGatewayConnection.java +104 −4 Original line number Original line Diff line number Diff line Loading @@ -38,10 +38,12 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.Network; import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgent; import android.net.NetworkAgent.ValidationStatus; import android.net.NetworkAgentConfig; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; import android.net.annotations.PolicyDirection; import android.net.annotations.PolicyDirection; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionConfiguration; Loading Loading @@ -151,6 +153,9 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String SAFEMODE_TIMEOUT_ALARM = TAG + "_SAFEMODE_TIMEOUT_ALARM"; private static final int[] MERGED_CAPABILITIES = private static final int[] MERGED_CAPABILITIES = new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; Loading @@ -167,6 +172,9 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) static final int TEARDOWN_TIMEOUT_SECONDS = 5; static final int TEARDOWN_TIMEOUT_SECONDS = 5; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int SAFEMODE_TIMEOUT_SECONDS = 30; private interface EventInfo {} private interface EventInfo {} /** /** Loading Loading @@ -409,6 +417,23 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/178426520): implement handling of this event // TODO(b/178426520): implement handling of this event private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; /** * Sent when this VcnGatewayConnection has entered Safemode. * * <p>A VcnGatewayConnection enters Safemode when it takes over {@link * #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}. * * <p>When a VcnGatewayConnection enters safe mode, it will fire {@link * VcnGatewayStatusCallback#onEnteredSafemode()} to notify its Vcn. The Vcn will then shut down * its VcnGatewayConnectin(s). * * <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not * validated yet), and RetryTimeoutState. * * @param arg1 The "all" token; this signal is always honored. */ private static final int EVENT_SAFEMODE_TIMEOUT_EXCEEDED = 10; @VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull @NonNull final DisconnectedState mDisconnectedState = new DisconnectedState(); final DisconnectedState mDisconnectedState = new DisconnectedState(); Loading Loading @@ -520,11 +545,13 @@ public class VcnGatewayConnection extends StateMachine { * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable * otherwise. * otherwise. */ */ private NetworkAgent mNetworkAgent; @VisibleForTesting(visibility = Visibility.PRIVATE) NetworkAgent mNetworkAgent; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; @Nullable private WakeupMessage mSafemodeTimeoutAlarm; public VcnGatewayConnection( public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext, Loading Loading @@ -611,6 +638,7 @@ public class VcnGatewayConnection extends StateMachine { cancelTeardownTimeoutAlarm(); cancelTeardownTimeoutAlarm(); cancelDisconnectRequestAlarm(); cancelDisconnectRequestAlarm(); cancelRetryTimeoutAlarm(); cancelRetryTimeoutAlarm(); cancelSafemodeAlarm(); mUnderlyingNetworkTracker.teardown(); mUnderlyingNetworkTracker.teardown(); } } Loading Loading @@ -899,6 +927,30 @@ public class VcnGatewayConnection extends StateMachine { removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); } } @VisibleForTesting(visibility = Visibility.PRIVATE) void setSafemodeAlarm() { // Only schedule a NEW alarm if none is already set. if (mSafemodeTimeoutAlarm != null) { return; } final Message delayedMessage = obtainMessage(EVENT_SAFEMODE_TIMEOUT_EXCEEDED, TOKEN_ALL); mSafemodeTimeoutAlarm = createScheduledAlarm( SAFEMODE_TIMEOUT_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); } private void cancelSafemodeAlarm() { if (mSafemodeTimeoutAlarm != null) { mSafemodeTimeoutAlarm.cancel(); mSafemodeTimeoutAlarm = null; } removeEqualMessages(EVENT_SAFEMODE_TIMEOUT_EXCEEDED); } private void sessionLost(int token, @Nullable Exception exception) { private void sessionLost(int token, @Nullable Exception exception) { sendMessageAndAcquireWakeLock( sendMessageAndAcquireWakeLock( EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); Loading Loading @@ -1072,6 +1124,8 @@ public class VcnGatewayConnection extends StateMachine { if (mIkeSession != null || mNetworkAgent != null) { if (mIkeSession != null || mNetworkAgent != null) { Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); } } cancelSafemodeAlarm(); } } @Override @Override Loading @@ -1095,6 +1149,12 @@ public class VcnGatewayConnection extends StateMachine { break; break; } } } } @Override protected void exitState() { // Safe to blindly set up, as it is cancelled and cleared on entering this state setSafemodeAlarm(); } } } private abstract class ActiveBaseState extends BaseState { private abstract class ActiveBaseState extends BaseState { Loading Loading @@ -1185,6 +1245,10 @@ public class VcnGatewayConnection extends StateMachine { transitionTo(mDisconnectedState); transitionTo(mDisconnectedState); } } break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1267,6 +1331,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1310,6 +1378,14 @@ public class VcnGatewayConnection extends StateMachine { public void unwanted() { public void unwanted() { teardownAsynchronously(); teardownAsynchronously(); } } @Override public void onValidationStatus( @ValidationStatus int status, @Nullable Uri redirectUri) { if (status == NetworkAgent.VALIDATION_STATUS_VALID) { clearFailedAttemptCounterAndSafeModeAlarm(); } } }; }; agent.register(); agent.register(); Loading @@ -1318,6 +1394,14 @@ public class VcnGatewayConnection extends StateMachine { return agent; return agent; } } protected void clearFailedAttemptCounterAndSafeModeAlarm() { mVcnContext.ensureRunningOnLooperThread(); // Validated connection, clear failed attempt counter mFailedAttempts = 0; cancelSafemodeAlarm(); } protected void applyTransform( protected void applyTransform( int token, int token, @NonNull IpSecTunnelInterface tunnelIface, @NonNull IpSecTunnelInterface tunnelIface, Loading Loading @@ -1397,9 +1481,6 @@ public class VcnGatewayConnection extends StateMachine { teardownAsynchronously(); teardownAsynchronously(); } } } } // Successful connection, clear failed attempt counter mFailedAttempts = 0; } } @Override @Override Loading Loading @@ -1436,6 +1517,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading Loading @@ -1478,7 +1563,18 @@ public class VcnGatewayConnection extends StateMachine { mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); } else { } else { updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig); updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig); // mNetworkAgent not null, so the VCN Network has already been established. Clear // the failed attempt counter and safe mode alarm since this transition is complete. clearFailedAttemptCounterAndSafeModeAlarm(); } } } @Override protected void exitState() { // Attempt to set the safe mode alarm - this requires the Vcn Network being validated // while in ConnectedState (which cancels the previous alarm) setSafemodeAlarm(); } } } } Loading Loading @@ -1526,6 +1622,10 @@ public class VcnGatewayConnection extends StateMachine { case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED: handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); break; break; case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: mGatewayStatusCallback.onEnteredSafemode(); mSafemodeTimeoutAlarm = null; break; default: default: logUnhandledMessage(msg); logUnhandledMessage(msg); break; break; Loading
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +28 −1 Original line number Original line Diff line number Diff line Loading @@ -34,8 +34,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.NetworkCapabilities; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; Loading Loading @@ -72,6 +74,11 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); } } @Test public void testEnterStateDoesNotCancelSafemodeAlarm() { verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test @Test public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { mGatewayConnection mGatewayConnection Loading Loading @@ -122,6 +129,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection @Test @Test public void testChildOpenedRegistersNetwork() throws Exception { public void testChildOpenedRegistersNetwork() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); final VcnChildSessionConfiguration mMockChildSessionConfig = final VcnChildSessionConfiguration mMockChildSessionConfig = mock(VcnChildSessionConfiguration.class); mock(VcnChildSessionConfiguration.class); doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) Loading Loading @@ -163,24 +173,41 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection for (int cap : mConfig.getAllExposedCapabilities()) { for (int cap : mConfig.getAllExposedCapabilities()) { assertTrue(nc.hasCapability(cap)); assertTrue(nc.hasCapability(cap)); } } // Now that Vcn Network is up, notify it as validated and verify the Safemode alarm is // canceled mGatewayConnection.mNetworkAgent.onValidationStatus( NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */); verify(mSafemodeTimeoutAlarm).cancel(); } } @Test @Test public void testChildSessionClosedTriggersDisconnect() throws Exception { public void testChildSessionClosedTriggersDisconnect() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); getChildSessionCallback().onClosed(); getChildSessionCallback().onClosed(); mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); // Since network never validated, verify mSafemodeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafemodeTimeoutAlarm); } } @Test @Test public void testIkeSessionClosedTriggersDisconnect() throws Exception { public void testIkeSessionClosedTriggersDisconnect() throws Exception { // Verify scheduled but not canceled when entering ConnectedState verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); getIkeSessionCallback().onClosed(); getIkeSessionCallback().onClosed(); mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); // Since network never validated, verify mSafemodeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafemodeTimeoutAlarm); } } } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -106,4 +106,9 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio verify(mIkeSession).close(); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } @Test public void testSafemodeTimeoutNotifiesCallback() { verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState); } } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +4 −1 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,8 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect .createIpSecTunnelInterface( .createIpSecTunnelInterface( DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network); DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network); mGatewayConnection.setTunnelInterface(tunnelIface); mGatewayConnection.setTunnelInterface(tunnelIface); mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState); // Don't need to transition to DisconnectedState because it is the starting state mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); } } Loading Loading @@ -78,6 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect mTestLooper.dispatchAll(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); } } @Test @Test Loading @@ -98,5 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect assertNull(mGatewayConnection.getCurrentState()); assertNull(mGatewayConnection.getCurrentState()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verifySafemodeTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -80,4 +80,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } } @Test public void testSafemodeTimeoutNotifiesCallback() { verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState); } } }