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