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