Merge "Implement Safemode alarm in VcnGatewayConnection."
This commit is contained in:
@@ -38,10 +38,12 @@ import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkAgent;
|
||||
import android.net.NetworkAgent.ValidationStatus;
|
||||
import android.net.NetworkAgentConfig;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.Uri;
|
||||
import android.net.annotations.PolicyDirection;
|
||||
import android.net.ipsec.ike.ChildSessionCallback;
|
||||
import android.net.ipsec.ike.ChildSessionConfiguration;
|
||||
@@ -151,6 +153,9 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
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 =
|
||||
new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
|
||||
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
|
||||
@@ -167,6 +172,9 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int TEARDOWN_TIMEOUT_SECONDS = 5;
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int SAFEMODE_TIMEOUT_SECONDS = 30;
|
||||
|
||||
private interface EventInfo {}
|
||||
|
||||
/**
|
||||
@@ -409,6 +417,23 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
// TODO(b/178426520): implement handling of this event
|
||||
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)
|
||||
@NonNull
|
||||
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
|
||||
* otherwise.
|
||||
*/
|
||||
private NetworkAgent mNetworkAgent;
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
NetworkAgent mNetworkAgent;
|
||||
|
||||
@Nullable private WakeupMessage mTeardownTimeoutAlarm;
|
||||
@Nullable private WakeupMessage mDisconnectRequestAlarm;
|
||||
@Nullable private WakeupMessage mRetryTimeoutAlarm;
|
||||
@Nullable private WakeupMessage mSafemodeTimeoutAlarm;
|
||||
|
||||
public VcnGatewayConnection(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@@ -611,6 +638,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
cancelTeardownTimeoutAlarm();
|
||||
cancelDisconnectRequestAlarm();
|
||||
cancelRetryTimeoutAlarm();
|
||||
cancelSafemodeAlarm();
|
||||
|
||||
mUnderlyingNetworkTracker.teardown();
|
||||
}
|
||||
@@ -899,6 +927,30 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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) {
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
|
||||
@@ -1072,6 +1124,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
if (mIkeSession != null || mNetworkAgent != null) {
|
||||
Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
|
||||
}
|
||||
|
||||
cancelSafemodeAlarm();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1095,6 +1149,12 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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 {
|
||||
@@ -1185,6 +1245,10 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
transitionTo(mDisconnectedState);
|
||||
}
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
@@ -1267,6 +1331,10 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
@@ -1310,6 +1378,14 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
public void unwanted() {
|
||||
teardownAsynchronously();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValidationStatus(
|
||||
@ValidationStatus int status, @Nullable Uri redirectUri) {
|
||||
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
|
||||
clearFailedAttemptCounterAndSafeModeAlarm();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
agent.register();
|
||||
@@ -1318,6 +1394,14 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
return agent;
|
||||
}
|
||||
|
||||
protected void clearFailedAttemptCounterAndSafeModeAlarm() {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
// Validated connection, clear failed attempt counter
|
||||
mFailedAttempts = 0;
|
||||
cancelSafemodeAlarm();
|
||||
}
|
||||
|
||||
protected void applyTransform(
|
||||
int token,
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@@ -1397,9 +1481,6 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
teardownAsynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
// Successful connection, clear failed attempt counter
|
||||
mFailedAttempts = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1436,6 +1517,10 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
@@ -1478,8 +1563,19 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
|
||||
} else {
|
||||
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:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
|
||||
@@ -34,8 +34,10 @@ import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkAgent;
|
||||
import android.net.NetworkCapabilities;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -72,6 +74,11 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnterStateDoesNotCancelSafemodeAlarm() {
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
|
||||
mGatewayConnection
|
||||
@@ -122,6 +129,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
|
||||
@Test
|
||||
public void testChildOpenedRegistersNetwork() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
final VcnChildSessionConfiguration mMockChildSessionConfig =
|
||||
mock(VcnChildSessionConfiguration.class);
|
||||
doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
|
||||
@@ -163,24 +173,41 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
for (int cap : mConfig.getAllExposedCapabilities()) {
|
||||
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
|
||||
public void testChildSessionClosedTriggersDisconnect() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
getChildSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafemodeTimeoutAlarm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIkeSessionClosedTriggersDisconnect() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
getIkeSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
|
||||
verify(mIkeSession).close();
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
|
||||
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafemodeTimeoutAlarm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,4 +106,9 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
|
||||
verify(mIkeSession).close();
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
.createIpSecTunnelInterface(
|
||||
DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network);
|
||||
mGatewayConnection.setTunnelInterface(tunnelIface);
|
||||
mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
|
||||
|
||||
// Don't need to transition to DisconnectedState because it is the starting state
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@@ -78,6 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,5 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
|
||||
assertNull(mGatewayConnection.getCurrentState());
|
||||
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +80,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,4 +91,9 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
|
||||
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
|
||||
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.VcnTestUtils.setupIpSecManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
@@ -50,6 +51,7 @@ import android.os.ParcelUuid;
|
||||
import android.os.PowerManager;
|
||||
import android.os.test.TestLooper;
|
||||
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.WakeupMessage;
|
||||
import com.android.server.IpSecService;
|
||||
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
@@ -108,6 +110,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
|
||||
@NonNull protected final WakeupMessage mDisconnectRequestAlarm;
|
||||
@NonNull protected final WakeupMessage mRetryTimeoutAlarm;
|
||||
@NonNull protected final WakeupMessage mSafemodeTimeoutAlarm;
|
||||
|
||||
@NonNull protected final IpSecService mIpSecSvc;
|
||||
@NonNull protected final ConnectivityManager mConnMgr;
|
||||
@@ -128,6 +131,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
mTeardownTimeoutAlarm = mock(WakeupMessage.class);
|
||||
mDisconnectRequestAlarm = mock(WakeupMessage.class);
|
||||
mRetryTimeoutAlarm = mock(WakeupMessage.class);
|
||||
mSafemodeTimeoutAlarm = mock(WakeupMessage.class);
|
||||
|
||||
mIpSecSvc = mock(IpSecService.class);
|
||||
setupIpSecManager(mContext, mIpSecSvc);
|
||||
@@ -150,6 +154,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
|
||||
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
|
||||
setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM);
|
||||
setUpWakeupMessage(mSafemodeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM);
|
||||
|
||||
doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
|
||||
}
|
||||
@@ -253,4 +258,24 @@ public class VcnGatewayConnectionTestBase {
|
||||
delayInMillis,
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user