Enable multiple active Ethernet interfaces

- reworked EthernetNetworkFactory to support multiple active Ethernet
interfaces
- allow vendors to specify network capabilities + ip config through XML
config overlay

Test: manual using hikey960 + multiple usb->eth adapters
Change-Id: Ie39bcb0d2a3f960f497222159c7bd5797accaa68
This commit is contained in:
Pavel Maltsev
2017-10-31 15:42:16 -07:00
parent a289c9eb47
commit 52d046eb8a
4 changed files with 809 additions and 473 deletions

View File

@@ -17,11 +17,8 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.server.net.IpConfigStore; import com.android.server.net.IpConfigStore;
@@ -29,34 +26,60 @@ import com.android.server.net.IpConfigStore;
/** /**
* This class provides an API to store and manage Ethernet network configuration. * This class provides an API to store and manage Ethernet network configuration.
*/ */
public class EthernetConfigStore extends IpConfigStore { public class EthernetConfigStore {
private static final String TAG = "EthernetConfigStore";
private static final String ipConfigFile = Environment.getDataDirectory() + private static final String ipConfigFile = Environment.getDataDirectory() +
"/misc/ethernet/ipconfig.txt"; "/misc/ethernet/ipconfig.txt";
private IpConfigStore mStore = new IpConfigStore();
private ArrayMap<String, IpConfiguration> mIpConfigurations;
private IpConfiguration mIpConfigurationForDefaultInterface;
private final Object mSync = new Object();
public EthernetConfigStore() { public EthernetConfigStore() {
mIpConfigurations = new ArrayMap<>(0);
} }
public IpConfiguration readIpAndProxyConfigurations() { public void read() {
SparseArray<IpConfiguration> networks = readIpAndProxyConfigurations(ipConfigFile); synchronized (mSync) {
ArrayMap<String, IpConfiguration> configs =
IpConfigStore.readIpConfigurations(ipConfigFile);
if (networks.size() == 0) { // This configuration may exist in old file versions when there was only a single active
Log.w(TAG, "No Ethernet configuration found. Using default."); // Ethernet interface.
return new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null); if (configs.containsKey("0")) {
mIpConfigurationForDefaultInterface = configs.remove("0");
} }
if (networks.size() > 1) { mIpConfigurations = configs;
// Currently we only support a single Ethernet interface. }
Log.w(TAG, "Multiple Ethernet configurations detected. Only reading first one.");
} }
return networks.valueAt(0); public void write(String iface, IpConfiguration config) {
boolean modified;
synchronized (mSync) {
if (config == null) {
modified = mIpConfigurations.remove(iface) != null;
} else {
IpConfiguration oldConfig = mIpConfigurations.put(iface, config);
modified = !config.equals(oldConfig);
} }
public void writeIpAndProxyConfigurations(IpConfiguration config) { if (modified) {
SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>(); mStore.writeIpConfigurations(ipConfigFile, mIpConfigurations);
networks.put(0, config); }
writeIpAndProxyConfigurations(ipConfigFile, networks); }
}
public ArrayMap<String, IpConfiguration> getIpConfigurations() {
synchronized (mSync) {
return new ArrayMap<>(mIpConfigurations);
}
}
public IpConfiguration getIpConfigurationForDefaultInterface() {
synchronized (mSync) {
return new IpConfiguration(mIpConfigurationForDefaultInterface);
}
} }
} }

View File

