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 android.net.http; 6 7 import android.annotation.IntDef; 8 import android.annotation.SuppressLint; 9 10 import androidx.annotation.NonNull; 11 import androidx.annotation.Nullable; 12 13 import java.lang.annotation.Retention; 14 import java.lang.annotation.RetentionPolicy; 15 import java.time.Duration; 16 17 /** 18 * A class configuring the HTTP connection migration functionality. 19 * 20 * <p>Connection migration stops open connections to servers from being destroyed when the 21 * client device switches its L4 connectivity (typically the IP address as a result of using 22 * a different network). This is particularly common with mobile devices losing 23 * wifi connectivity and switching to cellular data, or vice versa (a.k.a. the parking lot 24 * problem). QUIC uses connection identifiers which are independent of the underlying 25 * transport layer to make this possible. If the client connects to a new network and wants 26 * to preserve the existing connection, they can do so by using a connection identifier the server 27 * knows to be a continuation of the existing connection. 28 * 29 * <p>The features are only available for QUIC connections and the server needs to support 30 * connection migration. 31 * 32 * @see <a href="https://www.rfc-editor.org/rfc/rfc9000.html#section-9">Connection 33 * Migration specification</a> 34 */ 35 // SuppressLint to be consistent with other cronet code 36 @SuppressLint("UserHandleName") 37 public class ConnectionMigrationOptions { 38 private final @MigrationOptionState int mEnableDefaultNetworkMigration; 39 private final @MigrationOptionState int mEnablePathDegradationMigration; 40 @Nullable 41 private final Boolean mAllowServerMigration; 42 @Nullable 43 private final Boolean mMigrateIdleConnections; 44 @Nullable 45 private final Duration mIdleMigrationPeriod; 46 private final @MigrationOptionState int mAllowNonDefaultNetworkUsage; 47 @Nullable 48 private final Duration mMaxTimeOnNonDefaultNetwork; 49 @Nullable 50 private final Integer mMaxWriteErrorNonDefaultNetworkMigrationsCount; 51 @Nullable 52 private final Integer mMaxPathDegradingNonDefaultMigrationsCount; 53 54 /** 55 * Option is unspecified, platform default value will be used. 56 */ 57 public static final int MIGRATION_OPTION_UNSPECIFIED = 0; 58 59 /** 60 * Option is enabled. 61 */ 62 public static final int MIGRATION_OPTION_ENABLED = 1; 63 64 /** 65 * Option is disabled. 66 */ 67 public static final int MIGRATION_OPTION_DISABLED = 2; 68 69 /** @hide */ 70 @Retention(RetentionPolicy.SOURCE) 71 @IntDef(flag = false, prefix = "MIGRATION_OPTION_", value = { 72 MIGRATION_OPTION_UNSPECIFIED, 73 MIGRATION_OPTION_ENABLED, 74 MIGRATION_OPTION_DISABLED, 75 }) 76 public @interface MigrationOptionState {} 77 78 /** 79 * See {@link Builder#setDefaultNetworkMigration(int)} 80 */ getDefaultNetworkMigration()81 public @MigrationOptionState int getDefaultNetworkMigration() { 82 return mEnableDefaultNetworkMigration; 83 } 84 85 /** 86 * See {@link Builder#setPathDegradationMigration(int)} 87 */ getPathDegradationMigration()88 public @MigrationOptionState int getPathDegradationMigration() { 89 return mEnablePathDegradationMigration; 90 } 91 92 /** 93 * See {@link Builder#setAllowServerMigration} 94 * 95 * {@hide} 96 */ 97 @Nullable getAllowServerMigration()98 public Boolean getAllowServerMigration() { 99 return mAllowServerMigration; 100 } 101 102 /** 103 * See {@link Builder#setMigrateIdleConnections} 104 * 105 * {@hide} 106 */ 107 @Nullable getMigrateIdleConnections()108 public Boolean getMigrateIdleConnections() { 109 return mMigrateIdleConnections; 110 } 111 112 /** 113 * See {@link Builder#setIdleMigrationPeriodSeconds} 114 * 115 * {@hide} 116 */ 117 @Nullable getIdleMigrationPeriod()118 public Duration getIdleMigrationPeriod() { 119 return mIdleMigrationPeriod; 120 } 121 122 /** 123 * See {@link Builder#setAllowNonDefaultNetworkUsage(int)} 124 */ getAllowNonDefaultNetworkUsage()125 public @MigrationOptionState int getAllowNonDefaultNetworkUsage() { 126 return mAllowNonDefaultNetworkUsage; 127 } 128 129 /** 130 * See {@link Builder#setMaxTimeOnNonDefaultNetworkSeconds} 131 * 132 * {@hide} 133 */ 134 @Nullable getMaxTimeOnNonDefaultNetwork()135 public Duration getMaxTimeOnNonDefaultNetwork() { 136 return mMaxTimeOnNonDefaultNetwork; 137 } 138 139 /** 140 * See {@link Builder#setMaxWriteErrorNonDefaultNetworkMigrationsCount} 141 * 142 * {@hide} 143 */ 144 @Nullable getMaxWriteErrorNonDefaultNetworkMigrationsCount()145 public Integer getMaxWriteErrorNonDefaultNetworkMigrationsCount() { 146 return mMaxWriteErrorNonDefaultNetworkMigrationsCount; 147 } 148 149 /** 150 * See {@link Builder#setMaxPathDegradingNonDefaultNetworkMigrationsCount} 151 * 152 * {@hide} 153 */ 154 @Nullable getMaxPathDegradingNonDefaultMigrationsCount()155 public Integer getMaxPathDegradingNonDefaultMigrationsCount() { 156 return mMaxPathDegradingNonDefaultMigrationsCount; 157 } 158 ConnectionMigrationOptions(@onNull Builder builder)159 ConnectionMigrationOptions(@NonNull Builder builder) { 160 this.mEnableDefaultNetworkMigration = builder.mEnableDefaultNetworkMigration; 161 this.mEnablePathDegradationMigration = builder.mEnablePathDegradationMigration; 162 this.mAllowServerMigration = builder.mAllowServerMigration; 163 this.mMigrateIdleConnections = builder.mMigrateIdleConnections; 164 this.mIdleMigrationPeriod = builder.mIdleConnectionMigrationPeriod; 165 this.mAllowNonDefaultNetworkUsage = builder.mAllowNonDefaultNetworkUsage; 166 this.mMaxTimeOnNonDefaultNetwork = builder.mMaxTimeOnNonDefaultNetwork; 167 this.mMaxWriteErrorNonDefaultNetworkMigrationsCount = builder.mMaxWriteErrorNonDefaultNetworkMigrationsCount; 168 this.mMaxPathDegradingNonDefaultMigrationsCount = builder.mMaxPathDegradingNonDefaultMigrationsCount; 169 } 170 171 /** 172 * Builder for {@link ConnectionMigrationOptions}. 173 */ 174 public static final class Builder { 175 private @MigrationOptionState int mEnableDefaultNetworkMigration; 176 private @MigrationOptionState int mEnablePathDegradationMigration; 177 @Nullable 178 private Boolean mAllowServerMigration; 179 @Nullable 180 private Boolean mMigrateIdleConnections; 181 @Nullable 182 private Duration mIdleConnectionMigrationPeriod; 183 private @MigrationOptionState int mAllowNonDefaultNetworkUsage; 184 @Nullable 185 private Duration mMaxTimeOnNonDefaultNetwork; 186 @Nullable 187 private Integer mMaxWriteErrorNonDefaultNetworkMigrationsCount; 188 @Nullable 189 private Integer mMaxPathDegradingNonDefaultMigrationsCount; 190 Builder()191 public Builder() {} 192 193 /** 194 * Sets whether to enable the possibility of migrating connections on default network 195 * change. If enabled, active QUIC connections will be migrated onto the new network when 196 * the platform indicates that the default network is changing. 197 * 198 * @see <a href="https://developer.android.com/training/basics/network-ops/reading-network-state#listening-events">Android 199 * Network State</a> 200 * 201 * @param state one of the MIGRATION_OPTION_* values 202 * @return this builder for chaining 203 */ 204 @NonNull setDefaultNetworkMigration(@igrationOptionState int state)205 public Builder setDefaultNetworkMigration(@MigrationOptionState int state) { 206 this.mEnableDefaultNetworkMigration = state; 207 return this; 208 } 209 210 /** 211 * Sets whether to enable the possibility of migrating connections if the current path is 212 * performing poorly. 213 * 214 * <p>Depending on other configuration, this can result to migrating the connections within 215 * the same default network, or to a non-default network. 216 * 217 * @param state one of the MIGRATION_OPTION_* values 218 * @return this builder for chaining 219 */ 220 @NonNull setPathDegradationMigration(@igrationOptionState int state)221 public Builder setPathDegradationMigration(@MigrationOptionState int state) { 222 this.mEnablePathDegradationMigration = state; 223 return this; 224 } 225 226 /** 227 * Enables the possibility of migrating connections to an alternate server address 228 * at the server's request. 229 * 230 * @return this builder for chaining 231 * 232 * {@hide} 233 */ 234 @Experimental 235 @NonNull setAllowServerMigration(boolean allowServerMigration)236 public Builder setAllowServerMigration(boolean allowServerMigration) { 237 this.mAllowServerMigration = allowServerMigration; 238 return this; 239 } 240 241 /** 242 * Configures whether migration of idle connections should be enabled or not. 243 * 244 * <p>If set to true, idle connections will be migrated too, as long as they haven't been 245 * idle for too long. The setting is shared for all connection migration types. The maximum 246 * idle period for which connections will still be migrated can be customized using {@link 247 * #setIdleMigrationPeriodSeconds}. 248 * 249 * @return this builder for chaining 250 * 251 * {@hide} 252 */ 253 @Experimental 254 @NonNull setMigrateIdleConnections(boolean migrateIdleConnections)255 public Builder setMigrateIdleConnections(boolean migrateIdleConnections) { 256 this.mMigrateIdleConnections = migrateIdleConnections; 257 return this; 258 } 259 260 /** 261 * Sets the maximum idle period for which connections will still be migrated, in seconds. 262 * The setting is shared for all connection migration types. 263 * 264 * <p>Only relevant if {@link #setMigrateIdleConnections(boolean)} is enabled. 265 * 266 * @return this builder for chaining 267 * 268 * {@hide} 269 */ 270 @Experimental 271 @NonNull setIdleMigrationPeriodSeconds( @onNull Duration idleConnectionMigrationPeriod)272 public Builder setIdleMigrationPeriodSeconds( 273 @NonNull Duration idleConnectionMigrationPeriod) { 274 this.mIdleConnectionMigrationPeriod = idleConnectionMigrationPeriod; 275 return this; 276 } 277 278 /** 279 * Sets whether connections can be migrated to an alternate network when Cronet detects 280 * a degradation of the path currently in use. Requires setting 281 * {@link #setPathDegradationMigration(int)} to {@link #MIGRATION_OPTION_ENABLED} to 282 * have any effect. 283 * 284 * <p>Note: This setting can result in requests being sent on non-default metered networks, 285 * eating into the users' data budgets and incurring extra costs. Make sure you're using 286 * metered networks sparingly. 287 * 288 * @param state one of the MIGRATION_OPTION_* values 289 * @return this builder for chaining 290 */ 291 @Experimental 292 @NonNull setAllowNonDefaultNetworkUsage(@igrationOptionState int state)293 public Builder setAllowNonDefaultNetworkUsage(@MigrationOptionState int state) { 294 this.mAllowNonDefaultNetworkUsage = state; 295 return this; 296 } 297 298 /** 299 * Sets the maximum period for which connections migrated to non-default networks remain 300 * there before they're migrated back. This time is not cumulative - each migration off 301 * the default network for each connection measures and compares to this value separately. 302 * 303 * <p>Only relevant if {@link #setAllowNonDefaultNetworkUsage(int)} is set to 304 * {@link #MIGRATION_OPTION_ENABLED} 305 * 306 * @return this builder for chaining 307 * 308 * {@hide} 309 */ 310 @Experimental 311 @NonNull setMaxTimeOnNonDefaultNetworkSeconds( @onNull Duration maxTimeOnNonDefaultNetwork)312 public Builder setMaxTimeOnNonDefaultNetworkSeconds( 313 @NonNull Duration maxTimeOnNonDefaultNetwork) { 314 this.mMaxTimeOnNonDefaultNetwork = maxTimeOnNonDefaultNetwork; 315 return this; 316 } 317 318 /** 319 * Sets the maximum number of migrations to the non-default network upon encountering write 320 * errors. Counted cumulatively per network per connection. 321 * 322 * <p>Only relevant if {@link #setAllowNonDefaultNetworkUsage(int)} is set to 323 * {@link #MIGRATION_OPTION_ENABLED} 324 * 325 * @return this builder for chaining 326 * 327 * {@hide} 328 */ 329 @Experimental 330 @NonNull setMaxWriteErrorNonDefaultNetworkMigrationsCount( int maxWriteErrorNonDefaultMigrationsCount)331 public Builder setMaxWriteErrorNonDefaultNetworkMigrationsCount( 332 int maxWriteErrorNonDefaultMigrationsCount) { 333 this.mMaxWriteErrorNonDefaultNetworkMigrationsCount = maxWriteErrorNonDefaultMigrationsCount; 334 return this; 335 } 336 337 /** 338 * Sets the maximum number of migrations to the non-default network upon encountering path 339 * degradation for the existing connection. Counted cumulatively per network per connection. 340 * 341 * <p>Only relevant if {@link #setAllowNonDefaultNetworkUsage(int)} is set to 342 * {@link #MIGRATION_OPTION_ENABLED} 343 * 344 * @return this builder for chaining 345 * 346 * {@hide} 347 */ 348 @Experimental 349 @NonNull setMaxPathDegradingNonDefaultNetworkMigrationsCount( int maxPathDegradingNonDefaultMigrationsCount)350 public Builder setMaxPathDegradingNonDefaultNetworkMigrationsCount( 351 int maxPathDegradingNonDefaultMigrationsCount) { 352 this.mMaxPathDegradingNonDefaultMigrationsCount = maxPathDegradingNonDefaultMigrationsCount; 353 return this; 354 } 355 356 /** 357 * Creates and returns the final {@link ConnectionMigrationOptions} instance, based on the 358 * values in this builder. 359 */ 360 @NonNull build()361 public ConnectionMigrationOptions build() { 362 return new ConnectionMigrationOptions(this); 363 } 364 } 365 366 /** 367 * Creates a new builder for {@link ConnectionMigrationOptions}. 368 * 369 * {@hide} 370 */ 371 @NonNull builder()372 public static Builder builder() { 373 return new Builder(); 374 } 375 376 /** 377 * An annotation for APIs which are not considered stable yet. 378 * 379 * <p>Applications using experimental APIs must acknowledge that they're aware of using APIs 380 * that are not considered stable. The APIs might change functionality, break or cease to exist 381 * without notice. 382 * 383 * <p>It's highly recommended to reach out to Cronet maintainers ({@code net-dev@chromium.org}) 384 * before using one of the APIs annotated as experimental outside of debugging 385 * and proof-of-concept code. Be ready to help to help polishing the API, or for a "sorry, 386 * really not production ready yet". 387 * 388 * <p>If you still want to use an experimental API in production, you're doing so at your 389 * own risk. You have been warned. 390 * 391 * {@hide} 392 */ 393 public @interface Experimental {} 394 } 395