| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.cronet_sample_apk; |
| |
| import androidx.annotation.OptIn; |
| |
| import org.chromium.net.ConnectionMigrationOptions; |
| |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Adding an option here will make it show up in the list of available options. |
| * Each {@link Option} has the following attributes: |
| * <ul> |
| * <li>A short name which appears in bold on the options list.</li> |
| * <li>A description which provides a thorough explanation of what this option does.</li> |
| * <li>An {@link Action} which is applied on CronetEngine's Builder each time the user hits "Reset |
| * Engine". </li> <li>A default value, every option must have a default value.</li> |
| * </ul> |
| * <b>NOTE</b>: Each option must map to one {@link OptionsIdentifier OptionsIdentifier}. This is |
| * necessary to provide custom implementation for options that does not configure the builders. See |
| * {@link OptionsIdentifier#SLOW_DOWNLOAD} as an example. |
| * |
| * <p> To add a new option, do the following: |
| * <ol> |
| * <li> Add a new optionIdentifier {@link OptionsIdentifier} </li> |
| * <li> Inject a new Option instance into your optionIdentifier enum value. </li> |
| * <li> Implement the logic for the new option within a new {@link Action}. </li> |
| * <li> If the {@link Action} interface is not enough to satisfy the use-case. Feel free to add |
| * custom logic, See {@link OptionsIdentifier#SLOW_DOWNLOAD} as an example.</li> |
| * <li> Restart the APK and verify that your option is working as intended. </li> |
| * </ol> |
| */ |
| public class Options { |
| public enum OptionsIdentifier { |
| MIGRATE_SESSIONS_ON_NETWORK_CHANGE_V2( |
| new BooleanOption( |
| "migrate_sessions_on_network_change_v2", |
| "Enable QUIC connection migration. This only occurs when a network has " |
| + "completed" |
| + " disconnected and no longer reachable. QUIC will try to migrate " |
| + "the current session(maybe idle? depending on another option) to " |
| + "another network.", |
| new Action<>() { |
| @Override |
| public void configureBuilder(ActionData data, Boolean value) { |
| data.getMigrationBuilder().enableDefaultNetworkMigration(value); |
| } |
| }, |
| false)), |
| MIGRATION_SESSION_EARLY_V2( |
| new BooleanOption( |
| "migrate_sessions_early_v2", |
| "Enable QUIC early session migration. This will make quic send probing" |
| + " packets when the network is degrading, QUIC will migrate the " |
| + "sessions to a different network even before the original network " |
| + "has disconnected.", |
| new Action<Boolean>() { |
| @Override |
| @OptIn(markerClass = ConnectionMigrationOptions.Experimental.class) |
| public void configureBuilder(ActionData data, Boolean value) { |
| data.getMigrationBuilder().enablePathDegradationMigration(value); |
| data.getMigrationBuilder().allowNonDefaultNetworkUsage(value); |
| } |
| }, |
| false)), |
| SLOW_DOWNLOAD( |
| new BooleanOption( |
| "Slow Download (10s)", |
| "Hang the onReadCompleted for 10s before proceeding. This should simulate slow connection.", |
| new Action<>() {}, |
| false)); |
| |
| private final Option<?> mOption; |
| |
| OptionsIdentifier(Option<?> option) { |
| this.mOption = option; |
| } |
| |
| public Option<?> getOption() { |
| return mOption; |
| } |
| } |
| |
| private static final Map<OptionsIdentifier, Option> OPTIONS = |
| Collections.unmodifiableMap(createOptionsMap()); |
| private static final List<Option> OPTION_LIST = List.copyOf(OPTIONS.values()); |
| |
| private static Map<OptionsIdentifier, Option> createOptionsMap() { |
| Map<OptionsIdentifier, Option> optionsMap = new LinkedHashMap<>(); |
| for (OptionsIdentifier optionIdentifier : OptionsIdentifier.values()) { |
| optionsMap.put(optionIdentifier, optionIdentifier.getOption()); |
| } |
| return optionsMap; |
| } |
| |
| public abstract static class Option<T> { |
| private final String mOptionName; |
| private final String mOptionDescription; |
| private final Action<T> mAction; |
| private T mOptionValue; |
| |
| private Option( |
| String optionName, String optionDescription, Action<T> action, T defaultValue) { |
| this.mOptionName = optionName; |
| this.mOptionDescription = optionDescription; |
| this.mAction = action; |
| this.mOptionValue = defaultValue; |
| } |
| |
| public String getShortName() { |
| return mOptionName; |
| } |
| |
| public String getDescription() { |
| return mOptionDescription; |
| } |
| |
| void configure(ActionData data) { |
| mAction.configureBuilder(data, getValue()); |
| } |
| |
| abstract Class<T> getInputType(); |
| |
| T getValue() { |
| return mOptionValue; |
| } |
| |
| void setValue(T newValue) { |
| mOptionValue = newValue; |
| } |
| } |
| |
| public static class BooleanOption extends Option<Boolean> { |
| private BooleanOption( |
| String optionName, |
| String optionDescription, |
| Action<Boolean> action, |
| Boolean defaultValue) { |
| super(optionName, optionDescription, action, defaultValue); |
| } |
| |
| @Override |
| Class<Boolean> getInputType() { |
| return Boolean.class; |
| } |
| } |
| |
| private Options() {} |
| |
| public static List<Option> getOptions() { |
| return OPTION_LIST; |
| } |
| |
| public static boolean isBooleanOptionOn(OptionsIdentifier identifier) { |
| if (!OPTIONS.containsKey(identifier)) { |
| throw new IllegalArgumentException( |
| "The provided identifier does not map to any option"); |
| } |
| if (!OPTIONS.get(identifier).getInputType().equals(Boolean.class)) { |
| throw new IllegalStateException("The provided identifier maps to a non-boolean value"); |
| } |
| return (boolean) OPTIONS.get(identifier).getValue(); |
| } |
| } |