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