• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.networkstack.tethering;
18 
19 import static android.content.Context.TELEPHONY_SERVICE;
20 import static android.net.ConnectivityManager.TYPE_ETHERNET;
21 import static android.net.ConnectivityManager.TYPE_MOBILE;
22 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
23 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
24 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
25 
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.res.Resources;
29 import android.net.TetheringConfigurationParcel;
30 import android.net.util.SharedLog;
31 import android.provider.DeviceConfig;
32 import android.provider.Settings;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.text.TextUtils;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.modules.utils.build.SdkLevel;
39 import com.android.net.module.util.DeviceConfigUtils;
40 
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collection;
45 import java.util.StringJoiner;
46 
47 /**
48  * A utility class to encapsulate the various tethering configuration elements.
49  *
50  * This configuration data includes elements describing upstream properties
51  * (preferred and required types of upstream connectivity as well as default
52  * DNS servers to use if none are available) and downstream properties (such
53  * as regular expressions use to match suitable downstream interfaces and the
54  * DHCPv4 ranges to use).
55  *
56  * @hide
57  */
58 public class TetheringConfiguration {
59     private static final String TAG = TetheringConfiguration.class.getSimpleName();
60 
61     private static final String[] EMPTY_STRING_ARRAY = new String[0];
62 
63     private static final String TETHERING_MODULE_NAME = "com.android.tethering";
64 
65     // Default ranges used for the legacy DHCP server.
66     // USB is  192.168.42.1 and 255.255.255.0
67     // Wifi is 192.168.43.1 and 255.255.255.0
68     // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
69     // with 255.255.255.0
70     // P2P is 192.168.49.1 and 255.255.255.0
71     private static final String[] LEGACY_DHCP_DEFAULT_RANGE = {
72         "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
73         "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
74         "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
75         "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
76     };
77 
78     private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
79 
80     @VisibleForTesting
81     public static final int TETHER_USB_RNDIS_FUNCTION = 0;
82 
83     @VisibleForTesting
84     public static final int TETHER_USB_NCM_FUNCTION   = 1;
85 
86     /**
87      * Override enabling BPF offload configuration for tethering.
88      */
89     public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD =
90             "override_tether_enable_bpf_offload";
91 
92     /**
93      * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
94      */
95     public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
96             "tether_enable_legacy_dhcp_server";
97 
98     public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP =
99             "use_legacy_wifi_p2p_dedicated_ip";
100 
101     /**
102      * Flag use to enable select all prefix ranges feature.
103      * TODO: Remove this flag if there are no problems after M-2020-12 rolls out.
104      */
105     public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES =
106             "tether_enable_select_all_prefix_ranges";
107 
108     /**
109      * Experiment flag to force choosing upstreams automatically.
110      *
111      * This setting is intended to help force-enable the feature on OEM devices that disabled it
112      * via resource overlays, and later noticed issues. To that end, it overrides
113      * config_tether_upstream_automatic when set to true.
114      *
115      * This flag is enabled if !=0 and less than the module APEX version: see
116      * {@link DeviceConfigUtils#isFeatureEnabled}. It is also ignored after R, as later devices
117      * should just set config_tether_upstream_automatic to true instead.
118      */
119     public static final String TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION =
120             "tether_force_upstream_automatic_version";
121 
122     /**
123      * Settings key to foce choosing usb functions for usb tethering.
124      *
125      * TODO: Remove this hard code string and make Settings#TETHER_FORCE_USB_FUNCTIONS as API.
126      */
127     public static final String TETHER_FORCE_USB_FUNCTIONS =
128             "tether_force_usb_functions";
129     /**
130      * Default value that used to periodic polls tether offload stats from tethering offload HAL
131      * to make the data warnings work.
132      */
133     public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
134 
135     public final String[] tetherableUsbRegexs;
136     public final String[] tetherableWifiRegexs;
137     public final String[] tetherableWigigRegexs;
138     public final String[] tetherableWifiP2pRegexs;
139     public final String[] tetherableBluetoothRegexs;
140     public final String[] tetherableNcmRegexs;
141     public final boolean isDunRequired;
142     public final boolean chooseUpstreamAutomatically;
143     public final Collection<Integer> preferredUpstreamIfaceTypes;
144     public final String[] legacyDhcpRanges;
145     public final String[] defaultIPv4DNS;
146     public final boolean enableLegacyDhcpServer;
147 
148     public final String[] provisioningApp;
149     public final String provisioningAppNoUi;
150     public final int provisioningCheckPeriod;
151     public final String provisioningResponse;
152 
153     public final int activeDataSubId;
154 
155     private final int mOffloadPollInterval;
156     // TODO: Add to TetheringConfigurationParcel if required.
157     private final boolean mEnableBpfOffload;
158     private final boolean mEnableWifiP2pDedicatedIp;
159 
160     private final boolean mEnableSelectAllPrefixRange;
161     private final int mUsbTetheringFunction;
162     protected final ContentResolver mContentResolver;
163 
TetheringConfiguration(Context ctx, SharedLog log, int id)164     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
165         final SharedLog configLog = log.forSubComponent("config");
166 
167         activeDataSubId = id;
168         Resources res = getResources(ctx, activeDataSubId);
169         mContentResolver = ctx.getContentResolver();
170 
171         mUsbTetheringFunction = getUsbTetheringFunction(res);
172 
173         final String[] ncmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs);
174         // If usb tethering use NCM and config_tether_ncm_regexs is not empty, use
175         // config_tether_ncm_regexs for tetherableUsbRegexs.
176         if (isUsingNcm() && (ncmRegexs.length != 0)) {
177             tetherableUsbRegexs = ncmRegexs;
178             tetherableNcmRegexs = EMPTY_STRING_ARRAY;
179         } else {
180             tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
181             tetherableNcmRegexs = ncmRegexs;
182         }
183         // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
184         // us an interface name. Careful consideration needs to be given to
185         // implications for Settings and for provisioning checks.
186         tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
187         // TODO: Remove entire wigig code once tethering module no longer support R devices.
188         tetherableWigigRegexs = SdkLevel.isAtLeastS()
189                 ? new String[0] : getResourceStringArray(res, R.array.config_tether_wigig_regexs);
190         tetherableWifiP2pRegexs = getResourceStringArray(
191                 res, R.array.config_tether_wifi_p2p_regexs);
192         tetherableBluetoothRegexs = getResourceStringArray(
193                 res, R.array.config_tether_bluetooth_regexs);
194 
195         isDunRequired = checkDunRequired(ctx);
196 
197         final boolean forceAutomaticUpstream = !SdkLevel.isAtLeastS()
198                 && isFeatureEnabled(ctx, TETHER_FORCE_UPSTREAM_AUTOMATIC_VERSION);
199         chooseUpstreamAutomatically = forceAutomaticUpstream || getResourceBoolean(
200                 res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
201         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
202 
203         legacyDhcpRanges = getLegacyDhcpRanges(res);
204         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
205         mEnableBpfOffload = getEnableBpfOffload(res);
206         enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
207 
208         provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
209         provisioningAppNoUi = getResourceString(res,
210                 R.string.config_mobile_hotspot_provision_app_no_ui);
211         provisioningCheckPeriod = getResourceInteger(res,
212                 R.integer.config_mobile_hotspot_provision_check_period,
213                 0 /* No periodic re-check */);
214         provisioningResponse = getResourceString(res,
215                 R.string.config_mobile_hotspot_provision_response);
216 
217         mOffloadPollInterval = getResourceInteger(res,
218                 R.integer.config_tether_offload_poll_interval,
219                 DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
220 
221         mEnableWifiP2pDedicatedIp = getResourceBoolean(res,
222                 R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip,
223                 false /* defaultValue */);
224 
225         // Flags should normally not be booleans, but this is a kill-switch flag that is only used
226         // to turn off the feature, so binary rollback problems do not apply.
227         mEnableSelectAllPrefixRange = getDeviceConfigBoolean(
228                 TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */);
229 
230         configLog.log(toString());
231     }
232 
233     /** Check whether using ncm for usb tethering */
isUsingNcm()234     public boolean isUsingNcm() {
235         return mUsbTetheringFunction == TETHER_USB_NCM_FUNCTION;
236     }
237 
238     /** Check whether input interface belong to usb.*/
isUsb(String iface)239     public boolean isUsb(String iface) {
240         return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
241     }
242 
243     /** Check whether input interface belong to wifi.*/
isWifi(String iface)244     public boolean isWifi(String iface) {
245         return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
246     }
247 
248     /** Check whether input interface belong to wigig.*/
isWigig(String iface)249     public boolean isWigig(String iface) {
250         return matchesDownstreamRegexs(iface, tetherableWigigRegexs);
251     }
252 
253     /** Check whether this interface is Wifi P2P interface. */
isWifiP2p(String iface)254     public boolean isWifiP2p(String iface) {
255         return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
256     }
257 
258     /** Check whether using legacy mode for wifi P2P. */
isWifiP2pLegacyTetheringMode()259     public boolean isWifiP2pLegacyTetheringMode() {
260         return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
261     }
262 
263     /** Check whether input interface belong to bluetooth.*/
isBluetooth(String iface)264     public boolean isBluetooth(String iface) {
265         return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
266     }
267 
268     /** Check if interface is ncm */
isNcm(String iface)269     public boolean isNcm(String iface) {
270         return matchesDownstreamRegexs(iface, tetherableNcmRegexs);
271     }
272 
273     /** Check whether no ui entitlement application is available.*/
hasMobileHotspotProvisionApp()274     public boolean hasMobileHotspotProvisionApp() {
275         return !TextUtils.isEmpty(provisioningAppNoUi);
276     }
277 
278     /** Check whether dedicated wifi p2p address is enabled. */
shouldEnableWifiP2pDedicatedIp()279     public boolean shouldEnableWifiP2pDedicatedIp() {
280         return mEnableWifiP2pDedicatedIp;
281     }
282 
283     /** Does the dumping.*/
dump(PrintWriter pw)284     public void dump(PrintWriter pw) {
285         pw.print("activeDataSubId: ");
286         pw.println(activeDataSubId);
287 
288         dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
289         dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
290         dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
291         dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
292         dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs);
293 
294         pw.print("isDunRequired: ");
295         pw.println(isDunRequired);
296 
297         pw.print("chooseUpstreamAutomatically: ");
298         pw.println(chooseUpstreamAutomatically);
299         pw.print("legacyPreredUpstreamIfaceTypes: ");
300         pw.println(Arrays.toString(toIntArray(preferredUpstreamIfaceTypes)));
301 
302         dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
303         dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
304 
305         pw.print("offloadPollInterval: ");
306         pw.println(mOffloadPollInterval);
307 
308         dumpStringArray(pw, "provisioningApp", provisioningApp);
309         pw.print("provisioningAppNoUi: ");
310         pw.println(provisioningAppNoUi);
311 
312         pw.print("enableBpfOffload: ");
313         pw.println(mEnableBpfOffload);
314 
315         pw.print("enableLegacyDhcpServer: ");
316         pw.println(enableLegacyDhcpServer);
317 
318         pw.print("enableWifiP2pDedicatedIp: ");
319         pw.println(mEnableWifiP2pDedicatedIp);
320 
321         pw.print("mEnableSelectAllPrefixRange: ");
322         pw.println(mEnableSelectAllPrefixRange);
323 
324         pw.print("mUsbTetheringFunction: ");
325         pw.println(isUsingNcm() ? "NCM" : "RNDIS");
326     }
327 
328     /** Returns the string representation of this object.*/
toString()329     public String toString() {
330         final StringJoiner sj = new StringJoiner(" ");
331         sj.add(String.format("activeDataSubId:%d", activeDataSubId));
332         sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
333         sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
334         sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
335         sj.add(String.format("tetherableBluetoothRegexs:%s",
336                 makeString(tetherableBluetoothRegexs)));
337         sj.add(String.format("isDunRequired:%s", isDunRequired));
338         sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
339         sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval));
340         sj.add(String.format("preferredUpstreamIfaceTypes:%s",
341                 toIntArray(preferredUpstreamIfaceTypes)));
342         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
343         sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
344         sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
345         sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
346         return String.format("TetheringConfiguration{%s}", sj.toString());
347     }
348 
dumpStringArray(PrintWriter pw, String label, String[] values)349     private static void dumpStringArray(PrintWriter pw, String label, String[] values) {
350         pw.print(label);
351         pw.print(": ");
352 
353         if (values != null) {
354             final StringJoiner sj = new StringJoiner(", ", "[", "]");
355             for (String value : values) sj.add(value);
356             pw.print(sj.toString());
357         } else {
358             pw.print("null");
359         }
360 
361         pw.println();
362     }
363 
makeString(String[] strings)364     private static String makeString(String[] strings) {
365         if (strings == null) return "null";
366         final StringJoiner sj = new StringJoiner(",", "[", "]");
367         for (String s : strings) sj.add(s);
368         return sj.toString();
369     }
370 
371     /** Check whether dun is required. */
checkDunRequired(Context ctx)372     public static boolean checkDunRequired(Context ctx) {
373         final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
374         // TelephonyManager would uses the active data subscription, which should be the one used
375         // by tethering.
376         return (tm != null) ? tm.isTetheringApnRequired() : false;
377     }
378 
getOffloadPollInterval()379     public int getOffloadPollInterval() {
380         return mOffloadPollInterval;
381     }
382 
isBpfOffloadEnabled()383     public boolean isBpfOffloadEnabled() {
384         return mEnableBpfOffload;
385     }
386 
isSelectAllPrefixRangeEnabled()387     public boolean isSelectAllPrefixRangeEnabled() {
388         return mEnableSelectAllPrefixRange;
389     }
390 
getUsbTetheringFunction(Resources res)391     private int getUsbTetheringFunction(Resources res) {
392         final int valueFromRes = getResourceInteger(res, R.integer.config_tether_usb_functions,
393                 TETHER_USB_RNDIS_FUNCTION /* defaultValue */);
394         return getSettingsIntValue(TETHER_FORCE_USB_FUNCTIONS, valueFromRes);
395     }
396 
getSettingsIntValue(final String name, final int defaultValue)397     private int getSettingsIntValue(final String name, final int defaultValue) {
398         final String value = getSettingsValue(name);
399         try {
400             return value != null ? Integer.parseInt(value) : defaultValue;
401         } catch (NumberFormatException e) {
402             return defaultValue;
403         }
404     }
405 
406     @VisibleForTesting
getSettingsValue(final String name)407     protected String getSettingsValue(final String name) {
408         return Settings.Global.getString(mContentResolver, name);
409     }
410 
getUpstreamIfaceTypes(Resources res, boolean dunRequired)411     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
412         final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
413         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
414         for (int i : ifaceTypes) {
415             switch (i) {
416                 case TYPE_MOBILE:
417                 case TYPE_MOBILE_HIPRI:
418                     if (dunRequired) continue;
419                     break;
420                 case TYPE_MOBILE_DUN:
421                     if (!dunRequired) continue;
422                     break;
423             }
424             upstreamIfaceTypes.add(i);
425         }
426 
427         // Fix up upstream interface types for DUN or mobile. NOTE: independent
428         // of the value of |dunRequired|, cell data of one form or another is
429         // *always* an upstream, regardless of the upstream interface types
430         // specified by configuration resources.
431         if (dunRequired) {
432             appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
433         } else {
434             // Do not modify if a cellular interface type is already present in the
435             // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no
436             // cellular interface types are found in the upstream interface types.
437             // This preserves backwards compatibility and prevents the DUN and default
438             // mobile types incorrectly appearing together, which could happen on
439             // previous releases in the common case where checkDunRequired returned
440             // DUN_UNSPECIFIED.
441             if (!containsOneOf(upstreamIfaceTypes, TYPE_MOBILE, TYPE_MOBILE_HIPRI)) {
442                 upstreamIfaceTypes.add(TYPE_MOBILE);
443                 upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
444             }
445         }
446 
447         // Always make sure our good friend Ethernet is present.
448         // TODO: consider unilaterally forcing this at the front.
449         prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET);
450 
451         return upstreamIfaceTypes;
452     }
453 
matchesDownstreamRegexs(String iface, String[] regexs)454     private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
455         for (String regex : regexs) {
456             if (iface.matches(regex)) return true;
457         }
458         return false;
459     }
460 
getLegacyDhcpRanges(Resources res)461     private static String[] getLegacyDhcpRanges(Resources res) {
462         final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
463         if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
464             return fromResource;
465         }
466         return copy(LEGACY_DHCP_DEFAULT_RANGE);
467     }
468 
getResourceString(Resources res, final int resId)469     private static String getResourceString(Resources res, final int resId) {
470         try {
471             return res.getString(resId);
472         } catch (Resources.NotFoundException e) {
473             return "";
474         }
475     }
476 
getResourceBoolean(Resources res, int resId, boolean defaultValue)477     private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) {
478         try {
479             return res.getBoolean(resId);
480         } catch (Resources.NotFoundException e404) {
481             return defaultValue;
482         }
483     }
484 
getResourceStringArray(Resources res, int resId)485     private static String[] getResourceStringArray(Resources res, int resId) {
486         try {
487             final String[] strArray = res.getStringArray(resId);
488             return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
489         } catch (Resources.NotFoundException e404) {
490             return EMPTY_STRING_ARRAY;
491         }
492     }
493 
getResourceInteger(Resources res, int resId, int defaultValue)494     private static int getResourceInteger(Resources res, int resId, int defaultValue) {
495         try {
496             return res.getInteger(resId);
497         } catch (Resources.NotFoundException e404) {
498             return defaultValue;
499         }
500     }
501 
getEnableBpfOffload(final Resources res)502     private boolean getEnableBpfOffload(final Resources res) {
503         // Get BPF offload config
504         // Priority 1: Device config
505         // Priority 2: Resource config
506         // Priority 3: Default value
507         final boolean defaultValue = getResourceBoolean(
508                 res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
509 
510         return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
511     }
512 
getEnableLegacyDhcpServer(final Resources res)513     private boolean getEnableLegacyDhcpServer(final Resources res) {
514         return getResourceBoolean(
515                 res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
516                 || getDeviceConfigBoolean(
517                 TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
518     }
519 
getDeviceConfigBoolean(final String name, final boolean defaultValue)520     private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
521         // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
522         // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
523         // returned boolean value comes from device config or default value (because of null
524         // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
525         final String value = getDeviceConfigProperty(name);
526         return value != null ? Boolean.parseBoolean(value) : defaultValue;
527     }
528 
529     @VisibleForTesting
getDeviceConfigProperty(String name)530     protected String getDeviceConfigProperty(String name) {
531         return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
532     }
533 
534     @VisibleForTesting
isFeatureEnabled(Context ctx, String featureVersionFlag)535     protected boolean isFeatureEnabled(Context ctx, String featureVersionFlag) {
536         return DeviceConfigUtils.isFeatureEnabled(ctx, NAMESPACE_CONNECTIVITY, featureVersionFlag,
537                 TETHERING_MODULE_NAME, false /* defaultEnabled */);
538     }
539 
getResources(Context ctx, int subId)540     private Resources getResources(Context ctx, int subId) {
541         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
542             return getResourcesForSubIdWrapper(ctx, subId);
543         } else {
544             return ctx.getResources();
545         }
546     }
547 
548     @VisibleForTesting
getResourcesForSubIdWrapper(Context ctx, int subId)549     protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
550         return SubscriptionManager.getResourcesForSubId(ctx, subId);
551     }
552 
copy(String[] strarray)553     private static String[] copy(String[] strarray) {
554         return Arrays.copyOf(strarray, strarray.length);
555     }
556 
prependIfNotPresent(ArrayList<Integer> list, int value)557     private static void prependIfNotPresent(ArrayList<Integer> list, int value) {
558         if (list.contains(value)) return;
559         list.add(0, value);
560     }
561 
appendIfNotPresent(ArrayList<Integer> list, int value)562     private static void appendIfNotPresent(ArrayList<Integer> list, int value) {
563         if (list.contains(value)) return;
564         list.add(value);
565     }
566 
containsOneOf(ArrayList<Integer> list, Integer... values)567     private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) {
568         for (Integer value : values) {
569             if (list.contains(value)) return true;
570         }
571         return false;
572     }
573 
toIntArray(Collection<Integer> values)574     private static int[] toIntArray(Collection<Integer> values) {
575         final int[] result = new int[values.size()];
576         int index = 0;
577         for (Integer value : values) {
578             result[index++] = value;
579         }
580         return result;
581     }
582 
583     /**
584      * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
585      */
toStableParcelable()586     public TetheringConfigurationParcel toStableParcelable() {
587         final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
588         parcel.subId = activeDataSubId;
589         parcel.tetherableUsbRegexs = tetherableUsbRegexs;
590         parcel.tetherableWifiRegexs = tetherableWifiRegexs;
591         parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
592         parcel.isDunRequired = isDunRequired;
593         parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
594 
595         parcel.preferredUpstreamIfaceTypes = toIntArray(preferredUpstreamIfaceTypes);
596 
597         parcel.legacyDhcpRanges = legacyDhcpRanges;
598         parcel.defaultIPv4DNS = defaultIPv4DNS;
599         parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
600         parcel.provisioningApp = provisioningApp;
601         parcel.provisioningAppNoUi = provisioningAppNoUi;
602         parcel.provisioningCheckPeriod = provisioningCheckPeriod;
603         return parcel;
604     }
605 }
606