Merge "Implement Safemode alarm in VcnGatewayConnection."

This commit is contained in:
Cody Kesting
2021-02-16 22:29:21 +00:00
committed by Gerrit Code Review
7 changed files with 176 additions and 6 deletions

View File

@@ -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;
@@ -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;
@@ -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 {}
/** /**
@@ -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();
@@ -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,
@@ -611,6 +638,7 @@ public class VcnGatewayConnection extends StateMachine {
cancelTeardownTimeoutAlarm(); cancelTeardownTimeoutAlarm();
cancelDisconnectRequestAlarm(); cancelDisconnectRequestAlarm();
cancelRetryTimeoutAlarm(); cancelRetryTimeoutAlarm();
cancelSafemodeAlarm();
mUnderlyingNetworkTracker.teardown(); mUnderlyingNetworkTracker.teardown();
} }
@@ -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));
@@ -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
@@ -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 {
@@ -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;
@@ -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;
@@ -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();
@@ -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,
@@ -1397,9 +1481,6 @@ public class VcnGatewayConnection extends StateMachine {
teardownAsynchronously(); teardownAsynchronously();
} }
} }
// Successful connection, clear failed attempt counter
mFailedAttempts = 0;
} }
@Override @Override
@@ -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;
@@ -1478,8 +1563,19 @@ 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();
}
} }
/** /**
@@ -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;

View File

@@ -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;
@@ -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
@@ -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))
@@ -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);
} }
} }

View File

@@ -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);
}
} }

View File

@@ -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();
} }
@@ -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
@@ -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 */);
} }
} }

View File

@@ -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);
}
} }

View File

@@ -91,4 +91,9 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */); verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
} }
@Test
public void testSafemodeTimeoutNotifiesCallback() {
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
}
} }

View File

@@ -20,6 +20,7 @@ import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkR
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
@@ -50,6 +51,7 @@ import android.os.ParcelUuid;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import com.android.internal.util.State;
import com.android.internal.util.WakeupMessage; import com.android.internal.util.WakeupMessage;
import com.android.server.IpSecService; import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -108,6 +110,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm; @NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
@NonNull protected final WakeupMessage mDisconnectRequestAlarm; @NonNull protected final WakeupMessage mDisconnectRequestAlarm;
@NonNull protected final WakeupMessage mRetryTimeoutAlarm; @NonNull protected final WakeupMessage mRetryTimeoutAlarm;
@NonNull protected final WakeupMessage mSafemodeTimeoutAlarm;
@NonNull protected final IpSecService mIpSecSvc; @NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr; @NonNull protected final ConnectivityManager mConnMgr;
@@ -128,6 +131,7 @@ public class VcnGatewayConnectionTestBase {
mTeardownTimeoutAlarm = mock(WakeupMessage.class); mTeardownTimeoutAlarm = mock(WakeupMessage.class);
mDisconnectRequestAlarm = mock(WakeupMessage.class); mDisconnectRequestAlarm = mock(WakeupMessage.class);
mRetryTimeoutAlarm = mock(WakeupMessage.class); mRetryTimeoutAlarm = mock(WakeupMessage.class);
mSafemodeTimeoutAlarm = mock(WakeupMessage.class);
mIpSecSvc = mock(IpSecService.class); mIpSecSvc = mock(IpSecService.class);
setupIpSecManager(mContext, mIpSecSvc); setupIpSecManager(mContext, mIpSecSvc);
@@ -150,6 +154,7 @@ public class VcnGatewayConnectionTestBase {
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM); setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM); setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM); setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM);
setUpWakeupMessage(mSafemodeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM);
doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime(); doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
} }
@@ -253,4 +258,24 @@ public class VcnGatewayConnectionTestBase {
delayInMillis, delayInMillis,
expectCanceled); expectCanceled);
} }
protected Runnable verifySafemodeTimeoutAlarmAndGetCallback(boolean expectCanceled) {
return verifyWakeupMessageSetUpAndGetCallback(
VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM,
mSafemodeTimeoutAlarm,
TimeUnit.SECONDS.toMillis(VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS),
expectCanceled);
}
protected void verifySafemodeTimeoutNotifiesCallback(@NonNull State expectedState) {
// Safemode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
// state)
final Runnable delayedEvent =
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
delayedEvent.run();
mTestLooper.dispatchAll();
verify(mGatewayStatusCallback).onEnteredSafemode();
assertEquals(expectedState, mGatewayConnection.getCurrentState());
}
} }