From 944309551787611d13388a6659e2e0523f974f56 Mon Sep 17 00:00:00 2001 From: paulhu Date: Tue, 23 Mar 2021 00:24:50 +0800 Subject: [PATCH] Expose ConnectivitySettingsManager as module-lib API Have getter/setter methods for external modules that can get/set the setting values. Bug: 182538166 Test: atest FrameworksNetTests Merged-In: I82225a43f95e3e1d3e52c4e7a0fc541c0087292e Change-Id: I61cb00216494e35b6e0dfe444b177cad36ad8afb --- framework/api/module-lib-current.txt | 44 ++ .../net/ConnectivitySettingsManager.java | 657 +++++++++++++++++- 2 files changed, 700 insertions(+), 1 deletion(-) diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt index ac69a3e795..3a4cebc93a 100644 --- a/framework/api/module-lib-current.txt +++ b/framework/api/module-lib-current.txt @@ -40,6 +40,50 @@ package android.net { field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1 } + public class ConnectivitySettingsManager { + method public static void clearGlobalProxy(@NonNull android.content.Context); + method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context); + method public static int getCaptivePortalMode(@NonNull android.content.Context, int); + method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method @NonNull public static android.util.Range getDnsResolverSampleRanges(@NonNull android.content.Context); + method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int); + method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context); + method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean); + method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context); + method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context); + method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int); + method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context); + method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context); + method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean); + method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String); + method public static void setCaptivePortalMode(@NonNull android.content.Context, int); + method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range); + method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int); + method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo); + method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean); + method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int); + method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String); + method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int); + method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull String); + method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String); + method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean); + method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 + field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1 + field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2 + field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0 + field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1 + } + public final class NetworkAgentConfig implements android.os.Parcelable { method @Nullable public String getSubscriberId(); method public boolean isBypassableVpn(); diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java index bbd83931ee..e133d5e53f 100644 --- a/framework/src/android/net/ConnectivitySettingsManager.java +++ b/framework/src/android/net/ConnectivitySettingsManager.java @@ -16,16 +16,38 @@ package android.net; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; + import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; +import android.net.ConnectivityManager.MultipathPreference; +import android.net.ConnectivityManager.PrivateDnsMode; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Range; + +import com.android.net.module.util.ProxyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Duration; +import java.util.List; /** * A manager class for connectivity module settings. * * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public class ConnectivitySettingsManager { private ConnectivitySettingsManager() {} @@ -45,12 +67,16 @@ public class ConnectivitySettingsManager { * Network activity refers to transmitting or receiving data on the network interfaces. * * Tracking is disabled if set to zero or negative value. + * + * @hide */ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile"; /** * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE} * but for Wifi network. + * + * @hide */ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi"; @@ -58,12 +84,16 @@ public class ConnectivitySettingsManager { /** * Sample validity in seconds to configure for the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS = "dns_resolver_sample_validity_seconds"; /** * Success threshold in percent for use with the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT = "dns_resolver_success_threshold_percent"; @@ -71,24 +101,35 @@ public class ConnectivitySettingsManager { /** * Minimum number of samples needed for statistics to be considered meaningful in the * system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples"; /** * Maximum number taken into account for statistics purposes in the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples"; + private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; + private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; + /** Network switch notification settings */ /** * The maximum number of notifications shown in 24 hours when switching networks. + * + * @hide */ public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = "network_switch_notification_daily_limit"; /** * The minimum time in milliseconds between notifications when switching networks. + * + * @hide */ public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = "network_switch_notification_rate_limit_millis"; @@ -98,14 +139,18 @@ public class ConnectivitySettingsManager { /** * The URL used for HTTP captive portal detection upon a new connection. * A 204 response code from the server is used for validation. + * + * @hide */ public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; /** * What to do when connecting a network that presents a captive portal. - * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. + * Must be one of the CAPTIVE_PORTAL_MODE_* constants below. * * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. + * + * @hide */ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; @@ -139,11 +184,15 @@ public class ConnectivitySettingsManager { /** * Host name for global http proxy. Set via ConnectivityManager. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host"; /** * Integer host port for global http proxy. Set via ConnectivityManager. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port"; @@ -153,12 +202,16 @@ public class ConnectivitySettingsManager { * Domains should be listed in a comma- separated list. Example of * acceptable formats: ".domain1.com,my.domain2.com" Use * ConnectivityManager to set/get. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list"; /** * The location PAC File for the proxy. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url"; @@ -171,11 +224,15 @@ public class ConnectivitySettingsManager { * a specific provider. It may be used to store the provider name even when the * mode changes so that temporarily disabling and re-enabling the specific * provider mode does not necessitate retyping the provider hostname. + * + * @hide */ public static final String PRIVATE_DNS_MODE = "private_dns_mode"; /** * The specific Private DNS provider name. + * + * @hide */ public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier"; @@ -185,6 +242,8 @@ public class ConnectivitySettingsManager { * all of which require explicit user action to enable/configure. See also b/79719289. * * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above. + * + * @hide */ public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode"; @@ -194,6 +253,8 @@ public class ConnectivitySettingsManager { * The number of milliseconds to hold on to a PendingIntent based request. This delay gives * the receivers of the PendingIntent an opportunity to make a new network request before * the Network satisfying the request is potentially removed. + * + * @hide */ public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS = "connectivity_release_pending_intent_delay_ms"; @@ -205,6 +266,8 @@ public class ConnectivitySettingsManager { * See ConnectivityService for more info. * * (0 = disabled, 1 = enabled) + * + * @hide */ public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; @@ -217,6 +280,8 @@ public class ConnectivitySettingsManager { * See ConnectivityService for more info. * * (0 = disabled, 1 = enabled) + * + * @hide */ public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; @@ -228,14 +293,604 @@ public class ConnectivitySettingsManager { * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. * null: Ask the user whether to switch away from bad wifi. * 1: Avoid bad wifi. + * + * @hide */ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; + /** + * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. + */ + public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; + + /** + * Ask the user whether to switch away from bad wifi. + */ + public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; + + /** + * Avoid bad wifi. + */ + public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + NETWORK_AVOID_BAD_WIFI_IGNORE, + NETWORK_AVOID_BAD_WIFI_PROMPT, + NETWORK_AVOID_BAD_WIFI_AVOID, + }) + public @interface NetworkAvoidBadWifi {} + /** * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be * overridden by the system based on device or application state. If null, the value * specified by config_networkMeteredMultipathPreference is used. + * + * @hide */ public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference"; + + /** + * Get mobile data activity timeout from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default timeout if no setting value. + * @return The {@link Duration} of timeout to track mobile data activity. + */ + @NonNull + public static Duration getMobileDataActivityTimeout(@NonNull Context context, + @NonNull Duration def) { + final int timeout = Settings.Global.getInt( + context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds()); + return Duration.ofSeconds(timeout); + } + + /** + * Set mobile data activity timeout to {@link Settings}. + * Tracking is disabled if set to zero or negative value. + * + * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be + * ignored. + * + * @param context The {@link Context} to set the setting. + * @param timeout The mobile data activity timeout. + */ + public static void setMobileDataActivityTimeout(@NonNull Context context, + @NonNull Duration timeout) { + Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, + (int) timeout.getSeconds()); + } + + /** + * Get wifi data activity timeout from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default timeout if no setting value. + * @return The {@link Duration} of timeout to track wifi data activity. + */ + @NonNull + public static Duration getWifiDataActivityTimeout(@NonNull Context context, + @NonNull Duration def) { + final int timeout = Settings.Global.getInt( + context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds()); + return Duration.ofSeconds(timeout); + } + + /** + * Set wifi data activity timeout to {@link Settings}. + * Tracking is disabled if set to zero or negative value. + * + * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be + * ignored. + * + * @param context The {@link Context} to set the setting. + * @param timeout The wifi data activity timeout. + */ + public static void setWifiDataActivityTimeout(@NonNull Context context, + @NonNull Duration timeout) { + Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, + (int) timeout.getSeconds()); + } + + /** + * Get dns resolver sample validity duration from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default duration if no setting value. + * @return The {@link Duration} of sample validity duration to configure for the system DNS + * resolver. + */ + @NonNull + public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds()); + return Duration.ofSeconds(duration); + } + + /** + * Set dns resolver sample validity duration to {@link Settings}. The duration must be a + * positive number of seconds. + * + * @param context The {@link Context} to set the setting. + * @param duration The sample validity duration. + */ + public static void setDnsResolverSampleValidityDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.getSeconds(); + if (time <= 0) { + throw new IllegalArgumentException("Invalid duration"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time); + } + + /** + * Get dns resolver success threshold percent from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return The success threshold in percent for use with the system DNS resolver. + */ + public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) { + return Settings.Global.getInt( + context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def); + } + + /** + * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must + * be 0~100. + * + * @param context The {@link Context} to set the setting. + * @param percent The success threshold percent. + */ + public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context, + @IntRange(from = 0, to = 100) int percent) { + if (percent < 0 || percent > 100) { + throw new IllegalArgumentException("Percent must be 0~100"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent); + } + + /** + * Get dns resolver samples range from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The {@link Range} of samples needed for statistics to be considered + * meaningful in the system DNS resolver. + */ + @NonNull + public static Range getDnsResolverSampleRanges(@NonNull Context context) { + final int minSamples = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); + final int maxSamples = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); + return new Range<>(minSamples, maxSamples); + } + + /** + * Set dns resolver samples range to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param range The samples range. The minimum number should be more than 0 and the maximum + * number should be less that 64. + */ + public static void setDnsResolverSampleRanges(@NonNull Context context, + @NonNull Range range) { + if (range.getLower() < 0 || range.getUpper() > 64) { + throw new IllegalArgumentException("Argument must be 0~64"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower()); + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper()); + } + + /** + * Get maximum count (from {@link Settings}) of switching network notifications shown in 24 + * hours. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return The maximum count of notifications shown in 24 hours when switching networks. + */ + public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, + int def) { + return Settings.Global.getInt( + context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def); + } + + /** + * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours. + * The count must be at least 0. + * + * @param context The {@link Context} to set the setting. + * @param count The maximum count of switching network notifications shown in 24 hours. + */ + public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, + @IntRange(from = 0) int count) { + if (count < 0) { + throw new IllegalArgumentException("Count must be 0~10."); + } + Settings.Global.putInt( + context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count); + } + + /** + * Get minimum duration (from {@link Settings}) between each switching network notifications. + * + * @param context The {@link Context} to query the setting. + * @param def The default time if no setting value. + * @return The minimum duration between notifications when switching networks. + */ + @NonNull + public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Global.getInt(context.getContentResolver(), + NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis()); + return Duration.ofMillis(duration); + } + + /** + * Set minimum duration (to {@link Settings}) between each switching network notifications. + * + * @param context The {@link Context} to set the setting. + * @param duration The minimum duration between notifications when switching networks. + */ + public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.toMillis(); + if (time < 0) { + throw new IllegalArgumentException("Invalid duration."); + } + Settings.Global.putInt(context.getContentResolver(), + NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time); + } + + /** + * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection. + * + * @param context The {@link Context} to query the setting. + * @return The URL used for HTTP captive portal detection upon a new connection. + */ + @Nullable + public static String getCaptivePortalHttpUrl(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL); + } + + /** + * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection. + * This URL should respond with a 204 response to a GET request to indicate no captive portal is + * present. And this URL must be HTTP as redirect responses are used to find captive portal + * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal + * detection failed and lost the connection. + * + * @param context The {@link Context} to set the setting. + * @param url The URL used for HTTP captive portal detection upon a new connection. + */ + public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) { + Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url); + } + + /** + * Get mode (from {@link Settings}) when connecting a network that presents a captive portal. + * + * @param context The {@link Context} to query the setting. + * @param def The default mode if no setting value. + * @return The mode when connecting a network that presents a captive portal. + */ + @CaptivePortalMode + public static int getCaptivePortalMode(@NonNull Context context, + @CaptivePortalMode int def) { + return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def); + } + + /** + * Set mode (to {@link Settings}) when connecting a network that presents a captive portal. + * + * @param context The {@link Context} to set the setting. + * @param mode The mode when connecting a network that presents a captive portal. + */ + public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) { + if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE + || mode == CAPTIVE_PORTAL_MODE_PROMPT + || mode == CAPTIVE_PORTAL_MODE_AVOID)) { + throw new IllegalArgumentException("Invalid captive portal mode"); + } + Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode); + } + + /** + * Get the global HTTP proxy applied to the device, or null if none. + * + * @param context The {@link Context} to query the setting. + * @return The {@link ProxyInfo} which build from global http proxy settings. + */ + @Nullable + public static ProxyInfo getGlobalProxy(@NonNull Context context) { + final String host = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST); + final int port = Settings.Global.getInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */); + final String exclusionList = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST); + final String pacFileUrl = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC); + + if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) { + return null; // No global proxy. + } + + if (TextUtils.isEmpty(pacFileUrl)) { + return ProxyInfo.buildDirectProxy( + host, port, ProxyUtils.exclusionStringAsList(exclusionList)); + } else { + return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl)); + } + } + + /** + * Set global http proxy settings from given {@link ProxyInfo}. + * + * @param context The {@link Context} to set the setting. + * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from + * {@link ProxyInfo#buildPacProxy(Uri)} or + * {@link ProxyInfo#buildDirectProxy(String, int, List)} + */ + public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) { + final String host = proxyInfo.getHost(); + final int port = proxyInfo.getPort(); + final String exclusionList = proxyInfo.getExclusionListAsString(); + final String pacFileUrl = proxyInfo.getPacFileUrl().toString(); + + if (TextUtils.isEmpty(pacFileUrl)) { + Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host); + Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); + } else { + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); + Settings.Global.putInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); + } + } + + /** + * Clear all global http proxy settings. + * + * @param context The {@link Context} to set the setting. + */ + public static void clearGlobalProxy(@NonNull Context context) { + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); + Settings.Global.putInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); + } + + /** + * Get specific private dns provider name from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The specific private dns provider name, or null if no setting value. + */ + @Nullable + public static String getPrivateDnsHostname(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER); + } + + /** + * Set specific private dns provider name to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param specifier The specific private dns provider name. + */ + public static void setPrivateDnsHostname(@NonNull Context context, + @Nullable String specifier) { + Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER, specifier); + } + + /** + * Get default private dns mode from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The default private dns mode. + */ + @PrivateDnsMode + @NonNull + public static String getPrivateDnsDefaultMode(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE); + } + + /** + * Set default private dns mode to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_* + * constants. + */ + public static void setPrivateDnsDefaultMode(@NonNull Context context, + @NonNull @PrivateDnsMode String mode) { + if (!(mode == PRIVATE_DNS_MODE_OFF + || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC + || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { + throw new IllegalArgumentException("Invalid private dns mode"); + } + Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, mode); + } + + /** + * Get duration (from {@link Settings}) to keep a PendingIntent-based request. + * + * @param context The {@link Context} to query the setting. + * @param def The default duration if no setting value. + * @return The duration to keep a PendingIntent-based request. + */ + @NonNull + public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Secure.getInt(context.getContentResolver(), + CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis()); + return Duration.ofMillis(duration); + } + + /** + * Set duration (to {@link Settings}) to keep a PendingIntent-based request. + * + * @param context The {@link Context} to set the setting. + * @param duration The duration to keep a PendingIntent-based request. + */ + public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.toMillis(); + if (time < 0) { + throw new IllegalArgumentException("Invalid duration."); + } + Settings.Secure.putInt( + context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time); + } + + /** + * Read from {@link Settings} whether the mobile data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return Whether the mobile data connection should remain active even when higher + * priority networks are active. + */ + public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) { + final int enable = Settings.Global.getInt( + context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0)); + return (enable != 0) ? true : false; + } + + /** + * Write into {@link Settings} whether the mobile data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to set the setting. + * @param enable Whether the mobile data connection should remain active even when higher + * priority networks are active. + */ + public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) { + Settings.Global.putInt( + context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0)); + } + + /** + * Read from {@link Settings} whether the wifi data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return Whether the wifi data connection should remain active even when higher + * priority networks are active. + */ + public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) { + final int enable = Settings.Global.getInt( + context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0)); + return (enable != 0) ? true : false; + } + + /** + * Write into {@link Settings} whether the wifi data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to set the setting. + * @param enable Whether the wifi data connection should remain active even when higher + * priority networks are active + */ + public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) { + Settings.Global.putInt( + context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0)); + } + + /** + * Get avoid bad wifi setting from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The setting whether to automatically switch away from wifi networks that lose + * internet access. + */ + @NetworkAvoidBadWifi + public static int getNetworkAvoidBadWifi(@NonNull Context context) { + final String setting = + Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI); + if ("0".equals(setting)) { + return NETWORK_AVOID_BAD_WIFI_IGNORE; + } else if ("1".equals(setting)) { + return NETWORK_AVOID_BAD_WIFI_AVOID; + } else { + return NETWORK_AVOID_BAD_WIFI_PROMPT; + } + } + + /** + * Set avoid bad wifi setting to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param value Whether to automatically switch away from wifi networks that lose internet + * access. + */ + public static void setNetworkAvoidBadWifi(@NonNull Context context, + @NetworkAvoidBadWifi int value) { + final String setting; + if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) { + setting = "0"; + } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) { + setting = "1"; + } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) { + setting = null; + } else { + throw new IllegalArgumentException("Invalid avoid bad wifi setting"); + } + Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting); + } + + /** + * Get network metered multipath preference from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The network metered multipath preference which should be one of + * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified + * by config_networkMeteredMultipathPreference is used. + */ + @Nullable + public static String getNetworkMeteredMultipathPreference(@NonNull Context context) { + return Settings.Global.getString( + context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE); + } + + /** + * Set network metered multipath preference to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param preference The network metered multipath preference which should be one of + * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value + * specified by config_networkMeteredMultipathPreference is used. + */ + public static void setNetworkMeteredMultipathPreference(@NonNull Context context, + @NonNull @MultipathPreference String preference) { + if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER + || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY + || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) { + throw new IllegalArgumentException("Invalid private dns mode"); + } + Settings.Global.putString( + context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference); + } }