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