• 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 
205     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
removeInterface(String interfaceName)206     protected boolean removeInterface(String interfaceName) {
207         NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
208         if (iface != null) {
209             iface.unregisterNetworkOfferAndStop();
210             return true;
211         }
212         // TODO(b/236892130): if an interface is currently in server mode, it may not be properly
213         // removed.
214         // TODO: when false is returned, do not send a STATE_ABSENT callback.
215         Log.w(TAG, interfaceName + " is not tracked and cannot be removed");
216         return false;
217     }
218 
219     /** Returns true if state has been modified */
220     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
updateInterfaceLinkState(@onNull final String ifaceName, final boolean up)221     protected boolean updateInterfaceLinkState(@NonNull final String ifaceName, final boolean up) {
222         if (!hasInterface(ifaceName)) {
223             return false;
224         }
225 
226         if (DBG) {
227             Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up);
228         }
229 
230         NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
231         return iface.updateLinkState(up);
232     }
233 
234     @VisibleForTesting
hasInterface(String ifaceName)235     protected boolean hasInterface(String ifaceName) {
236         return mTrackingInterfaces.containsKey(ifaceName);
237     }
238 
239     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
240     @Nullable
getHwAddress(@onNull final String ifaceName)241     protected String getHwAddress(@NonNull final String ifaceName) {
242         if (!hasInterface(ifaceName)) {
243             return null;
244         }
245 
246         NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
247         return iface.mHwAddress;
248     }
249 
250     @VisibleForTesting
251     static class NetworkInterfaceState {
252         final String name;
253 
254         private final String mHwAddress;
255         private final Handler mHandler;
256         private final Context mContext;
257         private final NetworkProvider mNetworkProvider;
258         private final Dependencies mDeps;
259         private NetworkProvider.NetworkOfferCallback mNetworkOfferCallback;
260 
261         private static String sTcpBufferSizes = null;  // Lazy initialized.
262 
263         private boolean mLinkUp;
264         private int mLegacyType;
265         private LinkProperties mLinkProperties = new LinkProperties();
266         private final Set<Integer> mRequestIds = new ArraySet<>();
267 
268         private volatile @Nullable IpClientManager mIpClient;
269         private NetworkCapabilities mCapabilities;
270         private @Nullable EthernetIpClientCallback mIpClientCallback;
271         private @Nullable EthernetNetworkAgent mNetworkAgent;
272         private IpConfiguration mIpConfig;
273 
274         /**
275          * A map of TRANSPORT_* types to legacy transport types available for each type an ethernet
276          * interface could propagate.
277          *
278          * There are no legacy type equivalents to LOWPAN or WIFI_AWARE. These types are set to
279          * TYPE_NONE to match the behavior of their own network factories.
280          */
281         private static final SparseArray<Integer> sTransports = new SparseArray();
282         static {
sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET, ConnectivityManager.TYPE_ETHERNET)283             sTransports.put(NetworkCapabilities.TRANSPORT_ETHERNET,
284                     ConnectivityManager.TYPE_ETHERNET);
sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, ConnectivityManager.TYPE_BLUETOOTH)285             sTransports.put(NetworkCapabilities.TRANSPORT_BLUETOOTH,
286                     ConnectivityManager.TYPE_BLUETOOTH);
sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI)287             sTransports.put(NetworkCapabilities.TRANSPORT_WIFI, ConnectivityManager.TYPE_WIFI);
sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR, ConnectivityManager.TYPE_MOBILE)288             sTransports.put(NetworkCapabilities.TRANSPORT_CELLULAR,
289                     ConnectivityManager.TYPE_MOBILE);
sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE)290             sTransports.put(NetworkCapabilities.TRANSPORT_LOWPAN, ConnectivityManager.TYPE_NONE);
sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, ConnectivityManager.TYPE_NONE)291             sTransports.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE,
292                     ConnectivityManager.TYPE_NONE);
293         }
294 
295         private class EthernetIpClientCallback extends IpClientCallbacks {
296             private final ConditionVariable mIpClientStartCv = new ConditionVariable(false);
297             private final ConditionVariable mIpClientShutdownCv = new ConditionVariable(false);
298 
299             @Override
onIpClientCreated(IIpClient ipClient)300             public void onIpClientCreated(IIpClient ipClient) {
301                 mIpClient = mDeps.makeIpClientManager(ipClient);
302                 mIpClientStartCv.open();
303             }
304 
awaitIpClientStart()305             private void awaitIpClientStart() {
306                 mIpClientStartCv.block();
307             }
308 
awaitIpClientShutdown()309             private void awaitIpClientShutdown() {
310                 mIpClientShutdownCv.block();
311             }
312 
safelyPostOnHandler(Runnable r)313             private void safelyPostOnHandler(Runnable r) {
314                 mHandler.post(() -> {
315                     if (this != mIpClientCallback) {
316                         // At the time IpClient is stopped, an IpClient event may have already been
317                         // posted on the handler and is awaiting execution. Once that event is
318                         // executed, the associated callback object may not be valid anymore.
319                         Log.i(TAG, "Ignoring stale IpClientCallbacks " + this);
320                         return;
321                     }
322                     r.run();
323                 });
324             }
325 
326             @Override
onProvisioningSuccess(LinkProperties newLp)327             public void onProvisioningSuccess(LinkProperties newLp) {
328                 safelyPostOnHandler(() -> handleOnProvisioningSuccess(newLp));
329             }
330 
331             @Override
onProvisioningFailure(LinkProperties newLp)332             public void onProvisioningFailure(LinkProperties newLp) {
333                 // This cannot happen due to provisioning timeout, because our timeout is 0. It can
334                 // happen due to errors while provisioning or on provisioning loss.
335                 safelyPostOnHandler(() -> handleOnProvisioningFailure());
336             }
337 
338             @Override
onLinkPropertiesChange(LinkProperties newLp)339             public void onLinkPropertiesChange(LinkProperties newLp) {
340                 safelyPostOnHandler(() -> handleOnLinkPropertiesChange(newLp));
341             }
342 
343             @Override
onReachabilityLost(String logMsg)344             public void onReachabilityLost(String logMsg) {
345                 safelyPostOnHandler(() -> handleOnReachabilityLost(logMsg));
346             }
347 
348             @Override
onQuit()349             public void onQuit() {
350                 mIpClient = null;
351                 mIpClientShutdownCv.open();
352             }
353         }
354 
355         private class EthernetNetworkOfferCallback implements NetworkProvider.NetworkOfferCallback {
isStale()356             private boolean isStale() {
357                 return this != mNetworkOfferCallback;
358             }
359 
360             @Override
onNetworkNeeded(@onNull NetworkRequest request)361             public void onNetworkNeeded(@NonNull NetworkRequest request) {
362                 if (isStale()) {
363                     return;
364                 }
365                 if (DBG) {
366                     Log.d(TAG, String.format("%s: onNetworkNeeded for request: %s", name, request));
367                 }
368                 // When the network offer is first registered, onNetworkNeeded is called with all
369                 // existing requests.
370                 // ConnectivityService filters requests for us based on the NetworkCapabilities
371                 // passed in the registerNetworkOffer() call.
372                 mRequestIds.add(request.requestId);
373                 // if the network is already started, this is a no-op.
374                 start();
375             }
376 
377             @Override
onNetworkUnneeded(@onNull NetworkRequest request)378             public void onNetworkUnneeded(@NonNull NetworkRequest request) {
379                 if (isStale()) {
380                     return;
381                 }
382                 if (DBG) {
383                     Log.d(TAG,
384                             String.format("%s: onNetworkUnneeded for request: %s", name, request));
385                 }
386                 if (!mRequestIds.remove(request.requestId)) {
387                     // This can only happen if onNetworkNeeded was not called for a request or if
388                     // the requestId changed. Both should *never* happen.
389                     Log.wtf(TAG, "onNetworkUnneeded called for unknown request");
390                 }
391                 if (mRequestIds.isEmpty()) {
392                     // not currently serving any requests, stop the network.
393                     stop();
394                 }
395             }
396         }
397 
NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities, NetworkProvider networkProvider, Dependencies deps)398         NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
399                 @NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities,
400                 NetworkProvider networkProvider, Dependencies deps) {
401             name = ifaceName;
402             mIpConfig = Objects.requireNonNull(ipConfig);
403             mCapabilities = Objects.requireNonNull(capabilities);
404             mLegacyType = getLegacyType(mCapabilities);
405             mHandler = handler;
406             mContext = context;
407             mNetworkProvider = networkProvider;
408             mDeps = deps;
409             mHwAddress = hwAddress;
410         }
411 
412         /**
413          * Determines the legacy transport type from a NetworkCapabilities transport type. Defaults
414          * to legacy TYPE_NONE if there is no known conversion
415          */
getLegacyType(int transport)416         private static int getLegacyType(int transport) {
417             return sTransports.get(transport, ConnectivityManager.TYPE_NONE);
418         }
419 
getLegacyType(@onNull final NetworkCapabilities capabilities)420         private static int getLegacyType(@NonNull final NetworkCapabilities capabilities) {
421             final int[] transportTypes = capabilities.getTransportTypes();
422             if (transportTypes.length > 0) {
423                 return getLegacyType(transportTypes[0]);
424             }
425 
426             // Should never happen as transport is always one of ETHERNET or a valid override
427             throw new ConfigurationException("Network Capabilities do not have an associated "
428                     + "transport type.");
429         }
430 
getNetworkScore()431         private static NetworkScore getNetworkScore() {
432             return new NetworkScore.Builder().build();
433         }
434 
setCapabilities(@onNull final NetworkCapabilities capabilities)435         private void setCapabilities(@NonNull final NetworkCapabilities capabilities) {
436             mCapabilities = new NetworkCapabilities(capabilities);
437             mLegacyType = getLegacyType(mCapabilities);
438 
439             if (mLinkUp) {
440                 // registering a new network offer will update the existing one, not install a
441                 // new one.
442                 registerNetworkOffer();
443             }
444         }
445 
updateInterface(@ullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities capabilities)446         void updateInterface(@Nullable final IpConfiguration ipConfig,
447                 @Nullable final NetworkCapabilities capabilities) {
448             if (DBG) {
449                 Log.d(TAG, "updateInterface, iface: " + name
450                         + ", ipConfig: " + ipConfig + ", old ipConfig: " + mIpConfig
451                         + ", capabilities: " + capabilities + ", old capabilities: " + mCapabilities
452                 );
453             }
454 
455             if (null != ipConfig){
456                 mIpConfig = ipConfig;
457             }
458             if (null != capabilities) {
459                 setCapabilities(capabilities);
460             }
461             // TODO: Update this logic to only do a restart if required. Although a restart may
462             //  be required due to the capabilities or ipConfiguration values, not all
463             //  capabilities changes require a restart.
464             maybeRestart();
465         }
466 
isRestricted()467         boolean isRestricted() {
468             return !mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
469         }
470 
start()471         private void start() {
472             if (mIpClient != null) {
473                 if (DBG) Log.d(TAG, "IpClient already started");
474                 return;
475             }
476             if (DBG) {
477                 Log.d(TAG, String.format("Starting Ethernet IpClient(%s)", name));
478             }
479 
480             mIpClientCallback = new EthernetIpClientCallback();
481             mDeps.makeIpClient(mContext, name, mIpClientCallback);
482             mIpClientCallback.awaitIpClientStart();
483 
484             if (mIpConfig.getProxySettings() == ProxySettings.STATIC
485                     || mIpConfig.getProxySettings() == ProxySettings.PAC) {
486                 mIpClient.setHttpProxy(mIpConfig.getHttpProxy());
487             }
488 
489             if (sTcpBufferSizes == null) {
490                 sTcpBufferSizes = mDeps.getTcpBufferSizesFromResource(mContext);
491             }
492             if (!TextUtils.isEmpty(sTcpBufferSizes)) {
493                 mIpClient.setTcpBufferSizes(sTcpBufferSizes);
494             }
495 
496             mIpClient.startProvisioning(createProvisioningConfiguration(mIpConfig));
497         }
498 
handleOnProvisioningSuccess(@onNull final LinkProperties linkProperties)499         private void handleOnProvisioningSuccess(@NonNull final LinkProperties linkProperties) {
500             if (mNetworkAgent != null) {
501                 Log.e(TAG, "Already have a NetworkAgent - aborting new request");
502                 stop();
503                 return;
504             }
505             mLinkProperties = linkProperties;
506 
507             // Create our NetworkAgent.
508             final NetworkAgentConfig config = new NetworkAgentConfig.Builder()
509                     .setLegacyType(mLegacyType)
510                     .setLegacyTypeName(NETWORK_TYPE)
511                     .setLegacyExtraInfo(mHwAddress)
512                     .build();
513             mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(),
514                     mCapabilities, mLinkProperties, config, mNetworkProvider,
515                     new EthernetNetworkAgent.Callbacks() {
516                         @Override
517                         public void onNetworkUnwanted() {
518                             // if mNetworkAgent is null, we have already called stop.
519                             if (mNetworkAgent == null) return;
520 
521                             if (this == mNetworkAgent.getCallbacks()) {
522                                 stop();
523                             } else {
524                                 Log.d(TAG, "Ignoring unwanted as we have a more modern " +
525                                         "instance");
526                             }
527                         }
528                     });
529             mNetworkAgent.register();
530             mNetworkAgent.markConnected();
531         }
532 
handleOnProvisioningFailure()533         private void handleOnProvisioningFailure() {
534             // There is no point in continuing if the interface is gone as stop() will be triggered
535             // by removeInterface() when processed on the handler thread and start() won't
536             // work for a non-existent interface.
537             if (null == mDeps.getNetworkInterfaceByName(name)) {
538                 if (DBG) Log.d(TAG, name + " is no longer available.");
539                 // Send a callback in case a provisioning request was in progress.
540                 return;
541             }
542             maybeRestart();
543         }
544 
handleOnLinkPropertiesChange(LinkProperties linkProperties)545         private void handleOnLinkPropertiesChange(LinkProperties linkProperties) {
546             mLinkProperties = linkProperties;
547             if (mNetworkAgent != null) {
548                 mNetworkAgent.sendLinkPropertiesImpl(linkProperties);
549             }
550         }
551 
handleOnReachabilityLost(String logMsg)552         private void handleOnReachabilityLost(String logMsg) {
553             Log.i(TAG, "handleOnReachabilityLost " + logMsg);
554             if (mIpConfig.getIpAssignment() == IpAssignment.STATIC) {
555                 // Ignore NUD failures for static IP configurations, where restarting the IpClient
556                 // will not fix connectivity.
557                 // In this scenario, NetworkMonitor will not verify the network, so it will
558                 // eventually be torn down.
559                 return;
560             }
561             // Reachability lost will be seen only if the gateway is not reachable.
562             // Since ethernet FW doesn't have the mechanism to scan for new networks
563             // like WiFi, simply restart.
564             // If there is a better network, that will become default and apps
565             // will be able to use internet. If ethernet gets connected again,
566             // and has backhaul connectivity, it will become default.
567             maybeRestart();
568         }
569 
570         /** Returns true if state has been modified */
updateLinkState(final boolean up)571         boolean updateLinkState(final boolean up) {
572             if (mLinkUp == up)  {
573                 return false;
574             }
575             mLinkUp = up;
576 
577             if (!up) { // was up, goes down
578                 // retract network offer and stop IpClient.
579                 unregisterNetworkOfferAndStop();
580             } else { // was down, goes up
581                 // register network offer
582                 registerNetworkOffer();
583             }
584 
585             return true;
586         }
587 
stop()588         private void stop() {
589             // Unregister NetworkAgent before stopping IpClient, so destroyNativeNetwork (which
590             // deletes routes) hopefully happens before stop() finishes execution. Otherwise, it may
591             // delete the new routes when IpClient gets restarted.
592             if (mNetworkAgent != null) {
593                 mNetworkAgent.unregister();
594                 mNetworkAgent = null;
595             }
596 
597             // Invalidate all previous start requests
598             if (mIpClient != null) {
599                 mIpClient.shutdown();
600                 mIpClientCallback.awaitIpClientShutdown();
601                 mIpClient = null;
602             }
603 
604             mIpClientCallback = null;
605 
606             mLinkProperties.clear();
607         }
608 
registerNetworkOffer()609         private void registerNetworkOffer() {
610             // If mNetworkOfferCallback is already set, it should be reused to update the existing
611             // offer.
612             if (mNetworkOfferCallback == null) {
613                 mNetworkOfferCallback = new EthernetNetworkOfferCallback();
614             }
615             mNetworkProvider.registerNetworkOffer(getNetworkScore(),
616                     new NetworkCapabilities(mCapabilities), cmd -> mHandler.post(cmd),
617                     mNetworkOfferCallback);
618         }
619 
unregisterNetworkOfferAndStop()620         private void unregisterNetworkOfferAndStop() {
621             mNetworkProvider.unregisterNetworkOffer(mNetworkOfferCallback);
622             // Setting mNetworkOfferCallback to null allows the callback object to be identified
623             // as stale.
624             mNetworkOfferCallback = null;
625             stop();
626             mRequestIds.clear();
627         }
628 
createProvisioningConfiguration( @onNull final IpConfiguration config)629         private static ProvisioningConfiguration createProvisioningConfiguration(
630                 @NonNull final IpConfiguration config) {
631             if (config.getIpAssignment() == IpAssignment.STATIC) {
632                 return new ProvisioningConfiguration.Builder()
633                         .withStaticConfiguration(config.getStaticIpConfiguration())
634                         .build();
635             }
636             return new ProvisioningConfiguration.Builder()
637                         .withProvisioningTimeoutMs(0)
638                         .build();
639         }
640 
maybeRestart()641         void maybeRestart() {
642             if (mIpClient == null) {
643                 // If maybeRestart() is called from a provisioning failure, it is
644                 // possible that link disappeared in the meantime. In that
645                 // case, stop() has already been called and IpClient should not
646                 // get restarted to prevent a provisioning failure loop.
647                 Log.i(TAG, String.format("maybeRestart() called on stopped interface %s", name));
648                 return;
649             }
650             if (DBG) Log.d(TAG, "restart IpClient");
651             stop();
652             start();
653         }
654 
655         @Override
toString()656         public String toString() {
657             return getClass().getSimpleName() + "{ "
658                     + "iface: " + name + ", "
659                     + "up: " + mLinkUp + ", "
660                     + "hwAddress: " + mHwAddress + ", "
661                     + "networkCapabilities: " + mCapabilities + ", "
662                     + "networkAgent: " + mNetworkAgent + ", "
663                     + "ipClient: " + mIpClient + ","
664                     + "linkProperties: " + mLinkProperties
665                     + "}";
666         }
667     }
668 
dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args)669     void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
670         pw.println(getClass().getSimpleName());
671         pw.println("Tracking interfaces:");
672         pw.increaseIndent();
673         for (String iface: mTrackingInterfaces.keySet()) {
674             NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface);
675             pw.println(iface + ":" + ifaceState);
676             pw.increaseIndent();
677             if (null == ifaceState.mIpClient) {
678                 pw.println("IpClient is null");
679             }
680             pw.decreaseIndent();
681         }
682         pw.decreaseIndent();
683     }
684 }
685