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.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.DhcpResults; 22 import android.net.InterfaceConfiguration; 23 import android.net.NetworkUtils; 24 import android.net.IpConfiguration; 25 import android.net.IpConfiguration.IpAssignment; 26 import android.net.IpConfiguration.ProxySettings; 27 import android.net.LinkAddress; 28 import android.net.LinkProperties; 29 import android.net.NetworkAgent; 30 import android.net.NetworkCapabilities; 31 import android.net.NetworkFactory; 32 import android.net.NetworkInfo; 33 import android.net.NetworkInfo.DetailedState; 34 import android.net.NetworkRequest; 35 import android.net.EthernetManager; 36 import android.net.StaticIpConfiguration; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.INetworkManagementService; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.os.Messenger; 43 import android.os.RemoteException; 44 import android.os.ServiceManager; 45 import android.text.TextUtils; 46 import android.util.Log; 47 48 import com.android.internal.util.IndentingPrintWriter; 49 import com.android.server.net.BaseNetworkObserver; 50 51 import java.io.FileDescriptor; 52 import java.io.PrintWriter; 53 import java.net.Inet4Address; 54 import java.util.concurrent.atomic.AtomicBoolean; 55 import java.util.concurrent.atomic.AtomicInteger; 56 57 58 /** 59 * Manages connectivity for an Ethernet interface. 60 * 61 * Ethernet Interfaces may be present at boot time or appear after boot (e.g., 62 * for Ethernet adapters connected over USB). This class currently supports 63 * only one interface. When an interface appears on the system (or is present 64 * at boot time) this class will start tracking it and bring it up, and will 65 * attempt to connect when requested. Any other interfaces that subsequently 66 * appear will be ignored until the tracked interface disappears. Only 67 * interfaces whose names match the <code>config_ethernet_iface_regex</code> 68 * regular expression are tracked. 69 * 70 * This class reports a static network score of 70 when it is tracking an 71 * interface and that interface's link is up, and a score of 0 otherwise. 72 * 73 * @hide 74 */ 75 class EthernetNetworkFactory { 76 private static final String NETWORK_TYPE = "Ethernet"; 77 private static final String TAG = "EthernetNetworkFactory"; 78 private static final int NETWORK_SCORE = 70; 79 private static final boolean DBG = true; 80 81 /** Tracks interface changes. Called from NetworkManagementService. */ 82 private InterfaceObserver mInterfaceObserver; 83 84 /** For static IP configuration */ 85 private EthernetManager mEthernetManager; 86 87 /** To set link state and configure IP addresses. */ 88 private INetworkManagementService mNMService; 89 90 /* To communicate with ConnectivityManager */ 91 private NetworkCapabilities mNetworkCapabilities; 92 private NetworkAgent mNetworkAgent; 93 private LocalNetworkFactory mFactory; 94 private Context mContext; 95 96 /** Product-dependent regular expression of interface names we track. */ 97 private static String mIfaceMatch = ""; 98 99 /** Data members. All accesses to these must be synchronized(this). */ 100 private static String mIface = ""; 101 private String mHwAddr; 102 private static boolean mLinkUp; 103 private NetworkInfo mNetworkInfo; 104 private LinkProperties mLinkProperties; 105 106 EthernetNetworkFactory() { 107 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 108 mLinkProperties = new LinkProperties(); 109 initNetworkCapabilities(); 110 } 111 112 private class LocalNetworkFactory extends NetworkFactory { 113 LocalNetworkFactory(String name, Context context, Looper looper) { 114 super(looper, context, name, new NetworkCapabilities()); 115 } 116 117 protected void startNetwork() { 118 onRequestNetwork(); 119 } 120 protected void stopNetwork() { 121 } 122 } 123 124 125 /** 126 * Updates interface state variables. 127 * Called on link state changes or on startup. 128 */ 129 private void updateInterfaceState(String iface, boolean up) { 130 if (!mIface.equals(iface)) { 131 return; 132 } 133 Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); 134 135 synchronized(this) { 136 mLinkUp = up; 137 mNetworkInfo.setIsAvailable(up); 138 if (!up) { 139 // Tell the agent we're disconnected. It will call disconnect(). 140 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 141 } 142 updateAgent(); 143 // set our score lower than any network could go 144 // so we get dropped. TODO - just unregister the factory 145 // when link goes down. 146 mFactory.setScoreFilter(up ? NETWORK_SCORE : -1); 147 } 148 } 149 150 private class InterfaceObserver extends BaseNetworkObserver { 151 @Override 152 public void interfaceLinkStateChanged(String iface, boolean up) { 153 updateInterfaceState(iface, up); 154 } 155 156 @Override 157 public void interfaceAdded(String iface) { 158 maybeTrackInterface(iface); 159 } 160 161 @Override 162 public void interfaceRemoved(String iface) { 163 stopTrackingInterface(iface); 164 } 165 } 166 167 private void setInterfaceUp(String iface) { 168 // Bring up the interface so we get link status indications. 169 try { 170 mNMService.setInterfaceUp(iface); 171 String hwAddr = null; 172 InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); 173 174 if (config == null) { 175 Log.e(TAG, "Null iterface config for " + iface + ". Bailing out."); 176 return; 177 } 178 179 synchronized (this) { 180 if (mIface.isEmpty()) { 181 mIface = iface; 182 mHwAddr = config.getHardwareAddress(); 183 mNetworkInfo.setIsAvailable(true); 184 mNetworkInfo.setExtraInfo(mHwAddr); 185 } else { 186 Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface); 187 mNMService.setInterfaceDown(iface); 188 } 189 } 190 } catch (RemoteException e) { 191 Log.e(TAG, "Error upping interface " + mIface + ": " + e); 192 } 193 } 194 195 private boolean maybeTrackInterface(String iface) { 196 // If we don't already have an interface, and if this interface matches 197 // our regex, start tracking it. 198 if (!iface.matches(mIfaceMatch) || !mIface.isEmpty()) 199 return false; 200 201 Log.d(TAG, "Started tracking interface " + iface); 202 setInterfaceUp(iface); 203 return true; 204 } 205 206 private void stopTrackingInterface(String iface) { 207 if (!iface.equals(mIface)) 208 return; 209 210 Log.d(TAG, "Stopped tracking interface " + iface); 211 // TODO: Unify this codepath with stop(). 212 synchronized (this) { 213 NetworkUtils.stopDhcp(mIface); 214 mIface = ""; 215 mHwAddr = null; 216 mNetworkInfo.setExtraInfo(null); 217 mLinkUp = false; 218 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 219 updateAgent(); 220 mNetworkAgent = null; 221 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 222 mLinkProperties = new LinkProperties(); 223 } 224 } 225 226 private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) { 227 if (staticConfig.ipAddress != null && 228 staticConfig.gateway != null && 229 staticConfig.dnsServers.size() > 0) { 230 try { 231 Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + staticConfig); 232 InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface); 233 config.setLinkAddress(staticConfig.ipAddress); 234 mNMService.setInterfaceConfig(mIface, config); 235 return true; 236 } catch(RemoteException|IllegalStateException e) { 237 Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); 238 } 239 } else { 240 Log.e(TAG, "Invalid static IP configuration."); 241 } 242 return false; 243 } 244 245 public void updateAgent() { 246 synchronized (EthernetNetworkFactory.this) { 247 if (mNetworkAgent == null) return; 248 if (DBG) { 249 Log.i(TAG, "Updating mNetworkAgent with: " + 250 mNetworkCapabilities + ", " + 251 mNetworkInfo + ", " + 252 mLinkProperties); 253 } 254 mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); 255 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 256 mNetworkAgent.sendLinkProperties(mLinkProperties); 257 // never set the network score below 0. 258 mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); 259 } 260 } 261 262 /* Called by the NetworkFactory on the handler thread. */ 263 public void onRequestNetwork() { 264 // TODO: Handle DHCP renew. 265 Thread dhcpThread = new Thread(new Runnable() { 266 public void run() { 267 if (DBG) Log.i(TAG, "dhcpThread(+" + mIface + "): mNetworkInfo=" + mNetworkInfo); 268 LinkProperties linkProperties; 269 270 IpConfiguration config = mEthernetManager.getConfiguration(); 271 272 if (config.getIpAssignment() == IpAssignment.STATIC) { 273 if (!setStaticIpAddress(config.getStaticIpConfiguration())) { 274 // We've already logged an error. 275 return; 276 } 277 linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface); 278 } else { 279 mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); 280 281 DhcpResults dhcpResults = new DhcpResults(); 282 // TODO: Handle DHCP renewals better. 283 // In general runDhcp handles DHCP renewals for us, because 284 // the dhcp client stays running, but if the renewal fails, 285 // we will lose our IP address and connectivity without 286 // noticing. 287 if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { 288 Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); 289 // set our score lower than any network could go 290 // so we get dropped. 291 mFactory.setScoreFilter(-1); 292 return; 293 } 294 linkProperties = dhcpResults.toLinkProperties(mIface); 295 } 296 if (config.getProxySettings() == ProxySettings.STATIC || 297 config.getProxySettings() == ProxySettings.PAC) { 298 linkProperties.setHttpProxy(config.getHttpProxy()); 299 } 300 301 String tcpBufferSizes = mContext.getResources().getString( 302 com.android.internal.R.string.config_ethernet_tcp_buffers); 303 if (TextUtils.isEmpty(tcpBufferSizes) == false) { 304 linkProperties.setTcpBufferSizes(tcpBufferSizes); 305 } 306 307 synchronized(EthernetNetworkFactory.this) { 308 if (mNetworkAgent != null) { 309 Log.e(TAG, "Already have a NetworkAgent - aborting new request"); 310 return; 311 } 312 mLinkProperties = linkProperties; 313 mNetworkInfo.setIsAvailable(true); 314 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); 315 316 // Create our NetworkAgent. 317 mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext, 318 NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties, 319 NETWORK_SCORE) { 320 public void unwanted() { 321 synchronized(EthernetNetworkFactory.this) { 322 if (this == mNetworkAgent) { 323 NetworkUtils.stopDhcp(mIface); 324 325 mLinkProperties.clear(); 326 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, 327 mHwAddr); 328 updateAgent(); 329 mNetworkAgent = null; 330 try { 331 mNMService.clearInterfaceAddresses(mIface); 332 } catch (Exception e) { 333 Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); 334 } 335 } else { 336 Log.d(TAG, "Ignoring unwanted as we have a more modern " + 337 "instance"); 338 } 339 } 340 }; 341 }; 342 } 343 } 344 }); 345 dhcpThread.start(); 346 } 347 348 /** 349 * Begin monitoring connectivity 350 */ 351 public synchronized void start(Context context, Handler target) { 352 // The services we use. 353 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 354 mNMService = INetworkManagementService.Stub.asInterface(b); 355 mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); 356 357 // Interface match regex. 358 mIfaceMatch = context.getResources().getString( 359 com.android.internal.R.string.config_ethernet_iface_regex); 360 361 // Create and register our NetworkFactory. 362 mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, target.getLooper()); 363 mFactory.setCapabilityFilter(mNetworkCapabilities); 364 mFactory.setScoreFilter(-1); // this set high when we have an iface 365 mFactory.register(); 366 367 mContext = context; 368 369 // Start tracking interface change events. 370 mInterfaceObserver = new InterfaceObserver(); 371 try { 372 mNMService.registerObserver(mInterfaceObserver); 373 } catch (RemoteException e) { 374 Log.e(TAG, "Could not register InterfaceObserver " + e); 375 } 376 377 // If an Ethernet interface is already connected, start tracking that. 378 // Otherwise, the first Ethernet interface to appear will be tracked. 379 try { 380 final String[] ifaces = mNMService.listInterfaces(); 381 for (String iface : ifaces) { 382 synchronized(this) { 383 if (maybeTrackInterface(iface)) { 384 // We have our interface. Track it. 385 // Note: if the interface already has link (e.g., if we 386 // crashed and got restarted while it was running), 387 // we need to fake a link up notification so we start 388 // configuring it. Since we're already holding the lock, 389 // any real link up/down notification will only arrive 390 // after we've done this. 391 if (mNMService.getInterfaceConfig(iface).hasFlag("running")) { 392 updateInterfaceState(iface, true); 393 } 394 break; 395 } 396 } 397 } 398 } catch (RemoteException e) { 399 Log.e(TAG, "Could not get list of interfaces " + e); 400 } 401 } 402 403 public synchronized void stop() { 404 NetworkUtils.stopDhcp(mIface); 405 // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object 406 // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here: 407 // that sets the state to IDLE, and ConnectivityService will still think we're connected. 408 // 409 // TODO: stop using explicit comparisons to DISCONNECTED / SUSPENDED in ConnectivityService, 410 // and instead use isConnectedOrConnecting(). 411 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 412 mLinkUp = false; 413 updateAgent(); 414 mLinkProperties = new LinkProperties(); 415 mNetworkAgent = null; 416 mIface = ""; 417 mHwAddr = null; 418 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 419 mFactory.unregister(); 420 } 421 422 private void initNetworkCapabilities() { 423 mNetworkCapabilities = new NetworkCapabilities(); 424 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); 425 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 426 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 427 // We have no useful data on bandwidth. Say 100M up and 100M down. :-( 428 mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000); 429 mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000); 430 } 431 432 synchronized void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { 433 if (!TextUtils.isEmpty(mIface)) { 434 pw.println("Tracking interface: " + mIface); 435 pw.increaseIndent(); 436 pw.println("MAC address: " + mHwAddr); 437 pw.println("Link state: " + (mLinkUp ? "up" : "down")); 438 pw.decreaseIndent(); 439 } else { 440 pw.println("Not tracking any interface"); 441 } 442 443 pw.println(); 444 pw.println("NetworkInfo: " + mNetworkInfo); 445 pw.println("LinkProperties: " + mLinkProperties); 446 pw.println("NetworkAgent: " + mNetworkAgent); 447 } 448 } 449