Allow NETWORK_LOST disconnections to retry
This change adds the ability for disconnection requests to specify whether the full VcnGatewayConnection should be torn down. This is useful in cases where the cause is an ephemeral failure, and can be restarted at some later point (eg an underlying network appears, or a new NetworkRequest is filed that brings the VCN up.) Bug: 179944275 Test: atest FrameworksVcnTests Change-Id: Id1813bcbbb80541be97cc383ea9d1fe08554b7ae
This commit is contained in:
@@ -168,6 +168,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: ";
|
||||
private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST =
|
||||
"Underlying Network lost";
|
||||
private static final String DISCONNECT_REASON_NETWORK_AGENT_UNWANTED =
|
||||
"NetworkAgent was unwanted";
|
||||
private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
|
||||
private static final int TOKEN_ALL = Integer.MIN_VALUE;
|
||||
|
||||
@@ -379,13 +381,16 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
/** The reason why the disconnect was requested. */
|
||||
@NonNull public final String reason;
|
||||
|
||||
EventDisconnectRequestedInfo(@NonNull String reason) {
|
||||
public final boolean shouldTeardown;
|
||||
|
||||
EventDisconnectRequestedInfo(@NonNull String reason, boolean shouldTeardown) {
|
||||
this.reason = Objects.requireNonNull(reason);
|
||||
this.shouldTeardown = shouldTeardown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(reason);
|
||||
return Objects.hash(reason, shouldTeardown);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -395,7 +400,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
|
||||
final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other;
|
||||
return reason.equals(rhs.reason);
|
||||
return reason.equals(rhs.reason) && shouldTeardown == rhs.shouldTeardown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,10 +627,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
* <p>Once torn down, this VcnTunnel CANNOT be started again.
|
||||
*/
|
||||
public void teardownAsynchronously() {
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
|
||||
sendDisconnectRequestedAndAcquireWakelock(
|
||||
DISCONNECT_REASON_TEARDOWN, true /* shouldTeardown */);
|
||||
|
||||
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
|
||||
// is also called asynchronously when a NetworkAgent becomes unwanted
|
||||
@@ -892,7 +895,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
TOKEN_ALL,
|
||||
0 /* arg2 */,
|
||||
new EventDisconnectRequestedInfo(
|
||||
DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
|
||||
DISCONNECT_REASON_UNDERLYING_NETWORK_LOST,
|
||||
false /* shouldTeardown */));
|
||||
mDisconnectRequestAlarm =
|
||||
createScheduledAlarm(
|
||||
DISCONNECT_REQUEST_ALARM,
|
||||
@@ -909,7 +913,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
// Cancel any existing disconnect due to previous loss of underlying network
|
||||
removeEqualMessages(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
|
||||
new EventDisconnectRequestedInfo(
|
||||
DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldTeardown */));
|
||||
}
|
||||
|
||||
private void setRetryTimeoutAlarm(long delay) {
|
||||
@@ -1041,11 +1046,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
enterState();
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Uncaught exception", e);
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
|
||||
sendDisconnectRequestedAndAcquireWakelock(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldTeardown */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,11 +1085,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
processStateMsg(msg);
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Uncaught exception", e);
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
|
||||
sendDisconnectRequestedAndAcquireWakelock(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldTeardown */);
|
||||
}
|
||||
|
||||
// Attempt to release the WakeLock - only possible if the Handler queue is empty
|
||||
@@ -1104,11 +1103,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
exitState();
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Uncaught exception", e);
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
|
||||
sendDisconnectRequestedAndAcquireWakelock(
|
||||
DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldTeardown */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,11 +1137,11 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleDisconnectRequested(String msg) {
|
||||
protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) {
|
||||
// TODO(b/180526152): notify VcnStatusCallback for Network loss
|
||||
|
||||
Slog.v(TAG, "Tearing down. Cause: " + msg);
|
||||
mIsRunning = false;
|
||||
Slog.v(TAG, "Tearing down. Cause: " + info.reason);
|
||||
mIsRunning = !info.shouldTeardown;
|
||||
|
||||
teardownNetwork();
|
||||
|
||||
@@ -1200,9 +1196,11 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
mIsRunning = false;
|
||||
if (((EventDisconnectRequestedInfo) msg.obj).shouldTeardown) {
|
||||
mIsRunning = false;
|
||||
|
||||
quitNow();
|
||||
quitNow();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
@@ -1284,10 +1282,11 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj);
|
||||
mIsRunning = !info.shouldTeardown;
|
||||
teardownNetwork();
|
||||
|
||||
String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
|
||||
if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
|
||||
if (info.reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
|
||||
// TODO(b/180526152): notify VcnStatusCallback for Network loss
|
||||
|
||||
// Will trigger EVENT_SESSION_CLOSED immediately.
|
||||
@@ -1391,7 +1390,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
transitionTo(mConnectedState);
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
|
||||
break;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
@@ -1438,6 +1437,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
mVcnContext.getVcnNetworkProvider()) {
|
||||
@Override
|
||||
public void unwanted() {
|
||||
Slog.d(TAG, "NetworkAgent was unwanted");
|
||||
teardownAsynchronously();
|
||||
}
|
||||
|
||||
@@ -1577,7 +1577,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
|
||||
break;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
@@ -1682,7 +1682,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
transitionTo(mConnectingState);
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
|
||||
break;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
@@ -1924,6 +1924,14 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
mIkeSession = session;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldTeardown) {
|
||||
sendMessageAndAcquireWakeLock(
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(reason, shouldTeardown));
|
||||
}
|
||||
|
||||
private IkeSessionParams buildIkeParams() {
|
||||
// TODO: Implement this once IkeSessionParams is persisted
|
||||
return null;
|
||||
|
||||
@@ -290,4 +290,22 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
|
||||
new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTeardown() throws Exception {
|
||||
mGatewayConnection.teardownAsynchronously();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
assertFalse(mGatewayConnection.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
assertTrue(mGatewayConnection.isRunning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.server.vcn;
|
||||
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -111,4 +113,22 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTeardown() throws Exception {
|
||||
mGatewayConnection.teardownAsynchronously();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
assertFalse(mGatewayConnection.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
assertTrue(mGatewayConnection.isRunning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import static android.net.IpSecManager.IpSecTunnelInterface;
|
||||
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -101,5 +103,16 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
assertNull(mGatewayConnection.getCurrentState());
|
||||
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
assertFalse(mGatewayConnection.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
|
||||
assertTrue(mGatewayConnection.isRunning());
|
||||
// No safe mode timer changes expected.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.server.vcn;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -79,10 +81,20 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
|
||||
// Should do nothing; already tearing down.
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
assertFalse(mGatewayConnection.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
assertTrue(mGatewayConnection.isRunning());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.android.server.vcn;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -96,4 +99,22 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.teardownAsynchronously();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertNull(mGatewayConnection.getCurrentState());
|
||||
assertFalse(mGatewayConnection.isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTeardownDisconnectRequest() throws Exception {
|
||||
mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
|
||||
assertTrue(mGatewayConnection.isRunning());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user