Merge "Implement DisconnectingState" am: 2cfb250d6d
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1547274 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I265cfe4d4cd4a2b8f13bd18e71baf7d18e144d03
This commit is contained in:
@@ -122,7 +122,9 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
private static final int TOKEN_ALL = Integer.MIN_VALUE;
|
private static final int TOKEN_ALL = Integer.MIN_VALUE;
|
||||||
|
|
||||||
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
|
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 {}
|
private interface EventInfo {}
|
||||||
|
|
||||||
@@ -412,13 +414,6 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
*/
|
*/
|
||||||
private int mCurrentToken = -1;
|
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.
|
* The number of unsuccessful attempts since the last successful connection.
|
||||||
*
|
*
|
||||||
@@ -440,7 +435,7 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
|
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
|
||||||
* Migrating states, null otherwise.
|
* Migrating states, null otherwise.
|
||||||
*/
|
*/
|
||||||
private IkeSession mIkeSession;
|
private VcnIkeSession mIkeSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last known child configuration.
|
* The last known child configuration.
|
||||||
@@ -774,7 +769,70 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
*/
|
*/
|
||||||
private class DisconnectingState extends ActiveBaseState {
|
private class DisconnectingState extends ActiveBaseState {
|
||||||
@Override
|
@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;
|
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 */
|
/** External dependencies used by VcnGatewayConnection, for injection in tests */
|
||||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||||
public static class Dependencies {
|
public static class Dependencies {
|
||||||
@@ -958,19 +1048,67 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Builds a new IkeSession. */
|
/** Builds a new IkeSession. */
|
||||||
public IkeSession newIkeSession(
|
public VcnIkeSession newIkeSession(
|
||||||
VcnContext vcnContext,
|
VcnContext vcnContext,
|
||||||
IkeSessionParams ikeSessionParams,
|
IkeSessionParams ikeSessionParams,
|
||||||
ChildSessionParams childSessionParams,
|
ChildSessionParams childSessionParams,
|
||||||
IkeSessionCallback ikeSessionCallback,
|
IkeSessionCallback ikeSessionCallback,
|
||||||
ChildSessionCallback childSessionCallback) {
|
ChildSessionCallback childSessionCallback) {
|
||||||
return new IkeSession(
|
return new VcnIkeSession(
|
||||||
vcnContext.getContext(),
|
vcnContext,
|
||||||
ikeSessionParams,
|
ikeSessionParams,
|
||||||
childSessionParams,
|
childSessionParams,
|
||||||
new HandlerExecutor(new Handler(vcnContext.getLooper())),
|
|
||||||
ikeSessionCallback,
|
ikeSessionCallback,
|
||||||
childSessionCallback);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,11 +17,13 @@
|
|||||||
package com.android.server.vcn;
|
package com.android.server.vcn;
|
||||||
|
|
||||||
import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
|
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 com.android.server.vcn.VcnTestUtils.setupIpSecManager;
|
||||||
|
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -30,6 +32,7 @@ import android.net.IpSecTunnelInterfaceResponse;
|
|||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.ipsec.ike.IkeSessionCallback;
|
||||||
import android.net.vcn.VcnGatewayConnectionConfig;
|
import android.net.vcn.VcnGatewayConnectionConfig;
|
||||||
import android.net.vcn.VcnGatewayConnectionConfigTest;
|
import android.net.vcn.VcnGatewayConnectionConfigTest;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
@@ -38,6 +41,7 @@ import android.os.test.TestLooper;
|
|||||||
import com.android.server.IpSecService;
|
import com.android.server.IpSecService;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -68,6 +72,7 @@ public class VcnGatewayConnectionTestBase {
|
|||||||
|
|
||||||
@NonNull protected final IpSecService mIpSecSvc;
|
@NonNull protected final IpSecService mIpSecSvc;
|
||||||
|
|
||||||
|
protected VcnIkeSession mMockIkeSession;
|
||||||
protected VcnGatewayConnection mGatewayConnection;
|
protected VcnGatewayConnection mGatewayConnection;
|
||||||
|
|
||||||
public VcnGatewayConnectionTestBase() {
|
public VcnGatewayConnectionTestBase() {
|
||||||
@@ -100,6 +105,16 @@ public class VcnGatewayConnectionTestBase {
|
|||||||
TEST_IPSEC_TUNNEL_IFACE);
|
TEST_IPSEC_TUNNEL_IFACE);
|
||||||
doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
|
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);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user