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