blob: 09bce7f4feb67920dec1b22a173a34bd29413f93 [file] [log] [blame]
Erik Kline79868f82017-01-21 14:33:56 +09001/*
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
17package com.android.server.connectivity.tethering;
18
Erik Kline54f2f372017-05-15 21:11:47 +090019import static android.content.Context.TELEPHONY_SERVICE;
Erik Kline1e543512017-03-09 11:44:11 +090020import static android.net.ConnectivityManager.TYPE_ETHERNET;
Erik Kline79868f82017-01-21 14:33:56 +090021import static android.net.ConnectivityManager.TYPE_MOBILE;
22import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
23import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
24
25import android.content.Context;
26import android.content.res.Resources;
Erik Kline9db1b542017-03-16 14:10:27 +090027import android.net.ConnectivityManager;
Erik Kline79868f82017-01-21 14:33:56 +090028import android.telephony.TelephonyManager;
Erik Kline6bd74532017-05-19 10:10:41 +090029import android.net.util.SharedLog;
Erik Kline79868f82017-01-21 14:33:56 +090030
Chalard Jean918a68b2018-01-19 17:00:47 +090031import com.android.internal.annotations.VisibleForTesting;
32
Erik Kline9db1b542017-03-16 14:10:27 +090033import java.io.PrintWriter;
Erik Kline79868f82017-01-21 14:33:56 +090034import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.Collection;
Erik Kline9db1b542017-03-16 14:10:27 +090037import java.util.StringJoiner;
Erik Kline79868f82017-01-21 14:33:56 +090038
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 */
51public class TetheringConfiguration {
52 private static final String TAG = TetheringConfiguration.class.getSimpleName();
53
Chalard Jean918a68b2018-01-19 17:00:47 +090054 @VisibleForTesting
Erik Kline54f2f372017-05-15 21:11:47 +090055 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 Kline79868f82017-01-21 14:33:56 +090058
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 Kline6ee73da2017-07-08 20:36:37 +090076 public final int dunCheck;
Erik Kline79868f82017-01-21 14:33:56 +090077 public final boolean isDunRequired;
78 public final Collection<Integer> preferredUpstreamIfaceTypes;
79 public final String[] dhcpRanges;
80 public final String[] defaultIPv4DNS;
81
Erik Kline6bd74532017-05-19 10:10:41 +090082 public TetheringConfiguration(Context ctx, SharedLog log) {
83 final SharedLog configLog = log.forSubComponent("config");
84
Erik Kline79868f82017-01-21 14:33:56 +090085 tetherableUsbRegexs = ctx.getResources().getStringArray(
86 com.android.internal.R.array.config_tether_usb_regexs);
Erik Kline9e225542017-06-08 17:48:48 +090087 // 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 Kline79868f82017-01-21 14:33:56 +090090 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 Kline6ee73da2017-07-08 20:36:37 +090095 dunCheck = checkDunRequired(ctx);
Erik Kline6bd74532017-05-19 10:10:41 +090096 configLog.log("DUN check returned: " + dunCheckString(dunCheck));
97
Erik Kline54f2f372017-05-15 21:11:47 +090098 preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
99 isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
Erik Kline79868f82017-01-21 14:33:56 +0900100
101 dhcpRanges = getDhcpRanges(ctx);
102 defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
Erik Kline6bd74532017-05-19 10:10:41 +0900103
104 configLog.log(toString());
Erik Kline79868f82017-01-21 14:33:56 +0900105 }
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 Kline9db1b542017-03-16 14:10:27 +0900119 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 Kline6bd74532017-05-19 10:10:41 +0900127 dumpStringArray(pw, "preferredUpstreamIfaceTypes",
128 preferredUpstreamNames(preferredUpstreamIfaceTypes));
Erik Kline9db1b542017-03-16 14:10:27 +0900129
130 dumpStringArray(pw, "dhcpRanges", dhcpRanges);
131 dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
132 }
133
Erik Kline6bd74532017-05-19 10:10:41 +0900134 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 Kline9db1b542017-03-16 14:10:27 +0900146 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 Kline6bd74532017-05-19 10:10:41 +0900161 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 Kline6ee73da2017-07-08 20:36:37 +0900182 public static int checkDunRequired(Context ctx) {
Erik Kline54f2f372017-05-15 21:11:47 +0900183 final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
184 return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
Erik Kline79868f82017-01-21 14:33:56 +0900185 }
186
Erik Kline6bd74532017-05-19 10:10:41 +0900187 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 Kline54f2f372017-05-15 21:11:47 +0900197 private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
Erik Kline79868f82017-01-21 14:33:56 +0900198 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 Kline54f2f372017-05-15 21:11:47 +0900205 if (dunCheck == DUN_REQUIRED) continue;
Erik Kline79868f82017-01-21 14:33:56 +0900206 break;
207 case TYPE_MOBILE_DUN:
Erik Kline54f2f372017-05-15 21:11:47 +0900208 if (dunCheck == DUN_NOT_REQUIRED) continue;
Erik Kline79868f82017-01-21 14:33:56 +0900209 break;
210 }
211 upstreamIfaceTypes.add(i);
212 }
213
214 // Fix up upstream interface types for DUN or mobile. NOTE: independent
Jayachandran C58059822017-05-17 23:53:59 -0700215 // of the value of |dunCheck|, cell data of one form or another is
Erik Kline79868f82017-01-21 14:33:56 +0900216 // *always* an upstream, regardless of the upstream interface types
217 // specified by configuration resources.
Erik Kline54f2f372017-05-15 21:11:47 +0900218 if (dunCheck == DUN_REQUIRED) {
Erik Kline1e543512017-03-09 11:44:11 +0900219 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_DUN);
Jayachandran C58059822017-05-17 23:53:59 -0700220 } else if (dunCheck == DUN_NOT_REQUIRED) {
Erik Kline1e543512017-03-09 11:44:11 +0900221 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE);
222 appendIfNotPresent(upstreamIfaceTypes, TYPE_MOBILE_HIPRI);
Jayachandran C58059822017-05-17 23:53:59 -0700223 } 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 Kline1e543512017-03-09 11:44:11 +0900228 if (!(containsOneOf(upstreamIfaceTypes,
229 TYPE_MOBILE_DUN, TYPE_MOBILE, TYPE_MOBILE_HIPRI))) {
Jayachandran C58059822017-05-17 23:53:59 -0700230 upstreamIfaceTypes.add(TYPE_MOBILE);
231 upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
232 }
Erik Kline79868f82017-01-21 14:33:56 +0900233 }
234
Erik Kline1e543512017-03-09 11:44:11 +0900235 // 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 Kline79868f82017-01-21 14:33:56 +0900239 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 Kline1e543512017-03-09 11:44:11 +0900261
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 Kline79868f82017-01-21 14:33:56 +0900278}