Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3b37546 authored by Benedict Wong's avatar Benedict Wong Committed by Automerger Merge Worker
Browse files

Merge "Implement DisconnectingState" am: 2cfb250d

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1547274

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I265cfe4d4cd4a2b8f13bd18e71baf7d18e144d03
parents cb24b18b 2cfb250d
Loading
Loading
Loading
Loading
+152 −14
Original line number Diff line number Diff line
@@ -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 {}

@@ -412,13 +414,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.
     *
@@ -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,13 +1048,34 @@ 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(
            return new VcnIkeSession(
                    vcnContext,
                    ikeSessionParams,
                    childSessionParams,
                    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,
@@ -972,5 +1083,32 @@ public class VcnGatewayConnection extends StateMachine {
                            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);
        }
    }
}
+71 −0
Original line number Diff line number Diff line
/*
 * 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());
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -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();
    }
}