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