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