Merge "Implement Disconnected state"
This commit is contained in:
@@ -24,6 +24,9 @@ import android.net.NetworkCapabilities;
|
||||
import android.os.Handler;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,8 @@ public class UnderlyingNetworkTracker extends Handler {
|
||||
@NonNull public final LinkProperties linkProperties;
|
||||
public final boolean blocked;
|
||||
|
||||
private UnderlyingNetworkRecord(
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
UnderlyingNetworkRecord(
|
||||
@NonNull Network network,
|
||||
@NonNull NetworkCapabilities networkCapabilities,
|
||||
@NonNull LinkProperties linkProperties,
|
||||
|
||||
@@ -360,11 +360,25 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
*/
|
||||
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
|
||||
|
||||
@NonNull private final DisconnectedState mDisconnectedState = new DisconnectedState();
|
||||
@NonNull private final DisconnectingState mDisconnectingState = new DisconnectingState();
|
||||
@NonNull private final ConnectingState mConnectingState = new ConnectingState();
|
||||
@NonNull private final ConnectedState mConnectedState = new ConnectedState();
|
||||
@NonNull private final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
final DisconnectedState mDisconnectedState = new DisconnectedState();
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
final DisconnectingState mDisconnectingState = new DisconnectingState();
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
final ConnectingState mConnectingState = new ConnectingState();
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
final ConnectedState mConnectedState = new ConnectedState();
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
|
||||
|
||||
@NonNull private final VcnContext mVcnContext;
|
||||
@NonNull private final ParcelUuid mSubscriptionGroup;
|
||||
@@ -455,7 +469,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
|
||||
}
|
||||
|
||||
private VcnGatewayConnection(
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
VcnGatewayConnection(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull VcnGatewayConnectionConfig connectionConfig,
|
||||
@@ -508,7 +523,6 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
EVENT_DISCONNECT_REQUESTED,
|
||||
TOKEN_ALL,
|
||||
new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
|
||||
quit();
|
||||
|
||||
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
|
||||
// is also called asynchronously when a NetworkAgent becomes unwanted
|
||||
@@ -667,6 +681,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
|
||||
protected void handleDisconnectRequested(String msg) {
|
||||
Slog.v(TAG, "Tearing down. Cause: " + msg);
|
||||
mIsRunning = false;
|
||||
|
||||
teardownNetwork();
|
||||
teardownIke();
|
||||
|
||||
@@ -697,7 +713,37 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
*/
|
||||
private class DisconnectedState extends BaseState {
|
||||
@Override
|
||||
protected void processStateMsg(Message msg) {}
|
||||
protected void enterState() {
|
||||
if (!mIsRunning) {
|
||||
quitNow(); // Ignore all queued events; cleanup is complete.
|
||||
}
|
||||
|
||||
if (mIkeSession != null || mNetworkAgent != null) {
|
||||
Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processStateMsg(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_UNDERLYING_NETWORK_CHANGED:
|
||||
// First network found; start tunnel
|
||||
mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
|
||||
|
||||
if (mUnderlying != null) {
|
||||
transitionTo(mConnectingState);
|
||||
}
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
mIsRunning = false;
|
||||
|
||||
quitNow();
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ActiveBaseState extends BaseState {
|
||||
@@ -893,7 +939,32 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
/** External dependencies used by VcnGatewayConnection, for injection in tests. */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
|
||||
return mUnderlyingNetworkTrackerCallback;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
UnderlyingNetworkRecord getUnderlyingNetwork() {
|
||||
return mUnderlying;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) {
|
||||
mUnderlying = record;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
boolean isRunning() {
|
||||
return mIsRunning;
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
void setIsRunning(boolean isRunning) {
|
||||
mIsRunning = isRunning;
|
||||
}
|
||||
|
||||
/** External dependencies used by VcnGatewayConnection, for injection in tests */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public static class Dependencies {
|
||||
/** Builds a new UnderlyingNetworkTracker. */
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server;
|
||||
|
||||
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
|
||||
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -138,11 +139,16 @@ public class VcnManagementServiceTest {
|
||||
private final IBinder mMockIBinder = mock(IBinder.class);
|
||||
|
||||
public VcnManagementServiceTest() throws Exception {
|
||||
setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
|
||||
setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
|
||||
setupSystemService(
|
||||
mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
|
||||
setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
|
||||
mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
|
||||
setupSystemService(
|
||||
mMockContext, mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
|
||||
setupSystemService(
|
||||
mMockContext,
|
||||
mSubMgr,
|
||||
Context.TELEPHONY_SUBSCRIPTION_SERVICE,
|
||||
SubscriptionManager.class);
|
||||
setupSystemService(mMockContext, mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
|
||||
|
||||
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
|
||||
|
||||
@@ -186,11 +192,6 @@ public class VcnManagementServiceTest {
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
private void setupSystemService(Object service, String name, Class<?> serviceClass) {
|
||||
doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
|
||||
doReturn(service).when(mMockContext).getSystemService(name);
|
||||
}
|
||||
|
||||
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
|
||||
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
|
||||
.when(mSubMgr)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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;
|
||||
|
||||
/** Tests for VcnGatewayConnection.DisconnectedState */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
|
||||
final VcnGatewayConnection vgc =
|
||||
new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
|
||||
|
||||
vgc.setIsRunning(false);
|
||||
vgc.transitionTo(vgc.mDisconnectedState);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertNull(vgc.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetworkChangesTriggerStateTransitions() throws Exception {
|
||||
mGatewayConnection
|
||||
.getUnderlyingNetworkTrackerCallback()
|
||||
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
|
||||
mGatewayConnection
|
||||
.getUnderlyingNetworkTrackerCallback()
|
||||
.onSelectedUnderlyingNetworkChanged(null);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTeardown() throws Exception {
|
||||
mGatewayConnection.teardownAsynchronously();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertNull(mGatewayConnection.getCurrentState());
|
||||
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
|
||||
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 android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.net.IpSecManager;
|
||||
import android.net.IpSecTunnelInterfaceResponse;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.vcn.VcnGatewayConnectionConfig;
|
||||
import android.net.vcn.VcnGatewayConnectionConfigTest;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.test.TestLooper;
|
||||
|
||||
import com.android.server.IpSecService;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class VcnGatewayConnectionTestBase {
|
||||
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
|
||||
protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
|
||||
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
|
||||
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
|
||||
new UnderlyingNetworkRecord(
|
||||
new Network(0),
|
||||
new NetworkCapabilities(),
|
||||
new LinkProperties(),
|
||||
false /* blocked */);
|
||||
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
|
||||
new UnderlyingNetworkRecord(
|
||||
new Network(1),
|
||||
new NetworkCapabilities(),
|
||||
new LinkProperties(),
|
||||
false /* blocked */);
|
||||
|
||||
@NonNull protected final Context mContext;
|
||||
@NonNull protected final TestLooper mTestLooper;
|
||||
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
|
||||
@NonNull protected final VcnContext mVcnContext;
|
||||
@NonNull protected final VcnGatewayConnectionConfig mConfig;
|
||||
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
|
||||
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
|
||||
|
||||
@NonNull protected final IpSecService mIpSecSvc;
|
||||
|
||||
protected VcnGatewayConnection mGatewayConnection;
|
||||
|
||||
public VcnGatewayConnectionTestBase() {
|
||||
mContext = mock(Context.class);
|
||||
mTestLooper = new TestLooper();
|
||||
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
|
||||
mVcnContext = mock(VcnContext.class);
|
||||
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
|
||||
mDeps = mock(VcnGatewayConnection.Dependencies.class);
|
||||
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
|
||||
|
||||
mIpSecSvc = mock(IpSecService.class);
|
||||
setupIpSecManager(mContext, mIpSecSvc);
|
||||
|
||||
doReturn(mContext).when(mVcnContext).getContext();
|
||||
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
|
||||
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
|
||||
|
||||
doReturn(mUnderlyingNetworkTracker)
|
||||
.when(mDeps)
|
||||
.newUnderlyingNetworkTracker(any(), any(), any());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
IpSecTunnelInterfaceResponse resp =
|
||||
new IpSecTunnelInterfaceResponse(
|
||||
IpSecManager.Status.OK,
|
||||
TEST_IPSEC_TUNNEL_RESOURCE_ID,
|
||||
TEST_IPSEC_TUNNEL_IFACE);
|
||||
doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
|
||||
|
||||
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
|
||||
}
|
||||
}
|
||||
44
tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
Normal file
44
tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.IpSecManager;
|
||||
|
||||
import com.android.server.IpSecService;
|
||||
|
||||
public class VcnTestUtils {
|
||||
/** Mock system services by directly mocking the *Manager interface. */
|
||||
public static void setupSystemService(
|
||||
Context mockContext, Object service, String name, Class<?> serviceClass) {
|
||||
doReturn(name).when(mockContext).getSystemServiceName(serviceClass);
|
||||
doReturn(service).when(mockContext).getSystemService(name);
|
||||
}
|
||||
|
||||
/** Mock IpSecService by mocking the underlying service binder. */
|
||||
public static IpSecManager setupIpSecManager(Context mockContext, IpSecService service) {
|
||||
doReturn(Context.IPSEC_SERVICE).when(mockContext).getSystemServiceName(IpSecManager.class);
|
||||
|
||||
final IpSecManager ipSecMgr = new IpSecManager(mockContext, service);
|
||||
doReturn(ipSecMgr).when(mockContext).getSystemService(Context.IPSEC_SERVICE);
|
||||
|
||||
// Return to ensure this doesn't get reaped.
|
||||
return ipSecMgr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user