blob: 67dacb88f7ac38d0244b2e75d744f9f6219870f0 [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
lucaslin89621282021-06-21 16:39:08 +080023import static com.android.net.module.util.ConnectivitySettingsUtils.getPrivateDnsModeAsString;
24
paulhuc9925e02021-03-17 20:30:33 +080025import android.annotation.IntDef;
paulhu94430952021-03-23 00:24:50 +080026import android.annotation.IntRange;
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.annotation.SystemApi;
30import android.content.Context;
Chalard Jeanc8ef2192023-04-14 13:18:37 +090031import android.content.pm.PackageManager;
paulhu94430952021-03-23 00:24:50 +080032import android.net.ConnectivityManager.MultipathPreference;
paulhu92f128c2021-07-01 18:04:45 +080033import android.os.Binder;
34import android.os.Build;
paulhu344c1162021-05-11 09:42:50 +080035import android.os.Process;
36import android.os.UserHandle;
paulhu94430952021-03-23 00:24:50 +080037import android.provider.Settings;
38import android.text.TextUtils;
paulhu69afcd52021-04-27 00:14:47 +080039import android.util.ArraySet;
Chalard Jeanc8ef2192023-04-14 13:18:37 +090040import android.util.Log;
paulhu94430952021-03-23 00:24:50 +080041import android.util.Range;
42
lucaslin89621282021-06-21 16:39:08 +080043import com.android.net.module.util.ConnectivitySettingsUtils;
paulhu94430952021-03-23 00:24:50 +080044import com.android.net.module.util.ProxyUtils;
paulhuc9925e02021-03-17 20:30:33 +080045
46import java.lang.annotation.Retention;
47import java.lang.annotation.RetentionPolicy;
paulhu94430952021-03-23 00:24:50 +080048import java.time.Duration;
49import java.util.List;
paulhu69afcd52021-04-27 00:14:47 +080050import java.util.Set;
51import java.util.StringJoiner;
paulhuc9925e02021-03-17 20:30:33 +080052
paulhu845456e2021-03-17 17:19:09 +080053/**
54 * A manager class for connectivity module settings.
55 *
56 * @hide
57 */
paulhu94430952021-03-23 00:24:50 +080058@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
paulhu845456e2021-03-17 17:19:09 +080059public class ConnectivitySettingsManager {
Chalard Jeanc8ef2192023-04-14 13:18:37 +090060 private static final String TAG = ConnectivitySettingsManager.class.getSimpleName();
paulhu845456e2021-03-17 17:19:09 +080061
62 private ConnectivitySettingsManager() {}
63
paulhuc9925e02021-03-17 20:30:33 +080064 /** Data activity timeout settings */
65
66 /**
67 * Inactivity timeout to track mobile data activity.
68 *
69 * If set to a positive integer, it indicates the inactivity timeout value in seconds to
70 * infer the data activity of mobile network. After a period of no activity on mobile
71 * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
72 * intent is fired to indicate a transition of network status from "active" to "idle". Any
73 * subsequent activity on mobile networks triggers the firing of {@code
74 * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
75 *
76 * Network activity refers to transmitting or receiving data on the network interfaces.
77 *
78 * Tracking is disabled if set to zero or negative value.
paulhu94430952021-03-23 00:24:50 +080079 *
80 * @hide
paulhuc9925e02021-03-17 20:30:33 +080081 */
82 public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
83
84 /**
85 * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
86 * but for Wifi network.
paulhu94430952021-03-23 00:24:50 +080087 *
88 * @hide
paulhuc9925e02021-03-17 20:30:33 +080089 */
90 public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
91
92 /** Dns resolver settings */
93
94 /**
95 * Sample validity in seconds to configure for the system DNS resolver.
paulhu94430952021-03-23 00:24:50 +080096 *
97 * @hide
paulhuc9925e02021-03-17 20:30:33 +080098 */
99 public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
100 "dns_resolver_sample_validity_seconds";
101
102 /**
103 * Success threshold in percent for use with the system DNS resolver.
paulhu94430952021-03-23 00:24:50 +0800104 *
105 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800106 */
107 public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
108 "dns_resolver_success_threshold_percent";
109
110 /**
111 * Minimum number of samples needed for statistics to be considered meaningful in the
112 * system DNS resolver.
paulhu94430952021-03-23 00:24:50 +0800113 *
114 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800115 */
116 public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
117
118 /**
119 * Maximum number taken into account for statistics purposes in the system DNS resolver.
paulhu94430952021-03-23 00:24:50 +0800120 *
121 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800122 */
123 public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
124
paulhu94430952021-03-23 00:24:50 +0800125 private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
126 private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
127
paulhuc9925e02021-03-17 20:30:33 +0800128 /** Network switch notification settings */
129
130 /**
131 * The maximum number of notifications shown in 24 hours when switching networks.
paulhu94430952021-03-23 00:24:50 +0800132 *
133 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800134 */
135 public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
136 "network_switch_notification_daily_limit";
137
138 /**
139 * The minimum time in milliseconds between notifications when switching networks.
paulhu94430952021-03-23 00:24:50 +0800140 *
141 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800142 */
143 public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
144 "network_switch_notification_rate_limit_millis";
145
146 /** Captive portal settings */
147
148 /**
149 * The URL used for HTTP captive portal detection upon a new connection.
150 * A 204 response code from the server is used for validation.
paulhu94430952021-03-23 00:24:50 +0800151 *
152 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800153 */
154 public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
155
156 /**
157 * What to do when connecting a network that presents a captive portal.
paulhu94430952021-03-23 00:24:50 +0800158 * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
paulhuc9925e02021-03-17 20:30:33 +0800159 *
160 * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
paulhu94430952021-03-23 00:24:50 +0800161 *
162 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800163 */
164 public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
165
166 /**
167 * Don't attempt to detect captive portals.
168 */
169 public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
170
171 /**
172 * When detecting a captive portal, display a notification that
173 * prompts the user to sign in.
174 */
175 public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
176
177 /**
178 * When detecting a captive portal, immediately disconnect from the
179 * network and do not reconnect to that network in the future.
180 */
181 public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
182
183 /** @hide */
184 @Retention(RetentionPolicy.SOURCE)
185 @IntDef(value = {
186 CAPTIVE_PORTAL_MODE_IGNORE,
187 CAPTIVE_PORTAL_MODE_PROMPT,
188 CAPTIVE_PORTAL_MODE_AVOID,
189 })
190 public @interface CaptivePortalMode {}
191
192 /** Global http proxy settings */
193
194 /**
195 * Host name for global http proxy. Set via ConnectivityManager.
paulhu94430952021-03-23 00:24:50 +0800196 *
197 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800198 */
199 public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
200
201 /**
202 * Integer host port for global http proxy. Set via ConnectivityManager.
paulhu94430952021-03-23 00:24:50 +0800203 *
204 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800205 */
206 public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
207
208 /**
209 * Exclusion list for global proxy. This string contains a list of
210 * comma-separated domains where the global proxy does not apply.
211 * Domains should be listed in a comma- separated list. Example of
212 * acceptable formats: ".domain1.com,my.domain2.com" Use
213 * ConnectivityManager to set/get.
paulhu94430952021-03-23 00:24:50 +0800214 *
215 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800216 */
217 public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST =
218 "global_http_proxy_exclusion_list";
219
220 /**
221 * The location PAC File for the proxy.
paulhu94430952021-03-23 00:24:50 +0800222 *
223 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800224 */
225 public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
226
227 /** Private dns settings */
228
229 /**
230 * The requested Private DNS mode (string), and an accompanying specifier (string).
231 *
232 * Currently, the specifier holds the chosen provider name when the mode requests
233 * a specific provider. It may be used to store the provider name even when the
234 * mode changes so that temporarily disabling and re-enabling the specific
235 * provider mode does not necessitate retyping the provider hostname.
paulhu94430952021-03-23 00:24:50 +0800236 *
237 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800238 */
239 public static final String PRIVATE_DNS_MODE = "private_dns_mode";
240
241 /**
242 * The specific Private DNS provider name.
paulhu94430952021-03-23 00:24:50 +0800243 *
244 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800245 */
246 public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
247
248 /**
249 * Forced override of the default mode (hardcoded as "automatic", nee "opportunistic").
250 * This allows changing the default mode without effectively disabling other modes,
251 * all of which require explicit user action to enable/configure. See also b/79719289.
252 *
253 * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above.
paulhu94430952021-03-23 00:24:50 +0800254 *
255 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800256 */
257 public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
258
259 /** Other settings */
260
261 /**
262 * The number of milliseconds to hold on to a PendingIntent based request. This delay gives
263 * the receivers of the PendingIntent an opportunity to make a new network request before
264 * the Network satisfying the request is potentially removed.
paulhu94430952021-03-23 00:24:50 +0800265 *
266 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800267 */
268 public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
269 "connectivity_release_pending_intent_delay_ms";
270
271 /**
272 * Whether the mobile data connection should remain active even when higher
273 * priority networks like WiFi are active, to help make network switching faster.
274 *
275 * See ConnectivityService for more info.
276 *
277 * (0 = disabled, 1 = enabled)
paulhu94430952021-03-23 00:24:50 +0800278 *
279 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800280 */
281 public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
282
283 /**
284 * Whether the wifi data connection should remain active even when higher
285 * priority networks like Ethernet are active, to keep both networks.
286 * In the case where higher priority networks are connected, wifi will be
287 * unused unless an application explicitly requests to use it.
288 *
289 * See ConnectivityService for more info.
290 *
291 * (0 = disabled, 1 = enabled)
paulhu94430952021-03-23 00:24:50 +0800292 *
293 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800294 */
295 public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
296
paulhu845456e2021-03-17 17:19:09 +0800297 /**
298 * Whether to automatically switch away from wifi networks that lose Internet access.
299 * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always
300 * avoids such networks. Valid values are:
301 *
302 * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
303 * null: Ask the user whether to switch away from bad wifi.
304 * 1: Avoid bad wifi.
paulhu94430952021-03-23 00:24:50 +0800305 *
306 * @hide
paulhu845456e2021-03-17 17:19:09 +0800307 */
308 public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
309
310 /**
paulhu94430952021-03-23 00:24:50 +0800311 * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013.
312 */
313 public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0;
314
315 /**
316 * Ask the user whether to switch away from bad wifi.
317 */
318 public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1;
319
320 /**
321 * Avoid bad wifi.
322 */
323 public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2;
324
325 /** @hide */
326 @Retention(RetentionPolicy.SOURCE)
327 @IntDef(value = {
328 NETWORK_AVOID_BAD_WIFI_IGNORE,
329 NETWORK_AVOID_BAD_WIFI_PROMPT,
330 NETWORK_AVOID_BAD_WIFI_AVOID,
331 })
332 public @interface NetworkAvoidBadWifi {}
333
334 /**
paulhu845456e2021-03-17 17:19:09 +0800335 * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be
336 * overridden by the system based on device or application state. If null, the value
337 * specified by config_networkMeteredMultipathPreference is used.
paulhu94430952021-03-23 00:24:50 +0800338 *
339 * @hide
paulhu845456e2021-03-17 17:19:09 +0800340 */
341 public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
342 "network_metered_multipath_preference";
paulhu94430952021-03-23 00:24:50 +0800343
344 /**
paulhu344c1162021-05-11 09:42:50 +0800345 * A list of uids that should go on cellular networks in preference even when higher-priority
paulhu7a4eeed2021-03-25 13:17:58 +0800346 * networks are connected.
347 *
348 * @hide
349 */
paulhu344c1162021-05-11 09:42:50 +0800350 public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids";
paulhu7a4eeed2021-03-25 13:17:58 +0800351
352 /**
lucaslin57f9ba82021-04-23 21:03:39 +0800353 * One of the private DNS modes that indicates the private DNS mode is off.
354 */
lucaslin89621282021-06-21 16:39:08 +0800355 public static final int PRIVATE_DNS_MODE_OFF = ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OFF;
lucaslin57f9ba82021-04-23 21:03:39 +0800356
357 /**
358 * One of the private DNS modes that indicates the private DNS mode is automatic, which
359 * will try to use the current DNS as private DNS.
360 */
lucaslin89621282021-06-21 16:39:08 +0800361 public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC =
362 ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OPPORTUNISTIC;
lucaslin57f9ba82021-04-23 21:03:39 +0800363
364 /**
365 * One of the private DNS modes that indicates the private DNS mode is strict and the
366 * {@link #PRIVATE_DNS_SPECIFIER} is required, which will try to use the value of
367 * {@link #PRIVATE_DNS_SPECIFIER} as private DNS.
368 */
lucaslin89621282021-06-21 16:39:08 +0800369 public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME =
370 ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
lucaslin57f9ba82021-04-23 21:03:39 +0800371
372 /** @hide */
373 @Retention(RetentionPolicy.SOURCE)
374 @IntDef(value = {
375 PRIVATE_DNS_MODE_OFF,
376 PRIVATE_DNS_MODE_OPPORTUNISTIC,
377 PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
378 })
379 public @interface PrivateDnsMode {}
380
lucaslin57f9ba82021-04-23 21:03:39 +0800381 /**
paulhu91e78062021-05-26 16:16:57 +0800382 * A list of uids that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +0800383 *
384 * @hide
385 */
paulhu91e78062021-05-26 16:16:57 +0800386 public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
387 "uids_allowed_on_restricted_networks";
paulhu69afcd52021-04-27 00:14:47 +0800388
389 /**
Patrick Rohra2084362022-01-03 15:55:52 +0100390 * A global rate limit that applies to all networks with NET_CAPABILITY_INTERNET when enabled.
391 *
392 * @hide
393 */
394 public static final String INGRESS_RATE_LIMIT_BYTES_PER_SECOND =
395 "ingress_rate_limit_bytes_per_second";
396
397 /**
paulhu94430952021-03-23 00:24:50 +0800398 * Get mobile data activity timeout from {@link Settings}.
399 *
400 * @param context The {@link Context} to query the setting.
401 * @param def The default timeout if no setting value.
402 * @return The {@link Duration} of timeout to track mobile data activity.
403 */
404 @NonNull
405 public static Duration getMobileDataActivityTimeout(@NonNull Context context,
406 @NonNull Duration def) {
407 final int timeout = Settings.Global.getInt(
408 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds());
409 return Duration.ofSeconds(timeout);
410 }
411
412 /**
413 * Set mobile data activity timeout to {@link Settings}.
414 * Tracking is disabled if set to zero or negative value.
415 *
416 * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
417 * ignored.
418 *
419 * @param context The {@link Context} to set the setting.
420 * @param timeout The mobile data activity timeout.
421 */
422 public static void setMobileDataActivityTimeout(@NonNull Context context,
423 @NonNull Duration timeout) {
424 Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE,
425 (int) timeout.getSeconds());
426 }
427
428 /**
429 * Get wifi data activity timeout from {@link Settings}.
430 *
431 * @param context The {@link Context} to query the setting.
432 * @param def The default timeout if no setting value.
433 * @return The {@link Duration} of timeout to track wifi data activity.
434 */
435 @NonNull
436 public static Duration getWifiDataActivityTimeout(@NonNull Context context,
437 @NonNull Duration def) {
438 final int timeout = Settings.Global.getInt(
439 context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds());
440 return Duration.ofSeconds(timeout);
441 }
442
443 /**
444 * Set wifi data activity timeout to {@link Settings}.
445 * Tracking is disabled if set to zero or negative value.
446 *
447 * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be
448 * ignored.
449 *
450 * @param context The {@link Context} to set the setting.
451 * @param timeout The wifi data activity timeout.
452 */
453 public static void setWifiDataActivityTimeout(@NonNull Context context,
454 @NonNull Duration timeout) {
455 Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI,
456 (int) timeout.getSeconds());
457 }
458
459 /**
460 * Get dns resolver sample validity duration from {@link Settings}.
461 *
462 * @param context The {@link Context} to query the setting.
463 * @param def The default duration if no setting value.
464 * @return The {@link Duration} of sample validity duration to configure for the system DNS
465 * resolver.
466 */
467 @NonNull
468 public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context,
469 @NonNull Duration def) {
470 final int duration = Settings.Global.getInt(context.getContentResolver(),
471 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds());
472 return Duration.ofSeconds(duration);
473 }
474
475 /**
476 * Set dns resolver sample validity duration to {@link Settings}. The duration must be a
477 * positive number of seconds.
478 *
479 * @param context The {@link Context} to set the setting.
480 * @param duration The sample validity duration.
481 */
482 public static void setDnsResolverSampleValidityDuration(@NonNull Context context,
483 @NonNull Duration duration) {
484 final int time = (int) duration.getSeconds();
485 if (time <= 0) {
486 throw new IllegalArgumentException("Invalid duration");
487 }
488 Settings.Global.putInt(
489 context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time);
490 }
491
492 /**
493 * Get dns resolver success threshold percent from {@link Settings}.
494 *
495 * @param context The {@link Context} to query the setting.
496 * @param def The default value if no setting value.
497 * @return The success threshold in percent for use with the system DNS resolver.
498 */
499 public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) {
500 return Settings.Global.getInt(
501 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def);
502 }
503
504 /**
505 * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must
506 * be 0~100.
507 *
508 * @param context The {@link Context} to set the setting.
509 * @param percent The success threshold percent.
510 */
511 public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context,
512 @IntRange(from = 0, to = 100) int percent) {
513 if (percent < 0 || percent > 100) {
514 throw new IllegalArgumentException("Percent must be 0~100");
515 }
516 Settings.Global.putInt(
517 context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent);
518 }
519
520 /**
521 * Get dns resolver samples range from {@link Settings}.
522 *
523 * @param context The {@link Context} to query the setting.
524 * @return The {@link Range<Integer>} of samples needed for statistics to be considered
525 * meaningful in the system DNS resolver.
526 */
527 @NonNull
528 public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) {
529 final int minSamples = Settings.Global.getInt(context.getContentResolver(),
530 DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
531 final int maxSamples = Settings.Global.getInt(context.getContentResolver(),
532 DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
533 return new Range<>(minSamples, maxSamples);
534 }
535
536 /**
537 * Set dns resolver samples range to {@link Settings}.
538 *
539 * @param context The {@link Context} to set the setting.
540 * @param range The samples range. The minimum number should be more than 0 and the maximum
541 * number should be less that 64.
542 */
543 public static void setDnsResolverSampleRanges(@NonNull Context context,
544 @NonNull Range<Integer> range) {
545 if (range.getLower() < 0 || range.getUpper() > 64) {
546 throw new IllegalArgumentException("Argument must be 0~64");
547 }
548 Settings.Global.putInt(
549 context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower());
550 Settings.Global.putInt(
551 context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper());
552 }
553
554 /**
555 * Get maximum count (from {@link Settings}) of switching network notifications shown in 24
556 * hours.
557 *
558 * @param context The {@link Context} to query the setting.
559 * @param def The default value if no setting value.
560 * @return The maximum count of notifications shown in 24 hours when switching networks.
561 */
562 public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
563 int def) {
564 return Settings.Global.getInt(
565 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def);
566 }
567
568 /**
569 * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours.
570 * The count must be at least 0.
571 *
572 * @param context The {@link Context} to set the setting.
573 * @param count The maximum count of switching network notifications shown in 24 hours.
574 */
575 public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
576 @IntRange(from = 0) int count) {
577 if (count < 0) {
paulhu236a9922021-03-29 10:50:36 +0800578 throw new IllegalArgumentException("Count must be more than 0.");
paulhu94430952021-03-23 00:24:50 +0800579 }
580 Settings.Global.putInt(
581 context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
582 }
583
584 /**
585 * Get minimum duration (from {@link Settings}) between each switching network notifications.
586 *
587 * @param context The {@link Context} to query the setting.
588 * @param def The default time if no setting value.
589 * @return The minimum duration between notifications when switching networks.
590 */
591 @NonNull
592 public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context,
593 @NonNull Duration def) {
594 final int duration = Settings.Global.getInt(context.getContentResolver(),
595 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis());
596 return Duration.ofMillis(duration);
597 }
598
599 /**
600 * Set minimum duration (to {@link Settings}) between each switching network notifications.
paulhu236a9922021-03-29 10:50:36 +0800601 * The duration will be rounded down to the next millisecond, and must be positive.
paulhu94430952021-03-23 00:24:50 +0800602 *
603 * @param context The {@link Context} to set the setting.
604 * @param duration The minimum duration between notifications when switching networks.
605 */
606 public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context,
607 @NonNull Duration duration) {
608 final int time = (int) duration.toMillis();
609 if (time < 0) {
610 throw new IllegalArgumentException("Invalid duration.");
611 }
612 Settings.Global.putInt(context.getContentResolver(),
613 NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time);
614 }
615
616 /**
617 * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection.
618 *
619 * @param context The {@link Context} to query the setting.
620 * @return The URL used for HTTP captive portal detection upon a new connection.
621 */
622 @Nullable
623 public static String getCaptivePortalHttpUrl(@NonNull Context context) {
624 return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL);
625 }
626
627 /**
628 * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
paulhu236a9922021-03-29 10:50:36 +0800629 * The URL is accessed to check for connectivity and presence of a captive portal on a network.
630 * The URL should respond with HTTP status 204 to a GET request, and the stack will use
631 * redirection status as a signal for captive portal detection.
632 * If the URL is set to null or is otherwise incorrect or inaccessible, the stack will fail to
633 * detect connectivity and portals. This will often result in loss of connectivity.
paulhu94430952021-03-23 00:24:50 +0800634 *
635 * @param context The {@link Context} to set the setting.
636 * @param url The URL used for HTTP captive portal detection upon a new connection.
637 */
638 public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) {
639 Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url);
640 }
641
642 /**
643 * Get mode (from {@link Settings}) when connecting a network that presents a captive portal.
644 *
645 * @param context The {@link Context} to query the setting.
646 * @param def The default mode if no setting value.
647 * @return The mode when connecting a network that presents a captive portal.
648 */
649 @CaptivePortalMode
650 public static int getCaptivePortalMode(@NonNull Context context,
651 @CaptivePortalMode int def) {
652 return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def);
653 }
654
655 /**
656 * Set mode (to {@link Settings}) when connecting a network that presents a captive portal.
657 *
658 * @param context The {@link Context} to set the setting.
659 * @param mode The mode when connecting a network that presents a captive portal.
660 */
661 public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) {
662 if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE
663 || mode == CAPTIVE_PORTAL_MODE_PROMPT
664 || mode == CAPTIVE_PORTAL_MODE_AVOID)) {
665 throw new IllegalArgumentException("Invalid captive portal mode");
666 }
667 Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode);
668 }
669
670 /**
671 * Get the global HTTP proxy applied to the device, or null if none.
672 *
673 * @param context The {@link Context} to query the setting.
674 * @return The {@link ProxyInfo} which build from global http proxy settings.
675 */
676 @Nullable
677 public static ProxyInfo getGlobalProxy(@NonNull Context context) {
678 final String host = Settings.Global.getString(
679 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST);
680 final int port = Settings.Global.getInt(
681 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */);
682 final String exclusionList = Settings.Global.getString(
683 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
684 final String pacFileUrl = Settings.Global.getString(
685 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC);
686
687 if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) {
688 return null; // No global proxy.
689 }
690
691 if (TextUtils.isEmpty(pacFileUrl)) {
692 return ProxyInfo.buildDirectProxy(
693 host, port, ProxyUtils.exclusionStringAsList(exclusionList));
694 } else {
695 return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
696 }
697 }
698
699 /**
700 * Set global http proxy settings from given {@link ProxyInfo}.
701 *
Chalard Jeanc8ef2192023-04-14 13:18:37 +0900702 * <p class="note">
703 * While a {@link ProxyInfo} for a PAC proxy can be specified, not all devices support
704 * PAC proxies. In particular, smaller devices like watches often do not have the capabilities
705 * necessary to interpret the PAC file. In such cases, calling this API with a PAC proxy
706 * results in undefined behavior, including possibly breaking networking for applications.
707 * You can test for this by checking for the presence of {@link PackageManager.FEATURE_WEBVIEW}.
708 * </p>
709 *
paulhu94430952021-03-23 00:24:50 +0800710 * @param context The {@link Context} to set the setting.
711 * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from
712 * {@link ProxyInfo#buildPacProxy(Uri)} or
713 * {@link ProxyInfo#buildDirectProxy(String, int, List)}
Chalard Jeanc8ef2192023-04-14 13:18:37 +0900714 * @throws UnsupportedOperationException if |proxyInfo| codes for a PAC proxy but the system
715 * does not support PAC proxies.
paulhu94430952021-03-23 00:24:50 +0800716 */
717 public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) {
718 final String host = proxyInfo.getHost();
719 final int port = proxyInfo.getPort();
720 final String exclusionList = proxyInfo.getExclusionListAsString();
721 final String pacFileUrl = proxyInfo.getPacFileUrl().toString();
722
Chalard Jeanc8ef2192023-04-14 13:18:37 +0900723
724 if (!TextUtils.isEmpty(pacFileUrl)) {
725 final PackageManager pm = context.getPackageManager();
726 if (null != pm && !pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
727 Log.wtf(TAG, "PAC proxy can't be installed on a device without FEATURE_WEBVIEW");
728 }
729 }
730
paulhu94430952021-03-23 00:24:50 +0800731 if (TextUtils.isEmpty(pacFileUrl)) {
732 Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host);
733 Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port);
734 Settings.Global.putString(
735 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList);
736 Settings.Global.putString(
737 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
738 } else {
739 Settings.Global.putString(
740 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
741 Settings.Global.putString(
742 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
743 Settings.Global.putInt(
744 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
745 Settings.Global.putString(
746 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
747 }
748 }
749
750 /**
751 * Clear all global http proxy settings.
752 *
753 * @param context The {@link Context} to set the setting.
754 */
755 public static void clearGlobalProxy(@NonNull Context context) {
756 Settings.Global.putString(
757 context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */);
758 Settings.Global.putInt(
759 context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */);
760 Settings.Global.putString(
761 context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */);
762 Settings.Global.putString(
763 context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */);
764 }
765
lucaslin57f9ba82021-04-23 21:03:39 +0800766 /**
767 * Get private DNS mode from settings.
768 *
769 * @param context The Context to query the private DNS mode from settings.
770 * @return A string of private DNS mode.
771 */
772 @PrivateDnsMode
773 public static int getPrivateDnsMode(@NonNull Context context) {
lucaslin89621282021-06-21 16:39:08 +0800774 return ConnectivitySettingsUtils.getPrivateDnsMode(context);
lucaslin57f9ba82021-04-23 21:03:39 +0800775 }
776
777 /**
778 * Set private DNS mode to settings.
779 *
780 * @param context The {@link Context} to set the private DNS mode.
781 * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants.
782 */
783 public static void setPrivateDnsMode(@NonNull Context context, @PrivateDnsMode int mode) {
lucaslin89621282021-06-21 16:39:08 +0800784 ConnectivitySettingsUtils.setPrivateDnsMode(context, mode);
lucaslin57f9ba82021-04-23 21:03:39 +0800785 }
786
paulhu94430952021-03-23 00:24:50 +0800787 /**
788 * Get specific private dns provider name from {@link Settings}.
789 *
790 * @param context The {@link Context} to query the setting.
791 * @return The specific private dns provider name, or null if no setting value.
792 */
793 @Nullable
794 public static String getPrivateDnsHostname(@NonNull Context context) {
lucaslin89621282021-06-21 16:39:08 +0800795 return ConnectivitySettingsUtils.getPrivateDnsHostname(context);
paulhu94430952021-03-23 00:24:50 +0800796 }
797
798 /**
799 * Set specific private dns provider name to {@link Settings}.
800 *
801 * @param context The {@link Context} to set the setting.
802 * @param specifier The specific private dns provider name.
803 */
lucaslin89621282021-06-21 16:39:08 +0800804 public static void setPrivateDnsHostname(@NonNull Context context, @Nullable String specifier) {
805 ConnectivitySettingsUtils.setPrivateDnsHostname(context, specifier);
paulhu94430952021-03-23 00:24:50 +0800806 }
807
808 /**
809 * Get default private dns mode from {@link Settings}.
810 *
811 * @param context The {@link Context} to query the setting.
812 * @return The default private dns mode.
813 */
814 @PrivateDnsMode
815 @NonNull
816 public static String getPrivateDnsDefaultMode(@NonNull Context context) {
817 return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE);
818 }
819
820 /**
821 * Set default private dns mode to {@link Settings}.
822 *
823 * @param context The {@link Context} to set the setting.
824 * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_*
825 * constants.
826 */
827 public static void setPrivateDnsDefaultMode(@NonNull Context context,
lucaslin57f9ba82021-04-23 21:03:39 +0800828 @NonNull @PrivateDnsMode int mode) {
paulhu94430952021-03-23 00:24:50 +0800829 if (!(mode == PRIVATE_DNS_MODE_OFF
830 || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC
831 || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) {
832 throw new IllegalArgumentException("Invalid private dns mode");
833 }
lucaslin57f9ba82021-04-23 21:03:39 +0800834 Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE,
835 getPrivateDnsModeAsString(mode));
paulhu94430952021-03-23 00:24:50 +0800836 }
837
838 /**
839 * Get duration (from {@link Settings}) to keep a PendingIntent-based request.
840 *
841 * @param context The {@link Context} to query the setting.
842 * @param def The default duration if no setting value.
843 * @return The duration to keep a PendingIntent-based request.
844 */
845 @NonNull
846 public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context,
847 @NonNull Duration def) {
848 final int duration = Settings.Secure.getInt(context.getContentResolver(),
849 CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis());
850 return Duration.ofMillis(duration);
851 }
852
853 /**
854 * Set duration (to {@link Settings}) to keep a PendingIntent-based request.
paulhu236a9922021-03-29 10:50:36 +0800855 * The duration will be rounded down to the next millisecond, and must be positive.
paulhu94430952021-03-23 00:24:50 +0800856 *
857 * @param context The {@link Context} to set the setting.
858 * @param duration The duration to keep a PendingIntent-based request.
859 */
860 public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context,
861 @NonNull Duration duration) {
862 final int time = (int) duration.toMillis();
863 if (time < 0) {
864 throw new IllegalArgumentException("Invalid duration.");
865 }
866 Settings.Secure.putInt(
867 context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time);
868 }
869
870 /**
871 * Read from {@link Settings} whether the mobile data connection should remain active
872 * even when higher priority networks are active.
873 *
874 * @param context The {@link Context} to query the setting.
875 * @param def The default value if no setting value.
876 * @return Whether the mobile data connection should remain active even when higher
877 * priority networks are active.
878 */
879 public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) {
880 final int enable = Settings.Global.getInt(
881 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0));
882 return (enable != 0) ? true : false;
883 }
884
885 /**
886 * Write into {@link Settings} whether the mobile data connection should remain active
887 * even when higher priority networks are active.
888 *
889 * @param context The {@link Context} to set the setting.
890 * @param enable Whether the mobile data connection should remain active even when higher
891 * priority networks are active.
892 */
893 public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) {
894 Settings.Global.putInt(
895 context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0));
896 }
897
898 /**
899 * Read from {@link Settings} whether the wifi data connection should remain active
900 * even when higher priority networks are active.
901 *
902 * @param context The {@link Context} to query the setting.
903 * @param def The default value if no setting value.
904 * @return Whether the wifi data connection should remain active even when higher
905 * priority networks are active.
906 */
907 public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) {
908 final int enable = Settings.Global.getInt(
909 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0));
910 return (enable != 0) ? true : false;
911 }
912
913 /**
914 * Write into {@link Settings} whether the wifi data connection should remain active
915 * even when higher priority networks are active.
916 *
917 * @param context The {@link Context} to set the setting.
918 * @param enable Whether the wifi data connection should remain active even when higher
919 * priority networks are active
920 */
921 public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) {
922 Settings.Global.putInt(
923 context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0));
924 }
925
926 /**
927 * Get avoid bad wifi setting from {@link Settings}.
928 *
929 * @param context The {@link Context} to query the setting.
930 * @return The setting whether to automatically switch away from wifi networks that lose
931 * internet access.
932 */
933 @NetworkAvoidBadWifi
934 public static int getNetworkAvoidBadWifi(@NonNull Context context) {
935 final String setting =
936 Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI);
937 if ("0".equals(setting)) {
938 return NETWORK_AVOID_BAD_WIFI_IGNORE;
939 } else if ("1".equals(setting)) {
940 return NETWORK_AVOID_BAD_WIFI_AVOID;
941 } else {
942 return NETWORK_AVOID_BAD_WIFI_PROMPT;
943 }
944 }
945
946 /**
947 * Set avoid bad wifi setting to {@link Settings}.
948 *
949 * @param context The {@link Context} to set the setting.
950 * @param value Whether to automatically switch away from wifi networks that lose internet
951 * access.
952 */
953 public static void setNetworkAvoidBadWifi(@NonNull Context context,
954 @NetworkAvoidBadWifi int value) {
955 final String setting;
956 if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) {
957 setting = "0";
958 } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) {
959 setting = "1";
960 } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) {
961 setting = null;
962 } else {
963 throw new IllegalArgumentException("Invalid avoid bad wifi setting");
964 }
965 Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting);
966 }
967
968 /**
969 * Get network metered multipath preference from {@link Settings}.
970 *
971 * @param context The {@link Context} to query the setting.
972 * @return The network metered multipath preference which should be one of
973 * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified
974 * by config_networkMeteredMultipathPreference is used.
975 */
976 @Nullable
977 public static String getNetworkMeteredMultipathPreference(@NonNull Context context) {
978 return Settings.Global.getString(
979 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE);
980 }
981
982 /**
983 * Set network metered multipath preference to {@link Settings}.
984 *
985 * @param context The {@link Context} to set the setting.
986 * @param preference The network metered multipath preference which should be one of
987 * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value
988 * specified by config_networkMeteredMultipathPreference is used.
989 */
990 public static void setNetworkMeteredMultipathPreference(@NonNull Context context,
991 @NonNull @MultipathPreference String preference) {
992 if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER
993 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY
994 || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) {
995 throw new IllegalArgumentException("Invalid private dns mode");
996 }
997 Settings.Global.putString(
998 context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference);
999 }
paulhu7a4eeed2021-03-25 13:17:58 +08001000
paulhu91e78062021-05-26 16:16:57 +08001001 private static Set<Integer> getUidSetFromString(@Nullable String uidList) {
1002 final Set<Integer> uids = new ArraySet<>();
1003 if (TextUtils.isEmpty(uidList)) {
1004 return uids;
1005 }
1006 for (String uid : uidList.split(";")) {
1007 uids.add(Integer.valueOf(uid));
1008 }
1009 return uids;
1010 }
1011
1012 private static String getUidStringFromSet(@NonNull Set<Integer> uidList) {
1013 final StringJoiner joiner = new StringJoiner(";");
1014 for (Integer uid : uidList) {
1015 if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) {
1016 throw new IllegalArgumentException("Invalid uid");
1017 }
1018 joiner.add(uid.toString());
1019 }
1020 return joiner.toString();
1021 }
1022
paulhu7a4eeed2021-03-25 13:17:58 +08001023 /**
paulhu344c1162021-05-11 09:42:50 +08001024 * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
paulhu7a4eeed2021-03-25 13:17:58 +08001025 * even when higher-priority networks are connected.
1026 *
1027 * @param context The {@link Context} to query the setting.
paulhu344c1162021-05-11 09:42:50 +08001028 * @return A list of uids that should go on cellular networks in preference even when
paulhu7a4eeed2021-03-25 13:17:58 +08001029 * higher-priority networks are connected or null if no setting value.
1030 */
paulhu344c1162021-05-11 09:42:50 +08001031 @NonNull
1032 public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
1033 final String uidList = Settings.Secure.getString(
1034 context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
paulhu91e78062021-05-26 16:16:57 +08001035 return getUidSetFromString(uidList);
paulhu7a4eeed2021-03-25 13:17:58 +08001036 }
1037
1038 /**
paulhu344c1162021-05-11 09:42:50 +08001039 * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference
paulhu7a4eeed2021-03-25 13:17:58 +08001040 * even when higher-priority networks are connected.
1041 *
1042 * @param context The {@link Context} to set the setting.
paulhu344c1162021-05-11 09:42:50 +08001043 * @param uidList A list of uids that should go on cellular networks in preference even when
paulhu7a4eeed2021-03-25 13:17:58 +08001044 * higher-priority networks are connected.
1045 */
paulhu344c1162021-05-11 09:42:50 +08001046 public static void setMobileDataPreferredUids(@NonNull Context context,
1047 @NonNull Set<Integer> uidList) {
paulhu91e78062021-05-26 16:16:57 +08001048 final String uids = getUidStringFromSet(uidList);
1049 Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids);
paulhu7a4eeed2021-03-25 13:17:58 +08001050 }
paulhu69afcd52021-04-27 00:14:47 +08001051
1052 /**
paulhu91e78062021-05-26 16:16:57 +08001053 * Get the list of uids (from {@link Settings}) allowed to use restricted networks.
1054 *
1055 * Access to restricted networks is controlled by the (preinstalled-only)
1056 * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission, but highly privileged
1057 * callers can also set a list of uids that can access restricted networks.
1058 *
1059 * This is useful for example in some jurisdictions where government apps,
1060 * that can't be preinstalled, must still have access to emergency services.
paulhu69afcd52021-04-27 00:14:47 +08001061 *
1062 * @param context The {@link Context} to query the setting.
paulhu91e78062021-05-26 16:16:57 +08001063 * @return A list of uids that is allowed to use restricted networks or null if no setting
paulhu257a5cf2021-05-14 15:27:36 +08001064 * value.
paulhu69afcd52021-04-27 00:14:47 +08001065 */
1066 @NonNull
paulhu91e78062021-05-26 16:16:57 +08001067 public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
paulhu44280442021-05-31 10:42:13 +08001068 final String uidList = Settings.Global.getString(
paulhu91e78062021-05-26 16:16:57 +08001069 context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS);
1070 return getUidSetFromString(uidList);
paulhu69afcd52021-04-27 00:14:47 +08001071 }
1072
paulhu92f128c2021-07-01 18:04:45 +08001073 private static boolean isCallingFromSystem() {
1074 final int uid = Binder.getCallingUid();
1075 final int pid = Binder.getCallingPid();
1076 if (uid == Process.SYSTEM_UID && pid == Process.myPid()) {
1077 return true;
1078 }
1079 return false;
1080 }
1081
paulhu69afcd52021-04-27 00:14:47 +08001082 /**
paulhu91e78062021-05-26 16:16:57 +08001083 * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +08001084 *
1085 * @param context The {@link Context} to set the setting.
paulhu91e78062021-05-26 16:16:57 +08001086 * @param uidList A list of uids that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +08001087 */
paulhu91e78062021-05-26 16:16:57 +08001088 public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
1089 @NonNull Set<Integer> uidList) {
paulhu92f128c2021-07-01 18:04:45 +08001090 final boolean calledFromSystem = isCallingFromSystem();
1091 if (!calledFromSystem) {
1092 // Enforce NETWORK_SETTINGS check if it's debug build. This is for MTS test only.
1093 if (!Build.isDebuggable()) {
1094 throw new SecurityException("Only system can set this setting.");
1095 }
1096 context.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS,
1097 "Requires NETWORK_SETTINGS permission");
1098 }
paulhu91e78062021-05-26 16:16:57 +08001099 final String uids = getUidStringFromSet(uidList);
paulhu44280442021-05-31 10:42:13 +08001100 Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
paulhu91e78062021-05-26 16:16:57 +08001101 uids);
paulhu69afcd52021-04-27 00:14:47 +08001102 }
Patrick Rohra2084362022-01-03 15:55:52 +01001103
1104 /**
Patrick Rohrca7e5782022-02-18 10:34:20 +01001105 * Get the network bandwidth ingress rate limit.
Patrick Rohra2084362022-01-03 15:55:52 +01001106 *
Patrick Rohrca7e5782022-02-18 10:34:20 +01001107 * The limit is only applicable to networks that provide internet connectivity. -1 codes for no
1108 * bandwidth limitation.
Patrick Rohra2084362022-01-03 15:55:52 +01001109 *
1110 * @param context The {@link Context} to query the setting.
1111 * @return The rate limit in number of bytes per second or -1 if disabled.
1112 */
1113 public static long getIngressRateLimitInBytesPerSecond(@NonNull Context context) {
1114 return Settings.Global.getLong(context.getContentResolver(),
1115 INGRESS_RATE_LIMIT_BYTES_PER_SECOND, -1);
1116 }
1117
1118 /**
Patrick Rohrca7e5782022-02-18 10:34:20 +01001119 * Set the network bandwidth ingress rate limit.
Patrick Rohra2084362022-01-03 15:55:52 +01001120 *
Patrick Rohrca7e5782022-02-18 10:34:20 +01001121 * The limit is applied to all networks that provide internet connectivity. It is applied on a
1122 * per-network basis, meaning that global ingress rate could exceed the limit when communicating
1123 * on multiple networks simultaneously.
Patrick Rohra2084362022-01-03 15:55:52 +01001124 *
1125 * @param context The {@link Context} to set the setting.
1126 * @param rateLimitInBytesPerSec The rate limit in number of bytes per second or -1 to disable.
1127 */
1128 public static void setIngressRateLimitInBytesPerSecond(@NonNull Context context,
Patrick Rohrca7e5782022-02-18 10:34:20 +01001129 @IntRange(from = -1L, to = 0xFFFFFFFFL) long rateLimitInBytesPerSec) {
Patrick Rohra2084362022-01-03 15:55:52 +01001130 if (rateLimitInBytesPerSec < -1) {
1131 throw new IllegalArgumentException(
1132 "Rate limit must be within the range [-1, Integer.MAX_VALUE]");
1133 }
1134 Settings.Global.putLong(context.getContentResolver(),
1135 INGRESS_RATE_LIMIT_BYTES_PER_SECOND,
1136 rateLimitInBytesPerSec);
1137 }
paulhu845456e2021-03-17 17:19:09 +08001138}