Merge "Implement ConnectedState"
This commit is contained in:
@@ -214,7 +214,8 @@ public class Vcn extends Handler {
|
||||
}
|
||||
|
||||
/** Retrieves the network score for a VCN Network */
|
||||
private int getNetworkScore() {
|
||||
// Package visibility for use in VcnGatewayConnection
|
||||
static int getNetworkScore() {
|
||||
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
|
||||
// subGrp" value
|
||||
return 52;
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package com.android.server.vcn;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
|
||||
import static com.android.server.VcnManagementService.VDBG;
|
||||
|
||||
@@ -34,8 +37,10 @@ import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkAgent;
|
||||
import android.net.NetworkAgentConfig;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.annotations.PolicyDirection;
|
||||
import android.net.ipsec.ike.ChildSessionCallback;
|
||||
import android.net.ipsec.ike.ChildSessionConfiguration;
|
||||
@@ -47,10 +52,13 @@ import android.net.ipsec.ike.IkeSessionParams;
|
||||
import android.net.ipsec.ike.exceptions.IkeException;
|
||||
import android.net.ipsec.ike.exceptions.IkeProtocolException;
|
||||
import android.net.vcn.VcnGatewayConnectionConfig;
|
||||
import android.net.vcn.VcnTransportInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelUuid;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -64,6 +72,7 @@ import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -113,6 +122,9 @@ import java.util.concurrent.TimeUnit;
|
||||
public class VcnGatewayConnection extends StateMachine {
|
||||
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
|
||||
|
||||
private static final int[] MERGED_CAPABILITIES =
|
||||
new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
|
||||
|
||||
private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
|
||||
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
|
||||
|
||||
@@ -537,6 +549,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@Override
|
||||
public void onSelectedUnderlyingNetworkChanged(
|
||||
@Nullable UnderlyingNetworkRecord underlying) {
|
||||
// TODO(b/179091925): Move the delayed-message handling to BaseState
|
||||
|
||||
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
|
||||
// timeout.
|
||||
if (underlying == null) {
|
||||
@@ -921,6 +935,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
transitionTo(mDisconnectingState);
|
||||
break;
|
||||
case EVENT_SESSION_CLOSED:
|
||||
// Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
|
||||
// message may not be posted again. Defer to ensure immediate shutdown.
|
||||
deferMessage(msg);
|
||||
|
||||
transitionTo(mDisconnectingState);
|
||||
@@ -941,7 +957,108 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ConnectedStateBase extends ActiveBaseState {}
|
||||
private abstract class ConnectedStateBase extends ActiveBaseState {
|
||||
protected void updateNetworkAgent(
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull NetworkAgent agent,
|
||||
@NonNull ChildSessionConfiguration childConfig) {
|
||||
final NetworkCapabilities caps =
|
||||
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
|
||||
final LinkProperties lp =
|
||||
buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
|
||||
|
||||
agent.sendNetworkCapabilities(caps);
|
||||
agent.sendLinkProperties(lp);
|
||||
}
|
||||
|
||||
protected NetworkAgent buildNetworkAgent(
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull ChildSessionConfiguration childConfig) {
|
||||
final NetworkCapabilities caps =
|
||||
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
|
||||
final LinkProperties lp =
|
||||
buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
|
||||
|
||||
final NetworkAgent agent =
|
||||
new NetworkAgent(
|
||||
mVcnContext.getContext(),
|
||||
mVcnContext.getLooper(),
|
||||
TAG,
|
||||
caps,
|
||||
lp,
|
||||
Vcn.getNetworkScore(),
|
||||
new NetworkAgentConfig(),
|
||||
mVcnContext.getVcnNetworkProvider()) {
|
||||
@Override
|
||||
public void unwanted() {
|
||||
teardownAsynchronously();
|
||||
}
|
||||
};
|
||||
|
||||
agent.register();
|
||||
agent.markConnected();
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
protected void applyTransform(
|
||||
int token,
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull Network underlyingNetwork,
|
||||
@NonNull IpSecTransform transform,
|
||||
int direction) {
|
||||
try {
|
||||
// TODO: Set underlying network of tunnel interface
|
||||
|
||||
// Transforms do not need to be persisted; the IkeSession will keep them alive
|
||||
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
|
||||
} catch (IOException e) {
|
||||
Slog.d(TAG, "Transform application failed for network " + token, e);
|
||||
sessionLost(token, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupInterface(
|
||||
int token,
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull ChildSessionConfiguration childConfig) {
|
||||
setupInterface(token, tunnelIface, childConfig, null);
|
||||
}
|
||||
|
||||
protected void setupInterface(
|
||||
int token,
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull ChildSessionConfiguration childConfig,
|
||||
@Nullable ChildSessionConfiguration oldChildConfig) {
|
||||
try {
|
||||
final Set<LinkAddress> newAddrs =
|
||||
new ArraySet<>(childConfig.getInternalAddresses());
|
||||
final Set<LinkAddress> existingAddrs = new ArraySet<>();
|
||||
if (oldChildConfig != null) {
|
||||
existingAddrs.addAll(oldChildConfig.getInternalAddresses());
|
||||
}
|
||||
|
||||
final Set<LinkAddress> toAdd = new ArraySet<>();
|
||||
toAdd.addAll(newAddrs);
|
||||
toAdd.removeAll(existingAddrs);
|
||||
|
||||
final Set<LinkAddress> toRemove = new ArraySet<>();
|
||||
toRemove.addAll(existingAddrs);
|
||||
toRemove.removeAll(newAddrs);
|
||||
|
||||
for (LinkAddress address : toAdd) {
|
||||
tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
|
||||
}
|
||||
|
||||
for (LinkAddress address : toRemove) {
|
||||
tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
|
||||
sessionLost(token, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable state representing a VCN that has a functioning connection to the mobility anchor.
|
||||
@@ -951,7 +1068,89 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
*/
|
||||
class ConnectedState extends ConnectedStateBase {
|
||||
@Override
|
||||
protected void processStateMsg(Message msg) {}
|
||||
protected void enterState() throws Exception {
|
||||
// Successful connection, clear failed attempt counter
|
||||
mFailedAttempts = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processStateMsg(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_UNDERLYING_NETWORK_CHANGED:
|
||||
handleUnderlyingNetworkChanged(msg);
|
||||
break;
|
||||
case EVENT_SESSION_CLOSED:
|
||||
// Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
|
||||
// message may not be posted again. Defer to ensure immediate shutdown.
|
||||
deferMessage(msg);
|
||||
transitionTo(mDisconnectingState);
|
||||
break;
|
||||
case EVENT_SESSION_LOST:
|
||||
transitionTo(mDisconnectingState);
|
||||
break;
|
||||
case EVENT_TRANSFORM_CREATED:
|
||||
final EventTransformCreatedInfo transformCreatedInfo =
|
||||
(EventTransformCreatedInfo) msg.obj;
|
||||
|
||||
applyTransform(
|
||||
mCurrentToken,
|
||||
mTunnelIface,
|
||||
mUnderlying.network,
|
||||
transformCreatedInfo.transform,
|
||||
transformCreatedInfo.direction);
|
||||
break;
|
||||
case EVENT_SETUP_COMPLETED:
|
||||
mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
|
||||
|
||||
setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
|
||||
break;
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
|
||||
final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
|
||||
mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
|
||||
|
||||
if (mUnderlying == null) {
|
||||
// Ignored for now; a new network may be coming up. If none does, the delayed
|
||||
// NETWORK_LOST disconnect will be fired, and tear down the session + network.
|
||||
return;
|
||||
}
|
||||
|
||||
// mUnderlying assumed non-null, given check above.
|
||||
// If network changed, migrate. Otherwise, update any existing networkAgent.
|
||||
if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
|
||||
mIkeSession.setNetwork(mUnderlying.network);
|
||||
} else {
|
||||
// oldUnderlying is non-null & underlying network itself has not changed
|
||||
// (only network properties were changed).
|
||||
|
||||
// Network not yet set up, or child not yet connected.
|
||||
if (mNetworkAgent != null && mChildConfig != null) {
|
||||
// If only network properties changed and agent is active, update properties
|
||||
updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupInterfaceAndNetworkAgent(
|
||||
int token,
|
||||
@NonNull IpSecTunnelInterface tunnelIface,
|
||||
@NonNull ChildSessionConfiguration childConfig) {
|
||||
setupInterface(token, tunnelIface, childConfig);
|
||||
|
||||
if (mNetworkAgent == null) {
|
||||
mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
|
||||
} else {
|
||||
updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +1165,8 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static NetworkCapabilities buildNetworkCapabilities(
|
||||
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
|
||||
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
|
||||
@Nullable UnderlyingNetworkRecord underlying) {
|
||||
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
|
||||
|
||||
builder.addTransportType(TRANSPORT_CELLULAR);
|
||||
@@ -978,6 +1178,52 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
builder.addCapability(cap);
|
||||
}
|
||||
|
||||
if (underlying != null) {
|
||||
final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
|
||||
|
||||
// Mirror merged capabilities.
|
||||
for (int cap : MERGED_CAPABILITIES) {
|
||||
if (underlyingCaps.hasCapability(cap)) {
|
||||
builder.addCapability(cap);
|
||||
}
|
||||
}
|
||||
|
||||
// Set admin UIDs for ConnectivityDiagnostics use.
|
||||
final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
|
||||
Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
|
||||
|
||||
final int[] adminUids;
|
||||
if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
|
||||
&& 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
|
||||
underlyingAdminUids, underlyingCaps.getOwnerUid())) {
|
||||
adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
|
||||
adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
|
||||
Arrays.sort(adminUids);
|
||||
} else {
|
||||
adminUids = underlyingAdminUids;
|
||||
}
|
||||
builder.setAdministratorUids(adminUids);
|
||||
|
||||
// Set TransportInfo for SysUI use (never parcelled out of SystemServer).
|
||||
if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
|
||||
&& underlyingCaps.getTransportInfo() instanceof WifiInfo) {
|
||||
final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
|
||||
builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
|
||||
} else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
|
||||
&& underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
|
||||
final TelephonyNetworkSpecifier telNetSpecifier =
|
||||
(TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
|
||||
builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
|
||||
} else {
|
||||
Slog.wtf(
|
||||
TAG,
|
||||
"Unknown transport type or missing TransportInfo/NetworkSpecifier for"
|
||||
+ " non-null underlying network");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 android.net.IpSecManager.DIRECTION_IN;
|
||||
import static android.net.IpSecManager.DIRECTION_OUT;
|
||||
|
||||
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
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.ConnectedState */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
|
||||
private VcnIkeSession mIkeSession;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
|
||||
|
||||
mIkeSession = mGatewayConnection.buildIkeSession();
|
||||
mGatewayConnection.setIkeSession(mIkeSession);
|
||||
|
||||
mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnterStateCreatesNewIkeSession() throws Exception {
|
||||
verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
|
||||
mGatewayConnection
|
||||
.getUnderlyingNetworkTrackerCallback()
|
||||
.onSelectedUnderlyingNetworkChanged(null);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
|
||||
verify(mIkeSession, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewNetworkTriggersMigration() throws Exception {
|
||||
mGatewayConnection
|
||||
.getUnderlyingNetworkTrackerCallback()
|
||||
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
|
||||
verify(mIkeSession, never()).close();
|
||||
verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameNetworkDoesNotTriggerMigration() throws Exception {
|
||||
mGatewayConnection
|
||||
.getUnderlyingNetworkTrackerCallback()
|
||||
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatedTransformsAreApplied() throws Exception {
|
||||
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
|
||||
getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mIpSecSvc)
|
||||
.applyTunnelModeTransform(
|
||||
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
|
||||
}
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildSessionClosedTriggersDisconnect() throws Exception {
|
||||
getChildSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIkeSessionClosedTriggersDisconnect() throws Exception {
|
||||
getIkeSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
|
||||
verify(mIkeSession).close();
|
||||
}
|
||||
|
||||
// TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
|
||||
}
|
||||
@@ -16,20 +16,35 @@
|
||||
|
||||
package com.android.server.vcn;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.vcn.VcnGatewayConnectionConfigTest;
|
||||
import android.net.vcn.VcnTransportInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.Process;
|
||||
import android.os.test.TestLooper;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -42,6 +57,7 @@ import java.util.UUID;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class VcnGatewayConnectionTest {
|
||||
private static final int TEST_UID = Process.myUid();
|
||||
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
|
||||
private static final int TEST_SIM_SLOT_INDEX = 1;
|
||||
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
|
||||
@@ -61,22 +77,59 @@ public class VcnGatewayConnectionTest {
|
||||
@NonNull private final TestLooper mTestLooper;
|
||||
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
|
||||
@NonNull private final VcnGatewayConnection.Dependencies mDeps;
|
||||
@NonNull private final WifiInfo mWifiInfo;
|
||||
|
||||
public VcnGatewayConnectionTest() {
|
||||
mContext = mock(Context.class);
|
||||
mTestLooper = new TestLooper();
|
||||
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
|
||||
mDeps = mock(VcnGatewayConnection.Dependencies.class);
|
||||
mWifiInfo = mock(WifiInfo.class);
|
||||
}
|
||||
|
||||
private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
|
||||
final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
|
||||
underlyingCaps.addTransportType(transportType);
|
||||
underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
|
||||
underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
|
||||
|
||||
if (transportType == TRANSPORT_WIFI) {
|
||||
underlyingCaps.setTransportInfo(mWifiInfo);
|
||||
underlyingCaps.setOwnerUid(TEST_UID);
|
||||
} else if (transportType == TRANSPORT_CELLULAR) {
|
||||
underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
|
||||
underlyingCaps.setNetworkSpecifier(
|
||||
new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
|
||||
}
|
||||
|
||||
UnderlyingNetworkRecord record =
|
||||
new UnderlyingNetworkRecord(
|
||||
new Network(0), underlyingCaps, new LinkProperties(), false);
|
||||
final NetworkCapabilities vcnCaps =
|
||||
VcnGatewayConnection.buildNetworkCapabilities(
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig(), record);
|
||||
|
||||
assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
|
||||
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
|
||||
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
|
||||
assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
|
||||
assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
|
||||
|
||||
final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
|
||||
if (transportType == TRANSPORT_WIFI) {
|
||||
assertEquals(mWifiInfo, info.getWifiInfo());
|
||||
} else if (transportType == TRANSPORT_CELLULAR) {
|
||||
assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildNetworkCapabilities() throws Exception {
|
||||
final NetworkCapabilities caps =
|
||||
VcnGatewayConnection.buildNetworkCapabilities(
|
||||
VcnGatewayConnectionConfigTest.buildTestConfig());
|
||||
public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
|
||||
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
|
||||
}
|
||||
|
||||
for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
|
||||
assertTrue(caps.hasCapability(exposedCapability));
|
||||
}
|
||||
@Test
|
||||
public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
|
||||
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.net.IpSecConfig;
|
||||
import android.net.IpSecManager;
|
||||
import android.net.IpSecTransform;
|
||||
import android.net.IpSecTunnelInterfaceResponse;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
@@ -48,7 +50,10 @@ 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 int TEST_IPSEC_SPI_VALUE = 0x1234;
|
||||
protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
|
||||
protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
|
||||
protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
|
||||
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
|
||||
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
|
||||
new UnderlyingNetworkRecord(
|
||||
@@ -112,6 +117,10 @@ public class VcnGatewayConnectionTestBase {
|
||||
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
|
||||
}
|
||||
|
||||
protected IpSecTransform makeDummyIpSecTransform() throws Exception {
|
||||
return new IpSecTransform(mContext, new IpSecConfig());
|
||||
}
|
||||
|
||||
protected IkeSessionCallback getIkeSessionCallback() {
|
||||
ArgumentCaptor<IkeSessionCallback> captor =
|
||||
ArgumentCaptor.forClass(IkeSessionCallback.class);
|
||||
|
||||
Reference in New Issue
Block a user