diff options
Diffstat (limited to 'java/com/android/server/ethernet/EthernetTracker.java')
-rw-r--r-- | java/com/android/server/ethernet/EthernetTracker.java | 942 |
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; - } - } -} |