Merge changes Icb59b15d,I6fc6a266,I5cc340e5,I94db52a8

* changes:
  Request Test Networks for VCNs when in Test Mode.
  Specify if a VCN is in 'test-mode' in VcnContext.
  Create test-mode for VcnConfig.
  Require MOBIKE for IkeSessionParams in VCN configs.
This commit is contained in:
Treehugger Robot
2021-05-12 16:21:25 +00:00
committed by Gerrit Code Review
10 changed files with 202 additions and 19 deletions

View File

@@ -52,12 +52,17 @@ public final class VcnConfig implements Parcelable {
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile";
private final boolean mIsTestModeProfile;
private VcnConfig(
@NonNull String packageName,
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs,
boolean isTestModeProfile) {
mPackageName = packageName;
mGatewayConnectionConfigs =
Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
mIsTestModeProfile = isTestModeProfile;
validate();
}
@@ -77,6 +82,7 @@ public final class VcnConfig implements Parcelable {
new ArraySet<>(
PersistableBundleUtils.toList(
gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new));
mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY);
validate();
}
@@ -103,6 +109,15 @@ public final class VcnConfig implements Parcelable {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
}
/**
* Returns whether or not this VcnConfig is restricted to test networks.
*
* @hide
*/
public boolean isTestModeProfile() {
return mIsTestModeProfile;
}
/**
* Serializes this object to a PersistableBundle.
*
@@ -119,13 +134,14 @@ public final class VcnConfig implements Parcelable {
new ArrayList<>(mGatewayConnectionConfigs),
VcnGatewayConnectionConfig::toPersistableBundle);
result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle);
result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile);
return result;
}
@Override
public int hashCode() {
return Objects.hash(mPackageName, mGatewayConnectionConfigs);
return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
@Override
@@ -136,7 +152,8 @@ public final class VcnConfig implements Parcelable {
final VcnConfig rhs = (VcnConfig) other;
return mPackageName.equals(rhs.mPackageName)
&& mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
&& mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs)
&& mIsTestModeProfile == rhs.mIsTestModeProfile;
}
// Parcelable methods
@@ -172,6 +189,8 @@ public final class VcnConfig implements Parcelable {
@NonNull
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
private boolean mIsTestModeProfile = false;
public Builder(@NonNull Context context) {
Objects.requireNonNull(context, "context was null");
@@ -206,6 +225,22 @@ public final class VcnConfig implements Parcelable {
return this;
}
/**
* Restricts this VcnConfig to matching with test networks (only).
*
* <p>This method is for testing only, and must not be used by apps. Calling {@link
* VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage
* is enabled will require the MANAGE_TEST_NETWORKS permission.
*
* @return this {@link Builder} instance, for chaining
* @hide
*/
@NonNull
public Builder setIsTestModeProfile() {
mIsTestModeProfile = true;
return this;
}
/**
* Builds and validates the VcnConfig.
*
@@ -213,7 +248,7 @@ public final class VcnConfig implements Parcelable {
*/
@NonNull
public VcnConfig build() {
return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile);
}
}
}

View File

