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