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