• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.net.ConnectivityManager;
23 import android.net.EthernetManager;
24 import android.net.EthernetNetworkSpecifier;
25 import android.net.IpConfiguration;
26 import android.net.IpConfiguration.IpAssignment;
27 import android.net.IpConfiguration.ProxySettings;
28 import android.net.LinkProperties;
29 import android.net.NetworkAgentConfig;
30 import android.net.NetworkCapabilities;
31 import android.net.NetworkProvider;
32 import android.net.NetworkRequest;
33 import android.net.NetworkScore;
34 import android.net.ip.IIpClient;
35 import android.net.ip.IpClientCallbacks;
36 import android.net.ip.IpClientManager;
37 import android.net.ip.IpClientUtil;
38 import android.net.shared.ProvisioningConfiguration;
39 import android.os.ConditionVariable;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.text.TextUtils;
43 import android.util.AndroidRuntimeException;
44 import android.util.ArraySet;
45 import android.util.Log;
46 import android.util.SparseArray;
47 
48 import com.android.connectivity.resources.R;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.util.IndentingPrintWriter;
51 import com.android.net.module.util.InterfaceParams;
52 import com.android.server.connectivity.ConnectivityResources;
53 
54 import java.io.FileDescriptor;
55 import java.util.Objects;
56 import java.util.Set;
57 import java.util.concurrent.ConcurrentHashMap;
58 
59 /**
60  * Class that manages NetworkOffers for Ethernet networks.
61  *
62  * TODO: this class should be merged into EthernetTracker.
63  */
64 public class EthernetNetworkFactory {
65     private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
66     final static boolean DBG = true;
67 
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     private final NetworkProvider mProvider;
75     final Dependencies mDeps;
76 
77     public static class Dependencies {
makeIpClient(Context context, String iface, IpClientCallbacks callbacks)78         public void makeIpClient(Context context, String iface, IpClientCallbacks callbacks) {
79             IpClientUtil.makeIpClient(context, iface, callbacks);
80         }
81 
makeIpClientManager(@onNull final IIpClient ipClient)82         public IpClientManager makeIpClientManager(@NonNull final IIpClient ipClient) {
83             return new IpClientManager(ipClient, TAG);
84         }
85 
makeEthernetNetworkAgent(Context context, Looper looper, NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config, NetworkProvider provider, EthernetNetworkAgent.Callbacks cb)86         public EthernetNetworkAgent makeEthernetNetworkAgent(Context context, Looper looper,
87                 NetworkCapabilities nc, LinkProperties lp, NetworkAgentConfig config,
88                 NetworkProvider provider, EthernetNetworkAgent.Callbacks cb) {
89             return new EthernetNetworkAgent(context, looper, nc, lp, config, provider, cb);
90         }
91 
getNetworkInterfaceByName(String name)92         public InterfaceParams getNetworkInterfaceByName(String name) {
93             return InterfaceParams.getByName(name);
94         }
95 
getTcpBufferSizesFromResource(Context context)96         public String getTcpBufferSizesFromResource(Context context) {
97             final ConnectivityResources resources = new ConnectivityResources(context);
98             return resources.get().getString(R.string.config_ethernet_tcp_buffers);
99         }
100     }
101 
102     public static class ConfigurationException extends AndroidRuntimeException {
ConfigurationException(String msg)103         public ConfigurationException(String msg) {
104             super(msg);
105         }
106     }
107 
EthernetNetworkFactory(Handler handler, Context context)108     public EthernetNetworkFactory(Handler handler, Context context) {
109         this(handler, context, new NetworkProvider(context, handler.getLooper(), TAG),
110             new Dependencies());
111     }
112 
113     @VisibleForTesting
EthernetNetworkFactory(Handler handler, Context context, NetworkProvider provider, Dependencies deps)114     EthernetNetworkFactory(Handler handler, Context context, NetworkProvider provider,
115             Dependencies deps) {
116         mHandler = handler;
117         mContext = context;
118         mProvider = provider;
119         mDeps = deps;
120     }
121 
122     /**
123      * Registers the network provider with the system.
124      */
register()125     public void register() {
126         mContext.getSystemService(ConnectivityManager.class).registerNetworkProvider(mProvider);
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      */
133     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getAvailableInterfaces(boolean includeRestricted)134     protected 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 
146     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
addInterface(@onNull final String ifaceName, @NonNull final String hwAddress, @NonNull final IpConfiguration ipConfig, @NonNull final NetworkCapabilities capabilities)147     protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress,
148             @NonNull final IpConfiguration ipConfig,
149             @NonNull final NetworkCapabilities capabilities) {
150         if (mTrackingInterfaces.containsKey(ifaceName)) {
151             Log.e(TAG, "Interface with name " + ifaceName + " already exists.");
152             return;
153         }
154 
155         final NetworkCapabilities nc = new NetworkCapabilities.Builder(capabilities)
156                 .setNetworkSpecifier(new EthernetNetworkSpecifier(ifaceName))
157                 .build();
158 
159         if (DBG) {
160             Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + nc);
161         }
162 
163         final NetworkInterfaceState iface = new NetworkInterfaceState(
164                 ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, mProvider, mDeps);
165         mTrackingInterfaces.put(ifaceName, iface);
166     }
167 
168     @VisibleForTesting
getInterfaceState(@onNull String iface)169     protected int getInterfaceState(@NonNull String iface) {
170         final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface);
171         if (interfaceState == null) {
172             return EthernetManager.STATE_ABSENT;
173         } else if (!interfaceState.mLinkUp) {
174             return EthernetManager.STATE_LINK_DOWN;
175         } else {
176             return EthernetManager.STATE_LINK_UP;
177         }
178     }
179 
180     /**
181      * Update a network's configuration and restart it if necessary.
182      *
183      * @param ifaceName the interface name of the network to be updated.
184      * @param ipConfig the desired {@link IpConfiguration} for the given network or null. If
185      *                 {@code null} is passed, the existing IpConfiguration is not updated.
186      * @param capabilities the desired {@link NetworkCapabilities} for the given network. If
187      *                     {@code null} is passed, then the network's current
188      *                     {@link NetworkCapabilities} will be used in support of existing APIs as
189      *                     the public API does not allow this.
190      */
191     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
updateInterface(@onNull final String ifaceName, @Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities)192     protected void updateInterface(@NonNull final String ifaceName,
193             @Nullable final IpConfiguration ipConfig,
194             @Nullable final NetworkCapabilities capabilities) {
195         if (!hasInterface(ifaceName)) {
196             return;
197         }
198 
199         final NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
200         iface.updateInterface(ipConfig, capabilities);
201         mTrackingInterfaces.put(ifaceName, iface);
202         return;
203     }
204 
mixInCapabilities(NetworkCapabilities nc, NetworkCapabilities addedNc)205     private static NetworkCapabilities mixInCapabilities(NetworkCapabilities nc,
206             NetworkCapabilities addedNc) {
207        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(nc);
208        for (int transport : addedNc.getTransportTypes()) builder.addTransportType(transport);
209        for (int capability : addedNc.getCapabilities()) builder.addCapability(capability);
210        return builder.build();
211     }
212 
createDefaultNetworkCapabilities()213     private static NetworkCapabilities createDefaultNetworkCapabilities() {
214         return NetworkCapabilities.Builder
215                 .withoutDefaultCapabilities()
216                 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build();
217     }
218 
219     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
removeInterface(String interfaceName)220     protected boolean removeInterface(String interfaceName) {
221         NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
222         if (iface != null) {
223             iface.unregisterNetworkOfferAndStop();
224             return true;
225         }
226         // TODO(b/236892130): if an interface is currently in server mode, it may not be properly
227         // removed.
228         // TODO: when false is returned, do not send a STATE_ABSENT callback.
229         Log.w(TAG, interfaceName + " is not tracked and cannot be removed");
230         return false;
231     }
232 
233     /** Returns true if state has been modified */
234     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
updateInterfaceLinkState(@onNull final String ifaceName, final boolean up)235     protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up) {
236         if (!hasInterface(ifaceName)) {
237             return false;
238         }
239 
240         if (DBG) {
241             Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up);
242         }
243 
244         NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
245         return iface.updateLinkState(up);
246     }
247 
248     @VisibleForTesting
hasInterface(String ifaceName)249     protected boolean hasInterface(String ifaceName) {
250         return mTrackingInterfaces.containsKey(ifaceName);
251     }
252 
253     @VisibleForTesting
254     static class NetworkInterfaceState {
255         final String name;
256 
257         private final String mHwAddress;
258         private final Handler mHandler;
259         private final Context mContext;
260         private final NetworkProvider mNetworkProvider;
261         private final Dependencies mDeps;
262         private NetworkProvider.NetworkOfferCallback mNetworkOfferCallback;
263 
264         private static String sTcpBufferSizes = null;  // Lazy initialized.
265 
266         private boolean mLinkUp;
267         private int mLegacyType;
268         private LinkProperties mLinkProperties = new LinkProperties();
269         private final Set<Integer> mRequestIds = new ArraySet<>();
270 
271         private volatile @Nullable IpClientManager mIpClient;
272         private NetworkCapabilities mCapabilities;
273         private @Nullable EthernetIpClientCallback mIpClientCallback;
274         private @Nullable EthernetNetworkAgent mNetworkAgent;
275         private IpConfiguration mIpConfig;
276 
277         /**
278          * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet
279          * interface could propagate.
280          *
281          * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to
282          * TYPE_NONE to match the behavior of their own network factories.
283          */
284         private static final SparseArray<Integer> sTransports = new SparseArray();
285         static {
sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, ConnectivityManager.TYPE_ETHERNET)286             sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET,
287                     ConnectivityManager.TYPE_ETHERNET);
sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, ConnectivityManager.TYPE_BLUETOOTH)288             sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH,
289                     ConnectivityManager.TYPE_BLUETOOTH);
sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI)290             sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI);
sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, ConnectivityManager.TYPE_MOBILE)291             sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR,
292                     ConnectivityManager.TYPE_MOBILE);
sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE)293             sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE);
sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, ConnectivityManager.TYPE_NONE)294             sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE,
295                     ConnectivityManager.TYPE_NONE);
296         }
297 
298         private class EthernetIpClientCallback extends IpClientCallbacks {
299             private final ConditionVariable mIpClientStartCv = new ConditionVariable(false);
300             private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false);
301 
302             @Override
onIpClientCreated(IIpClient ipClient)303             public void onIpClientCreated(IIpClient ipClient) {
304                 mIpClient = mDeps.makeIpClientManager(ipClient);
305                 mIpClientStartCv.open();
306             }
307 
awaitIpClientStart()308             private void awaitIpClientStart() {
309                 mIpClientStartCv.block();
310             }
311 
awaitIpClientShutdown()312             private void awaitIpClientShutdown() {
313                 mIpClientShutdownCv.block();
314             }
315 
316             // At the time IpClient is stopped, an IpClient event may have already been posted on
317             // the back of the handler and is awaiting execution. Once that event is executed, the
318             // associated callback object may not be valid anymore
319             // (NetworkInterfaceState#mIpClientCallback points to a different object / null).
isCurrentCallback()320             private boolean isCurrentCallback() {
321                 return this == mIpClientCallback;
322             }
323 
handleIpEvent(final @NonNull Runnable r)324             private void handleIpEvent(final @NonNull Runnable r) {
325                 mHandler.post(() -> {
326                     if (!isCurrentCallback()) {
327                         Log.i(TAG, "Ignoring stale IpClientCallbacks " + this);
328                         return;
329                     }
330                     r.run();
331                 });
332             }
333 
334             @Override
onProvisioningSuccess(LinkProperties newLp)335             public void onProvisioningSuccess(LinkProperties newLp) {
336                 handleIpEvent(() -> onIpLayerStarted(newLp));
337             }
338 
339             @Override
onProvisioningFailure(LinkProperties newLp)340             public void onProvisioningFailure(LinkProperties newLp) {
341                 // This cannot happen due to provisioning timeout, because our timeout is 0. It can
342                 // happen due to errors while provisioning or on provisioning loss.
343                 handleIpEvent(() -> onIpLayerStopped());
344             }
345 
346             @Override
onLinkPropertiesChange(LinkProperties newLp)347             public void onLinkPropertiesChange(LinkProperties newLp) {
348                 handleIpEvent(() -> updateLinkProperties(newLp));
349             }
350 
351             @Override
onReachabilityLost(String logMsg)352             public void onReachabilityLost(String logMsg) {
353                 handleIpEvent(() -> updateNeighborLostEvent(logMsg));
354             }
355 
356             @Override
onQuit()357             public void onQuit() {
358                 mIpClient = null;
359                 mIpClientShutdownCv.open();
360             }
361         }
362 
363         private class EthernetNetworkOfferCallback implements NetworkProvider.NetworkOfferCallback {
isStale()364             private boolean isStale() {
365                 return this != mNetworkOfferCallback;
366             }
367 
368             @Override
onNetworkNeeded(@onNull NetworkRequest request)369             public void onNetworkNeeded(@NonNull NetworkRequest request) {
370                 if (isStale()) {
371                     return;
372                 }
373                 if (DBG) {
374                     Log.d(TAG, String.format("%s: onNetworkNeeded for request: %s", name, request));
375                 }
376                 // When the network offer is first registered, onNetworkNeeded is called with all
377                 // existing requests.
378                 // ConnectivityService filters requests for us based on the NetworkCapabilities
379                 // passed in the registerNetworkOffer() call.
380                 mRequestIds.add(request.requestId);
381                 // if the network is already started, this is a no-op.
382                 start();
383             }
384 
385             @Override
onNetworkUnneeded(@onNull NetworkRequest request)386             public void onNetworkUnneeded(@NonNull NetworkRequest request) {
387                 if (isStale()) {
388                     return;
389                 }
390                 if (DBG) {
391                     Log.d(TAG,
392                             String.format("%s: onNetworkUnneeded for request: %s", name, request));
393                 }
394                 if (!mRequestIds.remove(request.requestId)) {
395                     // This can only happen if onNetworkNeeded was not called for a request or if
396                     // the requestId changed. Both should *never* happen.
397                     Log.wtf(TAG, "onNetworkUnneeded called for unknown request");
398                 }
399                 if (mRequestIds.isEmpty()) {
400                     // not currently serving any requests, stop the network.
401                     stop();
402                 }
403             }
404         }
405 
NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities, NetworkProvider networkProvider, Dependencies deps)406         NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
407                 @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities,
408                 NetworkProvider networkProvider, Dependencies deps) {
409             name = ifaceName;
410             mIpConfig = Objects.requireNonNull(ipConfig);
411             mCapabilities = Objects.requireNonNull(capabilities);
412             mLegacyType = getLegacyType(mCapabilities);
413             mHandler = handler;
414             mContext = context;
415             mNetworkProvider = networkProvider;
416             mDeps = deps;
417             mHwAddress = hwAddress;
418         }
419 
420         /**
421          * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults
422          * to legacy TYPE_NONE if there is no known conversion
423          */
getLegacyType(int transport)424         private static int getLegacyType(int transport) {
425             return sTransports.get(transport, ConnectivityManager.TYPE_NONE);
426         }
427 
getLegacyType(@onNull final NetworkCapabilities capabilities)428         private static int getLegacyType(@NonNull final NetworkCapabilities capabilities) {
429             final int[] transportTypes = capabilities.getTransportTypes();
430             if (transportTypes.length > 0) {
431                 return getLegacyType(transportTypes[0]);
432             }
433 
434             // Should never happen as transport is always one of ETHERNET or a valid override
435             throw new ConfigurationException("Network Capabilities do not have an associated "
436                     + "transport type.");
437         }
438 
getNetworkScore()439         private static NetworkScore getNetworkScore() {
440             return new NetworkScore.Builder().build();
441         }
442 
setCapabilities(@onNull final NetworkCapabilities capabilities)443         private void setCapabilities(@NonNull final NetworkCapabilities capabilities) {
444             mCapabilities = new NetworkCapabilities(capabilities);
445             mLegacyType = getLegacyType(mCapabilities);
446 
447             if (mLinkUp) {
448                 // registering a new network offer will update the existing one, not install a
449                 // new one.
450                 registerNetworkOffer();
451             }
452         }
453 
updateInterface(@ullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities)454         void updateInterface(@Nullable final IpConfiguration ipConfig,
455                 @Nullable final NetworkCapabilities capabilities) {
456             if (DBG) {
457                 Log.d(TAG, "updateInterface, iface: " + name
458                         + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig
459                         + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities
460                 );
461             }
462 
463             if (null != ipConfig){
464                 mIpConfig = ipConfig;
465             }
466             if (null != capabilities) {
467                 setCapabilities(capabilities);
468             }
469             // TODO: Update this logic to only do a restart if required. Although a restart may
470             //  be required due to the capabilities or ipConfiguration values, not all
471             //  capabilities changes require a restart.
472             maybeRestart();
473         }
474 
isRestricted()475         boolean isRestricted() {
476             return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
477         }
478 
start()479         private void start() {
480             if (mIpClient != null) {
481                 if (DBG) Log.d(TAG, "IpClient already started");
482                 return;
483             }
484             if (DBG) {
485                 Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name));
486             }
487 
488             mIpClientCallback = new EthernetIpClientCallback();
489             mDeps.makeIpClient(mContext, name, mIpClientCallback);
490             mIpClientCallback.awaitIpClientStart();
491 
492             if (mIpConfig.getProxySettings() == ProxySettings.STATIC
493                     || mIpConfig.getProxySettings() == ProxySettings.PAC) {
494                 mIpClient.setHttpProxy(mIpConfig.getHttpProxy());
495             }
496 
497             if (sTcpBufferSizes == null) {
498                 sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext);
499             }
500             if (!TextUtils.isEmpty(sTcpBufferSizes)) {
501                 mIpClient.setTcpBufferSizes(sTcpBufferSizes);
502             }
503 
504             mIpClient.startProvisioning(createProvisioningConfiguration(mIpConfig));
505         }
506 
onIpLayerStarted(@onNull final LinkProperties linkProperties)507         void onIpLayerStarted(@NonNull final LinkProperties linkProperties) {
508             if (mNetworkAgent != null) {
509                 Log.e(TAG, "Already have a NetworkAgent - aborting new request");
510                 stop();
511                 return;
512             }
513             mLinkProperties = linkProperties;
514 
515             // Create our NetworkAgent.
516             final NetworkAgentConfig config = new NetworkAgentConfig.Builder()
517                     .setLegacyType(mLegacyType)
518                     .setLegacyTypeName(NETWORK_TYPE)
519                     .setLegacyExtraInfo(mHwAddress)
520                     .build();
521             mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(),
522                     mCapabilities, mLinkProperties, config, mNetworkProvider,
523                     new EthernetNetworkAgent.Callbacks() {
524                         @Override
525                         public void onNetworkUnwanted() {
526                             // if mNetworkAgent is null, we have already called stop.
527                             if (mNetworkAgent == null) return;
528 
529                             if (this == mNetworkAgent.getCallbacks()) {
530                                 stop();
531                             } else {
532                                 Log.d(TAG, "Ignoring unwanted as we have a more modern " +
533                                         "instance");
534                             }
535                         }
536                     });
537             mNetworkAgent.register();
538             mNetworkAgent.markConnected();
539         }
540 
onIpLayerStopped()541         void onIpLayerStopped() {
542             // There is no point in continuing if the interface is gone as stop() will be triggered
543             // by removeInterface() when processed on the handler thread and start() won't
544             // work for a non-existent interface.
545             if (null == mDeps.getNetworkInterfaceByName(name)) {
546                 if (DBG) Log.d(TAG, name + " is no longer available.");
547                 // Send a callback in case a provisioning request was in progress.
548                 return;
549             }
550             maybeRestart();
551         }
552 
ensureRunningOnEthernetHandlerThread()553         private void ensureRunningOnEthernetHandlerThread() {
554             if (mHandler.getLooper().getThread() != Thread.currentThread()) {
555                 throw new IllegalStateException(
556                         "Not running on the Ethernet thread: "
557                                 + Thread.currentThread().getName());
558             }
559         }
560 
updateLinkProperties(LinkProperties linkProperties)561         void updateLinkProperties(LinkProperties linkProperties) {
562             mLinkProperties = linkProperties;
563             if (mNetworkAgent != null) {
564                 mNetworkAgent.sendLinkPropertiesImpl(linkProperties);
565             }
566         }
567 
updateNeighborLostEvent(String logMsg)568         void updateNeighborLostEvent(String logMsg) {
569             Log.i(TAG, "updateNeighborLostEvent " + logMsg);
570             if (mIpConfig.getIpAssignment() == IpAssignment.STATIC) {
571                 // Ignore NUD failures for static IP configurations, where restarting the IpClient
572                 // will not fix connectivity.
573                 // In this scenario, NetworkMonitor will not verify the network, so it will
574                 // eventually be torn down.
575                 return;
576             }
577             // Reachability lost will be seen only if the gateway is not reachable.
578             // Since ethernet FW doesn't have the mechanism to scan for new networks
579             // like WiFi, simply restart.
580             // If there is a better network, that will become default and apps
581             // will be able to use internet. If ethernet gets connected again,
582             // and has backhaul connectivity, it will become default.
583             maybeRestart();
584         }
585 
586         /** Returns true if state has been modified */
updateLinkState(final boolean up)587         boolean updateLinkState(final boolean up) {
588             if (mLinkUp == up)  {
589                 return false;
590             }
591             mLinkUp = up;
592 
593             if (!up) { // was up, goes down
594                 // retract network offer and stop IpClient.
595                 unregisterNetworkOfferAndStop();
596             } else { // was down, goes up
597                 // register network offer
598                 registerNetworkOffer();
599             }
600 
601             return true;
602         }
603 
stop()604         private void stop() {
605             // Unregister NetworkAgent before stopping IpClient, so destroyNativeNetwork (which
606             // deletes routes) hopefully happens before stop() finishes execution. Otherwise, it may
607             // delete the new routes when IpClient gets restarted.
608             if (mNetworkAgent != null) {
609                 mNetworkAgent.unregister();
610                 mNetworkAgent = null;
611             }
612 
613             // Invalidate all previous start requests
614             if (mIpClient != null) {
615                 mIpClient.shutdown();
616                 mIpClientCallback.awaitIpClientShutdown();
617                 mIpClient = null;
618             }
619 
620             mIpClientCallback = null;
621 
622             mLinkProperties.clear();
623         }
624 
registerNetworkOffer()625         private void registerNetworkOffer() {
626             // If mNetworkOfferCallback is already set, it should be reused to update the existing
627             // offer.
628             if (mNetworkOfferCallback == null) {
629                 mNetworkOfferCallback = new EthernetNetworkOfferCallback();
630             }
631             mNetworkProvider.registerNetworkOffer(getNetworkScore(),
632                     new NetworkCapabilities(mCapabilities), cmd -> mHandler.post(cmd),
633                     mNetworkOfferCallback);
634         }
635 
unregisterNetworkOfferAndStop()636         private void unregisterNetworkOfferAndStop() {
637             mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback);
638             // Setting mNetworkOfferCallback to null allows the callback object to be identified
639             // as stale.
640             mNetworkOfferCallback = null;
641             stop();
642             mRequestIds.clear();
643         }
644 
createProvisioningConfiguration( @onNull final IpConfiguration config)645         private static ProvisioningConfiguration createProvisioningConfiguration(
646                 @NonNull final IpConfiguration config) {
647             if (config.getIpAssignment() == IpAssignment.STATIC) {
648                 return new ProvisioningConfiguration.Builder()
649                         .withStaticConfiguration(config.getStaticIpConfiguration())
650                         .build();
651             }
652             return new ProvisioningConfiguration.Builder()
653                         .withProvisioningTimeoutMs(0)
654                         .build();
655         }
656 
maybeRestart()657         void maybeRestart() {
658             if (mIpClient == null) {
659                 // If maybeRestart() is called from a provisioning failure, it is
660                 // possible that link disappeared in the meantime. In that
661                 // case, stop() has already been called and IpClient should not
662                 // get restarted to prevent a provisioning failure loop.
663                 Log.i(TAG, String.format("maybeRestart() called on stopped interface %s", name));
664                 return;
665             }
666             if (DBG) Log.d(TAG, "restart IpClient");
667             stop();
668             start();
669         }
670 
671         @Override
toString()672         public String toString() {
673             return getClass().getSimpleName() + "{ "
674                     + "iface: " + name + ", "
675                     + "up: " + mLinkUp + ", "
676                     + "hwAddress: " + mHwAddress + ", "
677                     + "networkCapabilities: " + mCapabilities + ", "
678                     + "networkAgent: " + mNetworkAgent + ", "
679                     + "ipClient: " + mIpClient + ","
680                     + "linkProperties: " + mLinkProperties
681                     + "}";
682         }
683     }
684 
dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)685     void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
686         pw.println(getClass().getSimpleName());
687         pw.println("Tracking interfaces:");
688         pw.increaseIndent();
689         for (String iface: mTrackingInterfaces.keySet()) {
690             NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface);
691             pw.println(iface + ":" + ifaceState);
692             pw.increaseIndent();
693             if (null == ifaceState.mIpClient) {
694                 pw.println("IpClient is null");
695             }
696             pw.decreaseIndent();
697         }
698         pw.decreaseIndent();
699     }
700 }
701