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