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