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