Merge "Implement Disconnected state" am: 4f27883485

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

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I712e744040e575cf37581ffce130757ef2a4ac97
This commit is contained in:
Benedict Wong
2021-01-25 23:02:14 +00:00
committed by Automerger Merge Worker
6 changed files with 328 additions and 19 deletions

View File

@@ -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,

View File

@@ -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. */

View File

@@ -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)

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View 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;
}
}