Merge changes Ib439bde4,I5bc0fe94,I72abf1e2,Id1813bcb
* changes:
Call setUnderlyingNetwork to complete VCN MOBIKE
Rename isRunning to isQuitting, and flip all booleans
Notify Vcn of VcnGatewayConnection quits
Allow NETWORK_LOST disconnections to retry
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index b6ddd93..b2db9f5 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -65,7 +65,7 @@
@NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ private boolean mIsQuitting = false;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -151,7 +151,7 @@
mVcnContext.ensureRunningOnLooperThread();
// Don't bother re-filing NetworkRequests if this Tracker has been torn down.
- if (!mIsRunning) {
+ if (mIsQuitting) {
return;
}
@@ -205,7 +205,7 @@
}
mCellBringupCallbacks.clear();
- mIsRunning = false;
+ mIsQuitting = true;
}
/** Returns whether the currently selected Network matches the given network. */
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 6ad30b5..9d39c67 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
@@ -84,6 +86,13 @@
*/
private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+ /**
+ * A GatewayConnection owned by this VCN quit.
+ *
+ * @param obj VcnGatewayConnectionConfig
+ */
+ private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@@ -208,6 +217,9 @@
case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
break;
+ case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
+ handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
@@ -263,7 +275,7 @@
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
- if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
if (VDBG) {
Slog.v(
getLogTag(),
@@ -278,7 +290,7 @@
// up
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
- if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(
getLogTag(),
"Bringing up new VcnGatewayConnection for request " + request.requestId);
@@ -289,12 +301,21 @@
mSubscriptionGroup,
mLastSnapshot,
gatewayConnectionConfig,
- new VcnGatewayStatusCallbackImpl());
+ new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
+ private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
+ Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
+ mVcnGatewayConnections.remove(config);
+
+ // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
+ // start a new GatewayConnection)
+ mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
+ }
+
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
@@ -305,9 +326,10 @@
}
}
- private boolean requestSatisfiedByGatewayConnectionConfig(
+ private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
+ builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
for (int cap : config.getAllExposedCapabilities()) {
builder.addCapability(cap);
}
@@ -339,9 +361,23 @@
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
+
+ /** Called by a VcnGatewayConnection to indicate that it has fully torn down. */
+ void onQuit();
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ public final VcnGatewayConnectionConfig mGatewayConnectionConfig;
+
+ VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ mGatewayConnectionConfig = gatewayConnectionConfig;
+ }
+
+ @Override
+ public void onQuit() {
+ sendMessage(obtainMessage(MSG_EVENT_GATEWAY_CONNECTION_QUIT, mGatewayConnectionConfig));
+ }
+
@Override
public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 06748a3..6bc9978 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -168,6 +168,8 @@
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 @@
/** The reason why the disconnect was requested. */
@NonNull public final String reason;
- EventDisconnectRequestedInfo(@NonNull String reason) {
+ public final boolean shouldQuit;
+
+ EventDisconnectRequestedInfo(@NonNull String reason, boolean shouldQuit) {
this.reason = Objects.requireNonNull(reason);
+ this.shouldQuit = shouldQuit;
}
@Override
public int hashCode() {
- return Objects.hash(reason);
+ return Objects.hash(reason, shouldQuit);
}
@Override
@@ -395,7 +400,7 @@
}
final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other;
- return reason.equals(rhs.reason);
+ return reason.equals(rhs.reason) && shouldQuit == rhs.shouldQuit;
}
}
@@ -488,8 +493,14 @@
*/
@NonNull private final VcnWakeLock mWakeLock;
- /** Running state of this VcnGatewayConnection. */
- private boolean mIsRunning = true;
+ /**
+ * Whether the VcnGatewayConnection is in the process of irreversibly quitting.
+ *
+ * <p>This variable is false for the lifecycle of the VcnGatewayConnection, until a command to
+ * teardown has been received. This may be flipped due to events such as the Network becoming
+ * unwanted, the owning VCN entering safe mode, or an irrecoverable internal failure.
+ */
+ private boolean mIsQuitting = false;
/**
* The token used by the primary/current/active session.
@@ -622,10 +633,8 @@
* <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 /* shouldQuit */);
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
// is also called asynchronously when a NetworkAgent becomes unwanted
@@ -646,6 +655,8 @@
cancelSafeModeAlarm();
mUnderlyingNetworkTracker.teardown();
+
+ mGatewayStatusCallback.onQuit();
}
/**
@@ -693,7 +704,7 @@
private void acquireWakeLock() {
mVcnContext.ensureRunningOnLooperThread();
- if (mIsRunning) {
+ if (!mIsQuitting) {
mWakeLock.acquire();
}
}
@@ -892,7 +903,7 @@
TOKEN_ALL,
0 /* arg2 */,
new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */));
mDisconnectRequestAlarm =
createScheduledAlarm(
DISCONNECT_REQUEST_ALARM,
@@ -909,7 +920,8 @@
// 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 /* shouldQuit */));
}
private void setRetryTimeoutAlarm(long delay) {
@@ -1041,11 +1053,8 @@
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 /* shouldQuit */);
}
}
@@ -1083,11 +1092,8 @@
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 /* shouldQuit */);
}
// Attempt to release the WakeLock - only possible if the Handler queue is empty
@@ -1104,11 +1110,8 @@
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 /* shouldQuit */);
}
}
@@ -1141,11 +1144,11 @@
}
}
- 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);
+ mIsQuitting = info.shouldQuit;
teardownNetwork();
@@ -1177,7 +1180,7 @@
private class DisconnectedState extends BaseState {
@Override
protected void enterState() {
- if (!mIsRunning) {
+ if (mIsQuitting) {
quitNow(); // Ignore all queued events; cleanup is complete.
}
@@ -1200,9 +1203,11 @@
}
break;
case EVENT_DISCONNECT_REQUESTED:
- mIsRunning = false;
+ if (((EventDisconnectRequestedInfo) msg.obj).shouldQuit) {
+ mIsQuitting = true;
- quitNow();
+ quitNow();
+ }
break;
default:
logUnhandledMessage(msg);
@@ -1284,10 +1289,11 @@
break;
case EVENT_DISCONNECT_REQUESTED:
+ EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj);
+ mIsQuitting = info.shouldQuit;
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.
@@ -1300,7 +1306,7 @@
case EVENT_SESSION_CLOSED:
mIkeSession = null;
- if (mIsRunning && mUnderlying != null) {
+ if (!mIsQuitting && mUnderlying != null) {
transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
} else {
teardownNetwork();
@@ -1391,7 +1397,7 @@
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 +1444,7 @@
mVcnContext.getVcnNetworkProvider()) {
@Override
public void unwanted() {
+ Slog.d(TAG, "NetworkAgent was unwanted");
teardownAsynchronously();
}
@@ -1471,7 +1478,7 @@
@NonNull IpSecTransform transform,
int direction) {
try {
- // TODO(b/180163196): Set underlying network of tunnel interface
+ tunnelIface.setUnderlyingNetwork(underlyingNetwork);
// Transforms do not need to be persisted; the IkeSession will keep them alive
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
@@ -1577,7 +1584,7 @@
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 +1689,7 @@
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();
@@ -1905,13 +1912,13 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- boolean isRunning() {
- return mIsRunning;
+ boolean isQuitting() {
+ return mIsQuitting;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- void setIsRunning(boolean isRunning) {
- mIsRunning = isRunning;
+ void setIsQuitting(boolean isQuitting) {
+ mIsQuitting = isQuitting;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1924,6 +1931,14 @@
mIkeSession = session;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(reason, shouldQuit));
+ }
+
private IkeSessionParams buildIkeParams() {
// TODO: Implement this once IkeSessionParams is persisted
return null;
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index bfeec01..a909695 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -67,9 +67,7 @@
mListeners.add(listener);
// Send listener all cached requests
- for (NetworkRequestEntry entry : mRequests.values()) {
- notifyListenerForEvent(listener, entry);
- }
+ resendAllRequests(listener);
}
/** Unregisters the specified listener from receiving future NetworkRequests. */
@@ -78,6 +76,14 @@
mListeners.remove(listener);
}
+ /** Sends all cached NetworkRequest(s) to the specified listener. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void resendAllRequests(@NonNull NetworkRequestListener listener) {
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ notifyListenerForEvent(listener, entry);
+ }
+ }
+
private void notifyListenerForEvent(
@NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) {
listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 69c21b9..69b2fb1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -143,11 +144,18 @@
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
+ verify(mIpSecSvc, times(2))
+ .setNetworkForTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
+ eq(TEST_UNDERLYING_NETWORK_RECORD_1.network),
+ any());
+
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
verify(mIpSecSvc)
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
+
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
@@ -290,4 +298,22 @@
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());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index 17ae19e..d07d2cf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -19,6 +19,8 @@
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 void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
}
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 9ea641f..5f27fab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -21,9 +21,12 @@
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.never;
import static org.mockito.Mockito.verify;
import android.net.IpSecManager;
@@ -54,7 +57,7 @@
}
@Test
- public void testEnterWhileNotRunningTriggersQuit() throws Exception {
+ public void testEnterWhileQuittingTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
new VcnGatewayConnection(
mVcnContext,
@@ -64,7 +67,7 @@
mGatewayStatusCallback,
mDeps);
- vgc.setIsRunning(false);
+ vgc.setIsQuitting(true);
vgc.transitionTo(vgc.mDisconnectedState);
mTestLooper.dispatchAll();
@@ -101,5 +104,18 @@
assertNull(mGatewayConnection.getCurrentState());
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
+ assertTrue(mGatewayConnection.isQuitting());
+ verify(mGatewayStatusCallback).onQuit();
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ verify(mGatewayStatusCallback, never()).onQuit();
+ // No safe mode timer changes expected.
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 7385204..661e03a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -18,6 +18,8 @@
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 @@
// Should do nothing; already tearing down.
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ assertTrue(mGatewayConnection.isQuitting());
}
@Test
public void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
}
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 5b0850b..85a0277 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -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 void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
}
+
+ @Test
+ public void testTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 9d33682..3dd710a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,6 +16,10 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
@@ -33,6 +37,7 @@
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -51,6 +56,11 @@
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0;
private static final int PROVIDER_ID = 5;
+ private static final int[][] TEST_CAPS =
+ new int[][] {
+ new int[] {NET_CAPABILITY_INTERNET, NET_CAPABILITY_MMS},
+ new int[] {NET_CAPABILITY_DUN}
+ };
private Context mContext;
private VcnContext mVcnContext;
@@ -91,13 +101,12 @@
mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
- for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ for (final int[] caps : TEST_CAPS) {
configBuilder.addGatewayConnectionConfig(
- VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(caps));
}
- configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
- mConfig = configBuilder.build();
+ mConfig = configBuilder.build();
mVcn =
new Vcn(
mVcnContext,
@@ -130,8 +139,7 @@
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
- startVcnGatewayWithCapabilities(
- requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+ startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -153,10 +161,19 @@
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability);
}
+ }
- // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
- // Expect one VcnGatewayConnection per capability.
- final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+ private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
+ for (final int[] caps : TEST_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, caps);
+ }
+ }
+
+ public Set<VcnGatewayConnection> startGatewaysAndGetGatewayConnections(
+ NetworkRequestListener requestListener) {
+ triggerVcnRequestListeners(requestListener);
+
+ final int numExpectedGateways = TEST_CAPS.length;
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertEquals(numExpectedGateways, gatewayConnections.size());
@@ -168,7 +185,16 @@
any(),
mGatewayStatusCallbackCaptor.capture());
- // Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
+ return gatewayConnections;
+ }
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ final Set<VcnGatewayConnection> gatewayConnections =
+ startGatewaysAndGetGatewayConnections(requestListener);
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
// all Gateways
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onEnteredSafeMode();
@@ -181,4 +207,31 @@
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnCallback).onEnteredSafeMode();
}
+
+ @Test
+ public void testGatewayQuit() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ final Set<VcnGatewayConnection> gatewayConnections =
+ new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
+
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onQuit();
+ mTestLooper.dispatchAll();
+
+ // Verify that the VCN requests the networkRequests be resent
+ assertEquals(1, mVcn.getVcnGatewayConnections().size());
+ verify(mVcnNetworkProvider).resendAllRequests(requestListener);
+
+ // Verify that the VcnGatewayConnection is restarted
+ triggerVcnRequestListeners(requestListener);
+ mTestLooper.dispatchAll();
+ assertEquals(2, mVcn.getVcnGatewayConnections().size());
+ verify(mDeps, times(gatewayConnections.size() + 1))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+ }
}