Loading core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java 0 → 100644 +291 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.net.vcn; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.PersistableBundle; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.server.vcn.util.PersistableBundleUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Objects; import java.util.Set; // TODO: Add documents /** @hide */ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority { private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds"; @NonNull private final Set<String> mAllowedNetworkPlmnIds; private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds"; @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; private static final String ALLOW_ROAMING_KEY = "mAllowRoaming"; private final boolean mAllowRoaming; private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic"; private final boolean mRequireOpportunistic; private VcnCellUnderlyingNetworkPriority( int networkQuality, boolean allowMetered, Set<String> allowedNetworkPlmnIds, Set<Integer> allowedSpecificCarrierIds, boolean allowRoaming, boolean requireOpportunistic) { super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered); mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds); mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds); mAllowRoaming = allowRoaming; mRequireOpportunistic = requireOpportunistic; validate(); } /** @hide */ @Override protected void validate() { super.validate(); validatePlmnIds(mAllowedNetworkPlmnIds); Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null"); } private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) { Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null"); // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal // digits. for (String id : allowedNetworkPlmnIds) { if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) { continue; } else { throw new IllegalArgumentException("Found invalid PLMN ID: " + id); } } } /** @hide */ @NonNull @VisibleForTesting(visibility = Visibility.PROTECTED) public static VcnCellUnderlyingNetworkPriority fromPersistableBundle( @NonNull PersistableBundle in) { Objects.requireNonNull(in, "PersistableBundle is null"); final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); final PersistableBundle plmnIdsBundle = in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY); Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null"); final Set<String> allowedNetworkPlmnIds = new ArraySet<String>( PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER)); final PersistableBundle specificCarrierIdsBundle = in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY); Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null"); final Set<Integer> allowedSpecificCarrierIds = new ArraySet<Integer>( PersistableBundleUtils.toList( specificCarrierIdsBundle, INTEGER_DESERIALIZER)); final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY); final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY); return new VcnCellUnderlyingNetworkPriority( networkQuality, allowMetered, allowedNetworkPlmnIds, allowedSpecificCarrierIds, allowRoaming, requireOpportunistic); } /** @hide */ @Override @NonNull @VisibleForTesting(visibility = Visibility.PROTECTED) public PersistableBundle toPersistableBundle() { final PersistableBundle result = super.toPersistableBundle(); final PersistableBundle plmnIdsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER); result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle); final PersistableBundle specificCarrierIdsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER); result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle); result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming); result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic); return result; } /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */ @NonNull public Set<String> getAllowedPlmnIds() { return Collections.unmodifiableSet(mAllowedNetworkPlmnIds); } /** * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is * acceptable. */ @NonNull public Set<Integer> getAllowedSpecificCarrierIds() { return Collections.unmodifiableSet(mAllowedSpecificCarrierIds); } /** Return if roaming is allowed. */ public boolean allowRoaming() { return mAllowRoaming; } /** Return if requiring an opportunistic network. */ public boolean requireOpportunistic() { return mRequireOpportunistic; } @Override public int hashCode() { return Objects.hash( super.hashCode(), mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, mAllowRoaming, mRequireOpportunistic); } @Override public boolean equals(@Nullable Object other) { if (!super.equals(other)) { return false; } if (!(other instanceof VcnCellUnderlyingNetworkPriority)) { return false; } final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other; return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds) && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds) && mAllowRoaming == rhs.mAllowRoaming && mRequireOpportunistic == rhs.mRequireOpportunistic; } /** This class is used to incrementally build WifiNetworkPriority objects. */ public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> { @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); private boolean mAllowRoaming = false; private boolean mRequireOpportunistic = false; /** Construct a Builder object. */ public Builder() {} /** * Set allowed operator PLMN IDs. * * <p>This is used to distinguish cases where roaming agreements may dictate a different * priority from a partner's networks. * * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()} * and {@link SubscriptionInfo#getMncString()}. */ @NonNull public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) { validatePlmnIds(allowedNetworkPlmnIds); mAllowedNetworkPlmnIds.clear(); mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds); return this; } /** * Set allowed specific carrier IDs. * * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}. */ @NonNull public Builder setAllowedSpecificCarrierIds( @NonNull Set<Integer> allowedSpecificCarrierIds) { Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null"); mAllowedSpecificCarrierIds.clear(); mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds); return this; } /** * Set if roaming is allowed. * * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code * false}. */ @NonNull public Builder setAllowRoaming(boolean allowRoaming) { mAllowRoaming = allowRoaming; return this; } /** * Set if requiring an opportunistic network. * * @param requireOpportunistic the flag to indicate if caller requires an opportunistic * network. Defaults to {@code false}. */ @NonNull public Builder setRequireOpportunistic(boolean requireOpportunistic) { mRequireOpportunistic = requireOpportunistic; return this; } /** Build the VcnCellUnderlyingNetworkPriority. */ @NonNull public VcnCellUnderlyingNetworkPriority build() { return new VcnCellUnderlyingNetworkPriority( mNetworkQuality, mAllowMetered, mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, mAllowRoaming, mRequireOpportunistic); } /** @hide */ @Override Builder self() { return this; } } } core/java/android/net/vcn/VcnGatewayConnectionConfig.java +97 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; import static com.android.internal.annotations.VisibleForTesting.Visibility; Loading @@ -41,6 +42,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import java.util.SortedSet; Loading Loading @@ -157,6 +159,34 @@ public final class VcnGatewayConnectionConfig { TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(15) }; private static final LinkedHashSet<VcnUnderlyingNetworkPriority> DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>(); static { DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(true /* requireOpportunistic */) .build()); DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnWifiUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .build()); DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(false /* requireOpportunistic */) .build()); } private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName"; @NonNull private final String mGatewayConnectionName; Loading @@ -166,6 +196,9 @@ public final class VcnGatewayConnectionConfig { private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; @NonNull private final SortedSet<Integer> mExposedCapabilities; private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities"; @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities; private static final String MAX_MTU_KEY = "mMaxMtu"; private final int mMaxMtu; Loading @@ -177,6 +210,7 @@ public final class VcnGatewayConnectionConfig { @NonNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { mGatewayConnectionName = gatewayConnectionName; Loading @@ -185,6 +219,11 @@ public final class VcnGatewayConnectionConfig { mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities); if (mUnderlyingNetworkPriorities.isEmpty()) { mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); } validate(); } Loading @@ -198,12 +237,19 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle exposedCapsBundle = in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); final PersistableBundle networkPrioritiesBundle = in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY); mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); mTunnelConnectionParams = TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle); mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); mUnderlyingNetworkPriorities = new LinkedHashSet<>( PersistableBundleUtils.toList( networkPrioritiesBundle, VcnUnderlyingNetworkPriority::fromPersistableBundle)); mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); mMaxMtu = in.getInt(MAX_MTU_KEY); Loading @@ -221,6 +267,7 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); Loading Loading @@ -302,6 +349,18 @@ public final class VcnGatewayConnectionConfig { return Collections.unmodifiableSet(mExposedCapabilities); } /** * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not * configured. * * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>) * @hide */ @NonNull public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() { return new LinkedHashSet<>(mUnderlyingNetworkPriorities); } /** * Retrieves the configured retry intervals. * Loading Loading @@ -338,10 +397,15 @@ public final class VcnGatewayConnectionConfig { PersistableBundleUtils.fromList( new ArrayList<>(mExposedCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); final PersistableBundle networkPrioritiesBundle = PersistableBundleUtils.fromList( new ArrayList<>(mUnderlyingNetworkPriorities), VcnUnderlyingNetworkPriority::toPersistableBundle); result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle); result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); result.putInt(MAX_MTU_KEY, mMaxMtu); Loading Loading @@ -379,6 +443,11 @@ public final class VcnGatewayConnectionConfig { @NonNull private final String mGatewayConnectionName; @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams; @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities = new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; private int mMaxMtu = DEFAULT_MAX_MTU; Loading Loading @@ -449,6 +518,33 @@ public final class VcnGatewayConnectionConfig { return this; } /** * Set the VcnUnderlyingNetworkPriority list. * * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that * are ordered from most to least preferred, or an empty list to use the default * prioritization. The default network prioritization is Opportunistic cellular, Carrier * WiFi and Macro cellular * @return */ /** @hide */ @NonNull public Builder setVcnUnderlyingNetworkPriorities( @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) { Objects.requireNonNull( mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); mUnderlyingNetworkPriorities.clear(); if (underlyingNetworkPriorities.isEmpty()) { mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); } else { mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities); } return this; } /** * Set the retry interval between VCN establishment attempts upon successive failures. * Loading Loading @@ -513,6 +609,7 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, mUnderlyingNetworkPriorities, mRetryIntervalsMs, mMaxMtu); } Loading core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java +1 −1 Original line number Diff line number Diff line Loading @@ -89,7 +89,7 @@ public abstract class VcnUnderlyingNetworkPriority { case NETWORK_PRIORITY_TYPE_WIFI: return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in); case NETWORK_PRIORITY_TYPE_CELL: throw new UnsupportedOperationException("Not implemented"); return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in); default: throw new IllegalArgumentException( "Invalid networkPriorityType:" + networkPriorityType); Loading services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +16 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ public class PersistableBundleUtils { private static final String PARCEL_UUID_KEY = "PARCEL_UUID"; private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY"; private static final String INTEGER_KEY = "INTEGER_KEY"; private static final String STRING_KEY = "STRING_KEY"; /** * Functional interface to convert an object of the specified type to a PersistableBundle. Loading Loading @@ -91,6 +92,21 @@ public class PersistableBundleUtils { return bundle.getInt(INTEGER_KEY); }; /** Serializer to convert s String to a PersistableBundle. */ public static final Serializer<String> STRING_SERIALIZER = (i) -> { final PersistableBundle result = new PersistableBundle(); result.putString(STRING_KEY, i); return result; }; /** Deserializer to convert a PersistableBundle to a String. */ public static final Deserializer<String> STRING_DESERIALIZER = (bundle) -> { Objects.requireNonNull(bundle, "PersistableBundle is null"); return bundle.getString(STRING_KEY); }; /** * Converts a ParcelUuid to a PersistableBundle. * Loading tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.net.vcn; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import java.util.HashSet; import java.util.Set; public class VcnCellUnderlyingNetworkPriorityTest { private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>(); private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>(); // Package private for use in VcnGatewayConnectionConfigTest static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() { return new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowedPlmnIds(ALLOWED_PLMN_IDS) .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(true /* requireOpportunistic */) .build(); } @Test public void testBuilderAndGetters() { final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); assertTrue(networkPriority.allowMetered()); assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds()); assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds()); assertTrue(networkPriority.allowRoaming()); assertTrue(networkPriority.requireOpportunistic()); } @Test public void testBuilderAndGettersForDefaultValues() { final VcnCellUnderlyingNetworkPriority networkPriority = new VcnCellUnderlyingNetworkPriority.Builder().build(); assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); assertFalse(networkPriority.allowMetered()); assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds()); assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds()); assertFalse(networkPriority.allowRoaming()); assertFalse(networkPriority.requireOpportunistic()); } @Test public void testPersistableBundle() { final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); assertEquals( networkPriority, VcnUnderlyingNetworkPriority.fromPersistableBundle( networkPriority.toPersistableBundle())); } } Loading
core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java 0 → 100644 +291 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.net.vcn; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.PersistableBundle; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.server.vcn.util.PersistableBundleUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Objects; import java.util.Set; // TODO: Add documents /** @hide */ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority { private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds"; @NonNull private final Set<String> mAllowedNetworkPlmnIds; private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds"; @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; private static final String ALLOW_ROAMING_KEY = "mAllowRoaming"; private final boolean mAllowRoaming; private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic"; private final boolean mRequireOpportunistic; private VcnCellUnderlyingNetworkPriority( int networkQuality, boolean allowMetered, Set<String> allowedNetworkPlmnIds, Set<Integer> allowedSpecificCarrierIds, boolean allowRoaming, boolean requireOpportunistic) { super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered); mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds); mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds); mAllowRoaming = allowRoaming; mRequireOpportunistic = requireOpportunistic; validate(); } /** @hide */ @Override protected void validate() { super.validate(); validatePlmnIds(mAllowedNetworkPlmnIds); Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null"); } private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) { Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null"); // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal // digits. for (String id : allowedNetworkPlmnIds) { if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) { continue; } else { throw new IllegalArgumentException("Found invalid PLMN ID: " + id); } } } /** @hide */ @NonNull @VisibleForTesting(visibility = Visibility.PROTECTED) public static VcnCellUnderlyingNetworkPriority fromPersistableBundle( @NonNull PersistableBundle in) { Objects.requireNonNull(in, "PersistableBundle is null"); final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); final PersistableBundle plmnIdsBundle = in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY); Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null"); final Set<String> allowedNetworkPlmnIds = new ArraySet<String>( PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER)); final PersistableBundle specificCarrierIdsBundle = in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY); Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null"); final Set<Integer> allowedSpecificCarrierIds = new ArraySet<Integer>( PersistableBundleUtils.toList( specificCarrierIdsBundle, INTEGER_DESERIALIZER)); final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY); final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY); return new VcnCellUnderlyingNetworkPriority( networkQuality, allowMetered, allowedNetworkPlmnIds, allowedSpecificCarrierIds, allowRoaming, requireOpportunistic); } /** @hide */ @Override @NonNull @VisibleForTesting(visibility = Visibility.PROTECTED) public PersistableBundle toPersistableBundle() { final PersistableBundle result = super.toPersistableBundle(); final PersistableBundle plmnIdsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER); result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle); final PersistableBundle specificCarrierIdsBundle = PersistableBundleUtils.fromList( new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER); result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle); result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming); result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic); return result; } /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */ @NonNull public Set<String> getAllowedPlmnIds() { return Collections.unmodifiableSet(mAllowedNetworkPlmnIds); } /** * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is * acceptable. */ @NonNull public Set<Integer> getAllowedSpecificCarrierIds() { return Collections.unmodifiableSet(mAllowedSpecificCarrierIds); } /** Return if roaming is allowed. */ public boolean allowRoaming() { return mAllowRoaming; } /** Return if requiring an opportunistic network. */ public boolean requireOpportunistic() { return mRequireOpportunistic; } @Override public int hashCode() { return Objects.hash( super.hashCode(), mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, mAllowRoaming, mRequireOpportunistic); } @Override public boolean equals(@Nullable Object other) { if (!super.equals(other)) { return false; } if (!(other instanceof VcnCellUnderlyingNetworkPriority)) { return false; } final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other; return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds) && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds) && mAllowRoaming == rhs.mAllowRoaming && mRequireOpportunistic == rhs.mRequireOpportunistic; } /** This class is used to incrementally build WifiNetworkPriority objects. */ public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> { @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); private boolean mAllowRoaming = false; private boolean mRequireOpportunistic = false; /** Construct a Builder object. */ public Builder() {} /** * Set allowed operator PLMN IDs. * * <p>This is used to distinguish cases where roaming agreements may dictate a different * priority from a partner's networks. * * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()} * and {@link SubscriptionInfo#getMncString()}. */ @NonNull public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) { validatePlmnIds(allowedNetworkPlmnIds); mAllowedNetworkPlmnIds.clear(); mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds); return this; } /** * Set allowed specific carrier IDs. * * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}. */ @NonNull public Builder setAllowedSpecificCarrierIds( @NonNull Set<Integer> allowedSpecificCarrierIds) { Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null"); mAllowedSpecificCarrierIds.clear(); mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds); return this; } /** * Set if roaming is allowed. * * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code * false}. */ @NonNull public Builder setAllowRoaming(boolean allowRoaming) { mAllowRoaming = allowRoaming; return this; } /** * Set if requiring an opportunistic network. * * @param requireOpportunistic the flag to indicate if caller requires an opportunistic * network. Defaults to {@code false}. */ @NonNull public Builder setRequireOpportunistic(boolean requireOpportunistic) { mRequireOpportunistic = requireOpportunistic; return this; } /** Build the VcnCellUnderlyingNetworkPriority. */ @NonNull public VcnCellUnderlyingNetworkPriority build() { return new VcnCellUnderlyingNetworkPriority( mNetworkQuality, mAllowMetered, mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, mAllowRoaming, mRequireOpportunistic); } /** @hide */ @Override Builder self() { return this; } } }
core/java/android/net/vcn/VcnGatewayConnectionConfig.java +97 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; import static com.android.internal.annotations.VisibleForTesting.Visibility; Loading @@ -41,6 +42,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import java.util.SortedSet; Loading Loading @@ -157,6 +159,34 @@ public final class VcnGatewayConnectionConfig { TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(15) }; private static final LinkedHashSet<VcnUnderlyingNetworkPriority> DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>(); static { DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(true /* requireOpportunistic */) .build()); DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnWifiUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .build()); DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(false /* requireOpportunistic */) .build()); } private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName"; @NonNull private final String mGatewayConnectionName; Loading @@ -166,6 +196,9 @@ public final class VcnGatewayConnectionConfig { private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; @NonNull private final SortedSet<Integer> mExposedCapabilities; private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities"; @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities; private static final String MAX_MTU_KEY = "mMaxMtu"; private final int mMaxMtu; Loading @@ -177,6 +210,7 @@ public final class VcnGatewayConnectionConfig { @NonNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { mGatewayConnectionName = gatewayConnectionName; Loading @@ -185,6 +219,11 @@ public final class VcnGatewayConnectionConfig { mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities); if (mUnderlyingNetworkPriorities.isEmpty()) { mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); } validate(); } Loading @@ -198,12 +237,19 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle exposedCapsBundle = in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); final PersistableBundle networkPrioritiesBundle = in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY); mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); mTunnelConnectionParams = TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle); mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); mUnderlyingNetworkPriorities = new LinkedHashSet<>( PersistableBundleUtils.toList( networkPrioritiesBundle, VcnUnderlyingNetworkPriority::fromPersistableBundle)); mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); mMaxMtu = in.getInt(MAX_MTU_KEY); Loading @@ -221,6 +267,7 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); Loading Loading @@ -302,6 +349,18 @@ public final class VcnGatewayConnectionConfig { return Collections.unmodifiableSet(mExposedCapabilities); } /** * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not * configured. * * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>) * @hide */ @NonNull public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() { return new LinkedHashSet<>(mUnderlyingNetworkPriorities); } /** * Retrieves the configured retry intervals. * Loading Loading @@ -338,10 +397,15 @@ public final class VcnGatewayConnectionConfig { PersistableBundleUtils.fromList( new ArrayList<>(mExposedCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); final PersistableBundle networkPrioritiesBundle = PersistableBundleUtils.fromList( new ArrayList<>(mUnderlyingNetworkPriorities), VcnUnderlyingNetworkPriority::toPersistableBundle); result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle); result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); result.putInt(MAX_MTU_KEY, mMaxMtu); Loading Loading @@ -379,6 +443,11 @@ public final class VcnGatewayConnectionConfig { @NonNull private final String mGatewayConnectionName; @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams; @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities = new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; private int mMaxMtu = DEFAULT_MAX_MTU; Loading Loading @@ -449,6 +518,33 @@ public final class VcnGatewayConnectionConfig { return this; } /** * Set the VcnUnderlyingNetworkPriority list. * * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that * are ordered from most to least preferred, or an empty list to use the default * prioritization. The default network prioritization is Opportunistic cellular, Carrier * WiFi and Macro cellular * @return */ /** @hide */ @NonNull public Builder setVcnUnderlyingNetworkPriorities( @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) { Objects.requireNonNull( mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); mUnderlyingNetworkPriorities.clear(); if (underlyingNetworkPriorities.isEmpty()) { mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); } else { mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities); } return this; } /** * Set the retry interval between VCN establishment attempts upon successive failures. * Loading Loading @@ -513,6 +609,7 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, mUnderlyingNetworkPriorities, mRetryIntervalsMs, mMaxMtu); } Loading
core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java +1 −1 Original line number Diff line number Diff line Loading @@ -89,7 +89,7 @@ public abstract class VcnUnderlyingNetworkPriority { case NETWORK_PRIORITY_TYPE_WIFI: return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in); case NETWORK_PRIORITY_TYPE_CELL: throw new UnsupportedOperationException("Not implemented"); return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in); default: throw new IllegalArgumentException( "Invalid networkPriorityType:" + networkPriorityType); Loading
services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +16 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ public class PersistableBundleUtils { private static final String PARCEL_UUID_KEY = "PARCEL_UUID"; private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY"; private static final String INTEGER_KEY = "INTEGER_KEY"; private static final String STRING_KEY = "STRING_KEY"; /** * Functional interface to convert an object of the specified type to a PersistableBundle. Loading Loading @@ -91,6 +92,21 @@ public class PersistableBundleUtils { return bundle.getInt(INTEGER_KEY); }; /** Serializer to convert s String to a PersistableBundle. */ public static final Serializer<String> STRING_SERIALIZER = (i) -> { final PersistableBundle result = new PersistableBundle(); result.putString(STRING_KEY, i); return result; }; /** Deserializer to convert a PersistableBundle to a String. */ public static final Deserializer<String> STRING_DESERIALIZER = (bundle) -> { Objects.requireNonNull(bundle, "PersistableBundle is null"); return bundle.getString(STRING_KEY); }; /** * Converts a ParcelUuid to a PersistableBundle. * Loading
tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 android.net.vcn; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY; import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; import java.util.HashSet; import java.util.Set; public class VcnCellUnderlyingNetworkPriorityTest { private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>(); private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>(); // Package private for use in VcnGatewayConnectionConfigTest static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() { return new VcnCellUnderlyingNetworkPriority.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) .setAllowMetered(true /* allowMetered */) .setAllowedPlmnIds(ALLOWED_PLMN_IDS) .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS) .setAllowRoaming(true /* allowRoaming */) .setRequireOpportunistic(true /* requireOpportunistic */) .build(); } @Test public void testBuilderAndGetters() { final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); assertTrue(networkPriority.allowMetered()); assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds()); assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds()); assertTrue(networkPriority.allowRoaming()); assertTrue(networkPriority.requireOpportunistic()); } @Test public void testBuilderAndGettersForDefaultValues() { final VcnCellUnderlyingNetworkPriority networkPriority = new VcnCellUnderlyingNetworkPriority.Builder().build(); assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); assertFalse(networkPriority.allowMetered()); assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds()); assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds()); assertFalse(networkPriority.allowRoaming()); assertFalse(networkPriority.requireOpportunistic()); } @Test public void testPersistableBundle() { final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); assertEquals( networkPriority, VcnUnderlyingNetworkPriority.fromPersistableBundle( networkPriority.toPersistableBundle())); } }