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