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 * A global rate limit that applies to all networks with NET_CAPABILITY_INTERNET when enabled. 388 * 389 * @hide 390 */ 391 public static final String INGRESS_RATE_LIMIT_BYTES_PER_SECOND = 392 "ingress_rate_limit_bytes_per_second"; 393 394 /** 395 * Get mobile data activity timeout from {@link Settings}. 396 * 397 * @param context The {@link Context} to query the setting. 398 * @param def The default timeout if no setting value. 399 * @return The {@link Duration} of timeout to track mobile data activity. 400 */ 401 @NonNull getMobileDataActivityTimeout(@onNull Context context, @NonNull Duration def)402 public static Duration getMobileDataActivityTimeout(@NonNull Context context, 403 @NonNull Duration def) { 404 final int timeout = Settings.Global.getInt( 405 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds()); 406 return Duration.ofSeconds(timeout); 407 } 408 409 /** 410 * Set mobile data activity timeout to {@link Settings}. 411 * Tracking is disabled if set to zero or negative value. 412 * 413 * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be 414 * ignored. 415 * 416 * @param context The {@link Context} to set the setting. 417 * @param timeout The mobile data activity timeout. 418 */ setMobileDataActivityTimeout(@onNull Context context, @NonNull Duration timeout)419 public static void setMobileDataActivityTimeout(@NonNull Context context, 420 @NonNull Duration timeout) { 421 Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, 422 (int) timeout.getSeconds()); 423 } 424 425 /** 426 * Get wifi data activity timeout from {@link Settings}. 427 * 428 * @param context The {@link Context} to query the setting. 429 * @param def The default timeout if no setting value. 430 * @return The {@link Duration} of timeout to track wifi data activity. 431 */ 432 @NonNull getWifiDataActivityTimeout(@onNull Context context, @NonNull Duration def)433 public static Duration getWifiDataActivityTimeout(@NonNull Context context, 434 @NonNull Duration def) { 435 final int timeout = Settings.Global.getInt( 436 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds()); 437 return Duration.ofSeconds(timeout); 438 } 439 440 /** 441 * Set wifi data activity timeout to {@link Settings}. 442 * Tracking is disabled if set to zero or negative value. 443 * 444 * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be 445 * ignored. 446 * 447 * @param context The {@link Context} to set the setting. 448 * @param timeout The wifi data activity timeout. 449 */ setWifiDataActivityTimeout(@onNull Context context, @NonNull Duration timeout)450 public static void setWifiDataActivityTimeout(@NonNull Context context, 451 @NonNull Duration timeout) { 452 Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, 453 (int) timeout.getSeconds()); 454 } 455 456 /** 457 * Get dns resolver sample validity duration from {@link Settings}. 458 * 459 * @param context The {@link Context} to query the setting. 460 * @param def The default duration if no setting value. 461 * @return The {@link Duration} of sample validity duration to configure for the system DNS 462 * resolver. 463 */ 464 @NonNull getDnsResolverSampleValidityDuration(@onNull Context context, @NonNull Duration def)465 public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context, 466 @NonNull Duration def) { 467 final int duration = Settings.Global.getInt(context.getContentResolver(), 468 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds()); 469 return Duration.ofSeconds(duration); 470 } 471 472 /** 473 * Set dns resolver sample validity duration to {@link Settings}. The duration must be a 474 * positive number of seconds. 475 * 476 * @param context The {@link Context} to set the setting. 477 * @param duration The sample validity duration. 478 */ setDnsResolverSampleValidityDuration(@onNull Context context, @NonNull Duration duration)479 public static void setDnsResolverSampleValidityDuration(@NonNull Context context, 480 @NonNull Duration duration) { 481 final int time = (int) duration.getSeconds(); 482 if (time <= 0) { 483 throw new IllegalArgumentException("Invalid duration"); 484 } 485 Settings.Global.putInt( 486 context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time); 487 } 488 489 /** 490 * Get dns resolver success threshold percent from {@link Settings}. 491 * 492 * @param context The {@link Context} to query the setting. 493 * @param def The default value if no setting value. 494 * @return The success threshold in percent for use with the system DNS resolver. 495 */ getDnsResolverSuccessThresholdPercent(@onNull Context context, int def)496 public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) { 497 return Settings.Global.getInt( 498 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def); 499 } 500 501 /** 502 * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must 503 * be 0~100. 504 * 505 * @param context The {@link Context} to set the setting. 506 * @param percent The success threshold percent. 507 */ setDnsResolverSuccessThresholdPercent(@onNull Context context, @IntRange(from = 0, to = 100) int percent)508 public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context, 509 @IntRange(from = 0, to = 100) int percent) { 510 if (percent < 0 || percent > 100) { 511 throw new IllegalArgumentException("Percent must be 0~100"); 512 } 513 Settings.Global.putInt( 514 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent); 515 } 516 517 /** 518 * Get dns resolver samples range from {@link Settings}. 519 * 520 * @param context The {@link Context} to query the setting. 521 * @return The {@link Range<Integer>} of samples needed for statistics to be considered 522 * meaningful in the system DNS resolver. 523 */ 524 @NonNull getDnsResolverSampleRanges(@onNull Context context)525 public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) { 526 final int minSamples = Settings.Global.getInt(context.getContentResolver(), 527 DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); 528 final int maxSamples = Settings.Global.getInt(context.getContentResolver(), 529 DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); 530 return new Range<>(minSamples, maxSamples); 531 } 532 533 /** 534 * Set dns resolver samples range to {@link Settings}. 535 * 536 * @param context The {@link Context} to set the setting. 537 * @param range The samples range. The minimum number should be more than 0 and the maximum 538 * number should be less that 64. 539 */ setDnsResolverSampleRanges(@onNull Context context, @NonNull Range<Integer> range)540 public static void setDnsResolverSampleRanges(@NonNull Context context, 541 @NonNull Range<Integer> range) { 542 if (range.getLower() < 0 || range.getUpper() > 64) { 543 throw new IllegalArgumentException("Argument must be 0~64"); 544 } 545 Settings.Global.putInt( 546 context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower()); 547 Settings.Global.putInt( 548 context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper()); 549 } 550 551 /** 552 * Get maximum count (from {@link Settings}) of switching network notifications shown in 24 553 * hours. 554 * 555 * @param context The {@link Context} to query the setting. 556 * @param def The default value if no setting value. 557 * @return The maximum count of notifications shown in 24 hours when switching networks. 558 */ getNetworkSwitchNotificationMaximumDailyCount(@onNull Context context, int def)559 public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, 560 int def) { 561 return Settings.Global.getInt( 562 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def); 563 } 564 565 /** 566 * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours. 567 * The count must be at least 0. 568 * 569 * @param context The {@link Context} to set the setting. 570 * @param count The maximum count of switching network notifications shown in 24 hours. 571 */ setNetworkSwitchNotificationMaximumDailyCount(@onNull Context context, @IntRange(from = 0) int count)572 public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, 573 @IntRange(from = 0) int count) { 574 if (count < 0) { 575 throw new IllegalArgumentException("Count must be more than 0."); 576 } 577 Settings.Global.putInt( 578 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count); 579 } 580 581 /** 582 * Get minimum duration (from {@link Settings}) between each switching network notifications. 583 * 584 * @param context The {@link Context} to query the setting. 585 * @param def The default time if no setting value. 586 * @return The minimum duration between notifications when switching networks. 587 */ 588 @NonNull getNetworkSwitchNotificationRateDuration(@onNull Context context, @NonNull Duration def)589 public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context, 590 @NonNull Duration def) { 591 final int duration = Settings.Global.getInt(context.getContentResolver(), 592 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis()); 593 return Duration.ofMillis(duration); 594 } 595 596 /** 597 * Set minimum duration (to {@link Settings}) between each switching network notifications. 598 * The duration will be rounded down to the next millisecond, and must be positive. 599 * 600 * @param context The {@link Context} to set the setting. 601 * @param duration The minimum duration between notifications when switching networks. 602 */ setNetworkSwitchNotificationRateDuration(@onNull Context context, @NonNull Duration duration)603 public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context, 604 @NonNull Duration duration) { 605 final int time = (int) duration.toMillis(); 606 if (time < 0) { 607 throw new IllegalArgumentException("Invalid duration."); 608 } 609 Settings.Global.putInt(context.getContentResolver(), 610 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time); 611 } 612 613 /** 614 * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection. 615 * 616 * @param context The {@link Context} to query the setting. 617 * @return The URL used for HTTP captive portal detection upon a new connection. 618 */ 619 @Nullable getCaptivePortalHttpUrl(@onNull Context context)620 public static String getCaptivePortalHttpUrl(@NonNull Context context) { 621 return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL); 622 } 623 624 /** 625 * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection. 626 * The URL is accessed to check for connectivity and presence of a captive portal on a network. 627 * The URL should respond with HTTP status 204 to a GET request, and the stack will use 628 * redirection status as a signal for captive portal detection. 629 * If the URL is set to null or is otherwise incorrect or inaccessible, the stack will fail to 630 * detect connectivity and portals. This will often result in loss of connectivity. 631 * 632 * @param context The {@link Context} to set the setting. 633 * @param url The URL used for HTTP captive portal detection upon a new connection. 634 */ setCaptivePortalHttpUrl(@onNull Context context, @Nullable String url)635 public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) { 636 Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url); 637 } 638 639 /** 640 * Get mode (from {@link Settings}) when connecting a network that presents a captive portal. 641 * 642 * @param context The {@link Context} to query the setting. 643 * @param def The default mode if no setting value. 644 * @return The mode when connecting a network that presents a captive portal. 645 */ 646 @CaptivePortalMode getCaptivePortalMode(@onNull Context context, @CaptivePortalMode int def)647 public static int getCaptivePortalMode(@NonNull Context context, 648 @CaptivePortalMode int def) { 649 return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def); 650 } 651 652 /** 653 * Set mode (to {@link Settings}) when connecting a network that presents a captive portal. 654 * 655 * @param context The {@link Context} to set the setting. 656 * @param mode The mode when connecting a network that presents a captive portal. 657 */ setCaptivePortalMode(@onNull Context context, @CaptivePortalMode int mode)658 public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) { 659 if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE 660 || mode == CAPTIVE_PORTAL_MODE_PROMPT 661 || mode == CAPTIVE_PORTAL_MODE_AVOID)) { 662 throw new IllegalArgumentException("Invalid captive portal mode"); 663 } 664 Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode); 665 } 666 667 /** 668 * Get the global HTTP proxy applied to the device, or null if none. 669 * 670 * @param context The {@link Context} to query the setting. 671 * @return The {@link ProxyInfo} which build from global http proxy settings. 672 */ 673 @Nullable getGlobalProxy(@onNull Context context)674 public static ProxyInfo getGlobalProxy(@NonNull Context context) { 675 final String host = Settings.Global.getString( 676 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST); 677 final int port = Settings.Global.getInt( 678 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */); 679 final String exclusionList = Settings.Global.getString( 680 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST); 681 final String pacFileUrl = Settings.Global.getString( 682 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC); 683 684 if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) { 685 return null; // No global proxy. 686 } 687 688 if (TextUtils.isEmpty(pacFileUrl)) { 689 return ProxyInfo.buildDirectProxy( 690 host, port, ProxyUtils.exclusionStringAsList(exclusionList)); 691 } else { 692 return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl)); 693 } 694 } 695 696 /** 697 * Set global http proxy settings from given {@link ProxyInfo}. 698 * 699 * @param context The {@link Context} to set the setting. 700 * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from 701 * {@link ProxyInfo#buildPacProxy(Uri)} or 702 * {@link ProxyInfo#buildDirectProxy(String, int, List)} 703 */ setGlobalProxy(@onNull Context context, @NonNull ProxyInfo proxyInfo)704 public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) { 705 final String host = proxyInfo.getHost(); 706 final int port = proxyInfo.getPort(); 707 final String exclusionList = proxyInfo.getExclusionListAsString(); 708 final String pacFileUrl = proxyInfo.getPacFileUrl().toString(); 709 710 if (TextUtils.isEmpty(pacFileUrl)) { 711 Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host); 712 Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port); 713 Settings.Global.putString( 714 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); 715 Settings.Global.putString( 716 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); 717 } else { 718 Settings.Global.putString( 719 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl); 720 Settings.Global.putString( 721 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); 722 Settings.Global.putInt( 723 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); 724 Settings.Global.putString( 725 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); 726 } 727 } 728 729 /** 730 * Clear all global http proxy settings. 731 * 732 * @param context The {@link Context} to set the setting. 733 */ clearGlobalProxy(@onNull Context context)734 public static void clearGlobalProxy(@NonNull Context context) { 735 Settings.Global.putString( 736 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); 737 Settings.Global.putInt( 738 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); 739 Settings.Global.putString( 740 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); 741 Settings.Global.putString( 742 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); 743 } 744 745 /** 746 * Get private DNS mode from settings. 747 * 748 * @param context The Context to query the private DNS mode from settings. 749 * @return A string of private DNS mode. 750 */ 751 @PrivateDnsMode getPrivateDnsMode(@onNull Context context)752 public static int getPrivateDnsMode(@NonNull Context context) { 753 return ConnectivitySettingsUtils.getPrivateDnsMode(context); 754 } 755 756 /** 757 * Set private DNS mode to settings. 758 * 759 * @param context The {@link Context} to set the private DNS mode. 760 * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants. 761 */ setPrivateDnsMode(@onNull Context context, @PrivateDnsMode int mode)762 public static void setPrivateDnsMode(@NonNull Context context, @PrivateDnsMode int mode) { 763 ConnectivitySettingsUtils.setPrivateDnsMode(context, mode); 764 } 765 766 /** 767 * Get specific private dns provider name from {@link Settings}. 768 * 769 * @param context The {@link Context} to query the setting. 770 * @return The specific private dns provider name, or null if no setting value. 771 */ 772 @Nullable getPrivateDnsHostname(@onNull Context context)773 public static String getPrivateDnsHostname(@NonNull Context context) { 774 return ConnectivitySettingsUtils.getPrivateDnsHostname(context); 775 } 776 777 /** 778 * Set specific private dns provider name to {@link Settings}. 779 * 780 * @param context The {@link Context} to set the setting. 781 * @param specifier The specific private dns provider name. 782 */ setPrivateDnsHostname(@onNull Context context, @Nullable String specifier)783 public static void setPrivateDnsHostname(@NonNull Context context, @Nullable String specifier) { 784 ConnectivitySettingsUtils.setPrivateDnsHostname(context, specifier); 785 } 786 787 /** 788 * Get default private dns mode from {@link Settings}. 789 * 790 * @param context The {@link Context} to query the setting. 791 * @return The default private dns mode. 792 */ 793 @PrivateDnsMode 794 @NonNull getPrivateDnsDefaultMode(@onNull Context context)795 public static String getPrivateDnsDefaultMode(@NonNull Context context) { 796 return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE); 797 } 798 799 /** 800 * Set default private dns mode to {@link Settings}. 801 * 802 * @param context The {@link Context} to set the setting. 803 * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_* 804 * constants. 805 */ setPrivateDnsDefaultMode(@onNull Context context, @NonNull @PrivateDnsMode int mode)806 public static void setPrivateDnsDefaultMode(@NonNull Context context, 807 @NonNull @PrivateDnsMode int mode) { 808 if (!(mode == PRIVATE_DNS_MODE_OFF 809 || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC 810 || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { 811 throw new IllegalArgumentException("Invalid private dns mode"); 812 } 813 Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, 814 getPrivateDnsModeAsString(mode)); 815 } 816 817 /** 818 * Get duration (from {@link Settings}) to keep a PendingIntent-based request. 819 * 820 * @param context The {@link Context} to query the setting. 821 * @param def The default duration if no setting value. 822 * @return The duration to keep a PendingIntent-based request. 823 */ 824 @NonNull getConnectivityKeepPendingIntentDuration(@onNull Context context, @NonNull Duration def)825 public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context, 826 @NonNull Duration def) { 827 final int duration = Settings.Secure.getInt(context.getContentResolver(), 828 CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis()); 829 return Duration.ofMillis(duration); 830 } 831 832 /** 833 * Set duration (to {@link Settings}) to keep a PendingIntent-based request. 834 * The duration will be rounded down to the next millisecond, and must be positive. 835 * 836 * @param context The {@link Context} to set the setting. 837 * @param duration The duration to keep a PendingIntent-based request. 838 */ setConnectivityKeepPendingIntentDuration(@onNull Context context, @NonNull Duration duration)839 public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context, 840 @NonNull Duration duration) { 841 final int time = (int) duration.toMillis(); 842 if (time < 0) { 843 throw new IllegalArgumentException("Invalid duration."); 844 } 845 Settings.Secure.putInt( 846 context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time); 847 } 848 849 /** 850 * Read from {@link Settings} whether the mobile data connection should remain active 851 * even when higher priority networks are active. 852 * 853 * @param context The {@link Context} to query the setting. 854 * @param def The default value if no setting value. 855 * @return Whether the mobile data connection should remain active even when higher 856 * priority networks are active. 857 */ getMobileDataAlwaysOn(@onNull Context context, boolean def)858 public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) { 859 final int enable = Settings.Global.getInt( 860 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0)); 861 return (enable != 0) ? true : false; 862 } 863 864 /** 865 * Write into {@link Settings} whether the mobile data connection should remain active 866 * even when higher priority networks are active. 867 * 868 * @param context The {@link Context} to set the setting. 869 * @param enable Whether the mobile data connection should remain active even when higher 870 * priority networks are active. 871 */ setMobileDataAlwaysOn(@onNull Context context, boolean enable)872 public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) { 873 Settings.Global.putInt( 874 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0)); 875 } 876 877 /** 878 * Read from {@link Settings} whether the wifi data connection should remain active 879 * even when higher priority networks are active. 880 * 881 * @param context The {@link Context} to query the setting. 882 * @param def The default value if no setting value. 883 * @return Whether the wifi data connection should remain active even when higher 884 * priority networks are active. 885 */ getWifiAlwaysRequested(@onNull Context context, boolean def)886 public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) { 887 final int enable = Settings.Global.getInt( 888 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0)); 889 return (enable != 0) ? true : false; 890 } 891 892 /** 893 * Write into {@link Settings} whether the wifi data connection should remain active 894 * even when higher priority networks are active. 895 * 896 * @param context The {@link Context} to set the setting. 897 * @param enable Whether the wifi data connection should remain active even when higher 898 * priority networks are active 899 */ setWifiAlwaysRequested(@onNull Context context, boolean enable)900 public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) { 901 Settings.Global.putInt( 902 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0)); 903 } 904 905 /** 906 * Get avoid bad wifi setting from {@link Settings}. 907 * 908 * @param context The {@link Context} to query the setting. 909 * @return The setting whether to automatically switch away from wifi networks that lose 910 * internet access. 911 */ 912 @NetworkAvoidBadWifi getNetworkAvoidBadWifi(@onNull Context context)913 public static int getNetworkAvoidBadWifi(@NonNull Context context) { 914 final String setting = 915 Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI); 916 if ("0".equals(setting)) { 917 return NETWORK_AVOID_BAD_WIFI_IGNORE; 918 } else if ("1".equals(setting)) { 919 return NETWORK_AVOID_BAD_WIFI_AVOID; 920 } else { 921 return NETWORK_AVOID_BAD_WIFI_PROMPT; 922 } 923 } 924 925 /** 926 * Set avoid bad wifi setting to {@link Settings}. 927 * 928 * @param context The {@link Context} to set the setting. 929 * @param value Whether to automatically switch away from wifi networks that lose internet 930 * access. 931 */ setNetworkAvoidBadWifi(@onNull Context context, @NetworkAvoidBadWifi int value)932 public static void setNetworkAvoidBadWifi(@NonNull Context context, 933 @NetworkAvoidBadWifi int value) { 934 final String setting; 935 if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) { 936 setting = "0"; 937 } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) { 938 setting = "1"; 939 } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) { 940 setting = null; 941 } else { 942 throw new IllegalArgumentException("Invalid avoid bad wifi setting"); 943 } 944 Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting); 945 } 946 947 /** 948 * Get network metered multipath preference from {@link Settings}. 949 * 950 * @param context The {@link Context} to query the setting. 951 * @return The network metered multipath preference which should be one of 952 * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified 953 * by config_networkMeteredMultipathPreference is used. 954 */ 955 @Nullable getNetworkMeteredMultipathPreference(@onNull Context context)956 public static String getNetworkMeteredMultipathPreference(@NonNull Context context) { 957 return Settings.Global.getString( 958 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE); 959 } 960 961 /** 962 * Set network metered multipath preference to {@link Settings}. 963 * 964 * @param context The {@link Context} to set the setting. 965 * @param preference The network metered multipath preference which should be one of 966 * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value 967 * specified by config_networkMeteredMultipathPreference is used. 968 */ setNetworkMeteredMultipathPreference(@onNull Context context, @NonNull @MultipathPreference String preference)969 public static void setNetworkMeteredMultipathPreference(@NonNull Context context, 970 @NonNull @MultipathPreference String preference) { 971 if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER 972 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY 973 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) { 974 throw new IllegalArgumentException("Invalid private dns mode"); 975 } 976 Settings.Global.putString( 977 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference); 978 } 979 getUidSetFromString(@ullable String uidList)980 private static Set<Integer> getUidSetFromString(@Nullable String uidList) { 981 final Set<Integer> uids = new ArraySet<>(); 982 if (TextUtils.isEmpty(uidList)) { 983 return uids; 984 } 985 for (String uid : uidList.split(";")) { 986 uids.add(Integer.valueOf(uid)); 987 } 988 return uids; 989 } 990 getUidStringFromSet(@onNull Set<Integer> uidList)991 private static String getUidStringFromSet(@NonNull Set<Integer> uidList) { 992 final StringJoiner joiner = new StringJoiner(";"); 993 for (Integer uid : uidList) { 994 if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) { 995 throw new IllegalArgumentException("Invalid uid"); 996 } 997 joiner.add(uid.toString()); 998 } 999 return joiner.toString(); 1000 } 1001 1002 /** 1003 * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference 1004 * even when higher-priority networks are connected. 1005 * 1006 * @param context The {@link Context} to query the setting. 1007 * @return A list of uids that should go on cellular networks in preference even when 1008 * higher-priority networks are connected or null if no setting value. 1009 */ 1010 @NonNull getMobileDataPreferredUids(@onNull Context context)1011 public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) { 1012 final String uidList = Settings.Secure.getString( 1013 context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS); 1014 return getUidSetFromString(uidList); 1015 } 1016 1017 /** 1018 * Set the list of uids(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 uidList A list of uids that should go on cellular networks in preference even when 1023 * higher-priority networks are connected. 1024 */ setMobileDataPreferredUids(@onNull Context context, @NonNull Set<Integer> uidList)1025 public static void setMobileDataPreferredUids(@NonNull Context context, 1026 @NonNull Set<Integer> uidList) { 1027 final String uids = getUidStringFromSet(uidList); 1028 Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids); 1029 } 1030 1031 /** 1032 * Get the list of uids (from {@link Settings}) allowed to use restricted networks. 1033 * 1034 * Access to restricted networks is controlled by the (preinstalled-only) 1035 * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission, but highly privileged 1036 * callers can also set a list of uids that can access restricted networks. 1037 * 1038 * This is useful for example in some jurisdictions where government apps, 1039 * that can't be preinstalled, must still have access to emergency services. 1040 * 1041 * @param context The {@link Context} to query the setting. 1042 * @return A list of uids that is allowed to use restricted networks or null if no setting 1043 * value. 1044 */ 1045 @NonNull getUidsAllowedOnRestrictedNetworks(@onNull Context context)1046 public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) { 1047 final String uidList = Settings.Global.getString( 1048 context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS); 1049 return getUidSetFromString(uidList); 1050 } 1051 isCallingFromSystem()1052 private static boolean isCallingFromSystem() { 1053 final int uid = Binder.getCallingUid(); 1054 final int pid = Binder.getCallingPid(); 1055 if (uid == Process.SYSTEM_UID && pid == Process.myPid()) { 1056 return true; 1057 } 1058 return false; 1059 } 1060 1061 /** 1062 * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks. 1063 * 1064 * @param context The {@link Context} to set the setting. 1065 * @param uidList A list of uids that is allowed to use restricted networks. 1066 */ setUidsAllowedOnRestrictedNetworks(@onNull Context context, @NonNull Set<Integer> uidList)1067 public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context, 1068 @NonNull Set<Integer> uidList) { 1069 final boolean calledFromSystem = isCallingFromSystem(); 1070 if (!calledFromSystem) { 1071 // Enforce NETWORK_SETTINGS check if it's debug build. This is for MTS test only. 1072 if (!Build.isDebuggable()) { 1073 throw new SecurityException("Only system can set this setting."); 1074 } 1075 context.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, 1076 "Requires NETWORK_SETTINGS permission"); 1077 } 1078 final String uids = getUidStringFromSet(uidList); 1079 Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS, 1080 uids); 1081 } 1082 1083 /** 1084 * Get the network bandwidth ingress rate limit. 1085 * 1086 * The limit is only applicable to networks that provide internet connectivity. -1 codes for no 1087 * bandwidth limitation. 1088 * 1089 * @param context The {@link Context} to query the setting. 1090 * @return The rate limit in number of bytes per second or -1 if disabled. 1091 */ getIngressRateLimitInBytesPerSecond(@onNull Context context)1092 public static long getIngressRateLimitInBytesPerSecond(@NonNull Context context) { 1093 return Settings.Global.getLong(context.getContentResolver(), 1094 INGRESS_RATE_LIMIT_BYTES_PER_SECOND, -1); 1095 } 1096 1097 /** 1098 * Set the network bandwidth ingress rate limit. 1099 * 1100 * The limit is applied to all networks that provide internet connectivity. It is applied on a 1101 * per-network basis, meaning that global ingress rate could exceed the limit when communicating 1102 * on multiple networks simultaneously. 1103 * 1104 * @param context The {@link Context} to set the setting. 1105 * @param rateLimitInBytesPerSec The rate limit in number of bytes per second or -1 to disable. 1106 */ setIngressRateLimitInBytesPerSecond(@onNull Context context, @IntRange(from = -1L, to = 0xFFFFFFFFL) long rateLimitInBytesPerSec)1107 public static void setIngressRateLimitInBytesPerSecond(@NonNull Context context, 1108 @IntRange(from = -1L, to = 0xFFFFFFFFL) long rateLimitInBytesPerSec) { 1109 if (rateLimitInBytesPerSec < -1) { 1110 throw new IllegalArgumentException( 1111 "Rate limit must be within the range [-1, Integer.MAX_VALUE]"); 1112 } 1113 Settings.Global.putLong(context.getContentResolver(), 1114 INGRESS_RATE_LIMIT_BYTES_PER_SECOND, 1115 rateLimitInBytesPerSec); 1116 } 1117 } 1118