1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net; 6 7 import androidx.annotation.Nullable; 8 import androidx.annotation.RequiresOptIn; 9 10 /** 11 * A class configuring Cronet's connection migration functionality. 12 * 13 * <p>Connection migration stops open connections to servers from being destroyed when the 14 * client device switches its L4 connectivity (typically the IP address as a result of using 15 * a different network). This is particularly common with mobile devices losing 16 * wifi connectivity and switching to cellular data, or vice versa (a.k.a. the parking lot 17 * problem). QUIC uses connection identifiers which are independent of the underlying 18 * transport layer to make this possible. If the client connects to a new network and wants 19 * to preserve the existing connection, they can do so by using a connection identifier the server 20 * knows to be a continuation of the existing connection. 21 * 22 * <p>The features are only available for QUIC connections and the server needs to support 23 * connection migration. 24 * 25 * @see <a href="https://www.rfc-editor.org/rfc/rfc9000.html#section-9">Connection 26 * Migration specification</a> 27 */ 28 public class ConnectionMigrationOptions { 29 @Nullable private final Boolean mEnableDefaultNetworkMigration; 30 @Nullable private final Boolean mEnablePathDegradationMigration; 31 @Nullable private final Boolean mAllowServerMigration; 32 @Nullable private final Boolean mMigrateIdleConnections; 33 @Nullable private final Long mIdleMigrationPeriodSeconds; 34 @Nullable private final Boolean mRetryPreHandshakeErrorsOnAlternateNetwork; 35 @Nullable private final Boolean mAllowNonDefaultNetworkUsage; 36 @Nullable private final Long mMaxTimeOnNonDefaultNetworkSeconds; 37 @Nullable private final Integer mMaxWriteErrorEagerMigrationsCount; 38 @Nullable private final Integer mMaxPathDegradingEagerMigrationsCount; 39 40 @Nullable getEnableDefaultNetworkMigration()41 public Boolean getEnableDefaultNetworkMigration() { 42 return mEnableDefaultNetworkMigration; 43 } 44 45 @Nullable getEnablePathDegradationMigration()46 public Boolean getEnablePathDegradationMigration() { 47 return mEnablePathDegradationMigration; 48 } 49 50 @Nullable getAllowServerMigration()51 public Boolean getAllowServerMigration() { 52 return mAllowServerMigration; 53 } 54 55 @Nullable getMigrateIdleConnections()56 public Boolean getMigrateIdleConnections() { 57 return mMigrateIdleConnections; 58 } 59 60 @Nullable getIdleMigrationPeriodSeconds()61 public Long getIdleMigrationPeriodSeconds() { 62 return mIdleMigrationPeriodSeconds; 63 } 64 65 @Nullable getRetryPreHandshakeErrorsOnAlternateNetwork()66 public Boolean getRetryPreHandshakeErrorsOnAlternateNetwork() { 67 return mRetryPreHandshakeErrorsOnAlternateNetwork; 68 } 69 70 @Nullable getAllowNonDefaultNetworkUsage()71 public Boolean getAllowNonDefaultNetworkUsage() { 72 return mAllowNonDefaultNetworkUsage; 73 } 74 75 @Nullable getMaxTimeOnNonDefaultNetworkSeconds()76 public Long getMaxTimeOnNonDefaultNetworkSeconds() { 77 return mMaxTimeOnNonDefaultNetworkSeconds; 78 } 79 80 @Nullable getMaxWriteErrorEagerMigrationsCount()81 public Integer getMaxWriteErrorEagerMigrationsCount() { 82 return mMaxWriteErrorEagerMigrationsCount; 83 } 84 85 @Nullable getMaxPathDegradingEagerMigrationsCount()86 public Integer getMaxPathDegradingEagerMigrationsCount() { 87 return mMaxPathDegradingEagerMigrationsCount; 88 } 89 ConnectionMigrationOptions(Builder builder)90 public ConnectionMigrationOptions(Builder builder) { 91 this.mEnableDefaultNetworkMigration = builder.mEnableDefaultNetworkConnectionMigration; 92 this.mEnablePathDegradationMigration = builder.mEnablePathDegradationMigration; 93 this.mAllowServerMigration = builder.mAllowServerMigration; 94 this.mMigrateIdleConnections = builder.mMigrateIdleConnections; 95 this.mIdleMigrationPeriodSeconds = builder.mIdleConnectionMigrationPeriodSeconds; 96 this.mRetryPreHandshakeErrorsOnAlternateNetwork = 97 builder.mRetryPreHandshakeErrorsOnAlternateNetwork; 98 this.mAllowNonDefaultNetworkUsage = builder.mAllowNonDefaultNetworkUsage; 99 this.mMaxTimeOnNonDefaultNetworkSeconds = builder.mMaxTimeOnNonDefaultNetworkSeconds; 100 this.mMaxWriteErrorEagerMigrationsCount = builder.mMaxWriteErrorEagerMigrationsCount; 101 this.mMaxPathDegradingEagerMigrationsCount = builder.mMaxPathDegradingEagerMigrationsCount; 102 } 103 104 /** Builder for {@link ConnectionMigrationOptions}. */ 105 public static class Builder { 106 @Nullable private Boolean mEnableDefaultNetworkConnectionMigration; 107 @Nullable private Boolean mEnablePathDegradationMigration; 108 @Nullable private Boolean mAllowServerMigration; 109 @Nullable private Boolean mMigrateIdleConnections; 110 @Nullable private Long mIdleConnectionMigrationPeriodSeconds; 111 @Nullable private Boolean mRetryPreHandshakeErrorsOnAlternateNetwork; 112 @Nullable private Boolean mAllowNonDefaultNetworkUsage; 113 @Nullable private Long mMaxTimeOnNonDefaultNetworkSeconds; 114 @Nullable private Integer mMaxWriteErrorEagerMigrationsCount; 115 @Nullable private Integer mMaxPathDegradingEagerMigrationsCount; 116 Builder()117 Builder() {} 118 119 /** 120 * Enables the possibility of migrating connections on default network change. If enabled, 121 * active QUIC connections will be migrated onto the new network when the platform indicates 122 * that the default network is changing. 123 * 124 * @see <a href="https://developer.android.com/training/basics/network-ops/reading-network-state#listening-events">Android 125 * Network State</a> 126 * 127 * @return this builder for chaining 128 */ enableDefaultNetworkMigration( boolean enableDefaultNetworkConnectionMigration)129 public Builder enableDefaultNetworkMigration( 130 boolean enableDefaultNetworkConnectionMigration) { 131 this.mEnableDefaultNetworkConnectionMigration = enableDefaultNetworkConnectionMigration; 132 return this; 133 } 134 135 /** 136 * Enables the possibility of migrating connections if the current path is performing 137 * poorly. 138 * 139 * <p>Depending on other configuration, this can result to migrating the connections within 140 * the same default network, or to a non-default network. 141 * 142 * @see #allowNonDefaultNetworkUsage(boolean) 143 * 144 * @return this builder for chaining 145 */ enablePathDegradationMigration(boolean enable)146 public Builder enablePathDegradationMigration(boolean enable) { 147 this.mEnablePathDegradationMigration = enable; 148 return this; 149 } 150 151 /** 152 * Enables the possibility of migrating connections to an alternate server address 153 * at the server's request. 154 * 155 * @return this builder for chaining 156 */ 157 @Experimental allowServerMigration(boolean allowServerMigration)158 public Builder allowServerMigration(boolean allowServerMigration) { 159 this.mAllowServerMigration = allowServerMigration; 160 return this; 161 } 162 163 /** 164 * Configures whether migration of idle connections should be enabled or not. 165 * 166 * <p>If set to true, idle connections will be migrated too, as long as they haven't been 167 * idle for too long. The setting is shared for all connection migration types. The maximum 168 * idle period for which connections will still be migrated can be customized using {@link 169 * #setIdleConnectionMigrationPeriodSeconds}. 170 * 171 * @return this builder for chaining 172 */ 173 @Experimental migrateIdleConnections(boolean migrateIdleConnections)174 public Builder migrateIdleConnections(boolean migrateIdleConnections) { 175 this.mMigrateIdleConnections = migrateIdleConnections; 176 return this; 177 } 178 179 /** 180 * Sets the maximum idle period for which connections will still be migrated, in seconds. 181 * The setting is shared for all connection migration types. 182 * 183 * <p>Only relevant if {@link #migrateIdleConnections(boolean)} is enabled. 184 * 185 * @return this builder for chaining 186 */ 187 @Experimental setIdleConnectionMigrationPeriodSeconds( long idleConnectionMigrationPeriodSeconds)188 public Builder setIdleConnectionMigrationPeriodSeconds( 189 long idleConnectionMigrationPeriodSeconds) { 190 this.mIdleConnectionMigrationPeriodSeconds = idleConnectionMigrationPeriodSeconds; 191 return this; 192 } 193 194 /** 195 * Sets whether connections can be migrated to an alternate network when Cronet detects 196 * a degradation of the path currently in use. 197 * 198 * <p>Note: This setting can result in requests being sent on non-default metered networks. 199 * Make sure you're using metered networks sparingly, and fine tune parameters like 200 * {@link #setMaxPathDegradingNonDefaultNetworkMigrationsCount(int)} 201 * and {@link #setMaxTimeOnNonDefaultNetworkSeconds} to limit the time on non-default 202 * networks. 203 * 204 * @return this builder for chaining 205 */ 206 @Experimental allowNonDefaultNetworkUsage(boolean enable)207 public Builder allowNonDefaultNetworkUsage(boolean enable) { 208 this.mAllowNonDefaultNetworkUsage = enable; 209 return this; 210 } 211 212 /** 213 * Sets the maximum period for which eagerly migrated connections should remain on the 214 * non-default network before they're migrated back. This time is not cumulative - each 215 * migration off the default network for each connection measures and compares to this value 216 * separately. 217 * 218 * <p>Only relevant if {@link #allowNonDefaultNetworkUsage(boolean)} is enabled. 219 * 220 * @return this builder for chaining 221 */ 222 @Experimental setMaxTimeOnNonDefaultNetworkSeconds( long maxTimeOnNonDefaultNetworkSeconds)223 public Builder setMaxTimeOnNonDefaultNetworkSeconds( 224 long maxTimeOnNonDefaultNetworkSeconds) { 225 this.mMaxTimeOnNonDefaultNetworkSeconds = maxTimeOnNonDefaultNetworkSeconds; 226 return this; 227 } 228 229 /** 230 * Sets the maximum number of migrations to the non-default network upon encountering write 231 * errors. Counted cumulatively per network per connection. 232 * 233 * <p>Only relevant if {@link #allowNonDefaultNetworkUsage(boolean)} is enabled. 234 * 235 * @return this builder for chaining 236 */ 237 @Experimental setMaxWriteErrorNonDefaultNetworkMigrationsCount( int maxWriteErrorEagerMigrationsCount)238 public Builder setMaxWriteErrorNonDefaultNetworkMigrationsCount( 239 int maxWriteErrorEagerMigrationsCount) { 240 this.mMaxWriteErrorEagerMigrationsCount = maxWriteErrorEagerMigrationsCount; 241 return this; 242 } 243 244 /** 245 * Sets the maximum number of migrations to the non-default network upon encountering path 246 * degradation for the existing connection. Counted cumulatively per network per connection. 247 * 248 * <p>Only relevant if {@link #allowNonDefaultNetworkUsage(boolean)} is enabled. 249 * 250 * @return this builder for chaining 251 */ 252 @Experimental setMaxPathDegradingNonDefaultNetworkMigrationsCount( int maxPathDegradingEagerMigrationsCount)253 public Builder setMaxPathDegradingNonDefaultNetworkMigrationsCount( 254 int maxPathDegradingEagerMigrationsCount) { 255 this.mMaxPathDegradingEagerMigrationsCount = maxPathDegradingEagerMigrationsCount; 256 return this; 257 } 258 259 /** 260 * Sets whether connections with pre-handshake errors should be retried on an alternative 261 * network. 262 * 263 * <p>If true, a new connection may be established an alternate network if it fails 264 * on the default network before handshake is confirmed. 265 * 266 * <p>Note: similarly to {@link #allowNonDefaultNetworkUsage(boolean)} this setting can 267 * result in requests being sent on non-default metered networks. Use with caution! 268 * 269 * @return this builder for chaining 270 */ 271 @Experimental retryPreHandshakeErrorsOnNonDefaultNetwork( boolean retryPreHandshakeErrorsOnAlternateNetwork)272 public Builder retryPreHandshakeErrorsOnNonDefaultNetwork( 273 boolean retryPreHandshakeErrorsOnAlternateNetwork) { 274 this.mRetryPreHandshakeErrorsOnAlternateNetwork = 275 retryPreHandshakeErrorsOnAlternateNetwork; 276 return this; 277 } 278 279 /** 280 * Creates and returns the final {@link ConnectionMigrationOptions} instance, based on the 281 * values in this builder. 282 */ build()283 public ConnectionMigrationOptions build() { 284 return new ConnectionMigrationOptions(this); 285 } 286 } 287 builder()288 public static Builder builder() { 289 return new Builder(); 290 } 291 292 /** 293 * An annotation for APIs which are not considered stable yet. 294 * 295 * <p>Experimental APIs are subject to change, breakage, or removal at any time and may not be 296 * production ready. 297 * 298 * <p>It's highly recommended to reach out to Cronet maintainers 299 * (<code>net-dev@chromium.org</code>) before using one of the APIs annotated as experimental 300 * outside of debugging and proof-of-concept code. 301 * 302 * <p>By using an Experimental API, applications acknowledge that they are doing so at their own 303 * risk. 304 */ 305 @RequiresOptIn 306 public @interface Experimental {} 307 } 308