Home | History | Annotate | Download | only in ethernet
      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 static android.net.ConnectivityManager.TYPE_ETHERNET;
     20 
     21 import android.content.Context;
     22 import android.net.IpConfiguration;
     23 import android.net.IpConfiguration.IpAssignment;
     24 import android.net.IpConfiguration.ProxySettings;
     25 import android.net.LinkProperties;
     26 import android.net.NetworkAgent;
     27 import android.net.NetworkCapabilities;
     28 import android.net.NetworkFactory;
     29 import android.net.NetworkInfo;
     30 import android.net.NetworkInfo.DetailedState;
     31 import android.net.NetworkRequest;
     32 import android.net.NetworkSpecifier;
     33 import android.net.StringNetworkSpecifier;
     34 import android.net.ip.IpClient;
     35 import android.net.ip.IpClient.ProvisioningConfiguration;
     36 import android.os.Handler;
     37 import android.text.TextUtils;
     38 import android.util.Log;
     39 
     40 import com.android.internal.util.IndentingPrintWriter;
     41 
     42 import java.io.FileDescriptor;
     43 import 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  */
     51 public 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