1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.net.vcn; 17 18 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; 19 20 import static com.android.internal.annotations.VisibleForTesting.Visibility; 21 22 import android.annotation.IntDef; 23 import android.annotation.IntRange; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SuppressLint; 27 import android.net.Network; 28 import android.net.NetworkCapabilities; 29 import android.net.ipsec.ike.IkeTunnelConnectionParams; 30 import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils; 31 import android.os.PersistableBundle; 32 import android.util.ArraySet; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.util.ArrayUtils; 36 import com.android.internal.util.Preconditions; 37 import com.android.server.vcn.util.PersistableBundleUtils; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collections; 44 import java.util.Objects; 45 import java.util.Set; 46 import java.util.SortedSet; 47 import java.util.TreeSet; 48 import java.util.concurrent.TimeUnit; 49 50 /** 51 * This class represents a configuration for a connection to a Virtual Carrier Network gateway. 52 * 53 * <p>Each VcnGatewayConnectionConfig represents a single logical connection to a carrier gateway, 54 * and may provide one or more telephony services (as represented by network capabilities). Each 55 * gateway is expected to provide mobility for a given session as the device roams across {@link 56 * Network}s. 57 * 58 * <p>A VCN connection based on this configuration will be brought up dynamically based on device 59 * settings, and filed NetworkRequests. Underlying Networks must provide INTERNET connectivity, and 60 * must be part of the subscription group under which this configuration is registered (see {@link 61 * VcnManager#setVcnConfig}). 62 * 63 * <p>As an abstraction of a cellular network, services that can be provided by a VCN network are 64 * limited to services provided by cellular networks: 65 * 66 * <ul> 67 * <li>{@link NetworkCapabilities#NET_CAPABILITY_MMS} 68 * <li>{@link NetworkCapabilities#NET_CAPABILITY_SUPL} 69 * <li>{@link NetworkCapabilities#NET_CAPABILITY_DUN} 70 * <li>{@link NetworkCapabilities#NET_CAPABILITY_FOTA} 71 * <li>{@link NetworkCapabilities#NET_CAPABILITY_IMS} 72 * <li>{@link NetworkCapabilities#NET_CAPABILITY_CBS} 73 * <li>{@link NetworkCapabilities#NET_CAPABILITY_IA} 74 * <li>{@link NetworkCapabilities#NET_CAPABILITY_RCS} 75 * <li>{@link NetworkCapabilities#NET_CAPABILITY_XCAP} 76 * <li>{@link NetworkCapabilities#NET_CAPABILITY_EIMS} 77 * <li>{@link NetworkCapabilities#NET_CAPABILITY_INTERNET} 78 * <li>{@link NetworkCapabilities#NET_CAPABILITY_MCX} 79 * </ul> 80 */ 81 public final class VcnGatewayConnectionConfig { 82 // TODO: Use MIN_MTU_V6 once it is public, @hide 83 @VisibleForTesting(visibility = Visibility.PRIVATE) 84 static final int MIN_MTU_V6 = 1280; 85 86 /** 87 * The set of allowed capabilities for exposed capabilities. 88 * 89 * @hide 90 */ 91 public static final Set<Integer> ALLOWED_CAPABILITIES; 92 93 static { 94 Set<Integer> allowedCaps = new ArraySet<>(); 95 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MMS); 96 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_SUPL); 97 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_DUN); 98 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_FOTA); 99 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IMS); 100 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_CBS); 101 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IA); 102 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_RCS); 103 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_XCAP); 104 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_EIMS); 105 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_INTERNET); 106 allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MCX); 107 108 ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps); 109 } 110 111 /** @hide */ 112 @Retention(RetentionPolicy.SOURCE) 113 @IntDef( 114 prefix = {"NET_CAPABILITY_"}, 115 value = { 116 NetworkCapabilities.NET_CAPABILITY_MMS, 117 NetworkCapabilities.NET_CAPABILITY_SUPL, 118 NetworkCapabilities.NET_CAPABILITY_DUN, 119 NetworkCapabilities.NET_CAPABILITY_FOTA, 120 NetworkCapabilities.NET_CAPABILITY_IMS, 121 NetworkCapabilities.NET_CAPABILITY_CBS, 122 NetworkCapabilities.NET_CAPABILITY_IA, 123 NetworkCapabilities.NET_CAPABILITY_RCS, 124 NetworkCapabilities.NET_CAPABILITY_XCAP, 125 NetworkCapabilities.NET_CAPABILITY_EIMS, 126 NetworkCapabilities.NET_CAPABILITY_INTERNET, 127 NetworkCapabilities.NET_CAPABILITY_MCX, 128 }) 129 public @interface VcnSupportedCapability {} 130 131 private static final int DEFAULT_MAX_MTU = 1500; 132 133 /** 134 * The maximum number of retry intervals that may be specified. 135 * 136 * <p>Limited to ensure an upper bound on config sizes. 137 */ 138 private static final int MAX_RETRY_INTERVAL_COUNT = 10; 139 140 /** 141 * The minimum allowable repeating retry interval 142 * 143 * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater 144 * than this value. 145 * 146 * @see {@link Builder#setRetryIntervalsMillis()} 147 */ 148 private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15); 149 150 private static final long[] DEFAULT_RETRY_INTERVALS_MS = 151 new long[] { 152 TimeUnit.SECONDS.toMillis(1), 153 TimeUnit.SECONDS.toMillis(2), 154 TimeUnit.SECONDS.toMillis(5), 155 TimeUnit.SECONDS.toMillis(30), 156 TimeUnit.MINUTES.toMillis(1), 157 TimeUnit.MINUTES.toMillis(5), 158 TimeUnit.MINUTES.toMillis(15) 159 }; 160 private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName"; 161 @NonNull private final String mGatewayConnectionName; 162 163 private static final String TUNNEL_CONNECTION_PARAMS_KEY = "mTunnelConnectionParams"; 164 @NonNull private IkeTunnelConnectionParams mTunnelConnectionParams; 165 166 private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; 167 @NonNull private final SortedSet<Integer> mExposedCapabilities; 168 169 private static final String MAX_MTU_KEY = "mMaxMtu"; 170 private final int mMaxMtu; 171 172 private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs"; 173 @NonNull private final long[] mRetryIntervalsMs; 174 175 /** Builds a VcnGatewayConnectionConfig with the specified parameters. */ VcnGatewayConnectionConfig( @onNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu)176 private VcnGatewayConnectionConfig( 177 @NonNull String gatewayConnectionName, 178 @NonNull IkeTunnelConnectionParams tunnelConnectionParams, 179 @NonNull Set<Integer> exposedCapabilities, 180 @NonNull long[] retryIntervalsMs, 181 @IntRange(from = MIN_MTU_V6) int maxMtu) { 182 mGatewayConnectionName = gatewayConnectionName; 183 mTunnelConnectionParams = tunnelConnectionParams; 184 mExposedCapabilities = new TreeSet(exposedCapabilities); 185 mRetryIntervalsMs = retryIntervalsMs; 186 mMaxMtu = maxMtu; 187 188 validate(); 189 } 190 191 /** @hide */ 192 @VisibleForTesting(visibility = Visibility.PRIVATE) VcnGatewayConnectionConfig(@onNull PersistableBundle in)193 public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) { 194 final PersistableBundle tunnelConnectionParamsBundle = 195 in.getPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY); 196 Objects.requireNonNull( 197 tunnelConnectionParamsBundle, "tunnelConnectionParamsBundle was null"); 198 199 final PersistableBundle exposedCapsBundle = 200 in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); 201 202 mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); 203 mTunnelConnectionParams = 204 TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle); 205 mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( 206 exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); 207 mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); 208 mMaxMtu = in.getInt(MAX_MTU_KEY); 209 210 validate(); 211 } 212 validate()213 private void validate() { 214 Objects.requireNonNull(mGatewayConnectionName, "gatewayConnectionName was null"); 215 Objects.requireNonNull(mTunnelConnectionParams, "tunnel connection parameter was null"); 216 217 Preconditions.checkArgument( 218 mExposedCapabilities != null && !mExposedCapabilities.isEmpty(), 219 "exposedCapsBundle was null or empty"); 220 for (Integer cap : getAllExposedCapabilities()) { 221 checkValidCapability(cap); 222 } 223 224 Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); 225 validateRetryInterval(mRetryIntervalsMs); 226 227 Preconditions.checkArgument( 228 mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)"); 229 } 230 checkValidCapability(int capability)231 private static void checkValidCapability(int capability) { 232 Preconditions.checkArgument( 233 ALLOWED_CAPABILITIES.contains(capability), 234 "NetworkCapability " + capability + "out of range"); 235 } 236 validateRetryInterval(@ullable long[] retryIntervalsMs)237 private static void validateRetryInterval(@Nullable long[] retryIntervalsMs) { 238 Preconditions.checkArgument( 239 retryIntervalsMs != null 240 && retryIntervalsMs.length > 0 241 && retryIntervalsMs.length <= MAX_RETRY_INTERVAL_COUNT, 242 "retryIntervalsMs was null, empty or exceed max interval count"); 243 244 final long repeatingInterval = retryIntervalsMs[retryIntervalsMs.length - 1]; 245 if (repeatingInterval < MINIMUM_REPEATING_RETRY_INTERVAL_MS) { 246 throw new IllegalArgumentException( 247 "Repeating retry interval was too short, must be a minimum of 15 minutes: " 248 + repeatingInterval); 249 } 250 } 251 252 /** 253 * Returns the configured Gateway Connection name. 254 * 255 * <p>This name is used by the configuring apps to distinguish between 256 * VcnGatewayConnectionConfigs configured on a single {@link VcnConfig}. This will be used as 257 * the identifier in VcnStatusCallback invocations. 258 * 259 * @see VcnManager.VcnStatusCallback#onGatewayConnectionError 260 */ 261 @NonNull getGatewayConnectionName()262 public String getGatewayConnectionName() { 263 return mGatewayConnectionName; 264 } 265 266 /** 267 * Returns tunnel connection parameters. 268 * 269 * @hide 270 */ 271 @NonNull getTunnelConnectionParams()272 public IkeTunnelConnectionParams getTunnelConnectionParams() { 273 return mTunnelConnectionParams; 274 } 275 276 /** 277 * Returns all exposed capabilities. 278 * 279 * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in 280 * ascending numerical order. 281 * 282 * @see Builder#addExposedCapability(int) 283 * @see Builder#removeExposedCapability(int) 284 */ 285 @NonNull getExposedCapabilities()286 public int[] getExposedCapabilities() { 287 // Sorted set guarantees ordering 288 return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities)); 289 } 290 291 /** 292 * Returns all exposed capabilities. 293 * 294 * <p>Left to prevent the need to make major changes while changes are actively in flight. 295 * 296 * @deprecated use getExposedCapabilities() instead 297 * @hide 298 */ 299 @Deprecated 300 @NonNull getAllExposedCapabilities()301 public Set<Integer> getAllExposedCapabilities() { 302 return Collections.unmodifiableSet(mExposedCapabilities); 303 } 304 305 /** 306 * Retrieves the configured retry intervals. 307 * 308 * @see Builder#setRetryIntervalsMillis(long[]) 309 */ 310 @NonNull getRetryIntervalsMillis()311 public long[] getRetryIntervalsMillis() { 312 return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length); 313 } 314 315 /** 316 * Retrieves the maximum MTU allowed for this Gateway Connection. 317 * 318 * @see Builder#setMaxMtu(int) 319 */ 320 @IntRange(from = MIN_MTU_V6) getMaxMtu()321 public int getMaxMtu() { 322 return mMaxMtu; 323 } 324 325 /** 326 * Converts this config to a PersistableBundle. 327 * 328 * @hide 329 */ 330 @NonNull 331 @VisibleForTesting(visibility = Visibility.PROTECTED) toPersistableBundle()332 public PersistableBundle toPersistableBundle() { 333 final PersistableBundle result = new PersistableBundle(); 334 335 final PersistableBundle tunnelConnectionParamsBundle = 336 TunnelConnectionParamsUtils.toPersistableBundle(mTunnelConnectionParams); 337 final PersistableBundle exposedCapsBundle = 338 PersistableBundleUtils.fromList( 339 new ArrayList<>(mExposedCapabilities), 340 PersistableBundleUtils.INTEGER_SERIALIZER); 341 342 result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); 343 result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); 344 result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); 345 result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); 346 result.putInt(MAX_MTU_KEY, mMaxMtu); 347 348 return result; 349 } 350 351 @Override hashCode()352 public int hashCode() { 353 return Objects.hash( 354 mGatewayConnectionName, 355 mTunnelConnectionParams, 356 mExposedCapabilities, 357 Arrays.hashCode(mRetryIntervalsMs), 358 mMaxMtu); 359 } 360 361 @Override equals(@ullable Object other)362 public boolean equals(@Nullable Object other) { 363 if (!(other instanceof VcnGatewayConnectionConfig)) { 364 return false; 365 } 366 367 final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other; 368 return mGatewayConnectionName.equals(rhs.mGatewayConnectionName) 369 && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams) 370 && mExposedCapabilities.equals(rhs.mExposedCapabilities) 371 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs) 372 && mMaxMtu == rhs.mMaxMtu; 373 } 374 375 /** 376 * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. 377 */ 378 public static final class Builder { 379 @NonNull private final String mGatewayConnectionName; 380 @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams; 381 @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); 382 @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; 383 private int mMaxMtu = DEFAULT_MAX_MTU; 384 385 // TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent. 386 // Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS 387 // when on Cell. 388 389 /** 390 * Construct a Builder object. 391 * 392 * @param gatewayConnectionName the String GatewayConnection name for this 393 * VcnGatewayConnectionConfig. Each VcnGatewayConnectionConfig within a {@link 394 * VcnConfig} must be given a unique name. This name is used by the caller to 395 * distinguish between VcnGatewayConnectionConfigs configured on a single {@link 396 * VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations. 397 * @param tunnelConnectionParams the IKE tunnel connection configuration 398 * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not 399 * configured to support MOBIKE 400 * @see IkeTunnelConnectionParams 401 * @see VcnManager.VcnStatusCallback#onGatewayConnectionError 402 */ Builder( @onNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams)403 public Builder( 404 @NonNull String gatewayConnectionName, 405 @NonNull IkeTunnelConnectionParams tunnelConnectionParams) { 406 Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null"); 407 Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null"); 408 if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) { 409 throw new IllegalArgumentException( 410 "MOBIKE must be configured for the provided IkeSessionParams"); 411 } 412 413 mGatewayConnectionName = gatewayConnectionName; 414 mTunnelConnectionParams = tunnelConnectionParams; 415 } 416 417 /** 418 * Add a capability that this VCN Gateway Connection will support. 419 * 420 * @param exposedCapability the app-facing capability to be exposed by this VCN Gateway 421 * Connection (i.e., the capabilities that this VCN Gateway Connection will support). 422 * @return this {@link Builder} instance, for chaining 423 * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway 424 * Connection 425 */ 426 @NonNull addExposedCapability(@cnSupportedCapability int exposedCapability)427 public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) { 428 checkValidCapability(exposedCapability); 429 430 mExposedCapabilities.add(exposedCapability); 431 return this; 432 } 433 434 /** 435 * Remove a capability that this VCN Gateway Connection will support. 436 * 437 * @param exposedCapability the app-facing capability to not be exposed by this VCN Gateway 438 * Connection (i.e., the capabilities that this VCN Gateway Connection will support) 439 * @return this {@link Builder} instance, for chaining 440 * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway 441 * Connection 442 */ 443 @NonNull 444 @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap removeExposedCapability(@cnSupportedCapability int exposedCapability)445 public Builder removeExposedCapability(@VcnSupportedCapability int exposedCapability) { 446 checkValidCapability(exposedCapability); 447 448 mExposedCapabilities.remove(exposedCapability); 449 return this; 450 } 451 452 /** 453 * Set the retry interval between VCN establishment attempts upon successive failures. 454 * 455 * <p>The last retry interval will be repeated until safe mode is entered, or a connection 456 * is successfully established, at which point the retry timers will be reset. For power 457 * reasons, the last (repeated) retry interval MUST be at least 15 minutes. 458 * 459 * <p>Retry intervals MAY be subject to system power saving modes. That is to say that if 460 * the system enters a power saving mode, the retry may not occur until the device leaves 461 * the specified power saving mode. Intervals are sequential, and intervals will NOT be 462 * skipped if system power saving results in delaying retries (even if it exceed multiple 463 * retry intervals). 464 * 465 * <p>Each Gateway Connection will retry according to the retry intervals configured, but if 466 * safe mode is enabled, all Gateway Connection(s) will be disabled. 467 * 468 * @param retryIntervalsMs an array of between 1 and 10 millisecond intervals after which 469 * the VCN will attempt to retry a session initiation. The last (repeating) retry 470 * interval must be at least 15 minutes. Defaults to: {@code [1s, 2s, 5s, 30s, 1m, 5m, 471 * 15m]} 472 * @return this {@link Builder} instance, for chaining 473 * @see VcnManager for additional discussion on fail-safe mode 474 */ 475 @NonNull setRetryIntervalsMillis(@onNull long[] retryIntervalsMs)476 public Builder setRetryIntervalsMillis(@NonNull long[] retryIntervalsMs) { 477 validateRetryInterval(retryIntervalsMs); 478 479 mRetryIntervalsMs = retryIntervalsMs; 480 return this; 481 } 482 483 /** 484 * Sets the maximum MTU allowed for this VCN Gateway Connection. 485 * 486 * <p>This MTU is applied to the VCN Gateway Connection exposed Networks, and represents the 487 * MTU of the virtualized network. 488 * 489 * <p>The system may reduce the MTU below the maximum specified based on signals such as the 490 * MTU of the underlying networks (and adjusted for Gateway Connection overhead). 491 * 492 * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than 493 * the IPv6 minimum MTU of 1280. Defaults to 1500. 494 * @return this {@link Builder} instance, for chaining 495 */ 496 @NonNull setMaxMtu(@ntRangefrom = MIN_MTU_V6) int maxMtu)497 public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) { 498 Preconditions.checkArgument( 499 maxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)"); 500 501 mMaxMtu = maxMtu; 502 return this; 503 } 504 505 /** 506 * Builds and validates the VcnGatewayConnectionConfig. 507 * 508 * @return an immutable VcnGatewayConnectionConfig instance 509 */ 510 @NonNull build()511 public VcnGatewayConnectionConfig build() { 512 return new VcnGatewayConnectionConfig( 513 mGatewayConnectionName, 514 mTunnelConnectionParams, 515 mExposedCapabilities, 516 mRetryIntervalsMs, 517 mMaxMtu); 518 } 519 } 520 } 521