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