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