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.server.connectivity.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 com.android.internal.R.array.config_mobile_hotspot_provision_app; 25 import static com.android.internal.R.array.config_tether_bluetooth_regexs; 26 import static com.android.internal.R.array.config_tether_dhcp_range; 27 import static com.android.internal.R.array.config_tether_usb_regexs; 28 import static com.android.internal.R.array.config_tether_upstream_types; 29 import static com.android.internal.R.array.config_tether_wifi_regexs; 30 import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; 31 32 import android.content.Context; 33 import android.content.res.Resources; 34 import android.net.ConnectivityManager; 35 import android.net.util.SharedLog; 36 import android.telephony.TelephonyManager; 37 import android.text.TextUtils; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.R; 41 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collection; 46 import java.util.StringJoiner; 47 48 49 /** 50 * A utility class to encapsulate the various tethering configuration elements. 51 * 52 * This configuration data includes elements describing upstream properties 53 * (preferred and required types of upstream connectivity as well as default 54 * DNS servers to use if none are available) and downstream properties (such 55 * as regular expressions use to match suitable downstream interfaces and the 56 * DHCPv4 ranges to use). 57 * 58 * @hide 59 */ 60 public class TetheringConfiguration { 61 private static final String TAG = TetheringConfiguration.class.getSimpleName(); 62 63 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 64 65 @VisibleForTesting 66 public static final int DUN_NOT_REQUIRED = 0; 67 public static final int DUN_REQUIRED = 1; 68 public static final int DUN_UNSPECIFIED = 2; 69 70 // USB is 192.168.42.1 and 255.255.255.0 71 // Wifi is 192.168.43.1 and 255.255.255.0 72 // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1 73 // with 255.255.255.0 74 // P2P is 192.168.49.1 and 255.255.255.0 75 private static final String[] DHCP_DEFAULT_RANGE = { 76 "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254", 77 "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254", 78 "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254", 79 "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254", 80 }; 81 82 private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"}; 83 84 public final String[] tetherableUsbRegexs; 85 public final String[] tetherableWifiRegexs; 86 public final String[] tetherableBluetoothRegexs; 87 public final int dunCheck; 88 public final boolean isDunRequired; 89 public final Collection<Integer> preferredUpstreamIfaceTypes; 90 public final String[] dhcpRanges; 91 public final String[] defaultIPv4DNS; 92 93 public final String[] provisioningApp; 94 public final String provisioningAppNoUi; 95 TetheringConfiguration(Context ctx, SharedLog log)96 public TetheringConfiguration(Context ctx, SharedLog log) { 97 final SharedLog configLog = log.forSubComponent("config"); 98 99 tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs); 100 // TODO: Evaluate deleting this altogether now that Wi-Fi always passes 101 // us an interface name. Careful consideration needs to be given to 102 // implications for Settings and for provisioning checks. 103 tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs); 104 tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs); 105 106 dunCheck = checkDunRequired(ctx); 107 configLog.log("DUN check returned: " + dunCheckString(dunCheck)); 108 109 preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck); 110 isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN); 111 112 dhcpRanges = getDhcpRanges(ctx); 113 defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); 114 115 provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app); 116 provisioningAppNoUi = getProvisioningAppNoUi(ctx); 117 118 configLog.log(toString()); 119 } 120 isUsb(String iface)121 public boolean isUsb(String iface) { 122 return matchesDownstreamRegexs(iface, tetherableUsbRegexs); 123 } 124 isWifi(String iface)125 public boolean isWifi(String iface) { 126 return matchesDownstreamRegexs(iface, tetherableWifiRegexs); 127 } 128 isBluetooth(String iface)129 public boolean isBluetooth(String iface) { 130 return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); 131 } 132 hasMobileHotspotProvisionApp()133 public boolean hasMobileHotspotProvisionApp() { 134 return !TextUtils.isEmpty(provisioningAppNoUi); 135 } 136 dump(PrintWriter pw)137 public void dump(PrintWriter pw) { 138 dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); 139 dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); 140 dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); 141 142 pw.print("isDunRequired: "); 143 pw.println(isDunRequired); 144 145 dumpStringArray(pw, "preferredUpstreamIfaceTypes", 146 preferredUpstreamNames(preferredUpstreamIfaceTypes)); 147 148 dumpStringArray(pw, "dhcpRanges", dhcpRanges); 149 dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); 150 151 dumpStringArray(pw, "provisioningApp", provisioningApp); 152 pw.print("provisioningAppNoUi: "); 153 pw.println(provisioningAppNoUi); 154 } 155 toString()156 public String toString() { 157 final StringJoiner sj = new StringJoiner(" "); 158 sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); 159 sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); 160 sj.add(String.format("tetherableBluetoothRegexs:%s", 161 makeString(tetherableBluetoothRegexs))); 162 sj.add(String.format("isDunRequired:%s", isDunRequired)); 163 sj.add(String.format("preferredUpstreamIfaceTypes:%s", 164 makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes)))); 165 sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); 166 sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi)); 167 return String.format("TetheringConfiguration{%s}", sj.toString()); 168 } 169 dumpStringArray(PrintWriter pw, String label, String[] values)170 private static void dumpStringArray(PrintWriter pw, String label, String[] values) { 171 pw.print(label); 172 pw.print(": "); 173 174 if (values != null) { 175 final StringJoiner sj = new StringJoiner(", ", "[", "]"); 176 for (String value : values) { sj.add(value); } 177 pw.print(sj.toString()); 178 } else { 179 pw.print("null"); 180 } 181 182 pw.println(); 183 } 184 makeString(String[] strings)185 private static String makeString(String[] strings) { 186 if (strings == null) return "null"; 187 final StringJoiner sj = new StringJoiner(",", "[", "]"); 188 for (String s : strings) sj.add(s); 189 return sj.toString(); 190 } 191 preferredUpstreamNames(Collection<Integer> upstreamTypes)192 private static String[] preferredUpstreamNames(Collection<Integer> upstreamTypes) { 193 String[] upstreamNames = null; 194 195 if (upstreamTypes != null) { 196 upstreamNames = new String[upstreamTypes.size()]; 197 int i = 0; 198 for (Integer netType : upstreamTypes) { 199 upstreamNames[i] = ConnectivityManager.getNetworkTypeName(netType); 200 i++; 201 } 202 } 203 204 return upstreamNames; 205 } 206 checkDunRequired(Context ctx)207 public static int checkDunRequired(Context ctx) { 208 final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); 209 return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED; 210 } 211 dunCheckString(int dunCheck)212 private static String dunCheckString(int dunCheck) { 213 switch (dunCheck) { 214 case DUN_NOT_REQUIRED: return "DUN_NOT_REQUIRED"; 215 case DUN_REQUIRED: return "DUN_REQUIRED"; 216 case DUN_UNSPECIFIED: return "DUN_UNSPECIFIED"; 217 default: 218 return String.format("UNKNOWN (%s)", dunCheck); 219 } 220 } 221 getUpstreamIfaceTypes(Context ctx, int dunCheck)222 private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) { 223 final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types); 224 final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); 225 for (int i : ifaceTypes) { 226 switch (i) { 227 case TYPE_MOBILE: 228 case TYPE_MOBILE_HIPRI: 229 if (dunCheck == DUN_REQUIRED) continue; 230 break; 231 case TYPE_MOBILE_DUN: 232 if (dunCheck == DUN_NOT_REQUIRED) continue; 233 break; 234 } 235 upstreamIfaceTypes.add(i); 236 } 237 238 // Fix up upstream interface types for DUN or mobile. NOTE: independent 239 // of the value of |dunCheck|, cell data of one form or another is 240 // *always* an upstream, regardless of the upstream interface types 241 // specified by configuration resources. 242 if (dunCheck == DUN_REQUIRED) { 243 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN); 244 } else if (dunCheck == DUN_NOT_REQUIRED) { 245 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE); 246 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_HIPRI); 247 } else { 248 // Fix upstream interface types for case DUN_UNSPECIFIED. 249 // Do not modify if a cellular interface type is already present in the 250 // upstream interface types. Add TYPE_MOBILE and TYPE_MOBILE_HIPRI if no 251 // cellular interface types are found in the upstream interface types. 252 if (!(containsOneOf(upstreamIfaceTypes, 253 TYPE_MOBILE_DUN, TYPE_MOBILE, TYPE_MOBILE_HIPRI))) { 254 upstreamIfaceTypes.add(TYPE_MOBILE); 255 upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI); 256 } 257 } 258 259 // Always make sure our good friend Ethernet is present. 260 // TODO: consider unilaterally forcing this at the front. 261 prependIfNotPresent(upstreamIfaceTypes, TYPE_ETHERNET); 262 263 return upstreamIfaceTypes; 264 } 265 matchesDownstreamRegexs(String iface, String[] regexs)266 private static boolean matchesDownstreamRegexs(String iface, String[] regexs) { 267 for (String regex : regexs) { 268 if (iface.matches(regex)) return true; 269 } 270 return false; 271 } 272 getDhcpRanges(Context ctx)273 private static String[] getDhcpRanges(Context ctx) { 274 final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range); 275 if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { 276 return fromResource; 277 } 278 return copy(DHCP_DEFAULT_RANGE); 279 } 280 getProvisioningAppNoUi(Context ctx)281 private static String getProvisioningAppNoUi(Context ctx) { 282 try { 283 return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui); 284 } catch (Resources.NotFoundException e) { 285 return ""; 286 } 287 } 288 getResourceStringArray(Context ctx, int resId)289 private static String[] getResourceStringArray(Context ctx, int resId) { 290 try { 291 final String[] strArray = ctx.getResources().getStringArray(resId); 292 return (strArray != null) ? strArray : EMPTY_STRING_ARRAY; 293 } catch (Resources.NotFoundException e404) { 294 return EMPTY_STRING_ARRAY; 295 } 296 } 297 copy(String[] strarray)298 private static String[] copy(String[] strarray) { 299 return Arrays.copyOf(strarray, strarray.length); 300 } 301 prependIfNotPresent(ArrayList<Integer> list, int value)302 private static void prependIfNotPresent(ArrayList<Integer> list, int value) { 303 if (list.contains(value)) return; 304 list.add(0, value); 305 } 306 appendIfNotPresent(ArrayList<Integer> list, int value)307 private static void appendIfNotPresent(ArrayList<Integer> list, int value) { 308 if (list.contains(value)) return; 309 list.add(value); 310 } 311 containsOneOf(ArrayList<Integer> list, Integer... values)312 private static boolean containsOneOf(ArrayList<Integer> list, Integer... values) { 313 for (Integer value : values) { 314 if (list.contains(value)) return true; 315 } 316 return false; 317 } 318 } 319