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