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