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 17package com.android.server.ethernet; 18 19import android.content.Context; 20import android.net.ConnectivityManager; 21import android.net.DhcpResults; 22import android.net.EthernetManager; 23import android.net.IEthernetServiceListener; 24import android.net.InterfaceConfiguration; 25import android.net.IpConfiguration; 26import android.net.IpConfiguration.IpAssignment; 27import android.net.IpConfiguration.ProxySettings; 28import android.net.LinkProperties; 29import android.net.NetworkAgent; 30import android.net.NetworkCapabilities; 31import android.net.NetworkFactory; 32import android.net.NetworkInfo; 33import android.net.NetworkInfo.DetailedState; 34import android.net.StaticIpConfiguration; 35import android.net.ip.IpManager; 36import android.net.ip.IpManager.ProvisioningConfiguration; 37import android.os.Handler; 38import android.os.IBinder; 39import android.os.INetworkManagementService; 40import android.os.Looper; 41import android.os.RemoteCallbackList; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.text.TextUtils; 45import android.util.Log; 46 47import com.android.internal.util.IndentingPrintWriter; 48import com.android.server.net.BaseNetworkObserver; 49 50import java.io.FileDescriptor; 51import java.io.PrintWriter; 52import java.util.Arrays; 53import java.util.concurrent.CountDownLatch; 54 55 56/** 57 * Manages connectivity for an Ethernet interface. 58 * 59 * Ethernet Interfaces may be present at boot time or appear after boot (e.g., 60 * for Ethernet adapters connected over USB). This class currently supports 61 * only one interface. When an interface appears on the system (or is present 62 * at boot time) this class will start tracking it and bring it up, and will 63 * attempt to connect when requested. Any other interfaces that subsequently 64 * appear will be ignored until the tracked interface disappears. Only 65 * interfaces whose names match the <code>config_ethernet_iface_regex</code> 66 * regular expression are tracked. 67 * 68 * This class reports a static network score of 70 when it is tracking an 69 * interface and that interface's link is up, and a score of 0 otherwise. 70 * 71 * @hide 72 */ 73class EthernetNetworkFactory { 74 private static final String NETWORK_TYPE = "Ethernet"; 75 private static final String TAG = "EthernetNetworkFactory"; 76 private static final int NETWORK_SCORE = 70; 77 private static final boolean DBG = true; 78 79 /** Tracks interface changes. Called from NetworkManagementService. */ 80 private InterfaceObserver mInterfaceObserver; 81 82 /** For static IP configuration */ 83 private EthernetManager mEthernetManager; 84 85 /** To set link state and configure IP addresses. */ 86 private INetworkManagementService mNMService; 87 88 /** All code runs here, including start(). */ 89 private Handler mHandler; 90 91 /* To communicate with ConnectivityManager */ 92 private NetworkCapabilities mNetworkCapabilities; 93 private NetworkAgent mNetworkAgent; 94 private LocalNetworkFactory mFactory; 95 private Context mContext; 96 97 /** Product-dependent regular expression of interface names we track. */ 98 private static String mIfaceMatch = ""; 99 100 /** To notify Ethernet status. */ 101 private final RemoteCallbackList<IEthernetServiceListener> mListeners; 102 103 /** Data members. All accesses to these must be on the handler thread. */ 104 private String mIface = ""; 105 private String mHwAddr; 106 private boolean mLinkUp; 107 private NetworkInfo mNetworkInfo; 108 private LinkProperties mLinkProperties; 109 private IpManager mIpManager; 110 private boolean mNetworkRequested = false; 111 112 EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) { 113 initNetworkCapabilities(); 114 clearInfo(); 115 mListeners = listeners; 116 } 117 118 private class LocalNetworkFactory extends NetworkFactory { 119 LocalNetworkFactory(String name, Context context, Looper looper) { 120 super(looper, context, name, new NetworkCapabilities()); 121 } 122 123 protected void startNetwork() { 124 if (!mNetworkRequested) { 125 mNetworkRequested = true; 126 maybeStartIpManager(); 127 } 128 } 129 130 protected void stopNetwork() { 131 mNetworkRequested = false; 132 stopIpManager(); 133 } 134 } 135 136 private void clearInfo() { 137 mLinkProperties = new LinkProperties(); 138 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 139 mNetworkInfo.setExtraInfo(mHwAddr); 140 mNetworkInfo.setIsAvailable(isTrackingInterface()); 141 } 142 143 private void stopIpManager() { 144 if (mIpManager != null) { 145 mIpManager.shutdown(); 146 mIpManager = null; 147 } 148 // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object 149 // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here: 150 // that sets the state to IDLE, and ConnectivityService will still think we're connected. 151 // 152 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 153 if (mNetworkAgent != null) { 154 updateAgent(); 155 mNetworkAgent = null; 156 } 157 clearInfo(); 158 } 159 160 /** 161 * Updates interface state variables. 162 * Called on link state changes or on startup. 163 */ 164 private void updateInterfaceState(String iface, boolean up) { 165 if (!mIface.equals(iface)) { 166 return; 167 } 168 Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); 169 170 mLinkUp = up; 171 if (up) { 172 maybeStartIpManager(); 173 } else { 174 stopIpManager(); 175 } 176 } 177 178 private class InterfaceObserver extends BaseNetworkObserver { 179 @Override 180 public void interfaceLinkStateChanged(String iface, boolean up) { 181 mHandler.post(() -> { 182 updateInterfaceState(iface, up); 183 }); 184 } 185 186 @Override 187 public void interfaceAdded(String iface) { 188 mHandler.post(() -> { 189 maybeTrackInterface(iface); 190 }); 191 } 192 193 @Override 194 public void interfaceRemoved(String iface) { 195 mHandler.post(() -> { 196 if (stopTrackingInterface(iface)) { 197 trackFirstAvailableInterface(); 198 } 199 }); 200 } 201 } 202 203 private void setInterfaceUp(String iface) { 204 // Bring up the interface so we get link status indications. 205 try { 206 mNMService.setInterfaceUp(iface); 207 String hwAddr = null; 208 InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); 209 210 if (config == null) { 211 Log.e(TAG, "Null interface config for " + iface + ". Bailing out."); 212 return; 213 } 214 215 if (!isTrackingInterface()) { 216 setInterfaceInfo(iface, config.getHardwareAddress()); 217 mNetworkInfo.setIsAvailable(true); 218 mNetworkInfo.setExtraInfo(mHwAddr); 219 } else { 220 Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface); 221 mNMService.setInterfaceDown(iface); 222 } 223 } catch (RemoteException | IllegalStateException e) { 224 // Either the system is crashing or the interface has disappeared. Just ignore the 225 // error; we haven't modified any state because we only do that if our calls succeed. 226 Log.e(TAG, "Error upping interface " + mIface + ": " + e); 227 } 228 } 229 230 private boolean maybeTrackInterface(String iface) { 231 // If we don't already have an interface, and if this interface matches 232 // our regex, start tracking it. 233 if (!iface.matches(mIfaceMatch) || isTrackingInterface()) 234 return false; 235 236 Log.d(TAG, "Started tracking interface " + iface); 237 setInterfaceUp(iface); 238 return true; 239 } 240 241 private boolean stopTrackingInterface(String iface) { 242 if (!iface.equals(mIface)) 243 return false; 244 245 Log.d(TAG, "Stopped tracking interface " + iface); 246 setInterfaceInfo("", null); 247 stopIpManager(); 248 return true; 249 } 250 251 private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) { 252 if (staticConfig.ipAddress != null && 253 staticConfig.gateway != null && 254 staticConfig.dnsServers.size() > 0) { 255 try { 256 Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + staticConfig); 257 InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface); 258 config.setLinkAddress(staticConfig.ipAddress); 259 mNMService.setInterfaceConfig(mIface, config); 260 return true; 261 } catch(RemoteException|IllegalStateException e) { 262 Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); 263 } 264 } else { 265 Log.e(TAG, "Invalid static IP configuration."); 266 } 267 return false; 268 } 269 270 public void updateAgent() { 271 if (mNetworkAgent == null) return; 272 if (DBG) { 273 Log.i(TAG, "Updating mNetworkAgent with: " + 274 mNetworkCapabilities + ", " + 275 mNetworkInfo + ", " + 276 mLinkProperties); 277 } 278 mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); 279 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 280 mNetworkAgent.sendLinkProperties(mLinkProperties); 281 // never set the network score below 0. 282 mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); 283 } 284 285 void onIpLayerStarted(LinkProperties linkProperties) { 286 if (mNetworkAgent != null) { 287 Log.e(TAG, "Already have a NetworkAgent - aborting new request"); 288 stopIpManager(); 289 return; 290 } 291 mLinkProperties = linkProperties; 292 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); 293 294 // Create our NetworkAgent. 295 mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext, 296 NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties, 297 NETWORK_SCORE) { 298 public void unwanted() { 299 if (this == mNetworkAgent) { 300 stopIpManager(); 301 } else if (mNetworkAgent != null) { 302 Log.d(TAG, "Ignoring unwanted as we have a more modern " + 303 "instance"); 304 } // Otherwise, we've already called stopIpManager. 305 } 306 }; 307 } 308 309 void onIpLayerStopped(LinkProperties linkProperties) { 310 // This cannot happen due to provisioning timeout, because our timeout is 0. It can only 311 // happen if we're provisioned and we lose provisioning. 312 stopIpManager(); 313 maybeStartIpManager(); 314 } 315 316 void updateLinkProperties(LinkProperties linkProperties) { 317 mLinkProperties = linkProperties; 318 if (mNetworkAgent != null) { 319 mNetworkAgent.sendLinkProperties(linkProperties); 320 } 321 } 322 323 public void maybeStartIpManager() { 324 if (mNetworkRequested && mIpManager == null && isTrackingInterface()) { 325 startIpManager(); 326 } 327 } 328 329 public void startIpManager() { 330 if (DBG) { 331 Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface, 332 mNetworkInfo)); 333 } 334 335 LinkProperties linkProperties; 336 337 IpConfiguration config = mEthernetManager.getConfiguration(); 338 339 if (config.getIpAssignment() == IpAssignment.STATIC) { 340 if (!setStaticIpAddress(config.getStaticIpConfiguration())) { 341 // We've already logged an error. 342 return; 343 } 344 linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface); 345 } else { 346 mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); 347 IpManager.Callback ipmCallback = new IpManager.Callback() { 348 @Override 349 public void onProvisioningSuccess(LinkProperties newLp) { 350 mHandler.post(() -> onIpLayerStarted(newLp)); 351 } 352 353 @Override 354 public void onProvisioningFailure(LinkProperties newLp) { 355 mHandler.post(() -> onIpLayerStopped(newLp)); 356 } 357 358 @Override 359 public void onLinkPropertiesChange(LinkProperties newLp) { 360 mHandler.post(() -> updateLinkProperties(newLp)); 361 } 362 }; 363 364 stopIpManager(); 365 mIpManager = new IpManager(mContext, mIface, ipmCallback); 366 367 if (config.getProxySettings() == ProxySettings.STATIC || 368 config.getProxySettings() == ProxySettings.PAC) { 369 mIpManager.setHttpProxy(config.getHttpProxy()); 370 } 371 372 final String tcpBufferSizes = mContext.getResources().getString( 373 com.android.internal.R.string.config_ethernet_tcp_buffers); 374 if (!TextUtils.isEmpty(tcpBufferSizes)) { 375 mIpManager.setTcpBufferSizes(tcpBufferSizes); 376 } 377 378 final ProvisioningConfiguration provisioningConfiguration = 379 mIpManager.buildProvisioningConfiguration() 380 .withProvisioningTimeoutMs(0) 381 .build(); 382 mIpManager.startProvisioning(provisioningConfiguration); 383 } 384 } 385 386 /** 387 * Begin monitoring connectivity 388 */ 389 public void start(Context context, Handler handler) { 390 mHandler = handler; 391 392 // The services we use. 393 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 394 mNMService = INetworkManagementService.Stub.asInterface(b); 395 mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); 396 397 // Interface match regex. 398 mIfaceMatch = context.getResources().getString( 399 com.android.internal.R.string.config_ethernet_iface_regex); 400 401 // Create and register our NetworkFactory. 402 mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, mHandler.getLooper()); 403 mFactory.setCapabilityFilter(mNetworkCapabilities); 404 mFactory.setScoreFilter(NETWORK_SCORE); 405 mFactory.register(); 406 407 mContext = context; 408 409 // Start tracking interface change events. 410 mInterfaceObserver = new InterfaceObserver(); 411 try { 412 mNMService.registerObserver(mInterfaceObserver); 413 } catch (RemoteException e) { 414 Log.e(TAG, "Could not register InterfaceObserver " + e); 415 } 416 417 // If an Ethernet interface is already connected, start tracking that. 418 // Otherwise, the first Ethernet interface to appear will be tracked. 419 mHandler.post(() -> trackFirstAvailableInterface()); 420 } 421 422 public void trackFirstAvailableInterface() { 423 try { 424 final String[] ifaces = mNMService.listInterfaces(); 425 for (String iface : ifaces) { 426 if (maybeTrackInterface(iface)) { 427 // We have our interface. Track it. 428 // Note: if the interface already has link (e.g., if we crashed and got 429 // restarted while it was running), we need to fake a link up notification so we 430 // start configuring it. 431 if (mNMService.getInterfaceConfig(iface).hasFlag("running")) { 432 updateInterfaceState(iface, true); 433 } 434 break; 435 } 436 } 437 } catch (RemoteException|IllegalStateException e) { 438 Log.e(TAG, "Could not get list of interfaces " + e); 439 } 440 } 441 442 public void stop() { 443 stopIpManager(); 444 setInterfaceInfo("", null); 445 mFactory.unregister(); 446 } 447 448 private void initNetworkCapabilities() { 449 mNetworkCapabilities = new NetworkCapabilities(); 450 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); 451 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 452 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 453 // We have no useful data on bandwidth. Say 100M up and 100M down. :-( 454 mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000); 455 mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000); 456 } 457 458 public boolean isTrackingInterface() { 459 return !TextUtils.isEmpty(mIface); 460 } 461 462 /** 463 * Set interface information and notify listeners if availability is changed. 464 */ 465 private void setInterfaceInfo(String iface, String hwAddr) { 466 boolean oldAvailable = isTrackingInterface(); 467 mIface = iface; 468 mHwAddr = hwAddr; 469 boolean available = isTrackingInterface(); 470 471 mNetworkInfo.setExtraInfo(mHwAddr); 472 mNetworkInfo.setIsAvailable(available); 473 474 if (oldAvailable != available) { 475 int n = mListeners.beginBroadcast(); 476 for (int i = 0; i < n; i++) { 477 try { 478 mListeners.getBroadcastItem(i).onAvailabilityChanged(available); 479 } catch (RemoteException e) { 480 // Do nothing here. 481 } 482 } 483 mListeners.finishBroadcast(); 484 } 485 } 486 487 private void postAndWaitForRunnable(Runnable r) throws InterruptedException { 488 CountDownLatch latch = new CountDownLatch(1); 489 mHandler.post(() -> { 490 try { 491 r.run(); 492 } finally { 493 latch.countDown(); 494 } 495 }); 496 latch.await(); 497 } 498 499 500 void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { 501 try { 502 postAndWaitForRunnable(() -> { 503 pw.println("Network Requested: " + mNetworkRequested); 504 if (isTrackingInterface()) { 505 pw.println("Tracking interface: " + mIface); 506 pw.increaseIndent(); 507 pw.println("MAC address: " + mHwAddr); 508 pw.println("Link state: " + (mLinkUp ? "up" : "down")); 509 pw.decreaseIndent(); 510 } else { 511 pw.println("Not tracking any interface"); 512 } 513 514 pw.println(); 515 pw.println("NetworkInfo: " + mNetworkInfo); 516 pw.println("LinkProperties: " + mLinkProperties); 517 pw.println("NetworkAgent: " + mNetworkAgent); 518 if (mIpManager != null) { 519 pw.println("IpManager:"); 520 pw.increaseIndent(); 521 mIpManager.dump(fd, pw, args); 522 pw.decreaseIndent(); 523 } 524 }); 525 } catch (InterruptedException e) { 526 throw new IllegalStateException("dump() interrupted"); 527 } 528 } 529} 530