1 /* 2 * Copyright (C) 2014 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.shared.LinkPropertiesParcelableUtil.toStableParcelable; 20 import static com.android.internal.util.Preconditions.checkNotNull; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.net.ConnectivityManager; 26 import android.net.IpConfiguration; 27 import android.net.IpConfiguration.IpAssignment; 28 import android.net.IpConfiguration.ProxySettings; 29 import android.net.LinkProperties; 30 import android.net.NetworkAgent; 31 import android.net.NetworkAgentConfig; 32 import android.net.NetworkCapabilities; 33 import android.net.NetworkFactory; 34 import android.net.NetworkRequest; 35 import android.net.NetworkSpecifier; 36 import android.net.StringNetworkSpecifier; 37 import android.net.ip.IIpClient; 38 import android.net.ip.IpClientCallbacks; 39 import android.net.ip.IpClientUtil; 40 import android.net.shared.ProvisioningConfiguration; 41 import android.net.util.InterfaceParams; 42 import android.os.ConditionVariable; 43 import android.os.Handler; 44 import android.os.RemoteException; 45 import android.text.TextUtils; 46 import android.util.AndroidRuntimeException; 47 import android.util.Log; 48 import android.util.SparseArray; 49 50 import com.android.internal.util.IndentingPrintWriter; 51 52 import java.io.FileDescriptor; 53 import java.util.concurrent.ConcurrentHashMap; 54 import java.util.Objects; 55 56 /** 57 * {@link NetworkFactory} that represents Ethernet networks. 58 * 59 * This class reports a static network score of 70 when it is tracking an interface and that 60 * interface's link is up, and a score of 0 otherwise. 61 */ 62 public class EthernetNetworkFactory extends NetworkFactory { 63 private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); 64 final static boolean DBG = true; 65 66 private final static int NETWORK_SCORE = 70; 67 private static final String NETWORK_TYPE = "Ethernet"; 68 69 private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces = 70 new ConcurrentHashMap<>(); 71 private final Handler mHandler; 72 private final Context mContext; 73 74 public static class ConfigurationException extends AndroidRuntimeException { ConfigurationException(String msg)75 public ConfigurationException(String msg) { 76 super(msg); 77 } 78 } 79 EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter)80 public EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter) { 81 super(handler.getLooper(), context, NETWORK_TYPE, filter); 82 83 mHandler = handler; 84 mContext = context; 85 86 setScoreFilter(NETWORK_SCORE); 87 } 88 89 @Override acceptRequest(NetworkRequest request, int score)90 public boolean acceptRequest(NetworkRequest request, int score) { 91 if (request.type == NetworkRequest.Type.TRACK_DEFAULT) { 92 return false; 93 } 94 95 if (DBG) { 96 Log.d(TAG, "acceptRequest, request: " + request + ", score: " + score); 97 } 98 99 return networkForRequest(request) != null; 100 } 101 102 @Override needNetworkFor(NetworkRequest networkRequest, int score)103 protected void needNetworkFor(NetworkRequest networkRequest, int score) { 104 NetworkInterfaceState network = networkForRequest(networkRequest); 105 106 if (network == null) { 107 Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); 108 return; 109 } 110 111 if (++network.refCount == 1) { 112 network.start(); 113 } 114 } 115 116 @Override releaseNetworkFor(NetworkRequest networkRequest)117 protected void releaseNetworkFor(NetworkRequest networkRequest) { 118 NetworkInterfaceState network = networkForRequest(networkRequest); 119 if (network == null) { 120 Log.e(TAG, "releaseNetworkFor, failed to get a network for " + networkRequest); 121 return; 122 } 123 124 if (--network.refCount == 0) { 125 network.stop(); 126 } 127 } 128 129 /** 130 * Returns an array of available interface names. The array is sorted: unrestricted interfaces 131 * goes first, then sorted by name. 132 */ getAvailableInterfaces(boolean includeRestricted)133 String[] getAvailableInterfaces(boolean includeRestricted) { 134 return mTrackingInterfaces.values() 135 .stream() 136 .filter(iface -> !iface.isRestricted() || includeRestricted) 137 .sorted((iface1, iface2) -> { 138 int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); 139 return r == 0 ? iface1.name.compareTo(iface2.name) : r; 140 }) 141 .map(iface -> iface.name) 142 .toArray(String[]::new); 143 } 144 addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities, IpConfiguration ipConfiguration)145 void addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities, 146 IpConfiguration ipConfiguration) { 147 if (mTrackingInterfaces.containsKey(ifaceName)) { 148 Log.e(TAG, "Interface with name " + ifaceName + " already exists."); 149 return; 150 } 151 152 if (DBG) { 153 Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + capabilities); 154 } 155 156 NetworkInterfaceState iface = new NetworkInterfaceState( 157 ifaceName, hwAddress, mHandler, mContext, capabilities, this); 158 iface.setIpConfig(ipConfiguration); 159 mTrackingInterfaces.put(ifaceName, iface); 160 161 updateCapabilityFilter(); 162 } 163 updateCapabilityFilter()164 private void updateCapabilityFilter() { 165 NetworkCapabilities capabilitiesFilter = new NetworkCapabilities(); 166 capabilitiesFilter.clearAll(); 167 168 for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { 169 capabilitiesFilter.combineCapabilities(iface.mCapabilities); 170 } 171 172 if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); 173 setCapabilityFilter(capabilitiesFilter); 174 } 175 removeInterface(String interfaceName)176 void removeInterface(String interfaceName) { 177 NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); 178 if (iface != null) { 179 iface.stop(); 180 } 181 182 updateCapabilityFilter(); 183 } 184 185 /** Returns true if state has been modified */ updateInterfaceLinkState(String ifaceName, boolean up)186 boolean updateInterfaceLinkState(String ifaceName, boolean up) { 187 if (!mTrackingInterfaces.containsKey(ifaceName)) { 188 return false; 189 } 190 191 if (DBG) { 192 Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); 193 } 194 195 NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); 196 return iface.updateLinkState(up); 197 } 198 hasInterface(String interfacName)199 boolean hasInterface(String interfacName) { 200 return mTrackingInterfaces.containsKey(interfacName); 201 } 202 updateIpConfiguration(String iface, IpConfiguration ipConfiguration)203 void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { 204 NetworkInterfaceState network = mTrackingInterfaces.get(iface); 205 if (network != null) { 206 network.setIpConfig(ipConfiguration); 207 } 208 } 209 networkForRequest(NetworkRequest request)210 private NetworkInterfaceState networkForRequest(NetworkRequest request) { 211 String requestedIface = null; 212 213 NetworkSpecifier specifier = request.networkCapabilities.getNetworkSpecifier(); 214 if (specifier instanceof StringNetworkSpecifier) { 215 requestedIface = ((StringNetworkSpecifier) specifier).specifier; 216 } 217 218 NetworkInterfaceState network = null; 219 if (!TextUtils.isEmpty(requestedIface)) { 220 NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); 221 if (n != null && n.satisfied(request.networkCapabilities)) { 222 network = n; 223 } 224 } else { 225 for (NetworkInterfaceState n : mTrackingInterfaces.values()) { 226 if (n.satisfied(request.networkCapabilities) && n.mLinkUp) { 227 network = n; 228 break; 229 } 230 } 231 } 232 233 if (DBG) { 234 Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); 235 } 236 237 return network; 238 } 239 240 private static class NetworkInterfaceState { 241 final String name; 242 243 private final String mHwAddress; 244 private final NetworkCapabilities mCapabilities; 245 private final Handler mHandler; 246 private final Context mContext; 247 private final NetworkFactory mNetworkFactory; 248 private final int mLegacyType; 249 250 private static String sTcpBufferSizes = null; // Lazy initialized. 251 252 private boolean mLinkUp; 253 private LinkProperties mLinkProperties = new LinkProperties(); 254 255 private volatile @Nullable IIpClient mIpClient; 256 private @Nullable IpClientCallbacksImpl mIpClientCallback; 257 private @Nullable NetworkAgent mNetworkAgent; 258 private @Nullable IpConfiguration mIpConfig; 259 260 /** 261 * An object to contain all transport type information, including base network score and 262 * the legacy transport type it maps to (if any) 263 */ 264 private static class TransportInfo { 265 final int mLegacyType; 266 final int mScore; 267 TransportInfo(int legacyType, int score)268 private TransportInfo(int legacyType, int score) { 269 mLegacyType = legacyType; 270 mScore = score; 271 } 272 } 273 274 /** 275 * A map of TRANSPORT_* types to TransportInfo, making scoring and legacy type information 276 * available for each type an ethernet interface could propagate. 277 * 278 * Unfortunately, base scores for the various transports are not yet centrally located. 279 * They've been lifted from the corresponding NetworkFactory files in the meantime. 280 * 281 * Additionally, there are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types 282 * are set to TYPE_NONE to match the behavior of their own network factories. 283 */ 284 private static final SparseArray<TransportInfo> sTransports = new SparseArray(); 285 static { 286 // LowpanInterfaceTracker.NETWORK_SCORE sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, new TransportInfo(ConnectivityManager.TYPE_NONE, 30))287 sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, 288 new TransportInfo(ConnectivityManager.TYPE_NONE, 30)); 289 // WifiAwareDataPathStateManager.NETWORK_FACTORY_SCORE_AVAIL sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, new TransportInfo(ConnectivityManager.TYPE_NONE, 1))290 sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, 291 new TransportInfo(ConnectivityManager.TYPE_NONE, 1)); 292 // EthernetNetworkFactory.NETWORK_SCORE sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, new TransportInfo(ConnectivityManager.TYPE_ETHERNET, 70))293 sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, 294 new TransportInfo(ConnectivityManager.TYPE_ETHERNET, 70)); 295 // BluetoothTetheringNetworkFactory.NETWORK_SCORE sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, new TransportInfo(ConnectivityManager.TYPE_BLUETOOTH, 69))296 sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, 297 new TransportInfo(ConnectivityManager.TYPE_BLUETOOTH, 69)); 298 // WifiNetworkFactory.SCORE_FILTER / NetworkAgent.WIFI_BASE_SCORE sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, new TransportInfo(ConnectivityManager.TYPE_WIFI, 60))299 sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, 300 new TransportInfo(ConnectivityManager.TYPE_WIFI, 60)); 301 // TelephonyNetworkFactory.TELEPHONY_NETWORK_SCORE sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, new TransportInfo(ConnectivityManager.TYPE_MOBILE, 50))302 sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, 303 new TransportInfo(ConnectivityManager.TYPE_MOBILE, 50)); 304 } 305 306 long refCount = 0; 307 308 private class IpClientCallbacksImpl extends IpClientCallbacks { 309 private final ConditionVariable mIpClientStartCv = new ConditionVariable(false); 310 private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false); 311 312 @Override onIpClientCreated(IIpClient ipClient)313 public void onIpClientCreated(IIpClient ipClient) { 314 mIpClient = ipClient; 315 mIpClientStartCv.open(); 316 } 317 awaitIpClientStart()318 private void awaitIpClientStart() { 319 mIpClientStartCv.block(); 320 } 321 awaitIpClientShutdown()322 private void awaitIpClientShutdown() { 323 mIpClientShutdownCv.block(); 324 } 325 326 @Override onProvisioningSuccess(LinkProperties newLp)327 public void onProvisioningSuccess(LinkProperties newLp) { 328 mHandler.post(() -> onIpLayerStarted(newLp)); 329 } 330 331 @Override onProvisioningFailure(LinkProperties newLp)332 public void onProvisioningFailure(LinkProperties newLp) { 333 mHandler.post(() -> onIpLayerStopped(newLp)); 334 } 335 336 @Override onLinkPropertiesChange(LinkProperties newLp)337 public void onLinkPropertiesChange(LinkProperties newLp) { 338 mHandler.post(() -> updateLinkProperties(newLp)); 339 } 340 341 @Override onQuit()342 public void onQuit() { 343 mIpClient = null; 344 mIpClientShutdownCv.open(); 345 } 346 } 347 shutdownIpClient(IIpClient ipClient)348 private static void shutdownIpClient(IIpClient ipClient) { 349 try { 350 ipClient.shutdown(); 351 } catch (RemoteException e) { 352 Log.e(TAG, "Error stopping IpClient", e); 353 } 354 } 355 NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, @NonNull NetworkCapabilities capabilities, NetworkFactory networkFactory)356 NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, 357 @NonNull NetworkCapabilities capabilities, NetworkFactory networkFactory) { 358 name = ifaceName; 359 mCapabilities = checkNotNull(capabilities); 360 mHandler = handler; 361 mContext = context; 362 mNetworkFactory = networkFactory; 363 int legacyType = ConnectivityManager.TYPE_NONE; 364 int[] transportTypes = mCapabilities.getTransportTypes(); 365 366 if (transportTypes.length > 0) { 367 legacyType = getLegacyType(transportTypes[0]); 368 } else { 369 // Should never happen as transport is always one of ETHERNET or a valid override 370 throw new ConfigurationException("Network Capabilities do not have an associated " 371 + "transport type."); 372 } 373 374 mHwAddress = hwAddress; 375 mLegacyType = legacyType; 376 } 377 setIpConfig(IpConfiguration ipConfig)378 void setIpConfig(IpConfiguration ipConfig) { 379 if (Objects.equals(this.mIpConfig, ipConfig)) { 380 if (DBG) Log.d(TAG, "ipConfig have not changed,so ignore setIpConfig"); 381 return; 382 } 383 this.mIpConfig = ipConfig; 384 if (mNetworkAgent != null) { 385 restart(); 386 } 387 } 388 satisfied(NetworkCapabilities requestedCapabilities)389 boolean satisfied(NetworkCapabilities requestedCapabilities) { 390 return requestedCapabilities.satisfiedByNetworkCapabilities(mCapabilities); 391 } 392 isRestricted()393 boolean isRestricted() { 394 return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 395 } 396 397 /** 398 * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults 399 * to legacy TYPE_NONE if there is no known conversion 400 */ getLegacyType(int transport)401 private static int getLegacyType(int transport) { 402 TransportInfo transportInfo = sTransports.get(transport, /* if dne */ null); 403 if (transportInfo != null) { 404 return transportInfo.mLegacyType; 405 } 406 return ConnectivityManager.TYPE_NONE; 407 } 408 409 /** 410 * Determines the network score based on the transport associated with the interface. 411 * Ethernet interfaces could propagate a transport types forward. Since we can't 412 * get more information about the statuses of the interfaces on the other end of the local 413 * interface, we'll best-effort assign the score as the base score of the assigned transport 414 * when the link is up. When the link is down, the score is set to zero. 415 * 416 * This function is called with the purpose of assigning and updating the network score of 417 * the member NetworkAgent. 418 */ getNetworkScore()419 private int getNetworkScore() { 420 // never set the network score below 0. 421 if (!mLinkUp) { 422 return 0; 423 } 424 425 int[] transportTypes = mCapabilities.getTransportTypes(); 426 if (transportTypes.length < 1) { 427 Log.w(TAG, "Network interface '" + mLinkProperties.getInterfaceName() + "' has no " 428 + "transport type associated with it. Score set to zero"); 429 return 0; 430 } 431 TransportInfo transportInfo = sTransports.get(transportTypes[0], /* if dne */ null); 432 if (transportInfo != null) { 433 return transportInfo.mScore; 434 } 435 return 0; 436 } 437 start()438 private void start() { 439 if (mIpClient != null) { 440 if (DBG) Log.d(TAG, "IpClient already started"); 441 return; 442 } 443 if (DBG) { 444 Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name)); 445 } 446 447 mIpClientCallback = new IpClientCallbacksImpl(); 448 IpClientUtil.makeIpClient(mContext, name, mIpClientCallback); 449 mIpClientCallback.awaitIpClientStart(); 450 if (sTcpBufferSizes == null) { 451 sTcpBufferSizes = mContext.getResources().getString( 452 com.android.internal.R.string.config_ethernet_tcp_buffers); 453 } 454 provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); 455 } 456 onIpLayerStarted(LinkProperties linkProperties)457 void onIpLayerStarted(LinkProperties linkProperties) { 458 if (mNetworkAgent != null) { 459 Log.e(TAG, "Already have a NetworkAgent - aborting new request"); 460 stop(); 461 return; 462 } 463 mLinkProperties = linkProperties; 464 465 // Create our NetworkAgent. 466 final NetworkAgentConfig config = new NetworkAgentConfig.Builder() 467 .setLegacyType(mLegacyType) 468 .setLegacyTypeName(NETWORK_TYPE) 469 .build(); 470 mNetworkAgent = new NetworkAgent(mContext, mHandler.getLooper(), 471 NETWORK_TYPE, mCapabilities, mLinkProperties, 472 getNetworkScore(), config, mNetworkFactory.getProvider()) { 473 public void unwanted() { 474 if (this == mNetworkAgent) { 475 stop(); 476 } else if (mNetworkAgent != null) { 477 Log.d(TAG, "Ignoring unwanted as we have a more modern " + 478 "instance"); 479 } // Otherwise, we've already called stop. 480 } 481 }; 482 mNetworkAgent.register(); 483 mNetworkAgent.setLegacyExtraInfo(mHwAddress); 484 mNetworkAgent.markConnected(); 485 } 486 onIpLayerStopped(LinkProperties linkProperties)487 void onIpLayerStopped(LinkProperties linkProperties) { 488 // This cannot happen due to provisioning timeout, because our timeout is 0. It can only 489 // happen if we're provisioned and we lose provisioning. 490 stop(); 491 // If the interface has disappeared provisioning will fail over and over again, so 492 // there is no point in starting again 493 if (null != InterfaceParams.getByName(name)) { 494 start(); 495 } 496 } 497 updateLinkProperties(LinkProperties linkProperties)498 void updateLinkProperties(LinkProperties linkProperties) { 499 mLinkProperties = linkProperties; 500 if (mNetworkAgent != null) { 501 mNetworkAgent.sendLinkProperties(linkProperties); 502 } 503 } 504 505 /** Returns true if state has been modified */ updateLinkState(boolean up)506 boolean updateLinkState(boolean up) { 507 if (mLinkUp == up) return false; 508 mLinkUp = up; 509 510 stop(); 511 if (up) { 512 start(); 513 } 514 515 return true; 516 } 517 stop()518 void stop() { 519 // Invalidate all previous start requests 520 if (mIpClient != null) { 521 shutdownIpClient(mIpClient); 522 mIpClientCallback.awaitIpClientShutdown(); 523 mIpClient = null; 524 } 525 mIpClientCallback = null; 526 527 if (mNetworkAgent != null) { 528 mNetworkAgent.unregister(); 529 mNetworkAgent = null; 530 } 531 mLinkProperties.clear(); 532 } 533 updateAgent()534 private void updateAgent() { 535 if (mNetworkAgent == null) return; 536 if (DBG) { 537 Log.i(TAG, "Updating mNetworkAgent with: " + 538 mCapabilities + ", " + 539 mLinkProperties); 540 } 541 mNetworkAgent.sendNetworkCapabilities(mCapabilities); 542 mNetworkAgent.sendLinkProperties(mLinkProperties); 543 544 // As a note, getNetworkScore() is fairly expensive to calculate. This is fine for now 545 // since the agent isn't updated frequently. Consider caching the score in the future if 546 // agent updating is required more often 547 mNetworkAgent.sendNetworkScore(getNetworkScore()); 548 } 549 provisionIpClient(IIpClient ipClient, IpConfiguration config, String tcpBufferSizes)550 private static void provisionIpClient(IIpClient ipClient, IpConfiguration config, 551 String tcpBufferSizes) { 552 if (config.getProxySettings() == ProxySettings.STATIC || 553 config.getProxySettings() == ProxySettings.PAC) { 554 try { 555 ipClient.setHttpProxy(toStableParcelable(config.getHttpProxy())); 556 } catch (RemoteException e) { 557 e.rethrowFromSystemServer(); 558 } 559 } 560 561 if (!TextUtils.isEmpty(tcpBufferSizes)) { 562 try { 563 ipClient.setTcpBufferSizes(tcpBufferSizes); 564 } catch (RemoteException e) { 565 e.rethrowFromSystemServer(); 566 } 567 } 568 569 final ProvisioningConfiguration provisioningConfiguration; 570 if (config.getIpAssignment() == IpAssignment.STATIC) { 571 provisioningConfiguration = new ProvisioningConfiguration.Builder() 572 .withStaticConfiguration(config.getStaticIpConfiguration()) 573 .build(); 574 } else { 575 provisioningConfiguration = new ProvisioningConfiguration.Builder() 576 .withProvisioningTimeoutMs(0) 577 .build(); 578 } 579 580 try { 581 ipClient.startProvisioning(provisioningConfiguration.toStableParcelable()); 582 } catch (RemoteException e) { 583 e.rethrowFromSystemServer(); 584 } 585 } 586 restart()587 void restart(){ 588 if (DBG) Log.d(TAG, "reconnecting Etherent"); 589 stop(); 590 start(); 591 } 592 593 @Override toString()594 public String toString() { 595 return getClass().getSimpleName() + "{ " 596 + "refCount: " + refCount + ", " 597 + "iface: " + name + ", " 598 + "up: " + mLinkUp + ", " 599 + "hwAddress: " + mHwAddress + ", " 600 + "networkCapabilities: " + mCapabilities + ", " 601 + "networkAgent: " + mNetworkAgent + ", " 602 + "score: " + getNetworkScore() + ", " 603 + "ipClient: " + mIpClient + "," 604 + "linkProperties: " + mLinkProperties 605 + "}"; 606 } 607 } 608 dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)609 void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { 610 super.dump(fd, pw, args); 611 pw.println(getClass().getSimpleName()); 612 pw.println("Tracking interfaces:"); 613 pw.increaseIndent(); 614 for (String iface: mTrackingInterfaces.keySet()) { 615 NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); 616 pw.println(iface + ":" + ifaceState); 617 pw.increaseIndent(); 618 final IIpClient ipClient = ifaceState.mIpClient; 619 if (ipClient != null) { 620 IpClientUtil.dumpIpClient(ipClient, fd, pw, args); 621 } else { 622 pw.println("IpClient is null"); 623 } 624 pw.decreaseIndent(); 625 } 626 pw.decreaseIndent(); 627 } 628 } 629