@@ -16,12 +16,9 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.EthernetManager;
import android.net.IEthernetServiceListener;
import android.net.InterfaceConfiguration;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings; import android.net.IpConfiguration.ProxySettings;
@@ -31,292 +28,215 @@ import android.net.NetworkCapabilities;
import android.net.NetworkFactory; import android.net.NetworkFactory;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.DetailedState;
import android.net.StaticIpConfiguration; import android.net.NetworkRequest;
import android.net.ip.IpManager; import android.net.NetworkSpecifier;
import android.net.ip.IpManager.ProvisioningConfiguration; import android.net.StringNetworkSpecifier;
import android.net.ip.IpClient;
import android.net.ip.IpClient.ProvisioningConfiguration;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
/** /**
* Manages connectivity for an Ethernet interface. * {@link NetworkFactory} that represents Ethernet networks.
* *
* Ethernet Interfaces may be present at boot time or appear after boot (e.g., * This class reports a static network score of 70 when it is tracking an interface and that
* for Ethernet adapters connected over USB). This class currently supports * interface's link is up, and a score of 0 otherwise.
* only one interface. When an interface appears on the system (or is present
* at boot time) this class will start tracking it and bring it up, and will
* attempt to connect when requested. Any other interfaces that subsequently
* appear will be ignored until the tracked interface disappears. Only
* interfaces whose names match the <code>config_ethernet_iface_regex</code>
* regular expression are tracked.
*
* This class reports a static network score of 70 when it is tracking an
* interface and that interface's link is up, and a score of 0 otherwise.
*
* @hide
*/ */
class EthernetNetworkFactory { public class EthernetNetworkFactory extends NetworkFactory {
private final static String TAG = EthernetNetworkFactory.class.getSimpleName();
final static boolean DBG = true;
private final static int NETWORK_SCORE = 70;
private static final String NETWORK_TYPE = "Ethernet"; private static final String NETWORK_TYPE = "Ethernet";
private static final String TAG = "EthernetNetworkFactory";
private static final int NETWORK_SCORE = 70;
private static final boolean DBG = true;
/** Tracks interface changes. Called from NetworkManagementService. */ private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces =
private InterfaceObserver mInterfaceObserver; new ConcurrentHashMap<>();
private final Handler mHandler;
private final Context mContext;
/** For static IP configuration */ public EthernetNetworkFactory(Handler handler, Context context, NetworkCapabilities filter) {
private EthernetManager mEthernetManager; super(handler.getLooper(), context, NETWORK_TYPE, filter);
/** To set link state and configure IP addresses. */ mHandler = handler;
private INetworkManagementService mNMService; mContext = context;
/** All code runs here, including start(). */ setScoreFilter(NETWORK_SCORE);
private Handler mHandler;
/* To communicate with ConnectivityManager */
private NetworkCapabilities mNetworkCapabilities;
private NetworkAgent mNetworkAgent;
private LocalNetworkFactory mFactory;
private Context mContext;
/** Product-dependent regular expression of interface names we track. */
private static String mIfaceMatch = "";
/** To notify Ethernet status. */
private final RemoteCallbackList<IEthernetServiceListener> mListeners;
/** Data members. All accesses to these must be on the handler thread. */
private String mIface = "";
private String mHwAddr;
private boolean mLinkUp;
private NetworkInfo mNetworkInfo;
private LinkProperties mLinkProperties;
private IpManager mIpManager;
private boolean mNetworkRequested = false;
EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
initNetworkCapabilities();
clearInfo();
mListeners = listeners;
} }
private class LocalNetworkFactory extends NetworkFactory { @Override
LocalNetworkFactory(String name, Context context, Looper looper) { public boolean acceptRequest(NetworkRequest request, int score) {
super(looper, context, name, new NetworkCapabilities()); if (DBG) {
Log.d(TAG, "acceptRequest, request: " + request + ", score: " + score);
} }
protected void startNetwork() { return networkForRequest(request) != null;
if (!mNetworkRequested) { }
mNetworkRequested = true;
maybeStartIpManager(); @Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
NetworkInterfaceState network = networkForRequest(networkRequest);
if (network == null) {
Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest);
return;
}
if (++network.refCount == 1) {
network.start();
} }
} }
protected void stopNetwork() { @Override
mNetworkRequested = false; protected void releaseNetworkFor(NetworkRequest networkRequest) {
stopIpManager(); NetworkInterfaceState network = networkForRequest(networkRequest);
} if (network == null) {
Log.e(TAG, "needNetworkFor, failed to get a network for " + networkRequest);
return;
} }
private void clearInfo() { if (--network.refCount == 1) {
mLinkProperties = new LinkProperties(); network.stop();
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
mNetworkInfo.setExtraInfo(mHwAddr);
mNetworkInfo.setIsAvailable(isTrackingInterface());
} }
private void stopIpManager() {
if (mIpManager != null) {
mIpManager.shutdown();
mIpManager = null;
}
// ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
// with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
// that sets the state to IDLE, and ConnectivityService will still think we're connected.
//
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
if (mNetworkAgent != null) {
updateAgent();
mNetworkAgent = null;
}
clearInfo();
} }
/** /**
* Updates interface state variables. * Returns an array of available interface names. The array is sorted: unrestricted interfaces
* Called on link state changes or on startup. * goes first, then sorted by name.
*/ */
private void updateInterfaceState(String iface, boolean up) { String[] getAvailableInterfaces(boolean includeRestricted) {
if (!mIface.equals(iface)) { return mTrackingInterfaces.values()
return; .stream()
} .filter(iface -> !iface.isRestricted() || includeRestricted)
Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); .sorted((iface1, iface2) -> {
int r = Boolean.compare(iface1.isRestricted(), iface2.isRestricted());
mLinkUp = up; return r == 0 ? iface1.name.compareTo(iface2.name) : r;
if (up) { })
maybeStartIpManager(); .map(iface -> iface.name)
} else { .toArray(String[]::new);
stopIpManager();
}
} }
private class InterfaceObserver extends BaseNetworkObserver { void addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities,
@Override IpConfiguration ipConfiguration) {
public void interfaceLinkStateChanged(String iface, boolean up) { if (mTrackingInterfaces.containsKey(ifaceName)) {
mHandler.post(() -> { Log.e(TAG, "Interface with name " + ifaceName + " already exists.");
updateInterfaceState(iface, up);
});
}
@Override
public void interfaceAdded(String iface) {
mHandler.post(() -> {
maybeTrackInterface(iface);
});
}
@Override
public void interfaceRemoved(String iface) {
mHandler.post(() -> {
if (stopTrackingInterface(iface)) {
trackFirstAvailableInterface();
}
});
}
}
private void setInterfaceUp(String iface) {
// Bring up the interface so we get link status indications.
try {
mNMService.setInterfaceUp(iface);
String hwAddr = null;
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
if (config == null) {
Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
return; return;
} }
if (!isTrackingInterface()) {
setInterfaceInfo(iface, config.getHardwareAddress());
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setExtraInfo(mHwAddr);
} else {
Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface);
mNMService.setInterfaceDown(iface);
}
} catch (RemoteException | IllegalStateException e) {
// Either the system is crashing or the interface has disappeared. Just ignore the
// error; we haven't modified any state because we only do that if our calls succeed.
Log.e(TAG, "Error upping interface " + mIface + ": " + e);
}
}
private boolean maybeTrackInterface(String iface) {
// If we don't already have an interface, and if this interface matches
// our regex, start tracking it.
if (!iface.matches(mIfaceMatch) || isTrackingInterface())
return false;
Log.d(TAG, "Started tracking interface " + iface);
setInterfaceUp(iface);
return true;
}
private boolean stopTrackingInterface(String iface) {
if (!iface.equals(mIface))
return false;
Log.d(TAG, "Stopped tracking interface " + iface);
setInterfaceInfo("", null);
stopIpManager();
return true;
}
public void updateAgent() {
if (mNetworkAgent == null) return;
if (DBG) { if (DBG) {
Log.i(TAG, "Updating mNetworkAgent with: " + Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + capabilities);
mNetworkCapabilities + ", " +
mNetworkInfo + ", " +
mLinkProperties);
}
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent.sendLinkProperties(mLinkProperties);
// never set the network score below 0.
mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
} }
void onIpLayerStarted(LinkProperties linkProperties) { NetworkInterfaceState iface = new NetworkInterfaceState(
if (mNetworkAgent != null) { ifaceName, hwAddress, mHandler, mContext, capabilities);
Log.e(TAG, "Already have a NetworkAgent - aborting new request"); iface.setIpConfig(ipConfiguration);
stopIpManager(); mTrackingInterfaces.put(ifaceName, iface);
return;
}
mLinkProperties = linkProperties;
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
// Create our NetworkAgent. updateCapabilityFilter();
mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext,
NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
NETWORK_SCORE) {
public void unwanted() {
if (this == mNetworkAgent) {
stopIpManager();
} else if (mNetworkAgent != null) {
Log.d(TAG, "Ignoring unwanted as we have a more modern " +
"instance");
} // Otherwise, we've already called stopIpManager.
}
};
} }
void onIpLayerStopped(LinkProperties linkProperties) { private void updateCapabilityFilter() {
// This cannot happen due to provisioning timeout, because our timeout is 0. It can only NetworkCapabilities capabilitiesFilter = new NetworkCapabilities();
// happen if we're provisioned and we lose provisioning. capabilitiesFilter.clearAll();
stopIpManager();
maybeStartIpManager(); for (NetworkInterfaceState iface: mTrackingInterfaces.values()) {
capabilitiesFilter.combineCapabilities(iface.mCapabilities);
} }
void updateLinkProperties(LinkProperties linkProperties) { if (DBG) Log.d(TAG, "updateCapabilityFilter: " + capabilitiesFilter);
mLinkProperties = linkProperties; setCapabilityFilter(capabilitiesFilter);
if (mNetworkAgent != null) {
mNetworkAgent.sendLinkProperties(linkProperties);
}
} }
public void maybeStartIpManager() { void removeInterface(String interfaceName) {
if (mNetworkRequested && mIpManager == null && isTrackingInterface()) { NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
startIpManager(); if (iface != null) {
} iface.stop();
}
updateCapabilityFilter();
}
/** Returns true if state has been modified */
boolean updateInterfaceLinkState(String ifaceName, boolean up) {
if (!mTrackingInterfaces.containsKey(ifaceName)) {
return false;
} }
public void startIpManager() {
if (DBG) { if (DBG) {
Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface, Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up);
mNetworkInfo));
} }
IpConfiguration config = mEthernetManager.getConfiguration(); NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
return iface.updateLinkState(up);
}
mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); boolean hasInterface(String interfacName) {
IpManager.Callback ipmCallback = new IpManager.Callback() { return mTrackingInterfaces.containsKey(interfacName);
}
void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
NetworkInterfaceState network = mTrackingInterfaces.get(iface);
if (network != null) {
network.setIpConfig(ipConfiguration);
}
}
private NetworkInterfaceState networkForRequest(NetworkRequest request) {
String requestedIface = null;
NetworkSpecifier specifier = request.networkCapabilities.getNetworkSpecifier();
if (specifier instanceof StringNetworkSpecifier) {
requestedIface = ((StringNetworkSpecifier) specifier).specifier;
}
NetworkInterfaceState network = null;
if (!TextUtils.isEmpty(requestedIface)) {
NetworkInterfaceState n = mTrackingInterfaces.get(requestedIface);
if (n != null && n.statisified(request.networkCapabilities)) {
network = n;
}
} else {
for (NetworkInterfaceState n : mTrackingInterfaces.values()) {
if (n.statisified(request.networkCapabilities)) {
network = n;
break;
}
}
}
if (DBG) {
Log.i(TAG, "networkForRequest, request: " + request + ", network: " + network);
}
return network;
}
private static class NetworkInterfaceState {
final String name;
private final String mHwAddress;
private final NetworkCapabilities mCapabilities;
private final Handler mHandler;
private final Context mContext;
private final NetworkInfo mNetworkInfo;
private static String sTcpBufferSizes = null; // Lazy initialized.
private boolean mLinkUp;
private LinkProperties mLinkProperties = new LinkProperties();
private IpClient mIpClient;
private NetworkAgent mNetworkAgent;
private IpConfiguration mIpConfig;
long refCount = 0;
private final IpClient.Callback mIpClientCallback = new IpClient.Callback() {
@Override @Override
public void onProvisioningSuccess(LinkProperties newLp) { public void onProvisioningSuccess(LinkProperties newLp) {
mHandler.post(() -> onIpLayerStarted(newLp)); mHandler.post(() -> onIpLayerStarted(newLp));
@@ -333,178 +253,191 @@ class EthernetNetworkFactory {
} }
}; };
stopIpManager(); NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
mIpManager = new IpManager(mContext, mIface, ipmCallback); NetworkCapabilities capabilities) {
name = ifaceName;
mCapabilities = capabilities;
mHandler = handler;
mContext = context;
if (config.getProxySettings() == ProxySettings.STATIC || mHwAddress = hwAddress;
config.getProxySettings() == ProxySettings.PAC) { mNetworkInfo = new NetworkInfo(TYPE_ETHERNET, 0, NETWORK_TYPE, "");
mIpManager.setHttpProxy(config.getHttpProxy()); mNetworkInfo.setExtraInfo(mHwAddress);
mNetworkInfo.setIsAvailable(true);
} }
final String tcpBufferSizes = mContext.getResources().getString( void setIpConfig(IpConfiguration ipConfig) {
this.mIpConfig = ipConfig;
}
boolean statisified(NetworkCapabilities requestedCapabilities) {
return requestedCapabilities.satisfiedByNetworkCapabilities(mCapabilities);
}
boolean isRestricted() {
return mCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
private void start() {
if (DBG) {
Log.d(TAG, String.format("starting IpClient(%s): mNetworkInfo=%s", name,
mNetworkInfo));
}
if (mIpClient != null) stop();
mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddress);
mIpClient = new IpClient(mContext, name, mIpClientCallback);
if (sTcpBufferSizes == null) {
sTcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_ethernet_tcp_buffers); com.android.internal.R.string.config_ethernet_tcp_buffers);
}
provisionIpClient(mIpClient, mIpConfig, sTcpBufferSizes);
}
void onIpLayerStarted(LinkProperties linkProperties) {
if (mNetworkAgent != null) {
Log.e(TAG, "Already have a NetworkAgent - aborting new request");
stop();
return;
}
mLinkProperties = linkProperties;
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddress);
mNetworkInfo.setIsAvailable(true);
// Create our NetworkAgent.
mNetworkAgent = new NetworkAgent(mHandler.getLooper(), mContext,
NETWORK_TYPE, mNetworkInfo, mCapabilities, mLinkProperties,
NETWORK_SCORE) {
public void unwanted() {
if (this == mNetworkAgent) {
stop();
} else if (mNetworkAgent != null) {
Log.d(TAG, "Ignoring unwanted as we have a more modern " +
"instance");
} // Otherwise, we've already called stop.
}
};
}
void onIpLayerStopped(LinkProperties linkProperties) {
// This cannot happen due to provisioning timeout, because our timeout is 0. It can only
// happen if we're provisioned and we lose provisioning.
start();
}
void updateLinkProperties(LinkProperties linkProperties) {
mLinkProperties = linkProperties;
if (mNetworkAgent != null) {
mNetworkAgent.sendLinkProperties(linkProperties);
}
}
/** Returns true if state has been modified */
boolean updateLinkState(boolean up) {
if (mLinkUp == up) return false;
mLinkUp = up;
if (up) {
start();
} else {
stop();
}
return true;
}
void stop() {
if (mIpClient != null) {
mIpClient.shutdown();
mIpClient = null;
}
// ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object
// with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here:
// that sets the state to IDLE, and ConnectivityService will still think we're connected.
//
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddress);
if (mNetworkAgent != null) {
updateAgent();
mNetworkAgent = null;
}
clear();
}
private void updateAgent() {
if (mNetworkAgent == null) return;
if (DBG) {
Log.i(TAG, "Updating mNetworkAgent with: " +
mCapabilities + ", " +
mNetworkInfo + ", " +
mLinkProperties);
}
mNetworkAgent.sendNetworkCapabilities(mCapabilities);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent.sendLinkProperties(mLinkProperties);
// never set the network score below 0.
mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
}
private void clear() {
mLinkProperties.clear();
mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
mNetworkInfo.setIsAvailable(false);
}
private static void provisionIpClient(IpClient ipClient, IpConfiguration config,
String tcpBufferSizes) {
if (config.getProxySettings() == ProxySettings.STATIC ||
config.getProxySettings() == ProxySettings.PAC) {
ipClient.setHttpProxy(config.getHttpProxy());
}
if (!TextUtils.isEmpty(tcpBufferSizes)) { if (!TextUtils.isEmpty(tcpBufferSizes)) {
mIpManager.setTcpBufferSizes(tcpBufferSizes); ipClient.setTcpBufferSizes(tcpBufferSizes);
} }
final ProvisioningConfiguration provisioningConfiguration; final ProvisioningConfiguration provisioningConfiguration;
if (config.getIpAssignment() == IpAssignment.STATIC) { if (config.getIpAssignment() == IpAssignment.STATIC) {
provisioningConfiguration = IpManager.buildProvisioningConfiguration() provisioningConfiguration = IpClient.buildProvisioningConfiguration()
.withStaticConfiguration(config.getStaticIpConfiguration()) .withStaticConfiguration(config.getStaticIpConfiguration())
.build(); .build();
} else { } else {
provisioningConfiguration = mIpManager.buildProvisioningConfiguration() provisioningConfiguration = IpClient.buildProvisioningConfiguration()
.withProvisioningTimeoutMs(0) .withProvisioningTimeoutMs(0)
.build(); .build();
} }
mIpManager.startProvisioning(provisioningConfiguration); ipClient.startProvisioning(provisioningConfiguration);
} }
/** @Override
* Begin monitoring connectivity public String toString() {
*/ return getClass().getSimpleName() + "{ "
public void start(Context context, Handler handler) { + "iface: " + name + ", "
mHandler = handler; + "up: " + mLinkUp + ", "
+ "hwAddress: " + mHwAddress + ", "
// The services we use. + "networkInfo: " + mNetworkInfo + ", "
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + "networkAgent: " + mNetworkAgent + ", "
mNMService = INetworkManagementService.Stub.asInterface(b); + "ipClient: " + mIpClient + ","
mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); + "linkProperties: " + mLinkProperties
+ "}";
// Interface match regex.
mIfaceMatch = context.getResources().getString(
com.android.internal.R.string.config_ethernet_iface_regex);
// Create and register our NetworkFactory.
mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, mHandler.getLooper());
mFactory.setCapabilityFilter(mNetworkCapabilities);
mFactory.setScoreFilter(NETWORK_SCORE);
mFactory.register();
mContext = context;
// Start tracking interface change events.
mInterfaceObserver = new InterfaceObserver();
try {
mNMService.registerObserver(mInterfaceObserver);
} catch (RemoteException e) {
Log.e(TAG, "Could not register InterfaceObserver " + e);
}
// If an Ethernet interface is already connected, start tracking that.
// Otherwise, the first Ethernet interface to appear will be tracked.
mHandler.post(() -> trackFirstAvailableInterface());
}
public void trackFirstAvailableInterface() {
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
if (maybeTrackInterface(iface)) {
// We have our interface. Track it.
// Note: if the interface already has link (e.g., if we crashed and got
// restarted while it was running), we need to fake a link up notification so we
// start configuring it.
if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {
updateInterfaceState(iface, true);
}
break;
} }
} }
} catch (RemoteException|IllegalStateException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
}
public void stop() {
stopIpManager();
setInterfaceInfo("", null);
mFactory.unregister();
}
private void initNetworkCapabilities() {
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
// We have no useful data on bandwidth. Say 100M up and 100M down. :-(
mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000);
mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000);
}
public boolean isTrackingInterface() {
return !TextUtils.isEmpty(mIface);
}
/**
* Set interface information and notify listeners if availability is changed.
*/
private void setInterfaceInfo(String iface, String hwAddr) {
boolean oldAvailable = isTrackingInterface();
mIface = iface;
mHwAddr = hwAddr;
boolean available = isTrackingInterface();
mNetworkInfo.setExtraInfo(mHwAddr);
mNetworkInfo.setIsAvailable(available);
if (oldAvailable != available) {
int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
mListeners.getBroadcastItem(i).onAvailabilityChanged(available);
} catch (RemoteException e) {
// Do nothing here.
}
}
mListeners.finishBroadcast();
}
}
private void postAndWaitForRunnable(Runnable r) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
try {
r.run();
} finally {
latch.countDown();
}
});
latch.await();
}
void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
try { super.dump(fd, pw, args);
postAndWaitForRunnable(() -> { pw.println(getClass().getSimpleName());
pw.println("Network Requested: " + mNetworkRequested); pw.println("Tracking interfaces:");
if (isTrackingInterface()) {
pw.println("Tracking interface: " + mIface);
pw.increaseIndent(); pw.increaseIndent();
pw.println("MAC address: " + mHwAddr); for (String iface: mTrackingInterfaces.keySet()) {
pw.println("Link state: " + (mLinkUp ? "up" : "down")); NetworkInterfaceState ifaceState = mTrackingInterfaces.get(iface);
pw.decreaseIndent(); pw.println(iface + ":" + ifaceState);
} else {
pw.println("Not tracking any interface");
}
pw.println();
pw.println("NetworkInfo: " + mNetworkInfo);
pw.println("LinkProperties: " + mLinkProperties);
pw.println("NetworkAgent: " + mNetworkAgent);
if (mIpManager != null) {
pw.println("IpManager:");
pw.increaseIndent(); pw.increaseIndent();
mIpManager.dump(fd, pw, args); ifaceState.mIpClient.dump(fd, pw, args);
pw.decreaseIndent(); pw.decreaseIndent();
} }
}); pw.decreaseIndent();
} catch (InterruptedException e) {
throw new IllegalStateException("dump() interrupted");
}
} }
} }

