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 static android.net.ConnectivityManager.TYPE_ETHERNET; 20 21import android.content.Context; 22import android.net.IpConfiguration; 23import android.net.IpConfiguration.IpAssignment; 24import android.net.IpConfiguration.ProxySettings; 25import android.net.LinkProperties; 26import android.net.NetworkAgent; 27import android.net.NetworkCapabilities; 28import android.net.NetworkFactory; 29import android.net.NetworkInfo; 30import android.net.NetworkInfo.DetailedState; 31import android.net.NetworkRequest; 32import android.net.NetworkSpecifier; 33import android.net.StringNetworkSpecifier; 34import android.net.ip.IpClient; 35import android.net.ip.IpClient.ProvisioningConfiguration; 36import android.os.Handler; 37import android.text.TextUtils; 38import android.util.Log; 39 40import com.android.internal.util.IndentingPrintWriter; 41 42import java.io.FileDescriptor; 43import java.util.concurrent.ConcurrentHashMap; 44 45/** 46 * {@link NetworkFactory} that represents Ethernet networks. 47 * 48 * This class reports a static network score of 70 when it is tracking an interface and that 49 * interface's link is up, and a score of 0 otherwise. 50 */ 51public class EthernetNetworkFactory extends NetworkFactory { 52 private final static String TAG = EthernetNetworkFactory.class.getSimpleName(); 53 final static boolean DBG = true; 54 55 private final static int NETWORK_SCORE = 70; 56 private static final String NETWORK_TYPE = "Ethernet"; 57 58 private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces = 59 new ConcurrentHashMap<>(); 60 private final Handler mHandler; 61 private final Context mContext; 62 63 public EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter) { 64 super(handler.getLooper(), context, NETWORK_TYPE, filter); 65 66 mHandler = handler; 67 mContext = context; 68 69 setScoreFilter(NETWORK_SCORE); 70 } 71 72 @Override 73 public boolean acceptRequest(NetworkRequest request, int score) { 74 if (DBG) { 75 Log.d(TAG, "acceptRequest, request: " + request + ", score: " + score); 76 } 77 78 return networkForRequest(request) != null; 79 } 80 81 @Override 82 protected void needNetworkFor(NetworkRequest networkRequest, int score) { 83 NetworkInterfaceState network = networkForRequest(networkRequest); 84 85 if (network == null) { 86 Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); 87 return; 88 } 89 90 if (++network.refCount == 1) { 91 network.start(); 92 } 93 } 94 95 @Override 96 protected void releaseNetworkFor(NetworkRequest networkRequest) { 97 NetworkInterfaceState network = networkForRequest(networkRequest); 98 if (network == null) { 99 Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest); 100 return; 101 } 102 103 if (--network.refCount == 1) { 104 network.stop(); 105 } 106 } 107 108 /** 109 * Returns an array of available interface names. The array is sorted: unrestricted interfaces 110 * goes first, then sorted by name. 111 */ 112 String[] getAvailableInterfaces(boolean includeRestricted) { 113 return mTrackingInterfaces.values() 114 .stream() 115 .filter(iface -> !iface.isRestricted() || includeRestricted) 116 .sorted((iface1, iface2) -> { 117 int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted()); 118 return r == 0 ? iface1.name.compareTo(iface2.name) : r; 119 }) 120 .map(iface -> iface.name) 121 .toArray(String[]::new); 122 } 123 124 void addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities, 125 IpConfiguration ipConfiguration) { 126 if (mTrackingInterfaces.containsKey(ifaceName)) { 127 Log.e(TAG, "Interface with name " + ifaceName + " already exists."); 128 return; 129 } 130 131 if (DBG) { 132 Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + capabilities); 133 } 134 135 NetworkInterfaceState iface = new NetworkInterfaceState( 136 ifaceName, hwAddress, mHandler, mContext, capabilities); 137 iface.setIpConfig(ipConfiguration); 138 mTrackingInterfaces.put(ifaceName, iface); 139 140 updateCapabilityFilter(); 141 } 142 143 private void updateCapabilityFilter() { 144 NetworkCapabilities capabilitiesFilter = new NetworkCapabilities(); 145 capabilitiesFilter.clearAll(); 146 147 for (NetworkInterfaceState iface: mTrackingInterfaces.values()) { 148 capabilitiesFilter.combineCapabilities(iface.mCapabilities); 149 } 150 151 if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter); 152 setCapabilityFilter(capabilitiesFilter); 153 } 154 155 void removeInterface(String interfaceName) { 156 NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); 157 if (iface != null) { 158 iface.stop(); 159 } 160 161 updateCapabilityFilter(); 162 } 163 164 /** Returns true if state has been modified */ 165 boolean updateInterfaceLinkState(String ifaceName, boolean up) { 166 if (!mTrackingInterfaces.containsKey(ifaceName)) { 167 return false; 168 } 169 170 if (DBG) { 171 Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up); 172 } 173 174 NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName); 175 return iface.updateLinkState(up); 176 } 177 178 boolean hasInterface(String interfacName) { 179 return mTrackingInterfaces.containsKey(interfacName); 180 } 181 182 void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) { 183 NetworkInterfaceState network = mTrackingInterfaces.get(iface); 184 if (network != null) { 185 network.setIpConfig(ipConfiguration); 186 } 187 } 188 189 private NetworkInterfaceState networkForRequest(NetworkRequest request) { 190 String requestedIface = null; 191 192 NetworkSpecifier specifier = request.networkCapabilities.getNetworkSpecifier(); 193 if (specifier instanceof StringNetworkSpecifier) { 194 requestedIface = ((StringNetworkSpecifier) specifier).specifier; 195 } 196 197 NetworkInterfaceState network = null; 198 if (!TextUtils.isEmpty(requestedIface)) { 199 NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface); 200 if (n != null && n.statisified(request.networkCapabilities)) { 201 network = n; 202 } 203 } else { 204 for (NetworkInterfaceState n : mTrackingInterfaces.values()) { 205 if (n.statisified(request.networkCapabilities)) { 206 network = n; 207 break; 208 } 209 } 210 } 211 212 if (DBG) { 213 Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network); 214 } 215 216 return network; 217 } 218 219 private static class NetworkInterfaceState { 220 final String name; 221 222 private final String mHwAddress; 223 private final NetworkCapabilities mCapabilities; 224 private final Handler mHandler; 225 private final Context mContext; 226 private final NetworkInfo mNetworkInfo; 227 228 private static String sTcpBufferSizes = null; // Lazy initialized. 229 230 private boolean mLinkUp; 231 private LinkProperties mLinkProperties = new LinkProperties(); 232 233 private IpClient mIpClient; 234 private NetworkAgent mNetworkAgent; 235 private IpConfiguration mIpConfig; 236 237 long refCount = 0; 238 239 private final IpClient.Callback mIpClientCallback = new IpClient.Callback() { 240 @Override 241 public void onProvisioningSuccess(LinkProperties newLp) { 242 mHandler.post(() -> onIpLayerStarted(newLp)); 243 } 244 245 @Override 246 public void onProvisioningFailure(LinkProperties newLp) { 247 mHandler.post(() -> onIpLayerStopped(newLp)); 248 } 249 250 @Override 251 public void onLinkPropertiesChange(LinkProperties newLp) { 252 mHandler.post(() -> updateLinkProperties(newLp)); 253 } 254 }; 255 256 NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context, 257 NetworkCapabilities capabilities) { 258 name = ifaceName; 259 mCapabilities = capabilities; 260 mHandler = handler; 261 mContext = context; 262 263 mHwAddress = hwAddress; 264 mNetworkInfo = new NetworkInfo(TYPE_ETHERNET, 0, NETWORK_TYPE, ""); 265 mNetworkInfo.setExtraInfo(mHwAddress); 266 mNetworkInfo.setIsAvailable(true); 267 } 268 269 void setIpConfig(IpConfiguration ipConfig) { 270 271 this.mIpConfig = ipConfig; 272 } 273 274 boolean statisified(NetworkCapabilities requestedCapabilities) { 275 return requestedCapabilities.satisfiedByNetworkCapabilities(mCapabilities); 276 } 277 278 boolean isRestricted() { 279 return mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 280 } 281 282 private void start() { 283 if (mIpClient != null) { 284 if (DBG) Log.d(TAG, "IpClient already started"); 285 return; 286 } 287 if (DBG) { 288 Log.d(TAG, String.format("starting IpClient(%s): mNetworkInfo=%s", name, 289 mNetworkInfo)); 290 } 291 292 mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddress); 293 294 mIpClient = new IpClient(mContext, name, mIpClientCallback); 295 296 if (sTcpBufferSizes == null) { 297 sTcpBufferSizes = mContext.getResources().getString( 298 com.android.internal.R.string.config_ethernet_tcp_buffers); 299 } 300 provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes); 301 } 302 303 void onIpLayerStarted(LinkProperties linkProperties) { 304 if (mNetworkAgent != null) { 305 Log.e(TAG, "Already have a NetworkAgent - aborting new request"); 306 stop(); 307 return; 308 } 309 mLinkProperties = linkProperties; 310 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddress); 311 mNetworkInfo.setIsAvailable(true); 312 313 // Create our NetworkAgent. 314 mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext, 315 NETWORK_TYPE, mNetworkInfo, mCapabilities, mLinkProperties, 316 NETWORK_SCORE) { 317 public void unwanted() { 318 if (this == mNetworkAgent) { 319 stop(); 320 } else if (mNetworkAgent != null) { 321 Log.d(TAG, "Ignoring unwanted as we have a more modern " + 322 "instance"); 323 } // Otherwise, we've already called stop. 324 } 325 }; 326 } 327 328 void onIpLayerStopped(LinkProperties linkProperties) { 329 // This cannot happen due to provisioning timeout, because our timeout is 0. It can only 330 // happen if we're provisioned and we lose provisioning. 331 stop(); 332 start(); 333 } 334 335 void updateLinkProperties(LinkProperties linkProperties) { 336 mLinkProperties = linkProperties; 337 if (mNetworkAgent != null) { 338 mNetworkAgent.sendLinkProperties(linkProperties); 339 } 340 } 341 342 /** Returns true if state has been modified */ 343 boolean updateLinkState(boolean up) { 344 if (mLinkUp == up) return false; 345 mLinkUp = up; 346 347 stop(); 348 if (up) { 349 start(); 350 } 351 352 return true; 353 } 354 355 void stop() { 356 if (mIpClient != null) { 357 mIpClient.shutdown(); 358 mIpClient.awaitShutdown(); 359 mIpClient = null; 360 } 361 362 // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object 363 // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here: 364 // that sets the state to IDLE, and ConnectivityService will still think we're connected. 365 // 366 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddress); 367 if (mNetworkAgent != null) { 368 updateAgent(); 369 mNetworkAgent = null; 370 } 371 clear(); 372 } 373 374 private void updateAgent() { 375 if (mNetworkAgent == null) return; 376 if (DBG) { 377 Log.i(TAG, "Updating mNetworkAgent with: " + 378 mCapabilities + ", " + 379 mNetworkInfo + ", " + 380 mLinkProperties); 381 } 382 mNetworkAgent.sendNetworkCapabilities(mCapabilities); 383 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 384 mNetworkAgent.sendLinkProperties(mLinkProperties); 385 // never set the network score below 0. 386 mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); 387 } 388 389 private void clear() { 390 mLinkProperties.clear(); 391 mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null); 392 mNetworkInfo.setIsAvailable(false); 393 } 394 395 private static void provisionIpClient(IpClient ipClient, IpConfiguration config, 396 String tcpBufferSizes) { 397 if (config.getProxySettings() == ProxySettings.STATIC || 398 config.getProxySettings() == ProxySettings.PAC) { 399 ipClient.setHttpProxy(config.getHttpProxy()); 400 } 401 402 if (!TextUtils.isEmpty(tcpBufferSizes)) { 403 ipClient.setTcpBufferSizes(tcpBufferSizes); 404 } 405 406 final ProvisioningConfiguration provisioningConfiguration; 407 if (config.getIpAssignment() == IpAssignment.STATIC) { 408 provisioningConfiguration = IpClient.buildProvisioningConfiguration() 409 .withStaticConfiguration(config.getStaticIpConfiguration()) 410 .build(); 411 } else { 412 provisioningConfiguration = IpClient.buildProvisioningConfiguration() 413 .withProvisioningTimeoutMs(0) 414 .build(); 415 } 416 417 ipClient.startProvisioning(provisioningConfiguration); 418 } 419 420 @Override 421 public String toString() { 422 return getClass().getSimpleName() + "{ " 423 + "iface: " + name + ", " 424 + "up: " + mLinkUp + ", " 425 + "hwAddress: " + mHwAddress + ", " 426 + "networkInfo: " + mNetworkInfo + ", " 427 + "networkAgent: " + mNetworkAgent + ", " 428 + "ipClient: " + mIpClient + "," 429 + "linkProperties: " + mLinkProperties 430 + "}"; 431 } 432 } 433 434 void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { 435 super.dump(fd, pw, args); 436 pw.println(getClass().getSimpleName()); 437 pw.println("Tracking interfaces:"); 438 pw.increaseIndent(); 439 for (String iface: mTrackingInterfaces.keySet()) { 440 NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface); 441 pw.println(iface + ":" + ifaceState); 442 pw.increaseIndent(); 443 final IpClient ipClient = ifaceState.mIpClient; 444 if (ipClient != null) { 445 ipClient.dump(fd, pw, args); 446 } else { 447 pw.println("IpClient is null"); 448 } 449 pw.decreaseIndent(); 450 } 451 pw.decreaseIndent(); 452 } 453} 454