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