summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Benedict Wong <benedictwong@google.com> 2021-01-27 21:53:15 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2021-01-27 21:53:15 +0000
commitef9733a9c91b8f38fc7767f7ea0a8ea3ae65ece6 (patch)
treecda657f4adab08011ca51b2f51ef61a48bf3ceed
parent16f762211ecb3cdf3ff079467df2e622b0513005 (diff)
parentd3b37546db7d3a11f67e76584370f3a7fec218e3 (diff)
Merge "Implement DisconnectingState" am: 2cfb250d6d am: d3b37546db
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1547274 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Iddeef57b3711591bd436f041ac0ce08971f93a26
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java166
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java71
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java15
3 files changed, 238 insertions, 14 deletions
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 0fa97a26e67c..8805fa2f4dbb 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -122,7 +122,9 @@ public class VcnGatewayConnection extends StateMachine {
private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -413,13 +415,6 @@ public class VcnGatewayConnection extends StateMachine {
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -440,7 +435,7 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -774,7 +769,70 @@ public class VcnGatewayConnection extends StateMachine {
*/
private class DisconnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
/**
@@ -946,6 +1004,38 @@ public class VcnGatewayConnection extends StateMachine {
mIsRunning = isRunning;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -958,19 +1048,67 @@ public class VcnGatewayConnection extends StateMachine {
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 000000000000..d0fec55a6827
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1725dd983115..346785907fcf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -17,11 +17,13 @@
package com.android.server.vcn;
import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
@@ -30,6 +32,7 @@ import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionCallback;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -38,6 +41,7 @@ import android.os.test.TestLooper;
import com.android.server.IpSecService;
import org.junit.Before;
+import org.mockito.ArgumentCaptor;
import java.util.UUID;
@@ -68,6 +72,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final IpSecService mIpSecSvc;
+ protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
public VcnGatewayConnectionTestBase() {
@@ -100,6 +105,16 @@ public class VcnGatewayConnectionTestBase {
TEST_IPSEC_TUNNEL_IFACE);
doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
}
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
}