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