1 /* 2 * Copyright (C) 2018 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.ethernet; 18 19 import static android.net.TestNetworkManager.TEST_TAP_PREFIX; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.net.IEthernetServiceListener; 24 import android.net.ITetheredInterfaceCallback; 25 import android.net.InterfaceConfiguration; 26 import android.net.IpConfiguration; 27 import android.net.IpConfiguration.IpAssignment; 28 import android.net.IpConfiguration.ProxySettings; 29 import android.net.LinkAddress; 30 import android.net.NetworkCapabilities; 31 import android.net.StaticIpConfiguration; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.INetworkManagementService; 35 import android.os.RemoteCallbackList; 36 import android.os.RemoteException; 37 import android.os.ServiceManager; 38 import android.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.util.Log; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.util.IndentingPrintWriter; 44 import com.android.server.net.BaseNetworkObserver; 45 46 import java.io.FileDescriptor; 47 import java.net.InetAddress; 48 import java.util.ArrayList; 49 import java.util.concurrent.ConcurrentHashMap; 50 51 /** 52 * Tracks Ethernet interfaces and manages interface configurations. 53 * 54 * <p>Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined 55 * in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by 56 * not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag. 57 * Interfaces could have associated {@link android.net.IpConfiguration}. 58 * Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters 59 * connected over USB). This class supports multiple interfaces. When an interface appears on the 60 * system (or is present at boot time) this class will start tracking it and bring it up. Only 61 * interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are 62 * tracked. 63 * 64 * <p>All public or package private methods must be thread-safe unless stated otherwise. 65 */ 66 final class EthernetTracker { 67 private static final int INTERFACE_MODE_CLIENT = 1; 68 private static final int INTERFACE_MODE_SERVER = 2; 69 70 private final static String TAG = EthernetTracker.class.getSimpleName(); 71 private final static boolean DBG = EthernetNetworkFactory.DBG; 72 73 private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+"; 74 75 /** 76 * Interface names we track. This is a product-dependent regular expression, plus, 77 * if setIncludeTestInterfaces is true, any test interfaces. 78 */ 79 private String mIfaceMatch; 80 private boolean mIncludeTestInterfaces = false; 81 82 /** Mapping between {iface name | mac address} -> {NetworkCapabilities} */ 83 private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities = 84 new ConcurrentHashMap<>(); 85 private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations = 86 new ConcurrentHashMap<>(); 87 88 private final Context mContext; 89 private final INetworkManagementService mNMService; 90 private final Handler mHandler; 91 private final EthernetNetworkFactory mFactory; 92 private final EthernetConfigStore mConfigStore; 93 94 private final RemoteCallbackList<IEthernetServiceListener> mListeners = 95 new RemoteCallbackList<>(); 96 private final TetheredInterfaceRequestList mTetheredInterfaceRequests = 97 new TetheredInterfaceRequestList(); 98 99 // Used only on the handler thread 100 private String mDefaultInterface; 101 private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT; 102 // Tracks whether clients were notified that the tethered interface is available 103 private boolean mTetheredInterfaceWasAvailable = false; 104 private volatile IpConfiguration mIpConfigForDefaultInterface; 105 106 private class TetheredInterfaceRequestList extends RemoteCallbackList<ITetheredInterfaceCallback> { 107 @Override onCallbackDied(ITetheredInterfaceCallback cb, Object cookie)108 public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) { 109 mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface); 110 } 111 } 112 EthernetTracker(Context context, Handler handler)113 EthernetTracker(Context context, Handler handler) { 114 mContext = context; 115 mHandler = handler; 116 117 // The services we use. 118 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 119 mNMService = INetworkManagementService.Stub.asInterface(b); 120 121 // Interface match regex. 122 updateIfaceMatchRegexp(); 123 124 // Read default Ethernet interface configuration from resources 125 final String[] interfaceConfigs = context.getResources().getStringArray( 126 com.android.internal.R.array.config_ethernet_interfaces); 127 for (String strConfig : interfaceConfigs) { 128 parseEthernetConfig(strConfig); 129 } 130 131 mConfigStore = new EthernetConfigStore(); 132 133 NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */); 134 mFactory = new EthernetNetworkFactory(handler, context, nc); 135 mFactory.register(); 136 } 137 start()138 void start() { 139 mConfigStore.read(); 140 141 // Default interface is just the first one we want to track. 142 mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface(); 143 final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations(); 144 for (int i = 0; i < configs.size(); i++) { 145 mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i)); 146 } 147 148 try { 149 mNMService.registerObserver(new InterfaceObserver()); 150 } catch (RemoteException e) { 151 Log.e(TAG, "Could not register InterfaceObserver " + e); 152 } 153 154 mHandler.post(this::trackAvailableInterfaces); 155 } 156 updateIpConfiguration(String iface, IpConfiguration ipConfiguration)157 void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { 158 if (DBG) { 159 Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration); 160 } 161 162 mConfigStore.write(iface, ipConfiguration); 163 mIpConfigurations.put(iface, ipConfiguration); 164 165 mHandler.post(() -> mFactory.updateIpConfiguration(iface, ipConfiguration)); 166 } 167 getIpConfiguration(String iface)168 IpConfiguration getIpConfiguration(String iface) { 169 return mIpConfigurations.get(iface); 170 } 171 isTrackingInterface(String iface)172 boolean isTrackingInterface(String iface) { 173 return mFactory.hasInterface(iface); 174 } 175 getInterfaces(boolean includeRestricted)176 String[] getInterfaces(boolean includeRestricted) { 177 return mFactory.getAvailableInterfaces(includeRestricted); 178 } 179 180 /** 181 * Returns true if given interface was configured as restricted (doesn't have 182 * NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false. 183 */ isRestrictedInterface(String iface)184 boolean isRestrictedInterface(String iface) { 185 final NetworkCapabilities nc = mNetworkCapabilities.get(iface); 186 return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 187 } 188 addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks)189 void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) { 190 mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks)); 191 } 192 removeListener(IEthernetServiceListener listener)193 void removeListener(IEthernetServiceListener listener) { 194 mListeners.unregister(listener); 195 } 196 setIncludeTestInterfaces(boolean include)197 public void setIncludeTestInterfaces(boolean include) { 198 mHandler.post(() -> { 199 mIncludeTestInterfaces = include; 200 updateIfaceMatchRegexp(); 201 mHandler.post(() -> trackAvailableInterfaces()); 202 }); 203 } 204 requestTetheredInterface(ITetheredInterfaceCallback callback)205 public void requestTetheredInterface(ITetheredInterfaceCallback callback) { 206 mHandler.post(() -> { 207 if (!mTetheredInterfaceRequests.register(callback)) { 208 // Remote process has already died 209 return; 210 } 211 if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) { 212 if (mTetheredInterfaceWasAvailable) { 213 notifyTetheredInterfaceAvailable(callback, mDefaultInterface); 214 } 215 return; 216 } 217 218 setDefaultInterfaceMode(INTERFACE_MODE_SERVER); 219 }); 220 } 221 releaseTetheredInterface(ITetheredInterfaceCallback callback)222 public void releaseTetheredInterface(ITetheredInterfaceCallback callback) { 223 mHandler.post(() -> { 224 mTetheredInterfaceRequests.unregister(callback); 225 maybeUntetherDefaultInterface(); 226 }); 227 } 228 notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface)229 private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) { 230 try { 231 cb.onAvailable(iface); 232 } catch (RemoteException e) { 233 Log.e(TAG, "Error sending tethered interface available callback", e); 234 } 235 } 236 notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb)237 private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) { 238 try { 239 cb.onUnavailable(); 240 } catch (RemoteException e) { 241 Log.e(TAG, "Error sending tethered interface available callback", e); 242 } 243 } 244 maybeUntetherDefaultInterface()245 private void maybeUntetherDefaultInterface() { 246 if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return; 247 if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return; 248 setDefaultInterfaceMode(INTERFACE_MODE_CLIENT); 249 } 250 setDefaultInterfaceMode(int mode)251 private void setDefaultInterfaceMode(int mode) { 252 Log.d(TAG, "Setting default interface mode to " + mode); 253 mDefaultInterfaceMode = mode; 254 if (mDefaultInterface != null) { 255 removeInterface(mDefaultInterface); 256 addInterface(mDefaultInterface); 257 } 258 } 259 getInterfaceMode(final String iface)260 private int getInterfaceMode(final String iface) { 261 if (iface.equals(mDefaultInterface)) { 262 return mDefaultInterfaceMode; 263 } 264 return INTERFACE_MODE_CLIENT; 265 } 266 removeInterface(String iface)267 private void removeInterface(String iface) { 268 mFactory.removeInterface(iface); 269 maybeUpdateServerModeInterfaceState(iface, false); 270 } 271 stopTrackingInterface(String iface)272 private void stopTrackingInterface(String iface) { 273 removeInterface(iface); 274 if (iface.equals(mDefaultInterface)) { 275 mDefaultInterface = null; 276 } 277 } 278 addInterface(String iface)279 private void addInterface(String iface) { 280 InterfaceConfiguration config = null; 281 // Bring up the interface so we get link status indications. 282 try { 283 mNMService.setInterfaceUp(iface); 284 config = mNMService.getInterfaceConfig(iface); 285 } catch (RemoteException | IllegalStateException e) { 286 // Either the system is crashing or the interface has disappeared. Just ignore the 287 // error; we haven't modified any state because we only do that if our calls succeed. 288 Log.e(TAG, "Error upping interface " + iface, e); 289 } 290 291 if (config == null) { 292 Log.e(TAG, "Null interface config for " + iface + ". Bailing out."); 293 return; 294 } 295 296 final String hwAddress = config.getHardwareAddress(); 297 298 NetworkCapabilities nc = mNetworkCapabilities.get(iface); 299 if (nc == null) { 300 // Try to resolve using mac address 301 nc = mNetworkCapabilities.get(hwAddress); 302 if (nc == null) { 303 final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP); 304 nc = createDefaultNetworkCapabilities(isTestIface); 305 } 306 } 307 308 final int mode = getInterfaceMode(iface); 309 if (mode == INTERFACE_MODE_CLIENT) { 310 IpConfiguration ipConfiguration = mIpConfigurations.get(iface); 311 if (ipConfiguration == null) { 312 ipConfiguration = createDefaultIpConfiguration(); 313 } 314 315 Log.d(TAG, "Tracking interface in client mode: " + iface); 316 mFactory.addInterface(iface, hwAddress, nc, ipConfiguration); 317 } else { 318 maybeUpdateServerModeInterfaceState(iface, true); 319 } 320 321 // Note: if the interface already has link (e.g., if we crashed and got 322 // restarted while it was running), we need to fake a link up notification so we 323 // start configuring it. 324 if (config.hasFlag("running")) { 325 updateInterfaceState(iface, true); 326 } 327 } 328 updateInterfaceState(String iface, boolean up)329 private void updateInterfaceState(String iface, boolean up) { 330 final int mode = getInterfaceMode(iface); 331 final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT) 332 && mFactory.updateInterfaceLinkState(iface, up); 333 334 if (factoryLinkStateUpdated) { 335 boolean restricted = isRestrictedInterface(iface); 336 int n = mListeners.beginBroadcast(); 337 for (int i = 0; i < n; i++) { 338 try { 339 if (restricted) { 340 ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i); 341 if (!listenerInfo.canUseRestrictedNetworks) { 342 continue; 343 } 344 } 345 mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up); 346 } catch (RemoteException e) { 347 // Do nothing here. 348 } 349 } 350 mListeners.finishBroadcast(); 351 } 352 } 353 maybeUpdateServerModeInterfaceState(String iface, boolean available)354 private void maybeUpdateServerModeInterfaceState(String iface, boolean available) { 355 if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return; 356 357 Log.d(TAG, (available ? "Tracking" : "No longer tracking") 358 + " interface in server mode: " + iface); 359 360 final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast(); 361 for (int i = 0; i < pendingCbs; i++) { 362 ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i); 363 if (available) { 364 notifyTetheredInterfaceAvailable(item, iface); 365 } else { 366 notifyTetheredInterfaceUnavailable(item); 367 } 368 } 369 mTetheredInterfaceRequests.finishBroadcast(); 370 mTetheredInterfaceWasAvailable = available; 371 } 372 maybeTrackInterface(String iface)373 private void maybeTrackInterface(String iface) { 374 if (!iface.matches(mIfaceMatch)) { 375 return; 376 } 377 378 // If we don't already track this interface, and if this interface matches 379 // our regex, start tracking it. 380 if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) { 381 if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface); 382 return; 383 } 384 if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface); 385 386 // TODO: avoid making an interface default if it has configured NetworkCapabilities. 387 if (mDefaultInterface == null) { 388 mDefaultInterface = iface; 389 } 390 391 if (mIpConfigForDefaultInterface != null) { 392 updateIpConfiguration(iface, mIpConfigForDefaultInterface); 393 mIpConfigForDefaultInterface = null; 394 } 395 396 addInterface(iface); 397 } 398 trackAvailableInterfaces()399 private void trackAvailableInterfaces() { 400 try { 401 final String[] ifaces = mNMService.listInterfaces(); 402 for (String iface : ifaces) { 403 maybeTrackInterface(iface); 404 } 405 } catch (RemoteException | IllegalStateException e) { 406 Log.e(TAG, "Could not get list of interfaces " + e); 407 } 408 } 409 410 411 private class InterfaceObserver extends BaseNetworkObserver { 412 413 @Override interfaceLinkStateChanged(String iface, boolean up)414 public void interfaceLinkStateChanged(String iface, boolean up) { 415 if (DBG) { 416 Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up); 417 } 418 mHandler.post(() -> updateInterfaceState(iface, up)); 419 } 420 421 @Override interfaceAdded(String iface)422 public void interfaceAdded(String iface) { 423 mHandler.post(() -> maybeTrackInterface(iface)); 424 } 425 426 @Override interfaceRemoved(String iface)427 public void interfaceRemoved(String iface) { 428 mHandler.post(() -> stopTrackingInterface(iface)); 429 } 430 } 431 432 private static class ListenerInfo { 433 434 boolean canUseRestrictedNetworks = false; 435 ListenerInfo(boolean canUseRestrictedNetworks)436 ListenerInfo(boolean canUseRestrictedNetworks) { 437 this.canUseRestrictedNetworks = canUseRestrictedNetworks; 438 } 439 } 440 441 /** 442 * Parses an Ethernet interface configuration 443 * 444 * @param configString represents an Ethernet configuration in the following format: {@code 445 * <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]} 446 */ parseEthernetConfig(String configString)447 private void parseEthernetConfig(String configString) { 448 String[] tokens = configString.split(";", /* limit of tokens */ 4); 449 String name = tokens[0]; 450 String capabilities = tokens.length > 1 ? tokens[1] : null; 451 String transport = tokens.length > 3 ? tokens[3] : null; 452 NetworkCapabilities nc = createNetworkCapabilities( 453 !TextUtils.isEmpty(capabilities) /* clear default capabilities */, capabilities, 454 transport); 455 mNetworkCapabilities.put(name, nc); 456 457 if (tokens.length > 2 && !TextUtils.isEmpty(tokens[2])) { 458 IpConfiguration ipConfig = parseStaticIpConfiguration(tokens[2]); 459 mIpConfigurations.put(name, ipConfig); 460 } 461 } 462 createDefaultNetworkCapabilities(boolean isTestIface)463 private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) { 464 NetworkCapabilities nc = createNetworkCapabilities(false /* clear default capabilities */); 465 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 466 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); 467 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); 468 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); 469 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); 470 471 if (isTestIface) { 472 nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST); 473 } else { 474 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 475 } 476 477 return nc; 478 } 479 createNetworkCapabilities(boolean clearDefaultCapabilities)480 private static NetworkCapabilities createNetworkCapabilities(boolean clearDefaultCapabilities) { 481 return createNetworkCapabilities(clearDefaultCapabilities, null, null); 482 } 483 484 /** 485 * Parses a static list of network capabilities 486 * 487 * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities 488 * @param commaSeparatedCapabilities A comma separated string list of integer encoded 489 * NetworkCapability.NET_CAPABILITY_* values 490 * @param overrideTransport A string representing a single integer encoded override transport 491 * type. Must be one of the NetworkCapability.TRANSPORT_* 492 * values. TRANSPORT_VPN is not supported. Errors with input 493 * will cause the override to be ignored. 494 */ 495 @VisibleForTesting createNetworkCapabilities( boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, @Nullable String overrideTransport)496 static NetworkCapabilities createNetworkCapabilities( 497 boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities, 498 @Nullable String overrideTransport) { 499 500 NetworkCapabilities nc = new NetworkCapabilities(); 501 if (clearDefaultCapabilities) { 502 nc.clearAll(); // Remove default capabilities and transports 503 } 504 505 // Determine the transport type. If someone has tried to define an override transport then 506 // attempt to add it. Since we can only have one override, all errors with it will 507 // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an 508 // override type. Wifi Aware and LoWPAN are currently unsupported as well. 509 int transport = NetworkCapabilities.TRANSPORT_ETHERNET; 510 if (!TextUtils.isEmpty(overrideTransport)) { 511 try { 512 int parsedTransport = Integer.valueOf(overrideTransport); 513 if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN 514 || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE 515 || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) { 516 Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. " 517 + "Defaulting to TRANSPORT_ETHERNET"); 518 } else { 519 transport = parsedTransport; 520 } 521 } catch (NumberFormatException nfe) { 522 Log.e(TAG, "Override transport type '" + overrideTransport + "' " 523 + "could not be parsed. Defaulting to TRANSPORT_ETHERNET"); 524 } 525 } 526 527 // Apply the transport. If the user supplied a valid number that is not a valid transport 528 // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens 529 try { 530 nc.addTransportType(transport); 531 } catch (IllegalArgumentException iae) { 532 Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. " 533 + "Defaulting to TRANSPORT_ETHERNET"); 534 nc.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); 535 } 536 537 nc.setLinkUpstreamBandwidthKbps(100 * 1000); 538 nc.setLinkDownstreamBandwidthKbps(100 * 1000); 539 540 if (!TextUtils.isEmpty(commaSeparatedCapabilities)) { 541 for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) { 542 if (!TextUtils.isEmpty(strNetworkCapability)) { 543 try { 544 nc.addCapability(Integer.valueOf(strNetworkCapability)); 545 } catch (NumberFormatException nfe) { 546 Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed"); 547 } catch (IllegalArgumentException iae) { 548 Log.e(TAG, strNetworkCapability + " is not a valid " 549 + "NetworkCapability.NET_CAPABILITY_* value"); 550 } 551 } 552 } 553 } 554 // Ethernet networks have no way to update the following capabilities, so they always 555 // have them. 556 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); 557 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED); 558 nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); 559 560 return nc; 561 } 562 563 /** 564 * Parses static IP configuration. 565 * 566 * @param staticIpConfig represents static IP configuration in the following format: {@code 567 * ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses> 568 * domains=<comma-sep-domains>} 569 */ 570 @VisibleForTesting parseStaticIpConfiguration(String staticIpConfig)571 static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) { 572 StaticIpConfiguration ipConfig = new StaticIpConfiguration(); 573 574 for (String keyValueAsString : staticIpConfig.trim().split(" ")) { 575 if (TextUtils.isEmpty(keyValueAsString)) continue; 576 577 String[] pair = keyValueAsString.split("="); 578 if (pair.length != 2) { 579 throw new IllegalArgumentException("Unexpected token: " + keyValueAsString 580 + " in " + staticIpConfig); 581 } 582 583 String key = pair[0]; 584 String value = pair[1]; 585 586 switch (key) { 587 case "ip": 588 ipConfig.ipAddress = new LinkAddress(value); 589 break; 590 case "domains": 591 ipConfig.domains = value; 592 break; 593 case "gateway": 594 ipConfig.gateway = InetAddress.parseNumericAddress(value); 595 break; 596 case "dns": { 597 ArrayList<InetAddress> dnsAddresses = new ArrayList<>(); 598 for (String address: value.split(",")) { 599 dnsAddresses.add(InetAddress.parseNumericAddress(address)); 600 } 601 ipConfig.dnsServers.addAll(dnsAddresses); 602 break; 603 } 604 default : { 605 throw new IllegalArgumentException("Unexpected key: " + key 606 + " in " + staticIpConfig); 607 } 608 } 609 } 610 return new IpConfiguration(IpAssignment.STATIC, ProxySettings.NONE, ipConfig, null); 611 } 612 createDefaultIpConfiguration()613 private static IpConfiguration createDefaultIpConfiguration() { 614 return new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null); 615 } 616 updateIfaceMatchRegexp()617 private void updateIfaceMatchRegexp() { 618 final String match = mContext.getResources().getString( 619 com.android.internal.R.string.config_ethernet_iface_regex); 620 mIfaceMatch = mIncludeTestInterfaces 621 ? "(" + match + "|" + TEST_IFACE_REGEXP + ")" 622 : match; 623 Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'"); 624 } 625 postAndWaitForRunnable(Runnable r)626 private void postAndWaitForRunnable(Runnable r) { 627 mHandler.runWithScissors(r, 2000L /* timeout */); 628 } 629 dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)630 void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { 631 postAndWaitForRunnable(() -> { 632 pw.println(getClass().getSimpleName()); 633 pw.println("Ethernet interface name filter: " + mIfaceMatch); 634 pw.println("Default interface: " + mDefaultInterface); 635 pw.println("Default interface mode: " + mDefaultInterfaceMode); 636 pw.println("Tethered interface requests: " 637 + mTetheredInterfaceRequests.getRegisteredCallbackCount()); 638 pw.println("Listeners: " + mListeners.getRegisteredCallbackCount()); 639 pw.println("IP Configurations:"); 640 pw.increaseIndent(); 641 for (String iface : mIpConfigurations.keySet()) { 642 pw.println(iface + ": " + mIpConfigurations.get(iface)); 643 } 644 pw.decreaseIndent(); 645 pw.println(); 646 647 pw.println("Network Capabilities:"); 648 pw.increaseIndent(); 649 for (String iface : mNetworkCapabilities.keySet()) { 650 pw.println(iface + ": " + mNetworkCapabilities.get(iface)); 651 } 652 pw.decreaseIndent(); 653 pw.println(); 654 655 mFactory.dump(fd, pw, args); 656 }); 657 } 658 } 659