View File

@@ -21,14 +21,10 @@ import android.content.pm.PackageManager;
import android.net.IEthernetManager; import android.net.IEthernetManager;
import android.net.IEthernetServiceListener; import android.net.IEthernetServiceListener;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.RemoteCallbackList;
import android.os.RemoteException; import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter;
@@ -41,31 +37,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* EthernetServiceImpl handles remote Ethernet operation requests by implementing * EthernetServiceImpl handles remote Ethernet operation requests by implementing
* the IEthernetManager interface. * the IEthernetManager interface.
*
* @hide
*/ */
public class EthernetServiceImpl extends IEthernetManager.Stub { public class EthernetServiceImpl extends IEthernetManager.Stub {
private static final String TAG = "EthernetServiceImpl"; private static final String TAG = "EthernetServiceImpl";
private final Context mContext; private final Context mContext;
private final EthernetConfigStore mEthernetConfigStore;
private final AtomicBoolean mStarted = new AtomicBoolean(false); private final AtomicBoolean mStarted = new AtomicBoolean(false);
private IpConfiguration mIpConfiguration;
private Handler mHandler; private Handler mHandler;
private final EthernetNetworkFactory mTracker; private EthernetTracker mTracker;
private final RemoteCallbackList<IEthernetServiceListener> mListeners =
new RemoteCallbackList<IEthernetServiceListener>();
public EthernetServiceImpl(Context context) { public EthernetServiceImpl(Context context) {
mContext = context; mContext = context;
Log.i(TAG, "Creating EthernetConfigStore");
mEthernetConfigStore = new EthernetConfigStore();
mIpConfiguration = mEthernetConfigStore.readIpAndProxyConfigurations();
Log.i(TAG, "Read stored IP configuration: " + mIpConfiguration);
mTracker = new EthernetNetworkFactory(mListeners);
} }
private void enforceAccessPermission() { private void enforceAccessPermission() {
@@ -80,6 +63,18 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
"ConnectivityService"); "ConnectivityService");
} }
private void enforceUseRestrictedNetworksPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS,
"ConnectivityService");
}
private boolean checkUseRestrictedNetworksPermission() {
return mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)
== PackageManager.PERMISSION_GRANTED;
}
public void start() { public void start() {
Log.i(TAG, "Starting Ethernet service"); Log.i(TAG, "Starting Ethernet service");
@@ -87,60 +82,68 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
handlerThread.start(); handlerThread.start();
mHandler = new Handler(handlerThread.getLooper()); mHandler = new Handler(handlerThread.getLooper());
mTracker.start(mContext, mHandler); mTracker = new EthernetTracker(mContext, mHandler);
mTracker.start();
mStarted.set(true); mStarted.set(true);
} }
@Override
public String[] getAvailableInterfaces() throws RemoteException {
return mTracker.getInterfaces(checkUseRestrictedNetworksPermission());
}
/** /**
* Get Ethernet configuration * Get Ethernet configuration
* @return the Ethernet Configuration, contained in {@link IpConfiguration}. * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
*/ */
@Override @Override
public IpConfiguration getConfiguration() { public IpConfiguration getConfiguration(String iface) {
enforceAccessPermission(); enforceAccessPermission();
synchronized (mIpConfiguration) { if (mTracker.isRestrictedInterface(iface)) {
return new IpConfiguration(mIpConfiguration); enforceUseRestrictedNetworksPermission();
} }
return new IpConfiguration(mTracker.getIpConfiguration(iface));
} }
/** /**
* Set Ethernet configuration * Set Ethernet configuration
*/ */
@Override @Override
public void setConfiguration(IpConfiguration config) { public void setConfiguration(String iface, IpConfiguration config) {
if (!mStarted.get()) { if (!mStarted.get()) {
Log.w(TAG, "System isn't ready enough to change ethernet configuration"); Log.w(TAG, "System isn't ready enough to change ethernet configuration");
} }
enforceConnectivityInternalPermission(); enforceConnectivityInternalPermission();
synchronized (mIpConfiguration) { if (mTracker.isRestrictedInterface(iface)) {
mEthernetConfigStore.writeIpAndProxyConfigurations(config); enforceUseRestrictedNetworksPermission();
}
// TODO: this does not check proxy settings, gateways, etc. // TODO: this does not check proxy settings, gateways, etc.
// Fix this by making IpConfiguration a complete representation of static configuration. // Fix this by making IpConfiguration a complete representation of static configuration.
if (!config.equals(mIpConfiguration)) { mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
mIpConfiguration = new IpConfiguration(config);
mTracker.stop();
mTracker.start(mContext, mHandler);
}
}
} }
/** /**
* Indicates whether the system currently has one or more * Indicates whether given interface is available.
* Ethernet interfaces.
*/ */
@Override @Override
public boolean isAvailable() { public boolean isAvailable(String iface) {
enforceAccessPermission(); enforceAccessPermission();
return mTracker.isTrackingInterface();
if (mTracker.isRestrictedInterface(iface)) {
enforceUseRestrictedNetworksPermission();
}
return mTracker.isTrackingInterface(iface);
} }
/** /**
* Addes a listener. * Adds a listener.
* @param listener A {@link IEthernetServiceListener} to add. * @param listener A {@link IEthernetServiceListener} to add.
*/ */
public void addListener(IEthernetServiceListener listener) { public void addListener(IEthernetServiceListener listener) {
@@ -148,7 +151,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
throw new IllegalArgumentException("listener must not be null"); throw new IllegalArgumentException("listener must not be null");
} }
enforceAccessPermission(); enforceAccessPermission();
mListeners.register(listener); mTracker.addListener(listener, checkUseRestrictedNetworksPermission());
} }
/** /**
@@ -160,7 +163,7 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
throw new IllegalArgumentException("listener must not be null"); throw new IllegalArgumentException("listener must not be null");
} }
enforceAccessPermission(); enforceAccessPermission();
mListeners.unregister(listener); mTracker.removeListener(listener);
} }
@Override @Override
@@ -179,12 +182,6 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
mTracker.dump(fd, pw, args); mTracker.dump(fd, pw, args);
pw.decreaseIndent(); pw.decreaseIndent();
pw.println();
pw.println("Stored Ethernet configuration: ");
pw.increaseIndent();
pw.println(mIpConfiguration);
pw.decreaseIndent();
pw.println("Handler:"); pw.println("Handler:");
pw.increaseIndent(); pw.increaseIndent();
mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl"); mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl");

View File

@@ -0,0 +1,383 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.ethernet;
import android.annotation.Nullable;
import android.content.Context;
import android.net.IEthernetServiceListener;
import android.net.InterfaceConfiguration;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor;
import java.util.concurrent.ConcurrentHashMap;
/**
* Tracks Ethernet interfaces and manages interface configurations.
*
* <p>Interfaces may have different {@link android.net.NetworkCapabilities}. This mapping is defined
* in {@code config_ethernet_interfaces}. Notably, some interfaces could be marked as restricted by
* not specifying {@link android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED} flag.
* Interfaces could have associated {@link android.net.IpConfiguration}.
* Ethernet Interfaces may be present at boot time or appear after boot (e.g., for Ethernet adapters
* connected over USB). This class supports multiple interfaces. When an interface appears on the
* system (or is present at boot time) this class will start tracking it and bring it up. Only
* interfaces whose names match the {@code config_ethernet_iface_regex} regular expression are
* tracked.
*
* <p>All public or package private methods must be thread-safe unless stated otherwise.
*/
final class EthernetTracker {
private final static String TAG = EthernetTracker.class.getSimpleName();
private final static boolean DBG = EthernetNetworkFactory.DBG;
/** Product-dependent regular expression of interface names we track. */
private final String mIfaceMatch;
/** Mapping between {iface name | mac address} -> {NetworkCapabilities} */
private final ConcurrentHashMap<String, NetworkCapabilities> mNetworkCapabilities =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, IpConfiguration> mIpConfigurations =
new ConcurrentHashMap<>();
private final INetworkManagementService mNMService;
private final Handler mHandler;
private final EthernetNetworkFactory mFactory;
private final EthernetConfigStore mConfigStore;
private final RemoteCallbackList<IEthernetServiceListener> mListeners =
new RemoteCallbackList<>();
private volatile IpConfiguration mIpConfigForDefaultInterface;
EthernetTracker(Context context, Handler handler) {
mHandler = handler;
// The services we use.
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNMService = INetworkManagementService.Stub.asInterface(b);
// Interface match regex.
mIfaceMatch = context.getResources().getString(
com.android.internal.R.string.config_ethernet_iface_regex);
// Read default Ethernet interface configuration from resources
final String[] interfaceConfigs = context.getResources().getStringArray(
com.android.internal.R.array.config_ethernet_interfaces);
for (String strConfig : interfaceConfigs) {
parseEthernetConfig(strConfig);
}
mConfigStore = new EthernetConfigStore();
NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);
mFactory = new EthernetNetworkFactory(handler, context, nc);
mFactory.register();
}
void start() {
mConfigStore.read();
// Default interface is just the first one we want to track.
mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();
final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations();
for (int i = 0; i < configs.size(); i++) {
mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));
}
try {
mNMService.registerObserver(new InterfaceObserver());
} catch (RemoteException e) {
Log.e(TAG, "Could not register InterfaceObserver " + e);
}
mHandler.post(this::trackAvailableInterfaces);
}
void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
if (DBG) {
Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
}
mConfigStore.write(iface, ipConfiguration);
mIpConfigurations.put(iface, ipConfiguration);
mHandler.post(() -> mFactory.updateIpConfiguration(iface, ipConfiguration));
}
IpConfiguration getIpConfiguration(String iface) {
return mIpConfigurations.get(iface);
}
boolean isTrackingInterface(String iface) {
return mFactory.hasInterface(iface);
}
String[] getInterfaces(boolean includeRestricted) {
return mFactory.getAvailableInterfaces(includeRestricted);
}
/**
* Returns true if given interface was configured as restricted (doesn't have
* NET_CAPABILITY_NOT_RESTRICTED) capability. Otherwise, returns false.
*/
boolean isRestrictedInterface(String iface) {
final NetworkCapabilities nc = mNetworkCapabilities.get(iface);
return nc != null && !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks));
}
void removeListener(IEthernetServiceListener listener) {
mListeners.unregister(listener);
}
private void removeInterface(String iface) {
mFactory.removeInterface(iface);
}
private void addInterface(String iface) {
InterfaceConfiguration config = null;
// Bring up the interface so we get link status indications.
try {
mNMService.setInterfaceUp(iface);
config = mNMService.getInterfaceConfig(iface);
} catch (RemoteException | IllegalStateException e) {
// Either the system is crashing or the interface has disappeared. Just ignore the
// error; we haven't modified any state because we only do that if our calls succeed.
Log.e(TAG, "Error upping interface " + iface, e);
}
if (config == null) {
Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
return;
}
final String hwAddress = config.getHardwareAddress();
NetworkCapabilities nc = mNetworkCapabilities.get(iface);
if (nc == null) {
// Try to resolve using mac address
nc = mNetworkCapabilities.get(hwAddress);
if (nc == null) {
nc = createDefaultNetworkCapabilities();
}
}
IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
if (ipConfiguration == null) {
ipConfiguration = createDefaultIpConfiguration();
}
Log.d(TAG, "Started tracking interface " + iface);
mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
// Note: if the interface already has link (e.g., if we crashed and got
// restarted while it was running), we need to fake a link up notification so we
// start configuring it.
if (config.hasFlag("running")) {
updateInterfaceState(iface, true);
}
}
private void updateInterfaceState(String iface, boolean up) {
boolean modified = mFactory.updateInterfaceLinkState(iface, up);
if (modified) {
boolean restricted = isRestrictedInterface(iface);
int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
if (restricted) {
ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i);
if (!listenerInfo.canUseRestrictedNetworks) {
continue;
}
}
mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up);
} catch (RemoteException e) {
// Do nothing here.
}
}
mListeners.finishBroadcast();
}
}
private void maybeTrackInterface(String iface) {
if (DBG) Log.i(TAG, "maybeTrackInterface " + iface);
// If we don't already track this interface, and if this interface matches
// our regex, start tracking it.
if (!iface.matches(mIfaceMatch) || mFactory.hasInterface(iface)) {
return;
}
if (mIpConfigForDefaultInterface != null) {
updateIpConfiguration(iface, mIpConfigForDefaultInterface);
mIpConfigForDefaultInterface = null;
}
addInterface(iface);
}
private void trackAvailableInterfaces() {
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
maybeTrackInterface(iface);
}
} catch (RemoteException | IllegalStateException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
}
private class InterfaceObserver extends BaseNetworkObserver {
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
if (DBG) {
Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up);
}
mHandler.post(() -> updateInterfaceState(iface, up));
}
@Override
public void interfaceAdded(String iface) {
mHandler.post(() -> maybeTrackInterface(iface));
}
@Override
public void interfaceRemoved(String iface) {
mHandler.post(() -> removeInterface(iface));
}
}
private static class ListenerInfo {
boolean canUseRestrictedNetworks = false;
ListenerInfo(boolean canUseRestrictedNetworks) {
this.canUseRestrictedNetworks = canUseRestrictedNetworks;
}
}
private void parseEthernetConfig(String configString) {
String[] tokens = configString.split(";");
String name = tokens[0];
String capabilities = tokens.length > 1 ? tokens[1] : null;
NetworkCapabilities nc = createNetworkCapabilities(
!TextUtils.isEmpty(capabilities) /* clear default capabilities */, capabilities);
mNetworkCapabilities.put(name, nc);
if (tokens.length > 2 && !TextUtils.isEmpty(tokens[2])) {
IpConfiguration ipConfig = createStaticIpConfiguration(tokens[2]);
mIpConfigurations.put(name, ipConfig);
}
}
private static NetworkCapabilities createDefaultNetworkCapabilities() {
NetworkCapabilities nc = createNetworkCapabilities(false /* clear default capabilities */);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
return nc;
}
private static NetworkCapabilities createNetworkCapabilities(boolean clearDefaultCapabilities) {
return createNetworkCapabilities(clearDefaultCapabilities, null);
}
private static NetworkCapabilities createNetworkCapabilities(
boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities) {
NetworkCapabilities nc = new NetworkCapabilities();
if (clearDefaultCapabilities) {
nc.clearAll(); // Remove default capabilities.
}
nc.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
nc.setLinkUpstreamBandwidthKbps(100 * 1000);
nc.setLinkDownstreamBandwidthKbps(100 * 1000);
if (!TextUtils.isEmpty(commaSeparatedCapabilities)) {
for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) {
if (!TextUtils.isEmpty(strNetworkCapability)) {
nc.addCapability(Integer.valueOf(strNetworkCapability));
}
}
}
return nc;
}
private static IpConfiguration createStaticIpConfiguration(String strIpAddress) {
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
staticIpConfiguration.ipAddress = new LinkAddress(strIpAddress);
return new IpConfiguration(
IpAssignment.STATIC, ProxySettings.NONE, staticIpConfiguration, null);
}
private static IpConfiguration createDefaultIpConfiguration() {
return new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
}
private void postAndWaitForRunnable(Runnable r) {
mHandler.runWithScissors(r, 2000L /* timeout */);
}
void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
postAndWaitForRunnable(() -> {
pw.println(getClass().getSimpleName());
pw.println("Ethernet interface name filter: " + mIfaceMatch);
pw.println("Listeners: " + mListeners.getRegisteredCallbackCount());
pw.println("IP Configurations:");
pw.increaseIndent();
for (String iface : mIpConfigurations.keySet()) {
pw.println(iface + ": " + mIpConfigurations.get(iface));
}
pw.decreaseIndent();
pw.println();
pw.println("Network Capabilities:");
pw.increaseIndent();
for (String iface : mNetworkCapabilities.keySet()) {
pw.println(iface + ": " + mNetworkCapabilities.get(iface));
}
pw.decreaseIndent();
pw.println();
mFactory.dump(fd, pw, args);
});
}
}