blob: 9c8d08fb1a643451222e72de9c9440cb3f89e273 [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
Lucas Lin611bc022021-06-21 20:22:10 +000023import 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;
Aaron Huangcff22942021-05-27 16:31:26 +080031import android.net.ConnectivityManager.MultipathPreference;
paulhu03635b32021-07-01 18:04:45 +080032import android.os.Binder;
33import android.os.Build;
paulhu344c1162021-05-11 09:42:50 +080034import android.os.Process;
35import android.os.UserHandle;
paulhu94430952021-03-23 00:24:50 +080036import android.provider.Settings;
37import android.text.TextUtils;
paulhu69afcd52021-04-27 00:14:47 +080038import android.util.ArraySet;
paulhu94430952021-03-23 00:24:50 +080039import android.util.Range;
40
Lucas Lin611bc022021-06-21 20:22:10 +000041import com.android.net.module.util.ConnectivitySettingsUtils;
paulhu94430952021-03-23 00:24:50 +080042import com.android.net.module.util.ProxyUtils;
paulhuc9925e02021-03-17 20:30:33 +080043
44import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
paulhu94430952021-03-23 00:24:50 +080046import java.time.Duration;
47import java.util.List;
paulhu69afcd52021-04-27 00:14:47 +080048import java.util.Set;
49import java.util.StringJoiner;
paulhuc9925e02021-03-17 20:30:33 +080050
paulhu845456e2021-03-17 17:19:09 +080051/**
52 * A manager class for connectivity module settings.
53 *
54 * @hide
55 */
paulhu94430952021-03-23 00:24:50 +080056@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
paulhu845456e2021-03-17 17:19:09 +080057public class ConnectivitySettingsManager {
58
59 private ConnectivitySettingsManager() {}
60
paulhuc9925e02021-03-17 20:30:33 +080061 /** 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.
paulhu94430952021-03-23 00:24:50 +080076 *
77 * @hide
paulhuc9925e02021-03-17 20:30:33 +080078 */
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.
paulhu94430952021-03-23 00:24:50 +080084 *
85 * @hide
paulhuc9925e02021-03-17 20:30:33 +080086 */
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.
paulhu94430952021-03-23 00:24:50 +080093 *
94 * @hide
paulhuc9925e02021-03-17 20:30:33 +080095 */
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.
paulhu94430952021-03-23 00:24:50 +0800101 *
102 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800103 */
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.
paulhu94430952021-03-23 00:24:50 +0800110 *
111 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800112 */
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.
paulhu94430952021-03-23 00:24:50 +0800117 *
118 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800119 */
120 public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
121
paulhu94430952021-03-23 00:24:50 +0800122 private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8;
123 private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64;
124
paulhuc9925e02021-03-17 20:30:33 +0800125 /** Network switch notification settings */
126
127 /**
128 * The maximum number of notifications shown in 24 hours when switching networks.
paulhu94430952021-03-23 00:24:50 +0800129 *
130 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800131 */
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.
paulhu94430952021-03-23 00:24:50 +0800137 *
138 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800139 */
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.
paulhu94430952021-03-23 00:24:50 +0800148 *
149 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800150 */
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.
paulhu94430952021-03-23 00:24:50 +0800155 * Must be one of the CAPTIVE_PORTAL_MODE_* constants below.
paulhuc9925e02021-03-17 20:30:33 +0800156 *
157 * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
paulhu94430952021-03-23 00:24:50 +0800158 *
159 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800160 */
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.
paulhu94430952021-03-23 00:24:50 +0800193 *
194 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800195 */
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.
paulhu94430952021-03-23 00:24:50 +0800200 *
201 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800202 */
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.
paulhu94430952021-03-23 00:24:50 +0800211 *
212 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800213 */
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.
paulhu94430952021-03-23 00:24:50 +0800219 *
220 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800221 */
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.
paulhu94430952021-03-23 00:24:50 +0800233 *
234 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800235 */
236 public static final String PRIVATE_DNS_MODE = "private_dns_mode";
237
238 /**
239 * The specific Private DNS provider name.
paulhu94430952021-03-23 00:24:50 +0800240 *
241 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800242 */
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.
paulhu94430952021-03-23 00:24:50 +0800251 *
252 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800253 */
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.
paulhu94430952021-03-23 00:24:50 +0800262 *
263 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800264 */
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)
paulhu94430952021-03-23 00:24:50 +0800275 *
276 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800277 */
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)
paulhu94430952021-03-23 00:24:50 +0800289 *
290 * @hide
paulhuc9925e02021-03-17 20:30:33 +0800291 */
292 public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
293
paulhu845456e2021-03-17 17:19:09 +0800294 /**
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.
paulhu94430952021-03-23 00:24:50 +0800302 *
303 * @hide
paulhu845456e2021-03-17 17:19:09 +0800304 */
305 public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
306
307 /**
paulhu94430952021-03-23 00:24:50 +0800308 * 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 /**
paulhu845456e2021-03-17 17:19:09 +0800332 * 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.
paulhu94430952021-03-23 00:24:50 +0800335 *
336 * @hide
paulhu845456e2021-03-17 17:19:09 +0800337 */
338 public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
339 "network_metered_multipath_preference";
paulhu94430952021-03-23 00:24:50 +0800340
341 /**
paulhu344c1162021-05-11 09:42:50 +0800342 * A list of uids that should go on cellular networks in preference even when higher-priority
paulhu7a4eeed2021-03-25 13:17:58 +0800343 * networks are connected.
344 *
345 * @hide
346 */
paulhu344c1162021-05-11 09:42:50 +0800347 public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids";
paulhu7a4eeed2021-03-25 13:17:58 +0800348
349 /**
lucaslin57f9ba82021-04-23 21:03:39 +0800350 * One of the private DNS modes that indicates the private DNS mode is off.
351 */
Lucas Lin611bc022021-06-21 20:22:10 +0000352 public static final int PRIVATE_DNS_MODE_OFF = ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OFF;
lucaslin57f9ba82021-04-23 21:03:39 +0800353
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 */
Lucas Lin611bc022021-06-21 20:22:10 +0000358 public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC =
359 ConnectivitySettingsUtils.PRIVATE_DNS_MODE_OPPORTUNISTIC;
lucaslin57f9ba82021-04-23 21:03:39 +0800360
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 */
Lucas Lin611bc022021-06-21 20:22:10 +0000366 public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME =
367 ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
lucaslin57f9ba82021-04-23 21:03:39 +0800368
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
lucaslin57f9ba82021-04-23 21:03:39 +0800378 /**
paulhu68aacb42021-05-26 16:16:57 +0800379 * A list of uids that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +0800380 *
381 * @hide
382 */
paulhu68aacb42021-05-26 16:16:57 +0800383 public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS =
384 "uids_allowed_on_restricted_networks";
paulhu69afcd52021-04-27 00:14:47 +0800385
386 /**
paulhu94430952021-03-23 00:24:50 +0800387 * 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
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 */
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
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 */
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
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 */
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 */
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 */
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
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 */
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 */
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 */
564 public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
565 @IntRange(from = 0) int count) {
566 if (count < 0) {
paulhu0903f732021-03-29 10:50:36 +0800567 throw new IllegalArgumentException("Count must be more than 0.");
paulhu94430952021-03-23 00:24:50 +0800568 }
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
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.
paulhu0903f732021-03-29 10:50:36 +0800590 * The duration will be rounded down to the next millisecond, and must be positive.
paulhu94430952021-03-23 00:24:50 +0800591 *
592 * @param context The {@link Context} to set the setting.
593 * @param duration The minimum duration between notifications when switching networks.
594 */
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
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.
paulhu0903f732021-03-29 10:50:36 +0800618 * 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.
paulhu94430952021-03-23 00:24:50 +0800623 *
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 */
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
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 */
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
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 */
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 */
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
lucaslin57f9ba82021-04-23 21:03:39 +0800737 /**
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
744 public static int getPrivateDnsMode(@NonNull Context context) {
Lucas Lin611bc022021-06-21 20:22:10 +0000745 return ConnectivitySettingsUtils.getPrivateDnsMode(context);
lucaslin57f9ba82021-04-23 21:03:39 +0800746 }
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 */
754 public static void setPrivateDnsMode(@NonNull Context context, @PrivateDnsMode int mode) {
Lucas Lin611bc022021-06-21 20:22:10 +0000755 ConnectivitySettingsUtils.setPrivateDnsMode(context, mode);
lucaslin57f9ba82021-04-23 21:03:39 +0800756 }
757
paulhu94430952021-03-23 00:24:50 +0800758 /**
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
765 public static String getPrivateDnsHostname(@NonNull Context context) {
Lucas Lin611bc022021-06-21 20:22:10 +0000766 return ConnectivitySettingsUtils.getPrivateDnsHostname(context);
paulhu94430952021-03-23 00:24:50 +0800767 }
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 */
Lucas Lin611bc022021-06-21 20:22:10 +0000775 public static void setPrivateDnsHostname(@NonNull Context context, @Nullable String specifier) {
776 ConnectivitySettingsUtils.setPrivateDnsHostname(context, specifier);
paulhu94430952021-03-23 00:24:50 +0800777 }
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
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 */
798 public static void setPrivateDnsDefaultMode(@NonNull Context context,
lucaslin57f9ba82021-04-23 21:03:39 +0800799 @NonNull @PrivateDnsMode int mode) {
paulhu94430952021-03-23 00:24:50 +0800800 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 }
lucaslin57f9ba82021-04-23 21:03:39 +0800805 Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE,
806 getPrivateDnsModeAsString(mode));
paulhu94430952021-03-23 00:24:50 +0800807 }
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
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.
paulhu0903f732021-03-29 10:50:36 +0800826 * The duration will be rounded down to the next millisecond, and must be positive.
paulhu94430952021-03-23 00:24:50 +0800827 *
828 * @param context The {@link Context} to set the setting.
829 * @param duration The duration to keep a PendingIntent-based request.
830 */
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 */
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 */
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 */
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 */
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
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 */
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
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 */
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 }
paulhu7a4eeed2021-03-25 13:17:58 +0800971
paulhu68aacb42021-05-26 16:16:57 +0800972 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
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
paulhu7a4eeed2021-03-25 13:17:58 +0800994 /**
paulhu344c1162021-05-11 09:42:50 +0800995 * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference
paulhu7a4eeed2021-03-25 13:17:58 +0800996 * even when higher-priority networks are connected.
997 *
998 * @param context The {@link Context} to query the setting.
paulhu344c1162021-05-11 09:42:50 +0800999 * @return A list of uids that should go on cellular networks in preference even when
paulhu7a4eeed2021-03-25 13:17:58 +08001000 * higher-priority networks are connected or null if no setting value.
1001 */
paulhu344c1162021-05-11 09:42:50 +08001002 @NonNull
1003 public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) {
1004 final String uidList = Settings.Secure.getString(
1005 context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS);
paulhu68aacb42021-05-26 16:16:57 +08001006 return getUidSetFromString(uidList);
paulhu7a4eeed2021-03-25 13:17:58 +08001007 }
1008
1009 /**
paulhu344c1162021-05-11 09:42:50 +08001010 * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference
paulhu7a4eeed2021-03-25 13:17:58 +08001011 * even when higher-priority networks are connected.
1012 *
1013 * @param context The {@link Context} to set the setting.
paulhu344c1162021-05-11 09:42:50 +08001014 * @param uidList A list of uids that should go on cellular networks in preference even when
paulhu7a4eeed2021-03-25 13:17:58 +08001015 * higher-priority networks are connected.
1016 */
paulhu344c1162021-05-11 09:42:50 +08001017 public static void setMobileDataPreferredUids(@NonNull Context context,
1018 @NonNull Set<Integer> uidList) {
paulhu68aacb42021-05-26 16:16:57 +08001019 final String uids = getUidStringFromSet(uidList);
1020 Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, uids);
paulhu7a4eeed2021-03-25 13:17:58 +08001021 }
paulhu69afcd52021-04-27 00:14:47 +08001022
1023 /**
paulhu68aacb42021-05-26 16:16:57 +08001024 * 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.
paulhu69afcd52021-04-27 00:14:47 +08001032 *
1033 * @param context The {@link Context} to query the setting.
paulhu68aacb42021-05-26 16:16:57 +08001034 * @return A list of uids that is allowed to use restricted networks or null if no setting
paulhu257a5cf2021-05-14 15:27:36 +08001035 * value.
paulhu69afcd52021-04-27 00:14:47 +08001036 */
1037 @NonNull
paulhu68aacb42021-05-26 16:16:57 +08001038 public static Set<Integer> getUidsAllowedOnRestrictedNetworks(@NonNull Context context) {
paulhua1ac5642021-05-31 10:42:13 +08001039 final String uidList = Settings.Global.getString(
paulhu68aacb42021-05-26 16:16:57 +08001040 context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS);
1041 return getUidSetFromString(uidList);
paulhu69afcd52021-04-27 00:14:47 +08001042 }
1043
paulhu03635b32021-07-01 18:04:45 +08001044 private static boolean isCallingFromSystem() {
Oliver Scottede22642022-01-10 20:18:08 +01001045 final int uid = UserHandle.getAppId(Binder.getCallingUid());
paulhu03635b32021-07-01 18:04:45 +08001046 final int pid = Binder.getCallingPid();
1047 if (uid == Process.SYSTEM_UID && pid == Process.myPid()) {
1048 return true;
1049 }
1050 return false;
1051 }
1052
paulhu69afcd52021-04-27 00:14:47 +08001053 /**
paulhu68aacb42021-05-26 16:16:57 +08001054 * Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +08001055 *
1056 * @param context The {@link Context} to set the setting.
paulhu68aacb42021-05-26 16:16:57 +08001057 * @param uidList A list of uids that is allowed to use restricted networks.
paulhu69afcd52021-04-27 00:14:47 +08001058 */
paulhu68aacb42021-05-26 16:16:57 +08001059 public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
1060 @NonNull Set<Integer> uidList) {
paulhu03635b32021-07-01 18:04:45 +08001061 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 }
paulhu68aacb42021-05-26 16:16:57 +08001070 final String uids = getUidStringFromSet(uidList);
paulhua1ac5642021-05-31 10:42:13 +08001071 Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
paulhu68aacb42021-05-26 16:16:57 +08001072 uids);
paulhu69afcd52021-04-27 00:14:47 +08001073 }
paulhu845456e2021-03-17 17:19:09 +08001074}