• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package android.net;
18 
19 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER;
20 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE;
21 import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY;
22 
23 import static com.android.net.module.util.ConnectivitySettingsUtils.getPrivateDnsModeAsString;
24 
25 import android.annotation.IntDef;
26 import android.annotation.IntRange;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.SystemApi;
30 import android.content.Context;
31 import android.net.ConnectivityManager.MultipathPreference;
32 import android.os.Binder;
33 import android.os.Build;
34 import android.os.Process;
35 import android.os.UserHandle;
36 import android.provider.Settings;
37 import android.text.TextUtils;
38 import android.util.ArraySet;
39 import android.util.Range;
40 
41 import com.android.net.module.util.ConnectivitySettingsUtils;
42 import com.android.net.module.util.ProxyUtils;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.time.Duration;
47 import java.util.List;
48 import java.util.Set;
49 import java.util.StringJoiner;
50 
51 /**
52  * A manager class for connectivity module settings.
53  *
54  * @hide
55  */
56 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
57 public class ConnectivitySettingsManager {
58 
ConnectivitySettingsManager()59     private ConnectivitySettingsManager() {}
60 
61     /** Data activity timeout settings */
62 
63     /**
64      * Inactivity timeout to track mobile data activity.
65      *
66      * If set to a positive integer, it indicates the inactivity timeout value in seconds to
67      * infer the data activity of mobile network. After a period of no activity on mobile
68      * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
69      * intent is fired to indicate a transition of network status from "active" to "idle". Any
70      * subsequent activity on mobile networks triggers the firing of {@code
71      * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
72      *
73      * Network activity refers to transmitting or receiving data on the network interfaces.
74      *
75      * Tracking is disabled if set to zero or negative value.
76      *
77      * @hide
78      */
79     public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
80 
81     /**
82      * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
83      * but for Wifi network.
84      *
85      * @hide
86      */
87     public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
88 
89     /** Dns resolver settings */
90 
91     /**
92      * Sample validity in seconds to configure for the system DNS resolver.
93      *
94      * @hide
95      */
96     public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
97             "dns_resolver_sample_validity_seconds";
98 
99     /**
100      * Success threshold in percent for use with the system DNS resolver.
101      *
102      * @hide
103      */
104     public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
105             "dns_resolver_success_threshold_percent";
106 
107     /**
108      * Minimum number of samples needed for statistics to be considered meaningful in the
109      * system DNS resolver.
110      *
111      * @hide
112      */
113     public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
114 
115     /**
116      * Maximum number taken into account for statistics purposes in the system DNS resolver.
117      *
118      * @hide
119      */
120     public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
121 
122     private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
123     private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
124 
125     /** Network switch notification settings */
126 
127     /**
128      * The maximum number of notifications shown in 24 hours when switching networks.
129      *
130      * @hide
131      */
132     public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
133             "network_switch_notification_daily_limit";
134 
135     /**
136      * The minimum time in milliseconds between notifications when switching networks.
137      *
138      * @hide
139      */
140     public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
141             "network_switch_notification_rate_limit_millis";
142 
143     /** Captive portal settings */
144 
145     /**
146      * The URL used for HTTP captive portal detection upon a new connection.
147      * A 204 response code from the server is used for validation.
148      *
149      * @hide
150      */
151     public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
152 
153     /**
154      * What to do when connecting a network that presents a captive portal.
155      * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
156      *
157      * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
158      *
159      * @hide
160      */
161     public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
162 
163     /**
164      * Don't attempt to detect captive portals.
165      */
166     public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
167 
168     /**
169      * When detecting a captive portal, display a notification that
170      * prompts the user to sign in.
171      */
172     public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
173 
174     /**
175      * When detecting a captive portal, immediately disconnect from the
176      * network and do not reconnect to that network in the future.
177      */
178     public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
179 
180     /** @hide */
181     @Retention(RetentionPolicy.SOURCE)
182     @IntDef(value = {
183             CAPTIVE_PORTAL_MODE_IGNORE,
184             CAPTIVE_PORTAL_MODE_PROMPT,
185             CAPTIVE_PORTAL_MODE_AVOID,
186     })
187     public @interface CaptivePortalMode {}
188 
189     /** Global http proxy settings */
190 
191     /**
192      * Host name for global http proxy. Set via ConnectivityManager.
193      *
194      * @hide
195      */
196     public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
197 
198     /**
199      * Integer host port for global http proxy. Set via ConnectivityManager.
200      *
201      * @hide
202      */
203     public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
204 
205     /**
206      * Exclusion list for global proxy. This string contains a list of
207      * comma-separated domains where the global proxy does not apply.
208      * Domains should be listed in a comma- separated list. Example of
209      * acceptable formats: ".domain1.com,my.domain2.com" Use
210      * ConnectivityManager to set/get.
211      *
212      * @hide
213      */
214     public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
215             "global_http_proxy_exclusion_list";
216 
217     /**
218      * The location PAC File for the proxy.
219      *
220      * @hide
221      */
222     public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
223 
224     /** Private dns settings */
225 
226     /**
227      * The requested Private DNS mode (string), and an accompanying specifier (string).
228      *
229      * Currently, the specifier holds the chosen provider name when the mode requests
230      * a specific provider. It may be used to store the provider name even when the
231      * mode changes so that temporarily disabling and re-enabling the specific
232      * provider mode does not necessitate retyping the provider hostname.
233      *
234      * @hide
235      */
236     public static final String PRIVATE_DNS_MODE = "private_dns_mode";
237 
238     /**
239      * The specific Private DNS provider name.
240      *
241      * @hide
242      */
243     public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
244 
245     /**
246      * Forced override of the default mode (hardcoded as "automatic", nee "opportunistic").
247      * This allows changing the default mode without effectively disabling other modes,
248      * all of which require explicit user action to enable/configure. See also b/79719289.
249      *
250      * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above.
251      *
252      * @hide
253      */
254     public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
255 
256     /** Other settings */
257 
258     /**
259      * The number of milliseconds to hold on to a PendingIntent based request. This delay gives
260      * the receivers of the PendingIntent an opportunity to make a new network request before
261      * the Network satisfying the request is potentially removed.
262      *
263      * @hide
264      */
265     public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
266             "connectivity_release_pending_intent_delay_ms";
267 
268     /**
269      * Whether the mobile data connection should remain active even when higher
270      * priority networks like WiFi are active, to help make network switching faster.
271      *
272      * See ConnectivityService for more info.
273      *
274      * (0 = disabled, 1 = enabled)
275      *
276      * @hide
277      */
278     public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
279 
280     /**
281      * Whether the wifi data connection should remain active even when higher
282      * priority networks like Ethernet are active, to keep both networks.
283      * In the case where higher priority networks are connected, wifi will be
284      * unused unless an application explicitly requests to use it.
285      *
286      * See ConnectivityService for more info.
287      *
288      * (0 = disabled, 1 = enabled)
289      *
290      * @hide
291      */
292     public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
293 
294     /**
295      * Whether to automatically switch away from wifi networks that lose Internet access.
296      * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
297      * avoids such networks. Valid values are:
298      *
299      * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
300      * null: Ask the user whether to switch away from bad wifi.
301      * 1: Avoid bad wifi.
302      *
303      * @hide
304      */
305     public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
306 
307     /**
308      * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
309      */
310     public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0;
311 
312     /**
313      * Ask the user whether to switch away from bad wifi.
314      */
315     public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1;
316 
317     /**
318      * Avoid bad wifi.
319      */
320     public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2;
321 
322     /** @hide */
323     @Retention(RetentionPolicy.SOURCE)
324     @IntDef(value = {
325             NETWORK_AVOID_BAD_WIFI_IGNORE,
326             NETWORK_AVOID_BAD_WIFI_PROMPT,
327             NETWORK_AVOID_BAD_WIFI_AVOID,
328     })
329     public @interface NetworkAvoidBadWifi {}
330 
331     /**
332      * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
333      * overridden by the system based on device or application state. If null, the value
334      * specified by config_networkMeteredMultipathPreference is used.
335      *
336      * @hide
337      */
338     public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
339             "network_metered_multipath_preference";
340 
341     /**
342      * A list of uids that should go on cellular networks in preference even when higher-priority
343      * networks are connected.
344      *
345      * @hide
346      */
347     public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids";
348 
349     /**
350      * One of the private DNS modes that indicates the private DNS mode is off.
351      */
352     public static final int PRIVATE_DNS_MODE_OFF = ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OFF;
353 
354     /**
355      * One of the private DNS modes that indicates the private DNS mode is automatic, which
356      * will try to use the current DNS as private DNS.
357      */
358     public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC =
359             ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OPPORTUNISTIC;
360 
361     /**
362      * One of the private DNS modes that indicates the private DNS mode is strict and the
363      * {@link #PRIVATE_DNS_SPECIFIER} is required, which will try to use the value of
364      * {@link #PRIVATE_DNS_SPECIFIER} as private DNS.
365      */
366     public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME =
367             ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
368 
369     /** @hide */
370     @Retention(RetentionPolicy.SOURCE)
371     @IntDef(value = {
372             PRIVATE_DNS_MODE_OFF,
373             PRIVATE_DNS_MODE_OPPORTUNISTIC,
374             PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
375     })
376     public @interface PrivateDnsMode {}
377 
378     /**
379      * A list of uids that is allowed to use restricted networks.
380      *
381      * @hide
382      */
383     public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
384             "uids_allowed_on_restricted_networks";
385 
386     /**
387      * Get mobile data activity timeout from {@link Settings}.
388      *
389      * @param context The {@link Context} to query the setting.
390      * @param def The default timeout if no setting value.
391      * @return The {@link Duration} of timeout to track mobile data activity.
392      */
393     @NonNull
getMobileDataActivityTimeout(@onNull Context context, @NonNull Duration def)394     public static Duration getMobileDataActivityTimeout(@NonNull Context context,
395             @NonNull Duration def) {
396         final int timeout = Settings.Global.getInt(
397                 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds());
398         return Duration.ofSeconds(timeout);
399     }
400 
401     /**
402      * Set mobile data activity timeout to {@link Settings}.
403      * Tracking is disabled if set to zero or negative value.
404      *
405      * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
406      * ignored.
407      *
408      * @param context The {@link Context} to set the setting.
409      * @param timeout The mobile data activity timeout.
410      */
setMobileDataActivityTimeout(@onNull Context context, @NonNull Duration timeout)411     public static void setMobileDataActivityTimeout(@NonNull Context context,
412             @NonNull Duration timeout) {
413         Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE,
414                 (int) timeout.getSeconds());
415     }
416 
417     /**
418      * Get wifi data activity timeout from {@link Settings}.
419      *
420      * @param context The {@link Context} to query the setting.
421      * @param def The default timeout if no setting value.
422      * @return The {@link Duration} of timeout to track wifi data activity.
423      */
424     @NonNull
getWifiDataActivityTimeout(@onNull Context context, @NonNull Duration def)425     public static Duration getWifiDataActivityTimeout(@NonNull Context context,
426             @NonNull Duration def) {
427         final int timeout = Settings.Global.getInt(
428                 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds());
429         return Duration.ofSeconds(timeout);
430     }
431 
432     /**
433      * Set wifi data activity timeout to {@link Settings}.
434      * Tracking is disabled if set to zero or negative value.
435      *
436      * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
437      * ignored.
438      *
439      * @param context The {@link Context} to set the setting.
440      * @param timeout The wifi data activity timeout.
441      */
setWifiDataActivityTimeout(@onNull Context context, @NonNull Duration timeout)442     public static void setWifiDataActivityTimeout(@NonNull Context context,
443             @NonNull Duration timeout) {
444         Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI,
445                 (int) timeout.getSeconds());
446     }
447 
448     /**
449      * Get dns resolver sample validity duration from {@link Settings}.
450      *
451      * @param context The {@link Context} to query the setting.
452      * @param def The default duration if no setting value.
453      * @return The {@link Duration} of sample validity duration to configure for the system DNS
454      *         resolver.
455      */
456     @NonNull
getDnsResolverSampleValidityDuration(@onNull Context context, @NonNull Duration def)457     public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context,
458             @NonNull Duration def) {
459         final int duration = Settings.Global.getInt(context.getContentResolver(),
460                 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds());
461         return Duration.ofSeconds(duration);
462     }
463 
464     /**
465      * Set dns resolver sample validity duration to {@link Settings}. The duration must be a
466      * positive number of seconds.
467      *
468      * @param context The {@link Context} to set the setting.
469      * @param duration The sample validity duration.
470      */
setDnsResolverSampleValidityDuration(@onNull Context context, @NonNull Duration duration)471     public static void setDnsResolverSampleValidityDuration(@NonNull Context context,
472             @NonNull Duration duration) {
473         final int time = (int) duration.getSeconds();
474         if (time <= 0) {
475             throw new IllegalArgumentException("Invalid duration");
476         }
477         Settings.Global.putInt(
478                 context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time);
479     }
480 
481     /**
482      * Get dns resolver success threshold percent from {@link Settings}.
483      *
484      * @param context The {@link Context} to query the setting.
485      * @param def The default value if no setting value.
486      * @return The success threshold in percent for use with the system DNS resolver.
487      */
getDnsResolverSuccessThresholdPercent(@onNull Context context, int def)488     public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) {
489         return Settings.Global.getInt(
490                 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def);
491     }
492 
493     /**
494      * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must
495      * be 0~100.
496      *
497      * @param context The {@link Context} to set the setting.
498      * @param percent The success threshold percent.
499      */
setDnsResolverSuccessThresholdPercent(@onNull Context context, @IntRange(from = 0, to = 100) int percent)500     public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context,
501             @IntRange(from = 0, to = 100) int percent) {
502         if (percent < 0 || percent > 100) {
503             throw new IllegalArgumentException("Percent must be 0~100");
504         }
505         Settings.Global.putInt(
506                 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent);
507     }
508 
509     /**
510      * Get dns resolver samples range from {@link Settings}.
511      *
512      * @param context The {@link Context} to query the setting.
513      * @return The {@link Range<Integer>} of samples needed for statistics to be considered
514      *         meaningful in the system DNS resolver.
515      */
516     @NonNull
getDnsResolverSampleRanges(@onNull Context context)517     public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) {
518         final int minSamples = Settings.Global.getInt(context.getContentResolver(),
519                 DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
520         final int maxSamples = Settings.Global.getInt(context.getContentResolver(),
521                 DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
522         return new Range<>(minSamples, maxSamples);
523     }
524 
525     /**
526      * Set dns resolver samples range to {@link Settings}.
527      *
528      * @param context The {@link Context} to set the setting.
529      * @param range The samples range. The minimum number should be more than 0 and the maximum
530      *              number should be less that 64.
531      */
setDnsResolverSampleRanges(@onNull Context context, @NonNull Range<Integer> range)532     public static void setDnsResolverSampleRanges(@NonNull Context context,
533             @NonNull Range<Integer> range) {
534         if (range.getLower() < 0 || range.getUpper() > 64) {
535             throw new IllegalArgumentException("Argument must be 0~64");
536         }
537         Settings.Global.putInt(
538                 context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower());
539         Settings.Global.putInt(
540                 context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper());
541     }
542 
543     /**
544      * Get maximum count (from {@link Settings}) of switching network notifications shown in 24
545      * hours.
546      *
547      * @param context The {@link Context} to query the setting.
548      * @param def The default value if no setting value.
549      * @return The maximum count of notifications shown in 24 hours when switching networks.
550      */
getNetworkSwitchNotificationMaximumDailyCount(@onNull Context context, int def)551     public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
552             int def) {
553         return Settings.Global.getInt(
554                 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def);
555     }
556 
557     /**
558      * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours.
559      * The count must be at least 0.
560      *
561      * @param context The {@link Context} to set the setting.
562      * @param count The maximum count of switching network notifications shown in 24 hours.
563      */
setNetworkSwitchNotificationMaximumDailyCount(@onNull Context context, @IntRange(from = 0) int count)564     public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
565             @IntRange(from = 0) int count) {
566         if (count < 0) {
567             throw new IllegalArgumentException("Count must be more than 0.");
568         }
569         Settings.Global.putInt(
570                 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
571     }
572 
573     /**
574      * Get minimum duration (from {@link Settings}) between each switching network notifications.
575      *
576      * @param context The {@link Context} to query the setting.
577      * @param def The default time if no setting value.
578      * @return The minimum duration between notifications when switching networks.
579      */
580     @NonNull
getNetworkSwitchNotificationRateDuration(@onNull Context context, @NonNull Duration def)581     public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context,
582             @NonNull Duration def) {
583         final int duration = Settings.Global.getInt(context.getContentResolver(),
584                 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis());
585         return Duration.ofMillis(duration);
586     }
587 
588     /**
589      * Set minimum duration (to {@link Settings}) between each switching network notifications.
590      * The duration will be rounded down to the next millisecond, and must be positive.
591      *
592      * @param context The {@link Context} to set the setting.
593      * @param duration The minimum duration between notifications when switching networks.
594      */
setNetworkSwitchNotificationRateDuration(@onNull Context context, @NonNull Duration duration)595     public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context,
596             @NonNull Duration duration) {
597         final int time = (int) duration.toMillis();
598         if (time < 0) {
599             throw new IllegalArgumentException("Invalid duration.");
600         }
601         Settings.Global.putInt(context.getContentResolver(),
602                 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time);
603     }
604 
605     /**
606      * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection.
607      *
608      * @param context The {@link Context} to query the setting.
609      * @return The URL used for HTTP captive portal detection upon a new connection.
610      */
611     @Nullable
getCaptivePortalHttpUrl(@onNull Context context)612     public static String getCaptivePortalHttpUrl(@NonNull Context context) {
613         return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL);
614     }
615 
616     /**
617      * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
618      * The URL is accessed to check for connectivity and presence of a captive portal on a network.
619      * The URL should respond with HTTP status 204 to a GET request, and the stack will use
620      * redirection status as a signal for captive portal detection.
621      * If the URL is set to null or is otherwise incorrect or inaccessible, the stack will fail to
622      * detect connectivity and portals. This will often result in loss of connectivity.
623      *
624      * @param context The {@link Context} to set the setting.
625      * @param url The URL used for HTTP captive portal detection upon a new connection.
626      */
setCaptivePortalHttpUrl(@onNull Context context, @Nullable String url)627     public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) {
628         Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url);
629     }
630 
631     /**
632      * Get mode (from {@link Settings}) when connecting a network that presents a captive portal.
633      *
634      * @param context The {@link Context} to query the setting.
635      * @param def The default mode if no setting value.
636      * @return The mode when connecting a network that presents a captive portal.
637      */
638     @CaptivePortalMode
getCaptivePortalMode(@onNull Context context, @CaptivePortalMode int def)639     public static int getCaptivePortalMode(@NonNull Context context,
640             @CaptivePortalMode int def) {
641         return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def);
642     }
643 
644     /**
645      * Set mode (to {@link Settings}) when connecting a network that presents a captive portal.
646      *
647      * @param context The {@link Context} to set the setting.
648      * @param mode The mode when connecting a network that presents a captive portal.
649      */
setCaptivePortalMode(@onNull Context context, @CaptivePortalMode int mode)650     public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) {
651         if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE
652                 || mode == CAPTIVE_PORTAL_MODE_PROMPT
653                 || mode == CAPTIVE_PORTAL_MODE_AVOID)) {
654             throw new IllegalArgumentException("Invalid captive portal mode");
655         }
656         Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode);
657     }
658 
659     /**
660      * Get the global HTTP proxy applied to the device, or null if none.
661      *
662      * @param context The {@link Context} to query the setting.
663      * @return The {@link ProxyInfo} which build from global http proxy settings.
664      */
665     @Nullable
getGlobalProxy(@onNull Context context)666     public static ProxyInfo getGlobalProxy(@NonNull Context context) {
667         final String host = Settings.Global.getString(
668                 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST);
669         final int port = Settings.Global.getInt(
670                 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */);
671         final String exclusionList = Settings.Global.getString(
672                 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
673         final String pacFileUrl = Settings.Global.getString(
674                 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC);
675 
676         if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) {
677             return null; // No global proxy.
678         }
679 
680         if (TextUtils.isEmpty(pacFileUrl)) {
681             return ProxyInfo.buildDirectProxy(
682                     host, port, ProxyUtils.exclusionStringAsList(exclusionList));
683         } else {
684             return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
685         }
686     }
687 
688     /**
689      * Set global http proxy settings from given {@link ProxyInfo}.
690      *
691      * @param context The {@link Context} to set the setting.
692      * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
693      *                    {@link ProxyInfo#buildPacProxy(Uri)} or
694      *                    {@link ProxyInfo#buildDirectProxy(String, int, List)}
695      */
setGlobalProxy(@onNull Context context, @NonNull ProxyInfo proxyInfo)696     public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
697         final String host = proxyInfo.getHost();
698         final int port = proxyInfo.getPort();
699         final String exclusionList = proxyInfo.getExclusionListAsString();
700         final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
701 
702         if (TextUtils.isEmpty(pacFileUrl)) {
703             Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
704             Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
705             Settings.Global.putString(
706                     context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList);
707             Settings.Global.putString(
708                     context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
709         } else {
710             Settings.Global.putString(
711                     context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
712             Settings.Global.putString(
713                     context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
714             Settings.Global.putInt(
715                     context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
716             Settings.Global.putString(
717                     context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
718         }
719     }
720 
721     /**
722      * Clear all global http proxy settings.
723      *
724      * @param context The {@link Context} to set the setting.
725      */
clearGlobalProxy(@onNull Context context)726     public static void clearGlobalProxy(@NonNull Context context) {
727         Settings.Global.putString(
728                 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
729         Settings.Global.putInt(
730                 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
731         Settings.Global.putString(
732                 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
733         Settings.Global.putString(
734                 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
735     }
736 
737     /**
738      * Get private DNS mode from settings.
739      *
740      * @param context The Context to query the private DNS mode from settings.
741      * @return A string of private DNS mode.
742      */
743     @PrivateDnsMode
getPrivateDnsMode(@onNull Context context)744     public static int getPrivateDnsMode(@NonNull Context context) {
745         return ConnectivitySettingsUtils.getPrivateDnsMode(context);
746     }
747 
748     /**
749      * Set private DNS mode to settings.
750      *
751      * @param context The {@link Context} to set the private DNS mode.
752      * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
753      */
setPrivateDnsMode(@onNull Context context, @PrivateDnsMode int mode)754     public static void setPrivateDnsMode(@NonNull Context context, @PrivateDnsMode int mode) {
755         ConnectivitySettingsUtils.setPrivateDnsMode(context, mode);
756     }
757 
758     /**
759      * Get specific private dns provider name from {@link Settings}.
760      *
761      * @param context The {@link Context} to query the setting.
762      * @return The specific private dns provider name, or null if no setting value.
763      */
764     @Nullable
getPrivateDnsHostname(@onNull Context context)765     public static String getPrivateDnsHostname(@NonNull Context context) {
766         return ConnectivitySettingsUtils.getPrivateDnsHostname(context);
767     }
768 
769     /**
770      * Set specific private dns provider name to {@link Settings}.
771      *
772      * @param context The {@link Context} to set the setting.
773      * @param specifier The specific private dns provider name.
774      */
setPrivateDnsHostname(@onNull Context context, @Nullable String specifier)775     public static void setPrivateDnsHostname(@NonNull Context context, @Nullable String specifier) {
776         ConnectivitySettingsUtils.setPrivateDnsHostname(context, specifier);
777     }
778 
779     /**
780      * Get default private dns mode from {@link Settings}.
781      *
782      * @param context The {@link Context} to query the setting.
783      * @return The default private dns mode.
784      */
785     @PrivateDnsMode
786     @NonNull
getPrivateDnsDefaultMode(@onNull Context context)787     public static String getPrivateDnsDefaultMode(@NonNull Context context) {
788         return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE);
789     }
790 
791     /**
792      * Set default private dns mode to {@link Settings}.
793      *
794      * @param context The {@link Context} to set the setting.
795      * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_*
796      *             constants.
797      */
setPrivateDnsDefaultMode(@onNull Context context, @NonNull @PrivateDnsMode int mode)798     public static void setPrivateDnsDefaultMode(@NonNull Context context,
799             @NonNull @PrivateDnsMode int mode) {
800         if (!(mode == PRIVATE_DNS_MODE_OFF
801                 || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
802                 || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
803             throw new IllegalArgumentException("Invalid private dns mode");
804         }
805         Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE,
806                 getPrivateDnsModeAsString(mode));
807     }
808 
809     /**
810      * Get duration (from {@link Settings}) to keep a PendingIntent-based request.
811      *
812      * @param context The {@link Context} to query the setting.
813      * @param def The default duration if no setting value.
814      * @return The duration to keep a PendingIntent-based request.
815      */
816     @NonNull
getConnectivityKeepPendingIntentDuration(@onNull Context context, @NonNull Duration def)817     public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context,
818             @NonNull Duration def) {
819         final int duration = Settings.Secure.getInt(context.getContentResolver(),
820                 CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis());
821         return Duration.ofMillis(duration);
822     }
823 
824     /**
825      * Set duration (to {@link Settings}) to keep a PendingIntent-based request.
826      * The duration will be rounded down to the next millisecond, and must be positive.
827      *
828      * @param context The {@link Context} to set the setting.
829      * @param duration The duration to keep a PendingIntent-based request.
830      */
setConnectivityKeepPendingIntentDuration(@onNull Context context, @NonNull Duration duration)831     public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context,
832             @NonNull Duration duration) {
833         final int time = (int) duration.toMillis();
834         if (time < 0) {
835             throw new IllegalArgumentException("Invalid duration.");
836         }
837         Settings.Secure.putInt(
838                 context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time);
839     }
840 
841     /**
842      * Read from {@link Settings} whether the mobile data connection should remain active
843      * even when higher priority networks are active.
844      *
845      * @param context The {@link Context} to query the setting.
846      * @param def The default value if no setting value.
847      * @return Whether the mobile data connection should remain active even when higher
848      *         priority networks are active.
849      */
getMobileDataAlwaysOn(@onNull Context context, boolean def)850     public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) {
851         final int enable = Settings.Global.getInt(
852                 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0));
853         return (enable != 0) ? true : false;
854     }
855 
856     /**
857      * Write into {@link Settings} whether the mobile data connection should remain active
858      * even when higher priority networks are active.
859      *
860      * @param context The {@link Context} to set the setting.
861      * @param enable Whether the mobile data connection should remain active even when higher
862      *               priority networks are active.
863      */
setMobileDataAlwaysOn(@onNull Context context, boolean enable)864     public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) {
865         Settings.Global.putInt(
866                 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0));
867     }
868 
869     /**
870      * Read from {@link Settings} whether the wifi data connection should remain active
871      * even when higher priority networks are active.
872      *
873      * @param context The {@link Context} to query the setting.
874      * @param def The default value if no setting value.
875      * @return Whether the wifi data connection should remain active even when higher
876      *         priority networks are active.
877      */
getWifiAlwaysRequested(@onNull Context context, boolean def)878     public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) {
879         final int enable = Settings.Global.getInt(
880                 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0));
881         return (enable != 0) ? true : false;
882     }
883 
884     /**
885      * Write into {@link Settings} whether the wifi data connection should remain active
886      * even when higher priority networks are active.
887      *
888      * @param context The {@link Context} to set the setting.
889      * @param enable Whether the wifi data connection should remain active even when higher
890      *               priority networks are active
891      */
setWifiAlwaysRequested(@onNull Context context, boolean enable)892     public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) {
893         Settings.Global.putInt(
894                 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0));
895     }
896 
897     /**
898      * Get avoid bad wifi setting from {@link Settings}.
899      *
900      * @param context The {@link Context} to query the setting.
901      * @return The setting whether to automatically switch away from wifi networks that lose
902      *         internet access.
903      */
904     @NetworkAvoidBadWifi
getNetworkAvoidBadWifi(@onNull Context context)905     public static int getNetworkAvoidBadWifi(@NonNull Context context) {
906         final String setting =
907                 Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI);
908         if ("0".equals(setting)) {
909             return NETWORK_AVOID_BAD_WIFI_IGNORE;
910         } else if ("1".equals(setting)) {
911             return NETWORK_AVOID_BAD_WIFI_AVOID;
912         } else {
913             return NETWORK_AVOID_BAD_WIFI_PROMPT;
914         }
915     }
916 
917     /**
918      * Set avoid bad wifi setting to {@link Settings}.
919      *
920      * @param context The {@link Context} to set the setting.
921      * @param value Whether to automatically switch away from wifi networks that lose internet
922      *              access.
923      */
setNetworkAvoidBadWifi(@onNull Context context, @NetworkAvoidBadWifi int value)924     public static void setNetworkAvoidBadWifi(@NonNull Context context,
925             @NetworkAvoidBadWifi int value) {
926         final String setting;
927         if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) {
928             setting = "0";
929         } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) {
930             setting = "1";
931         } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) {
932             setting = null;
933         } else {
934             throw new IllegalArgumentException("Invalid avoid bad wifi setting");
935         }
936         Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting);
937     }
938 
939     /**
940      * Get network metered multipath preference from {@link Settings}.
941      *
942      * @param context The {@link Context} to query the setting.
943      * @return The network metered multipath preference which should be one of
944      *         ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified
945      *         by config_networkMeteredMultipathPreference is used.
946      */
947     @Nullable
getNetworkMeteredMultipathPreference(@onNull Context context)948     public static String getNetworkMeteredMultipathPreference(@NonNull Context context) {
949         return Settings.Global.getString(
950                 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE);
951     }
952 
953     /**
954      * Set network metered multipath preference to {@link Settings}.
955      *
956      * @param context The {@link Context} to set the setting.
957      * @param preference The network metered multipath preference which should be one of
958      *                   ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value
959      *                   specified by config_networkMeteredMultipathPreference is used.
960      */
setNetworkMeteredMultipathPreference(@onNull Context context, @NonNull @MultipathPreference String preference)961     public static void setNetworkMeteredMultipathPreference(@NonNull Context context,
962             @NonNull @MultipathPreference String preference) {
963         if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER
964                 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY
965                 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) {
966             throw new IllegalArgumentException("Invalid private dns mode");
967         }
968         Settings.Global.putString(
969                 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
970     }
971 
getUidSetFromString(@ullable String uidList)972     private static Set<Integer> getUidSetFromString(@Nullable String uidList) {
973         final Set<Integer> uids = new ArraySet<>();
974         if (TextUtils.isEmpty(uidList)) {
975             return uids;
976         }
977         for (String uid : uidList.split(";")) {
978             uids.add(Integer.valueOf(uid));
979         }
980         return uids;
981     }
982 
getUidStringFromSet(@onNull Set<Integer> uidList)983     private static String getUidStringFromSet(@NonNull Set<Integer> uidList) {
984         final StringJoiner joiner = new StringJoiner(";");
985         for (Integer uid : uidList) {
986             if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
987                 throw new IllegalArgumentException("Invalid uid");
988             }
989             joiner.add(uid.toString());
990         }
991         return joiner.toString();
992     }
993 
994     /**
995      * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
996      * even when higher-priority networks are connected.
997      *
998      * @param context The {@link Context} to query the setting.
999      * @return A list of uids that should go on cellular networks in preference even when
1000      *         higher-priority networks are connected or null if no setting value.
1001      */
1002     @NonNull
getMobileDataPreferredUids(@onNull Context context)1003     public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
1004         final String uidList = Settings.Secure.getString(
1005                 context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
1006         return getUidSetFromString(uidList);
1007     }
1008 
1009     /**
1010      * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference
1011      * even when higher-priority networks are connected.
1012      *
1013      * @param context The {@link Context} to set the setting.
1014      * @param uidList A list of uids that should go on cellular networks in preference even when
1015      *             higher-priority networks are connected.
1016      */
setMobileDataPreferredUids(@onNull Context context, @NonNull Set<Integer> uidList)1017     public static void setMobileDataPreferredUids(@NonNull Context context,
1018             @NonNull Set<Integer> uidList) {
1019         final String uids = getUidStringFromSet(uidList);
1020         Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids);
1021     }
1022 
1023     /**
1024      * Get the list of uids (from {@link Settings}) allowed to use restricted networks.
1025      *
1026      * Access to restricted networks is controlled by the (preinstalled-only)
1027      * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission, but highly privileged
1028      * callers can also set a list of uids that can access restricted networks.
1029      *
1030      * This is useful for example in some jurisdictions where government apps,
1031      * that can't be preinstalled, must still have access to emergency services.
1032      *
1033      * @param context The {@link Context} to query the setting.
1034      * @return A list of uids that is allowed to use restricted networks or null if no setting
1035      *         value.
1036      */
1037     @NonNull
getUidsAllowedOnRestrictedNetworks(@onNull Context context)1038     public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
1039         final String uidList = Settings.Global.getString(
1040                 context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS);
1041         return getUidSetFromString(uidList);
1042     }
1043 
isCallingFromSystem()1044     private static boolean isCallingFromSystem() {
1045         final int uid = Binder.getCallingUid();
1046         final int pid = Binder.getCallingPid();
1047         if (uid == Process.SYSTEM_UID && pid == Process.myPid()) {
1048             return true;
1049         }
1050         return false;
1051     }
1052 
1053     /**
1054      * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
1055      *
1056      * @param context The {@link Context} to set the setting.
1057      * @param uidList A list of uids that is allowed to use restricted networks.
1058      */
setUidsAllowedOnRestrictedNetworks(@onNull Context context, @NonNull Set<Integer> uidList)1059     public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
1060             @NonNull Set<Integer> uidList) {
1061         final boolean calledFromSystem = isCallingFromSystem();
1062         if (!calledFromSystem) {
1063             // Enforce NETWORK_SETTINGS check if it's debug build. This is for MTS test only.
1064             if (!Build.isDebuggable()) {
1065                 throw new SecurityException("Only system can set this setting.");
1066             }
1067             context.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS,
1068                     "Requires NETWORK_SETTINGS permission");
1069         }
1070         final String uids = getUidStringFromSet(uidList);
1071         Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
1072                 uids);
1073     }
1074 }
1075