@@ -15,6 +15,8 @@
*/
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -433,6 +435,8 @@ public final class VcnGatewayConnectionConfig {
* distinguish between VcnGatewayConnectionConfigs configured on a single {@link
* VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
* @param tunnelConnectionParams the IKE tunnel connection configuration
* @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not
* configured to support MOBIKE
* @see IkeTunnelConnectionParams
* @see VcnManager.VcnStatusCallback#onGatewayConnectionError
*/
@@ -441,6 +445,10 @@ public final class VcnGatewayConnectionConfig {
@NonNull IkeTunnelConnectionParams tunnelConnectionParams) {
Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) {
throw new IllegalArgumentException(
"MOBIKE must be configured for the provided IkeSessionParams");
}
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;

View File

@@ -167,7 +167,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private final VcnNetworkProvider mNetworkProvider;
@NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
@NonNull private final VcnContext mVcnContext;
@NonNull private final BroadcastReceiver mPkgChangeReceiver;
@NonNull
@@ -212,7 +211,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mContext, mLooper, mTelephonySubscriptionTrackerCb);
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider);
mPkgChangeReceiver = new BroadcastReceiver() {
@Override
@@ -336,8 +334,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
public VcnContext newVcnContext(
@NonNull Context context,
@NonNull Looper looper,
@NonNull VcnNetworkProvider vcnNetworkProvider) {
return new VcnContext(context, looper, vcnNetworkProvider);
@NonNull VcnNetworkProvider vcnNetworkProvider,
boolean getIsInTestMode) {
return new VcnContext(context, looper, vcnNetworkProvider, getIsInTestMode);
}
/** Creates a new Vcn instance using the provided configuration */
@@ -421,6 +420,14 @@ public class VcnManagementService extends IVcnManagementService.Stub {
"Carrier privilege required for subscription group to set VCN Config");
}
private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) {
if (vcnConfig.isTestModeProfile()) {
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_TEST_NETWORKS,
"Test-mode require the MANAGE_TEST_NETWORKS permission");
}
}
private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
/**
* Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
@@ -544,8 +551,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
final VcnContext vcnContext =
mDeps.newVcnContext(
mContext, mLooper, mNetworkProvider, config.isTestModeProfile());
final Vcn newInstance =
mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -589,6 +599,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mContext.getSystemService(AppOpsManager.class)
.checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
enforceManageTestNetworksForTestMode(config);
enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
Binder.withCleanCallingIdentity(() -> {

View File

@@ -158,8 +158,15 @@ public class UnderlyingNetworkTracker {
* carrier owned networks may be selected, as the request specifies only subIds in the VCN's
* subscription group, while the VCN networks are excluded by virtue of not having subIds set on
* the VCN-exposed networks.
*
* <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return
* a NetworkRequest that only matches Test Networks.
*/
private NetworkRequest getRouteSelectionRequest() {
if (mVcnContext.isInTestMode()) {
return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
}
return getBaseNetworkRequestBuilder()
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
@@ -210,6 +217,16 @@ public class UnderlyingNetworkTracker {
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
/** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.setSubscriptionIds(subIds)
.build();
}
/**
* Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
*

View File

@@ -31,14 +31,17 @@ public class VcnContext {
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
private final boolean mIsInTestMode;
public VcnContext(
@NonNull Context context,
@NonNull Looper looper,
@NonNull VcnNetworkProvider vcnNetworkProvider) {
@NonNull VcnNetworkProvider vcnNetworkProvider,
boolean isInTestMode) {
mContext = Objects.requireNonNull(context, "Missing context");
mLooper = Objects.requireNonNull(looper, "Missing looper");
mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
mIsInTestMode = isInTestMode;
}
@NonNull
@@ -56,6 +59,10 @@ public class VcnContext {
return mVcnNetworkProvider;
}
public boolean isInTestMode() {
return mIsInTestMode;
}
/**
* Verifies that the caller is running on the VcnContext Thread.
*

View File

@@ -16,13 +16,17 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
import androidx.test.filters.SmallTest;
@@ -119,6 +123,21 @@ public class VcnGatewayConnectionConfigTest {
}
}
@Test
public void testBuilderRequiresMobikeEnabled() {
try {
final IkeSessionParams ikeParams =
IkeSessionParamsUtilsTest.createBuilderMinimum()
.removeIkeOption(IKE_OPTION_MOBIKE)
.build();
final IkeTunnelConnectionParams tunnelParams =
TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams);
fail("Expected exception due to MOBIKE not enabled");
} catch (IllegalArgumentException e) {
}
}
@Test
public void testBuilderRequiresNonEmptyExposedCaps() {
try {

View File

@@ -52,8 +52,8 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
// Package private for use in EncryptedTunnelParamsUtilsTest
static IkeSessionParams.Builder createBuilderMinimum() {
// Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest
public static IkeSessionParams.Builder createBuilderMinimum() {
final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100");
// TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated.
@@ -63,6 +63,7 @@ public class IkeSessionParamsUtilsTest {
.setLocalIdentification(new IkeFqdnIdentification("client.test.android.net"))
.setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net"))
.addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
.setAuthPsk("psk".getBytes());
}

View File

@@ -18,6 +18,7 @@ package android.net.vcn.persistablebundleutils;
import static org.junit.Assert.assertEquals;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import androidx.test.filters.SmallTest;
@@ -31,9 +32,13 @@ import org.junit.runner.RunWith;
public class TunnelConnectionParamsUtilsTest {
// Public for use in VcnGatewayConnectionConfigTest
public static IkeTunnelConnectionParams buildTestParams() {
return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build());
}
// Public for use in VcnGatewayConnectionConfigTest
public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) {
return new IkeTunnelConnectionParams(
IkeSessionParamsUtilsTest.createBuilderMinimum().build(),
TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build());
}
@Test

View File

@@ -37,6 +37,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -66,6 +67,7 @@ import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.os.IBinder;
@@ -197,7 +199,8 @@ public class VcnManagementServiceTest {
.newVcnContext(
eq(mMockContext),
eq(mTestLooper.getLooper()),
any(VcnNetworkProvider.class));
any(VcnNetworkProvider.class),
anyBoolean());
doReturn(mSubscriptionTracker)
.when(mMockDeps)
.newTelephonySubscriptionTracker(
@@ -370,6 +373,12 @@ public class VcnManagementServiceTest {
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
.newVcnContext(
eq(mMockContext),
eq(mTestLooper.getLooper()),
any(VcnNetworkProvider.class),
anyBoolean());
verify(mMockDeps)
.newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@@ -527,6 +536,28 @@ public class VcnManagementServiceTest {
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
public void testSetVcnConfigTestModeRequiresPermission() throws Exception {
doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS"))
.when(mMockContext)
.enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any());
final VcnConfig vcnConfig =
new VcnConfig.Builder(mMockContext)
.addGatewayConnectionConfig(
VcnGatewayConnectionConfigTest.buildTestConfig())
.setIsTestModeProfile()
.build();
try {
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
fail("Expected exception due to using test-mode without permission");
} catch (SecurityException e) {
verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@Test
public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -112,8 +113,14 @@ public class UnderlyingNetworkTrackerTest {
MockitoAnnotations.initMocks(this);
mTestLooper = new TestLooper();
mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
mVcnContext =
spy(
new VcnContext(
mContext,
mTestLooper.getLooper(),
mVcnNetworkProvider,
false /* isInTestMode */));
resetVcnContext();
setupSystemService(
mContext,
@@ -132,6 +139,11 @@ public class UnderlyingNetworkTrackerTest {
mNetworkTrackerCb);
}
private void resetVcnContext() {
reset(mVcnContext);
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
}
private static LinkProperties getLinkPropertiesWithName(String iface) {
LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(iface);
@@ -149,7 +161,29 @@ public class UnderlyingNetworkTrackerTest {
verifyNetworkRequestsRegistered(INITIAL_SUB_IDS);
}
@Test
public void testNetworkCallbacksRegisteredOnStartupForTestMode() {
resetVcnContext();
when(mVcnContext.isInTestMode()).thenReturn(true);
reset(mConnectivityManager);
mUnderlyingNetworkTracker =
new UnderlyingNetworkTracker(
mVcnContext,
SUB_GROUP,
mSubscriptionSnapshot,
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
mNetworkTrackerCb);
verifyNetworkRequestsRegistered(INITIAL_SUB_IDS, true /* expectTestMode */);
}
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verifyNetworkRequestsRegistered(expectedSubIds, false /* expectTestMode */);
}
private void verifyNetworkRequestsRegistered(
Set<Integer> expectedSubIds, boolean expectTestMode) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getWifiRequest(expectedSubIds)),
@@ -162,10 +196,16 @@ public class UnderlyingNetworkTrackerTest {
any(NetworkBringupCallback.class), any());
}
final NetworkRequest expectedRouteSelectionRequest =
expectTestMode
? getTestNetworkRequest(expectedSubIds)
: getRouteSelectionRequest(expectedSubIds);
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest(expectedSubIds)),
any(RouteSelectionCallback.class), any());
eq(expectedRouteSelectionRequest),
any(RouteSelectionCallback.class),
any());
}
@Test
@@ -204,6 +244,15 @@ public class UnderlyingNetworkTrackerTest {
return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
}
private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.setSubscriptionIds(netCapsSubIds)
.build();
}
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()