summaryrefslogtreecommitdiff
path: root/java/com/android/server/ethernet/EthernetTracker.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/server/ethernet/EthernetTracker.java')
-rw-r--r--java/com/android/server/ethernet/EthernetTracker.java942
1 files changed, 0 insertions, 942 deletions
diff --git a/java/com/android/server/ethernet/EthernetTracker.java b/java/com/android/server/ethernet/EthernetTracker.java
deleted file mode 100644
index c291b3f..0000000
--- a/java/com/android/server/ethernet/EthernetTracker.java
+++ /dev/null
@@ -1,942 +0,0 @@
-/*
- * 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 static android.net.EthernetManager.ETHERNET_STATE_DISABLED;
-import static android.net.EthernetManager.ETHERNET_STATE_ENABLED;
-import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityResources;
-import android.net.EthernetManager;
-import android.net.IEthernetServiceListener;
-import android.net.INetworkInterfaceOutcomeReceiver;
-import android.net.INetd;
-import android.net.ITetheredInterfaceCallback;
-import android.net.InterfaceConfigurationParcel;
-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.ConditionVariable;
-import android.os.Handler;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
-import com.android.net.module.util.NetdUtils;
-import com.android.net.module.util.PermissionUtils;
-
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-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.
- */
-@VisibleForTesting(visibility = PACKAGE)
-public class EthernetTracker {
- private static final int INTERFACE_MODE_CLIENT = 1;
- private static final int INTERFACE_MODE_SERVER = 2;
-
- private static final String TAG = EthernetTracker.class.getSimpleName();
- private static final boolean DBG = EthernetNetworkFactory.DBG;
-
- private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
- private static final String LEGACY_IFACE_REGEXP = "eth\\d";
-
- /**
- * Interface names we track. This is a product-dependent regular expression, plus,
- * if setIncludeTestInterfaces is true, any test interfaces.
- */
- private volatile String mIfaceMatch;
- /**
- * Track test interfaces if true, don't track otherwise.
- */
- private boolean mIncludeTestInterfaces = false;
-
- /** 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 Context mContext;
- private final INetd mNetd;
- private final Handler mHandler;
- private final EthernetNetworkFactory mFactory;
- private final EthernetConfigStore mConfigStore;
- private final Dependencies mDeps;
-
- private final RemoteCallbackList<IEthernetServiceListener> mListeners =
- new RemoteCallbackList<>();
- private final TetheredInterfaceRequestList mTetheredInterfaceRequests =
- new TetheredInterfaceRequestList();
-
- // Used only on the handler thread
- private String mDefaultInterface;
- private int mDefaultInterfaceMode = INTERFACE_MODE_CLIENT;
- // Tracks whether clients were notified that the tethered interface is available
- private boolean mTetheredInterfaceWasAvailable = false;
- private volatile IpConfiguration mIpConfigForDefaultInterface;
-
- private int mEthernetState = ETHERNET_STATE_ENABLED;
-
- private class TetheredInterfaceRequestList extends
- RemoteCallbackList<ITetheredInterfaceCallback> {
- @Override
- public void onCallbackDied(ITetheredInterfaceCallback cb, Object cookie) {
- mHandler.post(EthernetTracker.this::maybeUntetherDefaultInterface);
- }
- }
-
- public static class Dependencies {
- // TODO: remove legacy resource fallback after migrating its overlays.
- private String getPlatformRegexResource(Context context) {
- final Resources r = context.getResources();
- final int resId =
- r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName());
- return r.getString(resId);
- }
-
- // TODO: remove legacy resource fallback after migrating its overlays.
- private String[] getPlatformInterfaceConfigs(Context context) {
- final Resources r = context.getResources();
- final int resId = r.getIdentifier("config_ethernet_interfaces", "array",
- context.getPackageName());
- return r.getStringArray(resId);
- }
-
- public String getInterfaceRegexFromResource(Context context) {
- final String platformRegex = getPlatformRegexResource(context);
- final String match;
- if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) {
- // Platform resource is not the historical default: use the overlay
- match = platformRegex;
- } else {
- final ConnectivityResources resources = new ConnectivityResources(context);
- match = resources.get().getString(
- com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
- }
- return match;
- }
-
- public String[] getInterfaceConfigFromResource(Context context) {
- final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context);
- final String[] interfaceConfigs;
- if (platformInterfaceConfigs.length != 0) {
- // Platform resource is not the historical default: use the overlay
- interfaceConfigs = platformInterfaceConfigs;
- } else {
- final ConnectivityResources resources = new ConnectivityResources(context);
- interfaceConfigs = resources.get().getStringArray(
- com.android.connectivity.resources.R.array.config_ethernet_interfaces);
- }
- return interfaceConfigs;
- }
- }
-
- EthernetTracker(@NonNull final Context context, @NonNull final Handler handler,
- @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd) {
- this(context, handler, factory, netd, new Dependencies());
- }
-
- @VisibleForTesting
- EthernetTracker(@NonNull final Context context, @NonNull final Handler handler,
- @NonNull final EthernetNetworkFactory factory, @NonNull final INetd netd,
- @NonNull final Dependencies deps) {
- mContext = context;
- mHandler = handler;
- mFactory = factory;
- mNetd = netd;
- mDeps = deps;
-
- // Interface match regex.
- updateIfaceMatchRegexp();
-
- // Read default Ethernet interface configuration from resources
- final String[] interfaceConfigs = mDeps.getInterfaceConfigFromResource(context);
- for (String strConfig : interfaceConfigs) {
- parseEthernetConfig(strConfig);
- }
-
- mConfigStore = new EthernetConfigStore();
- }
-
- void start() {
- mFactory.register();
- 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 {
- PermissionUtils.enforceNetworkStackPermission(mContext);
- mNetd.registerUnsolicitedEventListener(new InterfaceObserver());
- } catch (RemoteException | ServiceSpecificException 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);
- }
- writeIpConfiguration(iface, ipConfiguration);
- mHandler.post(() -> {
- mFactory.updateInterface(iface, ipConfiguration, null, null);
- broadcastInterfaceStateChange(iface);
- });
- }
-
- private void writeIpConfiguration(@NonNull final String iface,
- @NonNull final IpConfiguration ipConfig) {
- mConfigStore.write(iface, ipConfig);
- mIpConfigurations.put(iface, ipConfig);
- }
-
- private IpConfiguration getIpConfigurationForCallback(String iface, int state) {
- return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface);
- }
-
- private void ensureRunningOnEthernetServiceThread() {
- if (mHandler.getLooper().getThread() != Thread.currentThread()) {
- throw new IllegalStateException(
- "Not running on EthernetService thread: "
- + Thread.currentThread().getName());
- }
- }
-
- /**
- * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all
- * listeners.
- */
- protected void broadcastInterfaceStateChange(@NonNull String iface) {
- ensureRunningOnEthernetServiceThread();
- final int state = mFactory.getInterfaceState(iface);
- final int role = getInterfaceRole(iface);
- final IpConfiguration config = getIpConfigurationForCallback(iface, state);
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- try {
- mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config);
- } catch (RemoteException e) {
- // Do nothing here.
- }
- }
- mListeners.finishBroadcast();
- }
-
- /**
- * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a
- * specific listener.
- */
- protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener,
- @NonNull String iface) {
- ensureRunningOnEthernetServiceThread();
- final int state = mFactory.getInterfaceState(iface);
- final int role = getInterfaceRole(iface);
- final IpConfiguration config = getIpConfigurationForCallback(iface, state);
- try {
- listener.onInterfaceStateChanged(iface, state, role, config);
- } catch (RemoteException e) {
- // Do nothing here.
- }
- }
-
- @VisibleForTesting(visibility = PACKAGE)
- protected void updateConfiguration(@NonNull final String iface,
- @Nullable final IpConfiguration ipConfig,
- @Nullable final NetworkCapabilities capabilities,
- @Nullable final INetworkInterfaceOutcomeReceiver listener) {
- if (DBG) {
- Log.i(TAG, "updateConfiguration, iface: " + iface + ", capabilities: " + capabilities
- + ", ipConfig: " + ipConfig);
- }
-
- final IpConfiguration localIpConfig = ipConfig == null
- ? null : new IpConfiguration(ipConfig);
- if (ipConfig != null) {
- writeIpConfiguration(iface, localIpConfig);
- }
-
- if (null != capabilities) {
- mNetworkCapabilities.put(iface, capabilities);
- }
- mHandler.post(() -> {
- mFactory.updateInterface(iface, localIpConfig, capabilities, listener);
- broadcastInterfaceStateChange(iface);
- });
- }
-
- @VisibleForTesting(visibility = PACKAGE)
- protected void connectNetwork(@NonNull final String iface,
- @Nullable final INetworkInterfaceOutcomeReceiver listener) {
- mHandler.post(() -> updateInterfaceState(iface, true, listener));
- }
-
- @VisibleForTesting(visibility = PACKAGE)
- protected void disconnectNetwork(@NonNull final String iface,
- @Nullable final INetworkInterfaceOutcomeReceiver listener) {
- mHandler.post(() -> updateInterfaceState(iface, false, listener));
- }
-
- IpConfiguration getIpConfiguration(String iface) {
- return mIpConfigurations.get(iface);
- }
-
- @VisibleForTesting(visibility = PACKAGE)
- protected boolean isTrackingInterface(String iface) {
- return mFactory.hasInterface(iface);
- }
-
- String[] getInterfaces(boolean includeRestricted) {
- return mFactory.getAvailableInterfaces(includeRestricted);
- }
-
- List<String> getInterfaceList() {
- final List<String> interfaceList = new ArrayList<String>();
- final String[] ifaces;
- try {
- ifaces = mNetd.interfaceGetList();
- } catch (RemoteException e) {
- Log.e(TAG, "Could not get list of interfaces " + e);
- return interfaceList;
- }
- final String ifaceMatch = mIfaceMatch;
- for (String iface : ifaces) {
- if (iface.matches(ifaceMatch)) interfaceList.add(iface);
- }
- return interfaceList;
- }
-
- /**
- * 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) {
- mHandler.post(() -> {
- if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) {
- // Remote process has already died
- return;
- }
- for (String iface : getInterfaces(canUseRestrictedNetworks)) {
- unicastInterfaceStateChange(listener, iface);
- }
-
- unicastEthernetStateChange(listener, mEthernetState);
- });
- }
-
- void removeListener(IEthernetServiceListener listener) {
- mHandler.post(() -> mListeners.unregister(listener));
- }
-
- public void setIncludeTestInterfaces(boolean include) {
- mHandler.post(() -> {
- mIncludeTestInterfaces = include;
- updateIfaceMatchRegexp();
- mHandler.post(() -> trackAvailableInterfaces());
- });
- }
-
- public void requestTetheredInterface(ITetheredInterfaceCallback callback) {
- mHandler.post(() -> {
- if (!mTetheredInterfaceRequests.register(callback)) {
- // Remote process has already died
- return;
- }
- if (mDefaultInterfaceMode == INTERFACE_MODE_SERVER) {
- if (mTetheredInterfaceWasAvailable) {
- notifyTetheredInterfaceAvailable(callback, mDefaultInterface);
- }
- return;
- }
-
- setDefaultInterfaceMode(INTERFACE_MODE_SERVER);
- });
- }
-
- public void releaseTetheredInterface(ITetheredInterfaceCallback callback) {
- mHandler.post(() -> {
- mTetheredInterfaceRequests.unregister(callback);
- maybeUntetherDefaultInterface();
- });
- }
-
- private void notifyTetheredInterfaceAvailable(ITetheredInterfaceCallback cb, String iface) {
- try {
- cb.onAvailable(iface);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending tethered interface available callback", e);
- }
- }
-
- private void notifyTetheredInterfaceUnavailable(ITetheredInterfaceCallback cb) {
- try {
- cb.onUnavailable();
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending tethered interface available callback", e);
- }
- }
-
- private void maybeUntetherDefaultInterface() {
- if (mTetheredInterfaceRequests.getRegisteredCallbackCount() > 0) return;
- if (mDefaultInterfaceMode == INTERFACE_MODE_CLIENT) return;
- setDefaultInterfaceMode(INTERFACE_MODE_CLIENT);
- }
-
- private void setDefaultInterfaceMode(int mode) {
- Log.d(TAG, "Setting default interface mode to " + mode);
- mDefaultInterfaceMode = mode;
- if (mDefaultInterface != null) {
- removeInterface(mDefaultInterface);
- addInterface(mDefaultInterface);
- }
- }
-
- private int getInterfaceRole(final String iface) {
- if (!mFactory.hasInterface(iface)) return EthernetManager.ROLE_NONE;
- final int mode = getInterfaceMode(iface);
- return (mode == INTERFACE_MODE_CLIENT)
- ? EthernetManager.ROLE_CLIENT
- : EthernetManager.ROLE_SERVER;
- }
-
- private int getInterfaceMode(final String iface) {
- if (iface.equals(mDefaultInterface)) {
- return mDefaultInterfaceMode;
- }
- return INTERFACE_MODE_CLIENT;
- }
-
- private void removeInterface(String iface) {
- mFactory.removeInterface(iface);
- maybeUpdateServerModeInterfaceState(iface, false);
- }
-
- private void stopTrackingInterface(String iface) {
- removeInterface(iface);
- if (iface.equals(mDefaultInterface)) {
- mDefaultInterface = null;
- }
- broadcastInterfaceStateChange(iface);
- }
-
- private void addInterface(String iface) {
- InterfaceConfigurationParcel config = null;
- // Bring up the interface so we get link status indications.
- try {
- PermissionUtils.enforceNetworkStackPermission(mContext);
- NetdUtils.setInterfaceUp(mNetd, iface);
- config = NetdUtils.getInterfaceConfigParcel(mNetd, iface);
- } catch (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 parcelable for " + iface + ". Bailing out.");
- return;
- }
-
- final String hwAddress = config.hwAddr;
-
- NetworkCapabilities nc = mNetworkCapabilities.get(iface);
- if (nc == null) {
- // Try to resolve using mac address
- nc = mNetworkCapabilities.get(hwAddress);
- if (nc == null) {
- final boolean isTestIface = iface.matches(TEST_IFACE_REGEXP);
- nc = createDefaultNetworkCapabilities(isTestIface);
- }
- }
-
- final int mode = getInterfaceMode(iface);
- if (mode == INTERFACE_MODE_CLIENT) {
- IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
- Log.d(TAG, "Tracking interface in client mode: " + iface);
- mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
- } else {
- maybeUpdateServerModeInterfaceState(iface, true);
- }
-
- // 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 (NetdUtils.hasFlag(config, "running")) {
- updateInterfaceState(iface, true);
- }
- }
-
- private void updateInterfaceState(String iface, boolean up) {
- updateInterfaceState(iface, up, null /* listener */);
- }
-
- private void updateInterfaceState(@NonNull final String iface, final boolean up,
- @Nullable final INetworkInterfaceOutcomeReceiver listener) {
- final int mode = getInterfaceMode(iface);
- final boolean factoryLinkStateUpdated = (mode == INTERFACE_MODE_CLIENT)
- && mFactory.updateInterfaceLinkState(iface, up, listener);
-
- if (factoryLinkStateUpdated) {
- broadcastInterfaceStateChange(iface);
- }
- }
-
- private void maybeUpdateServerModeInterfaceState(String iface, boolean available) {
- if (available == mTetheredInterfaceWasAvailable || !iface.equals(mDefaultInterface)) return;
-
- Log.d(TAG, (available ? "Tracking" : "No longer tracking")
- + " interface in server mode: " + iface);
-
- final int pendingCbs = mTetheredInterfaceRequests.beginBroadcast();
- for (int i = 0; i < pendingCbs; i++) {
- ITetheredInterfaceCallback item = mTetheredInterfaceRequests.getBroadcastItem(i);
- if (available) {
- notifyTetheredInterfaceAvailable(item, iface);
- } else {
- notifyTetheredInterfaceUnavailable(item);
- }
- }
- mTetheredInterfaceRequests.finishBroadcast();
- mTetheredInterfaceWasAvailable = available;
- }
-
- private void maybeTrackInterface(String iface) {
- if (!iface.matches(mIfaceMatch)) {
- return;
- }
-
- // If we don't already track this interface, and if this interface matches
- // our regex, start tracking it.
- if (mFactory.hasInterface(iface) || iface.equals(mDefaultInterface)) {
- if (DBG) Log.w(TAG, "Ignoring already-tracked interface " + iface);
- return;
- }
- if (DBG) Log.i(TAG, "maybeTrackInterface: " + iface);
-
- // TODO: avoid making an interface default if it has configured NetworkCapabilities.
- if (mDefaultInterface == null) {
- mDefaultInterface = iface;
- }
-
- if (mIpConfigForDefaultInterface != null) {
- updateIpConfiguration(iface, mIpConfigForDefaultInterface);
- mIpConfigForDefaultInterface = null;
- }
-
- addInterface(iface);
-
- broadcastInterfaceStateChange(iface);
- }
-
- private void trackAvailableInterfaces() {
- try {
- final String[] ifaces = mNetd.interfaceGetList();
- for (String iface : ifaces) {
- maybeTrackInterface(iface);
- }
- } catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "Could not get list of interfaces " + e);
- }
- }
-
- private class InterfaceObserver extends BaseNetdUnsolicitedEventListener {
-
- @Override
- public void onInterfaceLinkStateChanged(String iface, boolean up) {
- if (DBG) {
- Log.i(TAG, "interfaceLinkStateChanged, iface: " + iface + ", up: " + up);
- }
- mHandler.post(() -> updateInterfaceState(iface, up));
- }
-
- @Override
- public void onInterfaceAdded(String iface) {
- if (DBG) {
- Log.i(TAG, "onInterfaceAdded, iface: " + iface);
- }
- mHandler.post(() -> maybeTrackInterface(iface));
- }
-
- @Override
- public void onInterfaceRemoved(String iface) {
- if (DBG) {
- Log.i(TAG, "onInterfaceRemoved, iface: " + iface);
- }
- mHandler.post(() -> stopTrackingInterface(iface));
- }
- }
-
- private static class ListenerInfo {
-
- boolean canUseRestrictedNetworks = false;
-
- ListenerInfo(boolean canUseRestrictedNetworks) {
- this.canUseRestrictedNetworks = canUseRestrictedNetworks;
- }
- }
-
- /**
- * Parses an Ethernet interface configuration
- *
- * @param configString represents an Ethernet configuration in the following format: {@code
- * <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]}
- */
- private void parseEthernetConfig(String configString) {
- final EthernetTrackerConfig config = createEthernetTrackerConfig(configString);
- NetworkCapabilities nc = createNetworkCapabilities(
- !TextUtils.isEmpty(config.mCapabilities) /* clear default capabilities */,
- config.mCapabilities, config.mTransport).build();
- mNetworkCapabilities.put(config.mIface, nc);
-
- if (null != config.mIpConfig) {
- IpConfiguration ipConfig = parseStaticIpConfiguration(config.mIpConfig);
- mIpConfigurations.put(config.mIface, ipConfig);
- }
- }
-
- @VisibleForTesting
- static EthernetTrackerConfig createEthernetTrackerConfig(@NonNull final String configString) {
- Objects.requireNonNull(configString, "EthernetTrackerConfig requires non-null config");
- return new EthernetTrackerConfig(configString.split(";", /* limit of tokens */ 4));
- }
-
- private static NetworkCapabilities createDefaultNetworkCapabilities(boolean isTestIface) {
- NetworkCapabilities.Builder builder = createNetworkCapabilities(
- false /* clear default capabilities */, null, null)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
-
- if (isTestIface) {
- builder.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
- } else {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- }
-
- return builder.build();
- }
-
- /**
- * Parses a static list of network capabilities
- *
- * @param clearDefaultCapabilities Indicates whether or not to clear any default capabilities
- * @param commaSeparatedCapabilities A comma separated string list of integer encoded
- * NetworkCapability.NET_CAPABILITY_* values
- * @param overrideTransport A string representing a single integer encoded override transport
- * type. Must be one of the NetworkCapability.TRANSPORT_*
- * values. TRANSPORT_VPN is not supported. Errors with input
- * will cause the override to be ignored.
- */
- @VisibleForTesting
- static NetworkCapabilities.Builder createNetworkCapabilities(
- boolean clearDefaultCapabilities, @Nullable String commaSeparatedCapabilities,
- @Nullable String overrideTransport) {
-
- final NetworkCapabilities.Builder builder = clearDefaultCapabilities
- ? NetworkCapabilities.Builder.withoutDefaultCapabilities()
- : new NetworkCapabilities.Builder();
-
- // Determine the transport type. If someone has tried to define an override transport then
- // attempt to add it. Since we can only have one override, all errors with it will
- // gracefully default back to TRANSPORT_ETHERNET and warn the user. VPN is not allowed as an
- // override type. Wifi Aware and LoWPAN are currently unsupported as well.
- int transport = NetworkCapabilities.TRANSPORT_ETHERNET;
- if (!TextUtils.isEmpty(overrideTransport)) {
- try {
- int parsedTransport = Integer.valueOf(overrideTransport);
- if (parsedTransport == NetworkCapabilities.TRANSPORT_VPN
- || parsedTransport == NetworkCapabilities.TRANSPORT_WIFI_AWARE
- || parsedTransport == NetworkCapabilities.TRANSPORT_LOWPAN) {
- Log.e(TAG, "Override transport '" + parsedTransport + "' is not supported. "
- + "Defaulting to TRANSPORT_ETHERNET");
- } else {
- transport = parsedTransport;
- }
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Override transport type '" + overrideTransport + "' "
- + "could not be parsed. Defaulting to TRANSPORT_ETHERNET");
- }
- }
-
- // Apply the transport. If the user supplied a valid number that is not a valid transport
- // then adding will throw an exception. Default back to TRANSPORT_ETHERNET if that happens
- try {
- builder.addTransportType(transport);
- } catch (IllegalArgumentException iae) {
- Log.e(TAG, transport + " is not a valid NetworkCapability.TRANSPORT_* value. "
- + "Defaulting to TRANSPORT_ETHERNET");
- builder.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET);
- }
-
- builder.setLinkUpstreamBandwidthKbps(100 * 1000);
- builder.setLinkDownstreamBandwidthKbps(100 * 1000);
-
- if (!TextUtils.isEmpty(commaSeparatedCapabilities)) {
- for (String strNetworkCapability : commaSeparatedCapabilities.split(",")) {
- if (!TextUtils.isEmpty(strNetworkCapability)) {
- try {
- builder.addCapability(Integer.valueOf(strNetworkCapability));
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "Capability '" + strNetworkCapability + "' could not be parsed");
- } catch (IllegalArgumentException iae) {
- Log.e(TAG, strNetworkCapability + " is not a valid "
- + "NetworkCapability.NET_CAPABILITY_* value");
- }
- }
- }
- }
- // Ethernet networks have no way to update the following capabilities, so they always
- // have them.
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
-
- return builder;
- }
-
- /**
- * Parses static IP configuration.
- *
- * @param staticIpConfig represents static IP configuration in the following format: {@code
- * ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
- * domains=<comma-sep-domains>}
- */
- @VisibleForTesting
- static IpConfiguration parseStaticIpConfiguration(String staticIpConfig) {
- final StaticIpConfiguration.Builder staticIpConfigBuilder =
- new StaticIpConfiguration.Builder();
-
- for (String keyValueAsString : staticIpConfig.trim().split(" ")) {
- if (TextUtils.isEmpty(keyValueAsString)) continue;
-
- String[] pair = keyValueAsString.split("=");
- if (pair.length != 2) {
- throw new IllegalArgumentException("Unexpected token: " + keyValueAsString
- + " in " + staticIpConfig);
- }
-
- String key = pair[0];
- String value = pair[1];
-
- switch (key) {
- case "ip":
- staticIpConfigBuilder.setIpAddress(new LinkAddress(value));
- break;
- case "domains":
- staticIpConfigBuilder.setDomains(value);
- break;
- case "gateway":
- staticIpConfigBuilder.setGateway(InetAddress.parseNumericAddress(value));
- break;
- case "dns": {
- ArrayList<InetAddress> dnsAddresses = new ArrayList<>();
- for (String address: value.split(",")) {
- dnsAddresses.add(InetAddress.parseNumericAddress(address));
- }
- staticIpConfigBuilder.setDnsServers(dnsAddresses);
- break;
- }
- default : {
- throw new IllegalArgumentException("Unexpected key: " + key
- + " in " + staticIpConfig);
- }
- }
- }
- return createIpConfiguration(staticIpConfigBuilder.build());
- }
-
- private static IpConfiguration createIpConfiguration(
- @NonNull final StaticIpConfiguration staticIpConfig) {
- return new IpConfiguration.Builder().setStaticIpConfiguration(staticIpConfig).build();
- }
-
- private IpConfiguration getOrCreateIpConfiguration(String iface) {
- IpConfiguration ret = mIpConfigurations.get(iface);
- if (ret != null) return ret;
- ret = new IpConfiguration();
- ret.setIpAssignment(IpAssignment.DHCP);
- ret.setProxySettings(ProxySettings.NONE);
- return ret;
- }
-
- private void updateIfaceMatchRegexp() {
- final String match = mDeps.getInterfaceRegexFromResource(mContext);
- mIfaceMatch = mIncludeTestInterfaces
- ? "(" + match + "|" + TEST_IFACE_REGEXP + ")"
- : match;
- Log.d(TAG, "Interface match regexp set to '" + mIfaceMatch + "'");
- }
-
- /**
- * Validate if a given interface is valid for testing.
- *
- * @param iface the name of the interface to validate.
- * @return {@code true} if test interfaces are enabled and the given {@code iface} has a test
- * interface prefix, {@code false} otherwise.
- */
- public boolean isValidTestInterface(@NonNull final String iface) {
- return mIncludeTestInterfaces && iface.matches(TEST_IFACE_REGEXP);
- }
-
- private void postAndWaitForRunnable(Runnable r) {
- final ConditionVariable cv = new ConditionVariable();
- if (mHandler.post(() -> {
- r.run();
- cv.open();
- })) {
- cv.block(2000L);
- }
- }
-
- @VisibleForTesting(visibility = PACKAGE)
- protected void setEthernetEnabled(boolean enabled) {
- mHandler.post(() -> {
- int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED;
- if (mEthernetState == newState) return;
-
- mEthernetState = newState;
-
- if (enabled) {
- trackAvailableInterfaces();
- } else {
- // TODO: maybe also disable server mode interface as well.
- untrackFactoryInterfaces();
- }
- broadcastEthernetStateChange(mEthernetState);
- });
- }
-
- private void untrackFactoryInterfaces() {
- for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) {
- stopTrackingInterface(iface);
- }
- }
-
- private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener,
- int state) {
- ensureRunningOnEthernetServiceThread();
- try {
- listener.onEthernetStateChanged(state);
- } catch (RemoteException e) {
- // Do nothing here.
- }
- }
-
- private void broadcastEthernetStateChange(int state) {
- ensureRunningOnEthernetServiceThread();
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- try {
- mListeners.getBroadcastItem(i).onEthernetStateChanged(state);
- } catch (RemoteException e) {
- // Do nothing here.
- }
- }
- mListeners.finishBroadcast();
- }
-
- void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
- postAndWaitForRunnable(() -> {
- pw.println(getClass().getSimpleName());
- pw.println("Ethernet interface name filter: " + mIfaceMatch);
- pw.println("Default interface: " + mDefaultInterface);
- pw.println("Default interface mode: " + mDefaultInterfaceMode);
- pw.println("Tethered interface requests: "
- + mTetheredInterfaceRequests.getRegisteredCallbackCount());
- 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);
- });
- }
-
- @VisibleForTesting
- static class EthernetTrackerConfig {
- final String mIface;
- final String mCapabilities;
- final String mIpConfig;
- final String mTransport;
-
- EthernetTrackerConfig(@NonNull final String[] tokens) {
- Objects.requireNonNull(tokens, "EthernetTrackerConfig requires non-null tokens");
- mIface = tokens[0];
- mCapabilities = tokens.length > 1 ? tokens[1] : null;
- mIpConfig = tokens.length > 2 && !TextUtils.isEmpty(tokens[2]) ? tokens[2] : null;
- mTransport = tokens.length > 3 ? tokens[3] : null;
- }
- }
-}