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