EthernetNetworkFactory.java revision 20c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0
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.ConnectivityServiceProtocol.NetworkFactoryProtocol; 22import android.net.DhcpResults; 23import android.net.InterfaceConfiguration; 24import android.net.NetworkUtils; 25import android.net.IpConfiguration; 26import android.net.IpConfiguration.IpAssignment; 27import android.net.IpConfiguration.ProxySettings; 28import android.net.LinkAddress; 29import android.net.LinkProperties; 30import android.net.NetworkAgent; 31import android.net.NetworkCapabilities; 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 54class NetworkFactory extends Handler { 55 public interface Callback { 56 public void onRequestNetwork(NetworkRequest request, int currentScore); 57 public void onCancelRequest(NetworkRequest request); 58 } 59 60 private String mName; 61 private Callback mCallback; 62 private ConnectivityManager mCM; 63 64 NetworkFactory(String name, Context context, Looper looper, Callback callback) { 65 super(looper); 66 mCallback = callback; 67 mName = name; 68 mCM = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 69 } 70 71 public void register() { 72 logi("Registering network factory"); 73 mCM.registerNetworkFactory(new Messenger(this), mName); 74 } 75 76 @Override 77 public void handleMessage(Message message) { 78 switch(message.what) { 79 case NetworkFactoryProtocol.CMD_REQUEST_NETWORK: 80 mCallback.onRequestNetwork((NetworkRequest) message.obj, message.arg1); 81 break; 82 case NetworkFactoryProtocol.CMD_CANCEL_REQUEST: 83 mCallback.onCancelRequest((NetworkRequest) message.obj); 84 break; 85 default: 86 loge("Unhandled message " + message.what); 87 } 88 } 89 90 private void logi(String s) { 91 Log.i("NetworkFactory" + mName, s); 92 } 93 94 private void loge(String s) { 95 Log.e("NetworkFactory" + mName, s); 96 } 97} 98 99/** 100 * This class tracks the data connection associated with Ethernet. 101 * @hide 102 */ 103class EthernetNetworkFactory implements NetworkFactory.Callback { 104 private static final String NETWORK_TYPE = "ETHERNET"; 105 private static final String TAG = "EthernetNetworkFactory"; 106 private static final int NETWORK_SCORE = 70; 107 private static final boolean DBG = true; 108 109 /** Tracks interface changes. Called from the NetworkManagementService thread. */ 110 private InterfaceObserver mInterfaceObserver; 111 112 /** For static IP configuration */ 113 private EthernetManager mEthernetManager; 114 115 /** To set link state and configure IP addresses. */ 116 private INetworkManagementService mNMService; 117 118 /* To communicate with ConnectivityManager */ 119 private NetworkCapabilities mNetworkCapabilities; 120 private NetworkAgent mNetworkAgent; 121 private NetworkFactory mFactory; 122 123 /** Product-dependent regular expression of interface names we want to track. */ 124 private static String mIfaceMatch = ""; 125 126 /** Data members. All accesses must be synchronized(this). */ 127 private static String mIface = ""; 128 private String mHwAddr; 129 private static boolean mLinkUp; 130 private NetworkInfo mNetworkInfo; 131 private LinkProperties mLinkProperties; 132 133 EthernetNetworkFactory() { 134 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 135 mLinkProperties = new LinkProperties(); 136 initNetworkCapabilities(); 137 } 138 139 private void updateInterfaceState(String iface, boolean up) { 140 if (!mIface.equals(iface)) { 141 // We only support one interface. 142 return; 143 } 144 Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); 145 146 synchronized(this) { 147 mLinkUp = up; 148 mNetworkInfo.setIsAvailable(up); 149 DetailedState state = up ? DetailedState.CONNECTED: DetailedState.DISCONNECTED; 150 mNetworkInfo.setDetailedState(state, null, mHwAddr); 151 mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); 152 updateAgent(); 153 } 154 } 155 156 private class InterfaceObserver extends BaseNetworkObserver { 157 @Override 158 public void interfaceLinkStateChanged(String iface, boolean up) { 159 updateInterfaceState(iface, up); 160 } 161 162 @Override 163 public void interfaceAdded(String iface) { 164 maybeTrackInterface(iface); 165 } 166 167 @Override 168 public void interfaceRemoved(String iface) { 169 stopTrackingInterface(iface); 170 } 171 } 172 173 private void setInterfaceUp(String iface) { 174 // Bring up the interface so we get link status indications. 175 try { 176 mNMService.setInterfaceUp(iface); 177 String hwAddr = null; 178 InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); 179 180 if (config == null) { 181 Log.e(TAG, "Null iterface config for " + iface + ". Bailing out."); 182 return; 183 } 184 185 synchronized (this) { 186 if (mIface.isEmpty()) { 187 mIface = iface; 188 mHwAddr = config.getHardwareAddress(); 189 mNetworkInfo.setIsAvailable(true); 190 mNetworkInfo.setExtraInfo(mHwAddr); 191 } else { 192 Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface); 193 mNMService.setInterfaceDown(iface); 194 } 195 } 196 } catch (RemoteException e) { 197 Log.e(TAG, "Error upping interface " + mIface + ": " + e); 198 } 199 } 200 201 private boolean maybeTrackInterface(String iface) { 202 // If we don't already have an interface, and if this interface matches 203 // our regex, start tracking it. 204 if (!iface.matches(mIfaceMatch) || !mIface.isEmpty()) 205 return false; 206 207 Log.d(TAG, "Started tracking interface " + iface); 208 setInterfaceUp(iface); 209 return true; 210 } 211 212 private void stopTrackingInterface(String iface) { 213 if (!iface.equals(mIface)) 214 return; 215 216 Log.d(TAG, "Stopped tracking interface " + iface); 217 disconnect(); 218 synchronized (this) { 219 mIface = ""; 220 mHwAddr = null; 221 mNetworkInfo.setExtraInfo(null); 222 } 223 } 224 225 private void setStaticIpAddress(LinkProperties linkProperties) { 226 Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + mLinkProperties); 227 try { 228 InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface); 229 for (LinkAddress address: linkProperties.getLinkAddresses()) { 230 // IPv6 uses autoconfiguration. 231 if (address.getAddress() instanceof Inet4Address) { 232 config.setLinkAddress(address); 233 // This API only supports one IPv4 address. 234 mNMService.setInterfaceConfig(mIface, config); 235 break; 236 } 237 } 238 } catch(RemoteException e) { 239 Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); 240 } catch(IllegalStateException e) { 241 Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); 242 } 243 } 244 245 public void updateAgent() { 246 if (DBG) { 247 Log.i(TAG, "Updating mNetworkAgent with: " + 248 mNetworkCapabilities + ", " + 249 mNetworkInfo + ", " + 250 mLinkProperties); 251 } 252 mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); 253 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 254 mNetworkAgent.sendLinkProperties(mLinkProperties); 255 } 256 257 /* Called by the NetworkAgent on the handler thread. */ 258 public void connect() { 259 // TODO: Handle DHCP renew. 260 Thread dhcpThread = new Thread(new Runnable() { 261 public void run() { 262 if (DBG) Log.i(TAG, "dhcpThread: mNetworkInfo=" + mNetworkInfo); 263 mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); 264 LinkProperties linkProperties; 265 266 IpConfiguration config = mEthernetManager.getConfiguration(); 267 268 if (config.ipAssignment == IpAssignment.STATIC) { 269 linkProperties = config.linkProperties; 270 linkProperties.setInterfaceName(mIface); 271 setStaticIpAddress(linkProperties); 272 } else { 273 DhcpResults dhcpResults = new DhcpResults(); 274 // TODO: support more than one DHCP client. 275 if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { 276 Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); 277 mNetworkAgent.sendNetworkScore(0); 278 return; 279 } 280 linkProperties = dhcpResults.linkProperties; 281 } 282 if (config.proxySettings == ProxySettings.STATIC) { 283 linkProperties.setHttpProxy(config.linkProperties.getHttpProxy()); 284 } 285 286 synchronized(EthernetNetworkFactory.this) { 287 mLinkProperties = linkProperties; 288 mNetworkInfo.setIsAvailable(true); 289 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); 290 updateAgent(); 291 } 292 } 293 }); 294 dhcpThread.start(); 295 } 296 297 public void disconnect() { 298 NetworkUtils.stopDhcp(mIface); 299 300 synchronized(this) { 301 mLinkProperties.clear(); 302 mNetworkInfo.setIsAvailable(false); 303 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); 304 updateAgent(); 305 } 306 307 try { 308 mNMService.clearInterfaceAddresses(mIface); 309 } catch (Exception e) { 310 Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); 311 } 312 } 313 314 /** 315 * Begin monitoring connectivity 316 */ 317 public synchronized void start(Context context, Handler target) { 318 // The services we use. 319 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 320 mNMService = INetworkManagementService.Stub.asInterface(b); 321 mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); 322 323 // Interface match regex. 324 mIfaceMatch = context.getResources().getString( 325 com.android.internal.R.string.config_ethernet_iface_regex); 326 327 // Create our NetworkAgent. 328 mNetworkAgent = new NetworkAgent(target.getLooper(), context, NETWORK_TYPE) { 329 public synchronized void sendNetworkScore(int score) { 330 Log.i(TAG, "sendNetworkScore(" + score + ")"); 331 super.sendNetworkScore(score); 332 } 333 public void connect() { 334 EthernetNetworkFactory.this.connect(); 335 }; 336 public void disconnect() { 337 EthernetNetworkFactory.this.disconnect(); 338 }; 339 }; 340 mNetworkAgent.sendNetworkScore(0); 341 342 // Create and register our NetworkFactory. 343 mFactory = new NetworkFactory(NETWORK_TYPE, context, target.getLooper(), this); 344 mFactory.register(); 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 if (maybeTrackInterface(iface)) { 360 break; 361 } 362 } 363 } catch (RemoteException e) { 364 Log.e(TAG, "Could not get list of interfaces " + e); 365 } 366 } 367 368 public synchronized void stop() { 369 disconnect(); 370 mIface = ""; 371 mHwAddr = null; 372 mLinkUp = false; 373 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 374 mLinkProperties = new LinkProperties(); 375 } 376 377 public void onRequestNetwork(NetworkRequest request, int currentScore) { 378 Log.i(TAG, "onRequestNetwork: (" + currentScore + "): " + request); 379 // TODO check that the transport is compatible. 380 mNetworkAgent.addNetworkRequest(request, currentScore); 381 } 382 383 public void onCancelRequest(NetworkRequest request) { 384 Log.i(TAG, "onCancelRequest: " + request); 385 mNetworkAgent.removeNetworkRequest(request); 386 } 387 388 private void initNetworkCapabilities() { 389 mNetworkCapabilities = new NetworkCapabilities(); 390 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); 391 mNetworkCapabilities.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 392 mNetworkCapabilities.addNetworkCapability( 393 NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 394 // We have no useful data on bandwidth. Say 100M up and 100M down. :-( 395 mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000); 396 mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000); 397 } 398} 399